1 //===- InstrumentationMap.cpp - XRay Instrumentation Map ------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // Implementation of the InstrumentationMap type for XRay sleds.
11 //===----------------------------------------------------------------------===//
13 #include "llvm/XRay/InstrumentationMap.h"
14 #include "llvm/ADT/DenseMap.h"
15 #include "llvm/ADT/None.h"
16 #include "llvm/ADT/STLExtras.h"
17 #include "llvm/ADT/StringRef.h"
18 #include "llvm/ADT/Triple.h"
19 #include "llvm/ADT/Twine.h"
20 #include "llvm/Object/Binary.h"
21 #include "llvm/Object/ELFObjectFile.h"
22 #include "llvm/Object/ObjectFile.h"
23 #include "llvm/Support/DataExtractor.h"
24 #include "llvm/Support/Error.h"
25 #include "llvm/Support/FileSystem.h"
26 #include "llvm/Support/YAMLTraits.h"
30 #include <system_error>
36 Optional
<int32_t> InstrumentationMap::getFunctionId(uint64_t Addr
) const {
37 auto I
= FunctionIds
.find(Addr
);
38 if (I
!= FunctionIds
.end())
43 Optional
<uint64_t> InstrumentationMap::getFunctionAddr(int32_t FuncId
) const {
44 auto I
= FunctionAddresses
.find(FuncId
);
45 if (I
!= FunctionAddresses
.end())
50 using RelocMap
= DenseMap
<uint64_t, uint64_t>;
53 loadObj(StringRef Filename
, object::OwningBinary
<object::ObjectFile
> &ObjFile
,
54 InstrumentationMap::SledContainer
&Sleds
,
55 InstrumentationMap::FunctionAddressMap
&FunctionAddresses
,
56 InstrumentationMap::FunctionAddressReverseMap
&FunctionIds
) {
57 InstrumentationMap Map
;
59 // Find the section named "xray_instr_map".
60 if ((!ObjFile
.getBinary()->isELF() && !ObjFile
.getBinary()->isMachO()) ||
61 !(ObjFile
.getBinary()->getArch() == Triple::x86_64
||
62 ObjFile
.getBinary()->getArch() == Triple::ppc64le
))
63 return make_error
<StringError
>(
64 "File format not supported (only does ELF and Mach-O little endian 64-bit).",
65 std::make_error_code(std::errc::not_supported
));
67 StringRef Contents
= "";
68 const auto &Sections
= ObjFile
.getBinary()->sections();
69 auto I
= llvm::find_if(Sections
, [&](object::SectionRef Section
) {
70 Expected
<StringRef
> NameOrErr
= Section
.getName();
72 return *NameOrErr
== "xray_instr_map";
73 consumeError(NameOrErr
.takeError());
77 if (I
== Sections
.end())
78 return make_error
<StringError
>(
79 "Failed to find XRay instrumentation map.",
80 std::make_error_code(std::errc::executable_format_error
));
82 if (Expected
<StringRef
> E
= I
->getContents())
88 if (ObjFile
.getBinary()->isELF()) {
89 uint32_t RelativeRelocation
= [](object::ObjectFile
*ObjFile
) {
90 if (const auto *ELFObj
= dyn_cast
<object::ELF32LEObjectFile
>(ObjFile
))
91 return ELFObj
->getELFFile()->getRelativeRelocationType();
92 else if (const auto *ELFObj
= dyn_cast
<object::ELF32BEObjectFile
>(ObjFile
))
93 return ELFObj
->getELFFile()->getRelativeRelocationType();
94 else if (const auto *ELFObj
= dyn_cast
<object::ELF64LEObjectFile
>(ObjFile
))
95 return ELFObj
->getELFFile()->getRelativeRelocationType();
96 else if (const auto *ELFObj
= dyn_cast
<object::ELF64BEObjectFile
>(ObjFile
))
97 return ELFObj
->getELFFile()->getRelativeRelocationType();
99 return static_cast<uint32_t>(0);
100 }(ObjFile
.getBinary());
102 for (const object::SectionRef
&Section
: Sections
) {
103 for (const object::RelocationRef
&Reloc
: Section
.relocations()) {
104 if (Reloc
.getType() != RelativeRelocation
)
106 if (auto AddendOrErr
= object::ELFRelocationRef(Reloc
).getAddend())
107 Relocs
.insert({Reloc
.getOffset(), *AddendOrErr
});
112 // Copy the instrumentation map data into the Sleds data structure.
113 auto C
= Contents
.bytes_begin();
114 static constexpr size_t ELF64SledEntrySize
= 32;
116 if ((C
- Contents
.bytes_end()) % ELF64SledEntrySize
!= 0)
117 return make_error
<StringError
>(
118 Twine("Instrumentation map entries not evenly divisible by size of "
119 "an XRay sled entry in ELF64."),
120 std::make_error_code(std::errc::executable_format_error
));
122 auto RelocateOrElse
= [&](uint64_t Offset
, uint64_t Address
) {
124 uint64_t A
= I
->getAddress() + C
- Contents
.bytes_begin() + Offset
;
125 RelocMap::const_iterator R
= Relocs
.find(A
);
126 if (R
!= Relocs
.end())
134 for (; C
!= Contents
.bytes_end(); C
+= ELF64SledEntrySize
) {
135 DataExtractor
Extractor(
136 StringRef(reinterpret_cast<const char *>(C
), ELF64SledEntrySize
), true,
139 auto &Entry
= Sleds
.back();
140 uint64_t OffsetPtr
= 0;
141 uint64_t AddrOff
= OffsetPtr
;
142 Entry
.Address
= RelocateOrElse(AddrOff
, Extractor
.getU64(&OffsetPtr
));
143 uint64_t FuncOff
= OffsetPtr
;
144 Entry
.Function
= RelocateOrElse(FuncOff
, Extractor
.getU64(&OffsetPtr
));
145 auto Kind
= Extractor
.getU8(&OffsetPtr
);
146 static constexpr SledEntry::FunctionKinds Kinds
[] = {
147 SledEntry::FunctionKinds::ENTRY
, SledEntry::FunctionKinds::EXIT
,
148 SledEntry::FunctionKinds::TAIL
,
149 SledEntry::FunctionKinds::LOG_ARGS_ENTER
,
150 SledEntry::FunctionKinds::CUSTOM_EVENT
};
151 if (Kind
>= sizeof(Kinds
))
152 return errorCodeToError(
153 std::make_error_code(std::errc::executable_format_error
));
154 Entry
.Kind
= Kinds
[Kind
];
155 Entry
.AlwaysInstrument
= Extractor
.getU8(&OffsetPtr
) != 0;
157 // We do replicate the function id generation scheme implemented in the
159 // FIXME: Figure out how to keep this consistent with the XRay runtime.
161 CurFn
= Entry
.Function
;
162 FunctionAddresses
[FuncId
] = Entry
.Function
;
163 FunctionIds
[Entry
.Function
] = FuncId
;
165 if (Entry
.Function
!= CurFn
) {
167 CurFn
= Entry
.Function
;
168 FunctionAddresses
[FuncId
] = Entry
.Function
;
169 FunctionIds
[Entry
.Function
] = FuncId
;
172 return Error::success();
176 loadYAML(sys::fs::file_t Fd
, size_t FileSize
, StringRef Filename
,
177 InstrumentationMap::SledContainer
&Sleds
,
178 InstrumentationMap::FunctionAddressMap
&FunctionAddresses
,
179 InstrumentationMap::FunctionAddressReverseMap
&FunctionIds
) {
181 sys::fs::mapped_file_region
MappedFile(
182 Fd
, sys::fs::mapped_file_region::mapmode::readonly
, FileSize
, 0, EC
);
183 sys::fs::closeFile(Fd
);
185 return make_error
<StringError
>(
186 Twine("Failed memory-mapping file '") + Filename
+ "'.", EC
);
188 std::vector
<YAMLXRaySledEntry
> YAMLSleds
;
189 yaml::Input
In(StringRef(MappedFile
.data(), MappedFile
.size()));
192 return make_error
<StringError
>(
193 Twine("Failed loading YAML document from '") + Filename
+ "'.",
196 Sleds
.reserve(YAMLSleds
.size());
197 for (const auto &Y
: YAMLSleds
) {
198 FunctionAddresses
[Y
.FuncId
] = Y
.Function
;
199 FunctionIds
[Y
.Function
] = Y
.FuncId
;
201 SledEntry
{Y
.Address
, Y
.Function
, Y
.Kind
, Y
.AlwaysInstrument
});
203 return Error::success();
206 // FIXME: Create error types that encapsulate a bit more information than what
207 // StringError instances contain.
208 Expected
<InstrumentationMap
>
209 llvm::xray::loadInstrumentationMap(StringRef Filename
) {
210 // At this point we assume the file is an object file -- and if that doesn't
211 // work, we treat it as YAML.
212 // FIXME: Extend to support non-ELF and non-x86_64 binaries.
214 InstrumentationMap Map
;
215 auto ObjectFileOrError
= object::ObjectFile::createObjectFile(Filename
);
216 if (!ObjectFileOrError
) {
217 auto E
= ObjectFileOrError
.takeError();
218 // We try to load it as YAML if the ELF load didn't work.
219 Expected
<sys::fs::file_t
> FdOrErr
= sys::fs::openNativeFileForRead(Filename
);
221 // Report the ELF load error if YAML failed.
222 consumeError(FdOrErr
.takeError());
227 if (sys::fs::file_size(Filename
, FileSize
))
230 // If the file is empty, we return the original error.
234 // From this point on the errors will be only for the YAML parts, so we
235 // consume the errors at this point.
236 consumeError(std::move(E
));
237 if (auto E
= loadYAML(*FdOrErr
, FileSize
, Filename
, Map
.Sleds
,
238 Map
.FunctionAddresses
, Map
.FunctionIds
))
240 } else if (auto E
= loadObj(Filename
, *ObjectFileOrError
, Map
.Sleds
,
241 Map
.FunctionAddresses
, Map
.FunctionIds
)) {