LLVM  12.0.0git
InstrumentationMap.cpp
Go to the documentation of this file.
1 //===- InstrumentationMap.cpp - XRay Instrumentation Map ------------------===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Implementation of the InstrumentationMap type for XRay sleds.
10 //
11 //===----------------------------------------------------------------------===//
12 
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"
22 #include "llvm/Object/ObjectFile.h"
25 #include "llvm/Support/Error.h"
28 #include <algorithm>
29 #include <cstddef>
30 #include <cstdint>
31 #include <system_error>
32 #include <vector>
33 
34 using namespace llvm;
35 using namespace xray;
36 
38  auto I = FunctionIds.find(Addr);
39  if (I != FunctionIds.end())
40  return I->second;
41  return None;
42 }
43 
45  auto I = FunctionAddresses.find(FuncId);
46  if (I != FunctionAddresses.end())
47  return I->second;
48  return None;
49 }
50 
52 
53 static Error
56  InstrumentationMap::FunctionAddressMap &FunctionAddresses,
59 
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 "
68  "64-bit).",
69  std::make_error_code(std::errc::not_supported));
70 
71  StringRef Contents = "";
72  const auto &Sections = ObjFile.getBinary()->sections();
73  uint64_t Address = 0;
74  auto I = llvm::find_if(Sections, [&](object::SectionRef Section) {
75  Expected<StringRef> NameOrErr = Section.getName();
76  if (NameOrErr) {
77  Address = Section.getAddress();
78  return *NameOrErr == "xray_instr_map";
79  }
80  consumeError(NameOrErr.takeError());
81  return false;
82  });
83 
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));
88 
89  if (Expected<StringRef> E = I->getContents())
90  Contents = *E;
91  else
92  return E.takeError();
93 
94  RelocMap Relocs;
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();
108  else
109  return static_cast<uint32_t>(0);
110  }(ObjFile.getBinary());
111 
112  bool (*SupportsRelocation)(uint64_t);
114  std::tie(SupportsRelocation, Resolver) =
116 
117  for (const object::SectionRef &Section : Sections) {
118  for (const object::RelocationRef &Reloc : Section.relocations()) {
119  if (ObjFile.getBinary()->getArch() == Triple::arm) {
120  if (SupportsRelocation && SupportsRelocation(Reloc.getType())) {
121  Expected<uint64_t> ValueOrErr = Reloc.getSymbol()->getValue();
122  if (!ValueOrErr)
123  return ValueOrErr.takeError();
124  Relocs.insert({Reloc.getOffset(), Resolver(Reloc, *ValueOrErr, 0)});
125  }
126  } else if (SupportsRelocation && SupportsRelocation(Reloc.getType())) {
127  auto AddendOrErr = object::ELFRelocationRef(Reloc).getAddend();
128  auto A = AddendOrErr ? *AddendOrErr : 0;
129  Expected<uint64_t> ValueOrErr = Reloc.getSymbol()->getValue();
130  if (!ValueOrErr)
131  // TODO: Test this error.
132  return ValueOrErr.takeError();
133  Relocs.insert({Reloc.getOffset(), Resolver(Reloc, *ValueOrErr, A)});
134  } else if (Reloc.getType() == RelativeRelocation) {
135  if (auto AddendOrErr = object::ELFRelocationRef(Reloc).getAddend())
136  Relocs.insert({Reloc.getOffset(), *AddendOrErr});
137  }
138  }
139  }
140  }
141 
142  // Copy the instrumentation map data into the Sleds data structure.
143  auto C = Contents.bytes_begin();
144  bool Is32Bit = ObjFile.getBinary()->makeTriple().isArch32Bit();
145  size_t ELFSledEntrySize = Is32Bit ? 16 : 32;
146 
147  if ((C - Contents.bytes_end()) % ELFSledEntrySize != 0)
148  return make_error<StringError>(
149  Twine("Instrumentation map entries not evenly divisible by size of "
150  "an XRay sled entry."),
151  std::make_error_code(std::errc::executable_format_error));
152 
153  auto RelocateOrElse = [&](uint64_t Offset, uint64_t Address) {
154  if (!Address) {
155  uint64_t A = I->getAddress() + C - Contents.bytes_begin() + Offset;
156  RelocMap::const_iterator R = Relocs.find(A);
157  if (R != Relocs.end())
158  return R->second;
159  }
160  return Address;
161  };
162 
163  const int WordSize = Is32Bit ? 4 : 8;
164  int32_t FuncId = 1;
165  uint64_t CurFn = 0;
166  for (; C != Contents.bytes_end(); C += ELFSledEntrySize) {
167  DataExtractor Extractor(
168  StringRef(reinterpret_cast<const char *>(C), ELFSledEntrySize), true,
169  8);
170  Sleds.push_back({});
171  auto &Entry = Sleds.back();
172  uint64_t OffsetPtr = 0;
173  uint64_t AddrOff = OffsetPtr;
174  if (Is32Bit)
175  Entry.Address = RelocateOrElse(AddrOff, Extractor.getU32(&OffsetPtr));
176  else
177  Entry.Address = RelocateOrElse(AddrOff, Extractor.getU64(&OffsetPtr));
178  uint64_t FuncOff = OffsetPtr;
179  if (Is32Bit)
180  Entry.Function = RelocateOrElse(FuncOff, Extractor.getU32(&OffsetPtr));
181  else
182  Entry.Function = RelocateOrElse(FuncOff, Extractor.getU64(&OffsetPtr));
183  auto Kind = Extractor.getU8(&OffsetPtr);
184  static constexpr SledEntry::FunctionKinds Kinds[] = {
189  if (Kind >= sizeof(Kinds))
190  return errorCodeToError(
191  std::make_error_code(std::errc::executable_format_error));
192  Entry.Kind = Kinds[Kind];
193  Entry.AlwaysInstrument = Extractor.getU8(&OffsetPtr) != 0;
194  Entry.Version = Extractor.getU8(&OffsetPtr);
195  if (Entry.Version >= 2) {
196  Entry.Address += C - Contents.bytes_begin() + Address;
197  Entry.Function += C - Contents.bytes_begin() + WordSize + Address;
198  }
199 
200  // We do replicate the function id generation scheme implemented in the
201  // XRay runtime.
202  // FIXME: Figure out how to keep this consistent with the XRay runtime.
203  if (CurFn == 0) {
204  CurFn = Entry.Function;
205  FunctionAddresses[FuncId] = Entry.Function;
206  FunctionIds[Entry.Function] = FuncId;
207  }
208  if (Entry.Function != CurFn) {
209  ++FuncId;
210  CurFn = Entry.Function;
211  FunctionAddresses[FuncId] = Entry.Function;
212  FunctionIds[Entry.Function] = FuncId;
213  }
214  }
215  return Error::success();
216 }
217 
218 static Error
219 loadYAML(sys::fs::file_t Fd, size_t FileSize, StringRef Filename,
221  InstrumentationMap::FunctionAddressMap &FunctionAddresses,
223  std::error_code EC;
224  sys::fs::mapped_file_region MappedFile(
225  Fd, sys::fs::mapped_file_region::mapmode::readonly, FileSize, 0, EC);
226  sys::fs::closeFile(Fd);
227  if (EC)
228  return make_error<StringError>(
229  Twine("Failed memory-mapping file '") + Filename + "'.", EC);
230 
231  std::vector<YAMLXRaySledEntry> YAMLSleds;
232  yaml::Input In(StringRef(MappedFile.data(), MappedFile.size()));
233  In >> YAMLSleds;
234  if (In.error())
235  return make_error<StringError>(
236  Twine("Failed loading YAML document from '") + Filename + "'.",
237  In.error());
238 
239  Sleds.reserve(YAMLSleds.size());
240  for (const auto &Y : YAMLSleds) {
241  FunctionAddresses[Y.FuncId] = Y.Function;
242  FunctionIds[Y.Function] = Y.FuncId;
243  Sleds.push_back(SledEntry{Y.Address, Y.Function, Y.Kind, Y.AlwaysInstrument,
244  Y.Version});
245  }
246  return Error::success();
247 }
248 
249 // FIXME: Create error types that encapsulate a bit more information than what
250 // StringError instances contain.
253  // At this point we assume the file is an object file -- and if that doesn't
254  // work, we treat it as YAML.
255  // FIXME: Extend to support non-ELF and non-x86_64 binaries.
256 
257  InstrumentationMap Map;
258  auto ObjectFileOrError = object::ObjectFile::createObjectFile(Filename);
259  if (!ObjectFileOrError) {
260  auto E = ObjectFileOrError.takeError();
261  // We try to load it as YAML if the ELF load didn't work.
262  Expected<sys::fs::file_t> FdOrErr =
264  if (!FdOrErr) {
265  // Report the ELF load error if YAML failed.
266  consumeError(FdOrErr.takeError());
267  return std::move(E);
268  }
269 
270  uint64_t FileSize;
271  if (sys::fs::file_size(Filename, FileSize))
272  return std::move(E);
273 
274  // If the file is empty, we return the original error.
275  if (FileSize == 0)
276  return std::move(E);
277 
278  // From this point on the errors will be only for the YAML parts, so we
279  // consume the errors at this point.
280  consumeError(std::move(E));
281  if (auto E = loadYAML(*FdOrErr, FileSize, Filename, Map.Sleds,
282  Map.FunctionAddresses, Map.FunctionIds))
283  return std::move(E);
284  } else if (auto E = loadObj(Filename, *ObjectFileOrError, Map.Sleds,
285  Map.FunctionAddresses, Map.FunctionIds)) {
286  return std::move(E);
287  }
288  return Map;
289 }
const NoneType None
Definition: None.h:23
uint64_t CallInst * C
uint8_t getU8(uint64_t *offset_ptr, Error *Err=nullptr) const
Extract a uint8_t value from *offset_ptr.
static Expected< OwningBinary< ObjectFile > > createObjectFile(StringRef ObjectPath)
Definition: ObjectFile.cpp:185
Profile::FuncID FuncId
Definition: Profile.cpp:321
Optional< uint64_t > getFunctionAddr(int32_t FuncId) const
Returns the function address for a function id.
This class represents lattice values for constants.
Definition: AllocatorList.h:23
FunctionKinds
Each entry here represents the kinds of supported instrumentation map entries.
std::pair< bool(*)(uint64_t), RelocationResolver > getRelocationResolver(const ObjectFile &Obj)
Triple makeTriple() const
Create a triple from the data in this object file.
Definition: ObjectFile.cpp:106
uint32_t getU32(uint64_t *offset_ptr, Error *Err=nullptr) const
Extract a uint32_t value from *offset_ptr.
This class is the base class for all object file types.
Definition: ObjectFile.h:225
This class represents a memory mapped file.
Definition: FileSystem.h:1160
std::unordered_map< uint64_t, int32_t > FunctionAddressReverseMap
Error takeError()
Take ownership of the stored error.
Definition: Error.h:557
std::pair< iterator, bool > insert(const std::pair< KeyT, ValueT > &KV)
Definition: DenseMap.h:207
static GCMetadataPrinterRegistry::Add< OcamlGCMetadataPrinter > Y("ocaml", "ocaml 3.10-compatible collector")
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition: Twine.h:80
uint64_t getAddress() const
Definition: ObjectFile.h:445
std::vector< SledEntry > SledContainer
This is a value type class that represents a single relocation in the list of relocations in the obje...
Definition: ObjectFile.h:51
std::error_code make_error_code(BitcodeError E)
std::error_code file_size(const Twine &Path, uint64_t &Result)
Get file size.
Definition: FileSystem.h:696
Tagged union holding either a T or a Error.
Definition: APFloat.h:42
Expected< InstrumentationMap > loadInstrumentationMap(StringRef Filename)
Loads the instrumentation map from |Filename|.
static Error loadYAML(sys::fs::file_t Fd, size_t FileSize, StringRef Filename, InstrumentationMap::SledContainer &Sleds, InstrumentationMap::FunctionAddressMap &FunctionAddresses, InstrumentationMap::FunctionAddressReverseMap &FunctionIds)
static Error loadObj(StringRef Filename, object::OwningBinary< object::ObjectFile > &ObjFile, InstrumentationMap::SledContainer &Sleds, InstrumentationMap::FunctionAddressMap &FunctionAddresses, InstrumentationMap::FunctionAddressReverseMap &FunctionIds)
bool isArch32Bit() const
Test whether the architecture is 32-bit.
Definition: Triple.cpp:1307
Represents an XRay instrumentation sled entry from an object file.
section_iterator_range sections() const
Definition: ObjectFile.h:319
iterator find(const_arg_type_t< KeyT > Val)
Definition: DenseMap.h:150
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
Error errorCodeToError(std::error_code EC)
Helper for converting an std::error_code to a Error.
Definition: Error.cpp:87
Interface for looking up the initializer for a variable name, used by Init::resolveReferences.
Definition: Record.h:1870
Expected< int64_t > getAddend() const
bool isELF() const
Definition: Binary.h:118
void consumeError(Error Err)
Consume a Error without doing anything.
Definition: Error.h:1006
static ErrorSuccess success()
Create a success value.
Definition: Error.h:332
virtual Triple::ArchType getArch() const =0
Expected< file_t > openNativeFileForRead(const Twine &Name, OpenFlags Flags=OF_None, SmallVectorImpl< char > *RealPath=nullptr)
Opens the file with the given name in a read-only mode, returning its open file descriptor.
std::error_code closeFile(file_t &F)
Close the file object.
Expected< StringRef > getName() const
Definition: ObjectFile.h:441
Optional< int32_t > getFunctionId(uint64_t Addr) const
Returns an XRay computed function id, provided a function address.
iterator_range< relocation_iterator > relocations() const
Definition: ObjectFile.h:132
#define I(x, y, z)
Definition: MD5.cpp:59
iterator end()
Definition: DenseMap.h:83
std::unordered_map< int32_t, uint64_t > FunctionAddressMap
uint64_t(*)(RelocationRef R, uint64_t S, uint64_t A) RelocationResolver
The InstrumentationMap represents the computed function id&#39;s and indicated function addresses from an...
Lightweight error class with error context and mandatory checking.
Definition: Error.h:157
uint64_t getU64(uint64_t *offset_ptr, Error *Err=nullptr) const
Extract a uint64_t value from *offset_ptr.
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:57
bool isMachO() const
Definition: Binary.h:122
auto find_if(R &&Range, UnaryPredicate P)
Provide wrappers to std::find_if which take ranges instead of having to pass begin/end explicitly...
Definition: STLExtras.h:1518
This is a value type class that represents a single section in the list of sections in the object fil...
Definition: ObjectFile.h:80