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/STLExtras.h"
16 #include "llvm/ADT/StringRef.h"
17 #include "llvm/ADT/Twine.h"
18 #include "llvm/Object/Binary.h"
19 #include "llvm/Object/ELFObjectFile.h"
20 #include "llvm/Object/ObjectFile.h"
21 #include "llvm/Object/RelocationResolver.h"
22 #include "llvm/Support/DataExtractor.h"
23 #include "llvm/Support/Error.h"
24 #include "llvm/Support/FileSystem.h"
25 #include "llvm/Support/YAMLTraits.h"
26 #include "llvm/TargetParser/Triple.h"
30 #include <system_error>
36 std::optional
<int32_t> InstrumentationMap::getFunctionId(uint64_t Addr
) const {
37 auto I
= FunctionIds
.find(Addr
);
38 if (I
!= FunctionIds
.end())
43 std::optional
<uint64_t>
44 InstrumentationMap::getFunctionAddr(int32_t FuncId
) const {
45 auto I
= FunctionAddresses
.find(FuncId
);
46 if (I
!= FunctionAddresses
.end())
51 using RelocMap
= DenseMap
<uint64_t, uint64_t>;
54 loadObj(StringRef Filename
, object::OwningBinary
<object::ObjectFile
> &ObjFile
,
55 InstrumentationMap::SledContainer
&Sleds
,
56 InstrumentationMap::FunctionAddressMap
&FunctionAddresses
,
57 InstrumentationMap::FunctionAddressReverseMap
&FunctionIds
) {
58 InstrumentationMap Map
;
60 // Find the section named "xray_instr_map".
61 if ((!ObjFile
.getBinary()->isELF() && !ObjFile
.getBinary()->isMachO()) ||
62 !(ObjFile
.getBinary()->getArch() == Triple::x86_64
||
63 ObjFile
.getBinary()->getArch() == Triple::ppc64le
||
64 ObjFile
.getBinary()->getArch() == Triple::arm
||
65 ObjFile
.getBinary()->getArch() == Triple::aarch64
))
66 return make_error
<StringError
>(
67 "File format not supported (only does ELF and Mach-O little endian "
69 std::make_error_code(std::errc::not_supported
));
71 StringRef Contents
= "";
72 const auto &Sections
= ObjFile
.getBinary()->sections();
74 auto I
= llvm::find_if(Sections
, [&](object::SectionRef Section
) {
75 Expected
<StringRef
> NameOrErr
= Section
.getName();
77 Address
= Section
.getAddress();
78 return *NameOrErr
== "xray_instr_map";
80 consumeError(NameOrErr
.takeError());
84 if (I
== Sections
.end())
85 return make_error
<StringError
>(
86 "Failed to find XRay instrumentation map.",
87 std::make_error_code(std::errc::executable_format_error
));
89 if (Error E
= I
->getContents().moveInto(Contents
))
93 if (ObjFile
.getBinary()->isELF()) {
94 uint32_t RelativeRelocation
= [](object::ObjectFile
*ObjFile
) {
95 if (const auto *ELFObj
= dyn_cast
<object::ELF32LEObjectFile
>(ObjFile
))
96 return ELFObj
->getELFFile().getRelativeRelocationType();
97 else if (const auto *ELFObj
=
98 dyn_cast
<object::ELF32BEObjectFile
>(ObjFile
))
99 return ELFObj
->getELFFile().getRelativeRelocationType();
100 else if (const auto *ELFObj
=
101 dyn_cast
<object::ELF64LEObjectFile
>(ObjFile
))
102 return ELFObj
->getELFFile().getRelativeRelocationType();
103 else if (const auto *ELFObj
=
104 dyn_cast
<object::ELF64BEObjectFile
>(ObjFile
))
105 return ELFObj
->getELFFile().getRelativeRelocationType();
107 return static_cast<uint32_t>(0);
108 }(ObjFile
.getBinary());
110 object::SupportsRelocation Supports
;
111 object::RelocationResolver Resolver
;
112 std::tie(Supports
, Resolver
) =
113 object::getRelocationResolver(*ObjFile
.getBinary());
115 for (const object::SectionRef
&Section
: Sections
) {
116 for (const object::RelocationRef
&Reloc
: Section
.relocations()) {
117 if (ObjFile
.getBinary()->getArch() == Triple::arm
) {
118 if (Supports
&& Supports(Reloc
.getType())) {
119 Expected
<uint64_t> ValueOrErr
= Reloc
.getSymbol()->getValue();
121 return ValueOrErr
.takeError();
124 object::resolveRelocation(Resolver
, Reloc
, *ValueOrErr
, 0)});
126 } else if (Supports
&& Supports(Reloc
.getType())) {
127 auto AddendOrErr
= object::ELFRelocationRef(Reloc
).getAddend();
128 auto A
= AddendOrErr
? *AddendOrErr
: 0;
129 Expected
<uint64_t> ValueOrErr
= Reloc
.getSymbol()->getValue();
131 // TODO: Test this error.
132 return ValueOrErr
.takeError();
135 object::resolveRelocation(Resolver
, Reloc
, *ValueOrErr
, A
)});
136 } else if (Reloc
.getType() == RelativeRelocation
) {
137 if (auto AddendOrErr
= object::ELFRelocationRef(Reloc
).getAddend())
138 Relocs
.insert({Reloc
.getOffset(), *AddendOrErr
});
144 // Copy the instrumentation map data into the Sleds data structure.
145 auto C
= Contents
.bytes_begin();
146 bool Is32Bit
= ObjFile
.getBinary()->makeTriple().isArch32Bit();
147 size_t ELFSledEntrySize
= Is32Bit
? 16 : 32;
149 if ((C
- Contents
.bytes_end()) % ELFSledEntrySize
!= 0)
150 return make_error
<StringError
>(
151 Twine("Instrumentation map entries not evenly divisible by size of "
152 "an XRay sled entry."),
153 std::make_error_code(std::errc::executable_format_error
));
155 auto RelocateOrElse
= [&](uint64_t Offset
, uint64_t Address
) {
157 uint64_t A
= I
->getAddress() + C
- Contents
.bytes_begin() + Offset
;
158 RelocMap::const_iterator R
= Relocs
.find(A
);
159 if (R
!= Relocs
.end())
165 const int WordSize
= Is32Bit
? 4 : 8;
168 for (; C
!= Contents
.bytes_end(); C
+= ELFSledEntrySize
) {
169 DataExtractor
Extractor(
170 StringRef(reinterpret_cast<const char *>(C
), ELFSledEntrySize
), true,
173 auto &Entry
= Sleds
.back();
174 uint64_t OffsetPtr
= 0;
175 uint64_t AddrOff
= OffsetPtr
;
177 Entry
.Address
= RelocateOrElse(AddrOff
, Extractor
.getU32(&OffsetPtr
));
179 Entry
.Address
= RelocateOrElse(AddrOff
, Extractor
.getU64(&OffsetPtr
));
180 uint64_t FuncOff
= OffsetPtr
;
182 Entry
.Function
= RelocateOrElse(FuncOff
, Extractor
.getU32(&OffsetPtr
));
184 Entry
.Function
= RelocateOrElse(FuncOff
, Extractor
.getU64(&OffsetPtr
));
185 auto Kind
= Extractor
.getU8(&OffsetPtr
);
186 static constexpr SledEntry::FunctionKinds Kinds
[] = {
187 SledEntry::FunctionKinds::ENTRY
, SledEntry::FunctionKinds::EXIT
,
188 SledEntry::FunctionKinds::TAIL
,
189 SledEntry::FunctionKinds::LOG_ARGS_ENTER
,
190 SledEntry::FunctionKinds::CUSTOM_EVENT
};
191 if (Kind
>= std::size(Kinds
))
192 return errorCodeToError(
193 std::make_error_code(std::errc::executable_format_error
));
194 Entry
.Kind
= Kinds
[Kind
];
195 Entry
.AlwaysInstrument
= Extractor
.getU8(&OffsetPtr
) != 0;
196 Entry
.Version
= Extractor
.getU8(&OffsetPtr
);
197 if (Entry
.Version
>= 2) {
198 Entry
.Address
+= C
- Contents
.bytes_begin() + Address
;
199 Entry
.Function
+= C
- Contents
.bytes_begin() + WordSize
+ Address
;
202 // We do replicate the function id generation scheme implemented in the
204 // FIXME: Figure out how to keep this consistent with the XRay runtime.
206 CurFn
= Entry
.Function
;
207 FunctionAddresses
[FuncId
] = Entry
.Function
;
208 FunctionIds
[Entry
.Function
] = FuncId
;
210 if (Entry
.Function
!= CurFn
) {
212 CurFn
= Entry
.Function
;
213 FunctionAddresses
[FuncId
] = Entry
.Function
;
214 FunctionIds
[Entry
.Function
] = FuncId
;
217 return Error::success();
221 loadYAML(sys::fs::file_t Fd
, size_t FileSize
, StringRef Filename
,
222 InstrumentationMap::SledContainer
&Sleds
,
223 InstrumentationMap::FunctionAddressMap
&FunctionAddresses
,
224 InstrumentationMap::FunctionAddressReverseMap
&FunctionIds
) {
226 sys::fs::mapped_file_region
MappedFile(
227 Fd
, sys::fs::mapped_file_region::mapmode::readonly
, FileSize
, 0, EC
);
228 sys::fs::closeFile(Fd
);
230 return make_error
<StringError
>(
231 Twine("Failed memory-mapping file '") + Filename
+ "'.", EC
);
233 std::vector
<YAMLXRaySledEntry
> YAMLSleds
;
234 yaml::Input
In(StringRef(MappedFile
.data(), MappedFile
.size()));
237 return make_error
<StringError
>(
238 Twine("Failed loading YAML document from '") + Filename
+ "'.",
241 Sleds
.reserve(YAMLSleds
.size());
242 for (const auto &Y
: YAMLSleds
) {
243 FunctionAddresses
[Y
.FuncId
] = Y
.Function
;
244 FunctionIds
[Y
.Function
] = Y
.FuncId
;
245 Sleds
.push_back(SledEntry
{Y
.Address
, Y
.Function
, Y
.Kind
, Y
.AlwaysInstrument
,
248 return Error::success();
251 // FIXME: Create error types that encapsulate a bit more information than what
252 // StringError instances contain.
253 Expected
<InstrumentationMap
>
254 llvm::xray::loadInstrumentationMap(StringRef Filename
) {
255 // At this point we assume the file is an object file -- and if that doesn't
256 // work, we treat it as YAML.
257 // FIXME: Extend to support non-ELF and non-x86_64 binaries.
259 InstrumentationMap Map
;
260 auto ObjectFileOrError
= object::ObjectFile::createObjectFile(Filename
);
261 if (!ObjectFileOrError
) {
262 auto E
= ObjectFileOrError
.takeError();
263 // We try to load it as YAML if the ELF load didn't work.
264 Expected
<sys::fs::file_t
> FdOrErr
=
265 sys::fs::openNativeFileForRead(Filename
);
267 // Report the ELF load error if YAML failed.
268 consumeError(FdOrErr
.takeError());
273 if (sys::fs::file_size(Filename
, FileSize
))
276 // If the file is empty, we return the original error.
280 // From this point on the errors will be only for the YAML parts, so we
281 // consume the errors at this point.
282 consumeError(std::move(E
));
283 if (auto E
= loadYAML(*FdOrErr
, FileSize
, Filename
, Map
.Sleds
,
284 Map
.FunctionAddresses
, Map
.FunctionIds
))
286 } else if (auto E
= loadObj(Filename
, *ObjectFileOrError
, Map
.Sleds
,
287 Map
.FunctionAddresses
, Map
.FunctionIds
)) {