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/Object/RelocationResolver.h"
24 #include "llvm/Support/DataExtractor.h"
25 #include "llvm/Support/Error.h"
26 #include "llvm/Support/FileSystem.h"
27 #include "llvm/Support/YAMLTraits.h"
31 #include <system_error>
37 Optional
<int32_t> InstrumentationMap::getFunctionId(uint64_t Addr
) const {
38 auto I
= FunctionIds
.find(Addr
);
39 if (I
!= FunctionIds
.end())
44 Optional
<uint64_t> 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 (Expected
<StringRef
> E
= I
->getContents())
95 if (ObjFile
.getBinary()->isELF()) {
96 uint32_t RelativeRelocation
= [](object::ObjectFile
*ObjFile
) {
97 if (const auto *ELFObj
= dyn_cast
<object::ELF32LEObjectFile
>(ObjFile
))
98 return ELFObj
->getELFFile().getRelativeRelocationType();
99 else if (const auto *ELFObj
=
100 dyn_cast
<object::ELF32BEObjectFile
>(ObjFile
))
101 return ELFObj
->getELFFile().getRelativeRelocationType();
102 else if (const auto *ELFObj
=
103 dyn_cast
<object::ELF64LEObjectFile
>(ObjFile
))
104 return ELFObj
->getELFFile().getRelativeRelocationType();
105 else if (const auto *ELFObj
=
106 dyn_cast
<object::ELF64BEObjectFile
>(ObjFile
))
107 return ELFObj
->getELFFile().getRelativeRelocationType();
109 return static_cast<uint32_t>(0);
110 }(ObjFile
.getBinary());
112 object::SupportsRelocation Supports
;
113 object::RelocationResolver Resolver
;
114 std::tie(Supports
, Resolver
) =
115 object::getRelocationResolver(*ObjFile
.getBinary());
117 for (const object::SectionRef
&Section
: Sections
) {
118 for (const object::RelocationRef
&Reloc
: Section
.relocations()) {
119 if (ObjFile
.getBinary()->getArch() == Triple::arm
) {
120 if (Supports
&& Supports(Reloc
.getType())) {
121 Expected
<uint64_t> ValueOrErr
= Reloc
.getSymbol()->getValue();
123 return ValueOrErr
.takeError();
126 object::resolveRelocation(Resolver
, Reloc
, *ValueOrErr
, 0)});
128 } else if (Supports
&& Supports(Reloc
.getType())) {
129 auto AddendOrErr
= object::ELFRelocationRef(Reloc
).getAddend();
130 auto A
= AddendOrErr
? *AddendOrErr
: 0;
131 Expected
<uint64_t> ValueOrErr
= Reloc
.getSymbol()->getValue();
133 // TODO: Test this error.
134 return ValueOrErr
.takeError();
137 object::resolveRelocation(Resolver
, Reloc
, *ValueOrErr
, A
)});
138 } else if (Reloc
.getType() == RelativeRelocation
) {
139 if (auto AddendOrErr
= object::ELFRelocationRef(Reloc
).getAddend())
140 Relocs
.insert({Reloc
.getOffset(), *AddendOrErr
});
146 // Copy the instrumentation map data into the Sleds data structure.
147 auto C
= Contents
.bytes_begin();
148 bool Is32Bit
= ObjFile
.getBinary()->makeTriple().isArch32Bit();
149 size_t ELFSledEntrySize
= Is32Bit
? 16 : 32;
151 if ((C
- Contents
.bytes_end()) % ELFSledEntrySize
!= 0)
152 return make_error
<StringError
>(
153 Twine("Instrumentation map entries not evenly divisible by size of "
154 "an XRay sled entry."),
155 std::make_error_code(std::errc::executable_format_error
));
157 auto RelocateOrElse
= [&](uint64_t Offset
, uint64_t Address
) {
159 uint64_t A
= I
->getAddress() + C
- Contents
.bytes_begin() + Offset
;
160 RelocMap::const_iterator R
= Relocs
.find(A
);
161 if (R
!= Relocs
.end())
167 const int WordSize
= Is32Bit
? 4 : 8;
170 for (; C
!= Contents
.bytes_end(); C
+= ELFSledEntrySize
) {
171 DataExtractor
Extractor(
172 StringRef(reinterpret_cast<const char *>(C
), ELFSledEntrySize
), true,
175 auto &Entry
= Sleds
.back();
176 uint64_t OffsetPtr
= 0;
177 uint64_t AddrOff
= OffsetPtr
;
179 Entry
.Address
= RelocateOrElse(AddrOff
, Extractor
.getU32(&OffsetPtr
));
181 Entry
.Address
= RelocateOrElse(AddrOff
, Extractor
.getU64(&OffsetPtr
));
182 uint64_t FuncOff
= OffsetPtr
;
184 Entry
.Function
= RelocateOrElse(FuncOff
, Extractor
.getU32(&OffsetPtr
));
186 Entry
.Function
= RelocateOrElse(FuncOff
, Extractor
.getU64(&OffsetPtr
));
187 auto Kind
= Extractor
.getU8(&OffsetPtr
);
188 static constexpr SledEntry::FunctionKinds Kinds
[] = {
189 SledEntry::FunctionKinds::ENTRY
, SledEntry::FunctionKinds::EXIT
,
190 SledEntry::FunctionKinds::TAIL
,
191 SledEntry::FunctionKinds::LOG_ARGS_ENTER
,
192 SledEntry::FunctionKinds::CUSTOM_EVENT
};
193 if (Kind
>= sizeof(Kinds
))
194 return errorCodeToError(
195 std::make_error_code(std::errc::executable_format_error
));
196 Entry
.Kind
= Kinds
[Kind
];
197 Entry
.AlwaysInstrument
= Extractor
.getU8(&OffsetPtr
) != 0;
198 Entry
.Version
= Extractor
.getU8(&OffsetPtr
);
199 if (Entry
.Version
>= 2) {
200 Entry
.Address
+= C
- Contents
.bytes_begin() + Address
;
201 Entry
.Function
+= C
- Contents
.bytes_begin() + WordSize
+ Address
;
204 // We do replicate the function id generation scheme implemented in the
206 // FIXME: Figure out how to keep this consistent with the XRay runtime.
208 CurFn
= Entry
.Function
;
209 FunctionAddresses
[FuncId
] = Entry
.Function
;
210 FunctionIds
[Entry
.Function
] = FuncId
;
212 if (Entry
.Function
!= CurFn
) {
214 CurFn
= Entry
.Function
;
215 FunctionAddresses
[FuncId
] = Entry
.Function
;
216 FunctionIds
[Entry
.Function
] = FuncId
;
219 return Error::success();
223 loadYAML(sys::fs::file_t Fd
, size_t FileSize
, StringRef Filename
,
224 InstrumentationMap::SledContainer
&Sleds
,
225 InstrumentationMap::FunctionAddressMap
&FunctionAddresses
,
226 InstrumentationMap::FunctionAddressReverseMap
&FunctionIds
) {
228 sys::fs::mapped_file_region
MappedFile(
229 Fd
, sys::fs::mapped_file_region::mapmode::readonly
, FileSize
, 0, EC
);
230 sys::fs::closeFile(Fd
);
232 return make_error
<StringError
>(
233 Twine("Failed memory-mapping file '") + Filename
+ "'.", EC
);
235 std::vector
<YAMLXRaySledEntry
> YAMLSleds
;
236 yaml::Input
In(StringRef(MappedFile
.data(), MappedFile
.size()));
239 return make_error
<StringError
>(
240 Twine("Failed loading YAML document from '") + Filename
+ "'.",
243 Sleds
.reserve(YAMLSleds
.size());
244 for (const auto &Y
: YAMLSleds
) {
245 FunctionAddresses
[Y
.FuncId
] = Y
.Function
;
246 FunctionIds
[Y
.Function
] = Y
.FuncId
;
247 Sleds
.push_back(SledEntry
{Y
.Address
, Y
.Function
, Y
.Kind
, Y
.AlwaysInstrument
,
250 return Error::success();
253 // FIXME: Create error types that encapsulate a bit more information than what
254 // StringError instances contain.
255 Expected
<InstrumentationMap
>
256 llvm::xray::loadInstrumentationMap(StringRef Filename
) {
257 // At this point we assume the file is an object file -- and if that doesn't
258 // work, we treat it as YAML.
259 // FIXME: Extend to support non-ELF and non-x86_64 binaries.
261 InstrumentationMap Map
;
262 auto ObjectFileOrError
= object::ObjectFile::createObjectFile(Filename
);
263 if (!ObjectFileOrError
) {
264 auto E
= ObjectFileOrError
.takeError();
265 // We try to load it as YAML if the ELF load didn't work.
266 Expected
<sys::fs::file_t
> FdOrErr
=
267 sys::fs::openNativeFileForRead(Filename
);
269 // Report the ELF load error if YAML failed.
270 consumeError(FdOrErr
.takeError());
275 if (sys::fs::file_size(Filename
, FileSize
))
278 // If the file is empty, we return the original error.
282 // From this point on the errors will be only for the YAML parts, so we
283 // consume the errors at this point.
284 consumeError(std::move(E
));
285 if (auto E
= loadYAML(*FdOrErr
, FileSize
, Filename
, Map
.Sleds
,
286 Map
.FunctionAddresses
, Map
.FunctionIds
))
288 } else if (auto E
= loadObj(Filename
, *ObjectFileOrError
, Map
.Sleds
,
289 Map
.FunctionAddresses
, Map
.FunctionIds
)) {