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
) {
71 if (Section
.getName(Name
))
73 return Name
== "xray_instr_map";
76 if (I
== Sections
.end())
77 return make_error
<StringError
>(
78 "Failed to find XRay instrumentation map.",
79 std::make_error_code(std::errc::executable_format_error
));
81 if (Expected
<StringRef
> E
= I
->getContents())
87 if (ObjFile
.getBinary()->isELF()) {
88 uint32_t RelativeRelocation
= [](object::ObjectFile
*ObjFile
) {
89 if (const auto *ELFObj
= dyn_cast
<object::ELF32LEObjectFile
>(ObjFile
))
90 return ELFObj
->getELFFile()->getRelativeRelocationType();
91 else if (const auto *ELFObj
= dyn_cast
<object::ELF32BEObjectFile
>(ObjFile
))
92 return ELFObj
->getELFFile()->getRelativeRelocationType();
93 else if (const auto *ELFObj
= dyn_cast
<object::ELF64LEObjectFile
>(ObjFile
))
94 return ELFObj
->getELFFile()->getRelativeRelocationType();
95 else if (const auto *ELFObj
= dyn_cast
<object::ELF64BEObjectFile
>(ObjFile
))
96 return ELFObj
->getELFFile()->getRelativeRelocationType();
98 return static_cast<uint32_t>(0);
99 }(ObjFile
.getBinary());
101 for (const object::SectionRef
&Section
: Sections
) {
102 for (const object::RelocationRef
&Reloc
: Section
.relocations()) {
103 if (Reloc
.getType() != RelativeRelocation
)
105 if (auto AddendOrErr
= object::ELFRelocationRef(Reloc
).getAddend())
106 Relocs
.insert({Reloc
.getOffset(), *AddendOrErr
});
111 // Copy the instrumentation map data into the Sleds data structure.
112 auto C
= Contents
.bytes_begin();
113 static constexpr size_t ELF64SledEntrySize
= 32;
115 if ((C
- Contents
.bytes_end()) % ELF64SledEntrySize
!= 0)
116 return make_error
<StringError
>(
117 Twine("Instrumentation map entries not evenly divisible by size of "
118 "an XRay sled entry in ELF64."),
119 std::make_error_code(std::errc::executable_format_error
));
121 auto RelocateOrElse
= [&](uint64_t Offset
, uint64_t Address
) {
123 uint64_t A
= I
->getAddress() + C
- Contents
.bytes_begin() + Offset
;
124 RelocMap::const_iterator R
= Relocs
.find(A
);
125 if (R
!= Relocs
.end())
133 for (; C
!= Contents
.bytes_end(); C
+= ELF64SledEntrySize
) {
134 DataExtractor
Extractor(
135 StringRef(reinterpret_cast<const char *>(C
), ELF64SledEntrySize
), true,
138 auto &Entry
= Sleds
.back();
139 uint64_t OffsetPtr
= 0;
140 uint64_t AddrOff
= OffsetPtr
;
141 Entry
.Address
= RelocateOrElse(AddrOff
, Extractor
.getU64(&OffsetPtr
));
142 uint64_t FuncOff
= OffsetPtr
;
143 Entry
.Function
= RelocateOrElse(FuncOff
, Extractor
.getU64(&OffsetPtr
));
144 auto Kind
= Extractor
.getU8(&OffsetPtr
);
145 static constexpr SledEntry::FunctionKinds Kinds
[] = {
146 SledEntry::FunctionKinds::ENTRY
, SledEntry::FunctionKinds::EXIT
,
147 SledEntry::FunctionKinds::TAIL
,
148 SledEntry::FunctionKinds::LOG_ARGS_ENTER
,
149 SledEntry::FunctionKinds::CUSTOM_EVENT
};
150 if (Kind
>= sizeof(Kinds
))
151 return errorCodeToError(
152 std::make_error_code(std::errc::executable_format_error
));
153 Entry
.Kind
= Kinds
[Kind
];
154 Entry
.AlwaysInstrument
= Extractor
.getU8(&OffsetPtr
) != 0;
156 // We do replicate the function id generation scheme implemented in the
158 // FIXME: Figure out how to keep this consistent with the XRay runtime.
160 CurFn
= Entry
.Function
;
161 FunctionAddresses
[FuncId
] = Entry
.Function
;
162 FunctionIds
[Entry
.Function
] = FuncId
;
164 if (Entry
.Function
!= CurFn
) {
166 CurFn
= Entry
.Function
;
167 FunctionAddresses
[FuncId
] = Entry
.Function
;
168 FunctionIds
[Entry
.Function
] = FuncId
;
171 return Error::success();
175 loadYAML(sys::fs::file_t Fd
, size_t FileSize
, StringRef Filename
,
176 InstrumentationMap::SledContainer
&Sleds
,
177 InstrumentationMap::FunctionAddressMap
&FunctionAddresses
,
178 InstrumentationMap::FunctionAddressReverseMap
&FunctionIds
) {
180 sys::fs::mapped_file_region
MappedFile(
181 Fd
, sys::fs::mapped_file_region::mapmode::readonly
, FileSize
, 0, EC
);
182 sys::fs::closeFile(Fd
);
184 return make_error
<StringError
>(
185 Twine("Failed memory-mapping file '") + Filename
+ "'.", EC
);
187 std::vector
<YAMLXRaySledEntry
> YAMLSleds
;
188 yaml::Input
In(StringRef(MappedFile
.data(), MappedFile
.size()));
191 return make_error
<StringError
>(
192 Twine("Failed loading YAML document from '") + Filename
+ "'.",
195 Sleds
.reserve(YAMLSleds
.size());
196 for (const auto &Y
: YAMLSleds
) {
197 FunctionAddresses
[Y
.FuncId
] = Y
.Function
;
198 FunctionIds
[Y
.Function
] = Y
.FuncId
;
200 SledEntry
{Y
.Address
, Y
.Function
, Y
.Kind
, Y
.AlwaysInstrument
});
202 return Error::success();
205 // FIXME: Create error types that encapsulate a bit more information than what
206 // StringError instances contain.
207 Expected
<InstrumentationMap
>
208 llvm::xray::loadInstrumentationMap(StringRef Filename
) {
209 // At this point we assume the file is an object file -- and if that doesn't
210 // work, we treat it as YAML.
211 // FIXME: Extend to support non-ELF and non-x86_64 binaries.
213 InstrumentationMap Map
;
214 auto ObjectFileOrError
= object::ObjectFile::createObjectFile(Filename
);
215 if (!ObjectFileOrError
) {
216 auto E
= ObjectFileOrError
.takeError();
217 // We try to load it as YAML if the ELF load didn't work.
218 Expected
<sys::fs::file_t
> FdOrErr
= sys::fs::openNativeFileForRead(Filename
);
220 // Report the ELF load error if YAML failed.
221 consumeError(FdOrErr
.takeError());
226 if (sys::fs::file_size(Filename
, FileSize
))
229 // If the file is empty, we return the original error.
233 // From this point on the errors will be only for the YAML parts, so we
234 // consume the errors at this point.
235 consumeError(std::move(E
));
236 if (auto E
= loadYAML(*FdOrErr
, FileSize
, Filename
, Map
.Sleds
,
237 Map
.FunctionAddresses
, Map
.FunctionIds
))
239 } else if (auto E
= loadObj(Filename
, *ObjectFileOrError
, Map
.Sleds
,
240 Map
.FunctionAddresses
, Map
.FunctionIds
)) {