xref: /openbsd-src/gnu/llvm/llvm/lib/XRay/InstrumentationMap.cpp (revision d415bd752c734aee168c4ee86ff32e8cc249eb16)
109467b48Spatrick //===- InstrumentationMap.cpp - XRay Instrumentation Map ------------------===//
209467b48Spatrick //
309467b48Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
409467b48Spatrick // See https://llvm.org/LICENSE.txt for license information.
509467b48Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
609467b48Spatrick //
709467b48Spatrick //===----------------------------------------------------------------------===//
809467b48Spatrick //
909467b48Spatrick // Implementation of the InstrumentationMap type for XRay sleds.
1009467b48Spatrick //
1109467b48Spatrick //===----------------------------------------------------------------------===//
1209467b48Spatrick 
1309467b48Spatrick #include "llvm/XRay/InstrumentationMap.h"
1409467b48Spatrick #include "llvm/ADT/DenseMap.h"
1509467b48Spatrick #include "llvm/ADT/STLExtras.h"
1609467b48Spatrick #include "llvm/ADT/StringRef.h"
1709467b48Spatrick #include "llvm/ADT/Triple.h"
1809467b48Spatrick #include "llvm/ADT/Twine.h"
1909467b48Spatrick #include "llvm/Object/Binary.h"
2009467b48Spatrick #include "llvm/Object/ELFObjectFile.h"
2109467b48Spatrick #include "llvm/Object/ObjectFile.h"
2209467b48Spatrick #include "llvm/Object/RelocationResolver.h"
2309467b48Spatrick #include "llvm/Support/DataExtractor.h"
2409467b48Spatrick #include "llvm/Support/Error.h"
2509467b48Spatrick #include "llvm/Support/FileSystem.h"
2609467b48Spatrick #include "llvm/Support/YAMLTraits.h"
2709467b48Spatrick #include <algorithm>
2809467b48Spatrick #include <cstddef>
2909467b48Spatrick #include <cstdint>
3009467b48Spatrick #include <system_error>
3109467b48Spatrick #include <vector>
3209467b48Spatrick 
3309467b48Spatrick using namespace llvm;
3409467b48Spatrick using namespace xray;
3509467b48Spatrick 
getFunctionId(uint64_t Addr) const36*d415bd75Srobert std::optional<int32_t> InstrumentationMap::getFunctionId(uint64_t Addr) const {
3709467b48Spatrick   auto I = FunctionIds.find(Addr);
3809467b48Spatrick   if (I != FunctionIds.end())
3909467b48Spatrick     return I->second;
40*d415bd75Srobert   return std::nullopt;
4109467b48Spatrick }
4209467b48Spatrick 
43*d415bd75Srobert std::optional<uint64_t>
getFunctionAddr(int32_t FuncId) const44*d415bd75Srobert InstrumentationMap::getFunctionAddr(int32_t FuncId) const {
4509467b48Spatrick   auto I = FunctionAddresses.find(FuncId);
4609467b48Spatrick   if (I != FunctionAddresses.end())
4709467b48Spatrick     return I->second;
48*d415bd75Srobert   return std::nullopt;
4909467b48Spatrick }
5009467b48Spatrick 
5109467b48Spatrick using RelocMap = DenseMap<uint64_t, uint64_t>;
5209467b48Spatrick 
5309467b48Spatrick static Error
loadObj(StringRef Filename,object::OwningBinary<object::ObjectFile> & ObjFile,InstrumentationMap::SledContainer & Sleds,InstrumentationMap::FunctionAddressMap & FunctionAddresses,InstrumentationMap::FunctionAddressReverseMap & FunctionIds)5409467b48Spatrick loadObj(StringRef Filename, object::OwningBinary<object::ObjectFile> &ObjFile,
5509467b48Spatrick         InstrumentationMap::SledContainer &Sleds,
5609467b48Spatrick         InstrumentationMap::FunctionAddressMap &FunctionAddresses,
5709467b48Spatrick         InstrumentationMap::FunctionAddressReverseMap &FunctionIds) {
5809467b48Spatrick   InstrumentationMap Map;
5909467b48Spatrick 
6009467b48Spatrick   // Find the section named "xray_instr_map".
6109467b48Spatrick   if ((!ObjFile.getBinary()->isELF() && !ObjFile.getBinary()->isMachO()) ||
6209467b48Spatrick       !(ObjFile.getBinary()->getArch() == Triple::x86_64 ||
6309467b48Spatrick         ObjFile.getBinary()->getArch() == Triple::ppc64le ||
64097a140dSpatrick         ObjFile.getBinary()->getArch() == Triple::arm ||
6509467b48Spatrick         ObjFile.getBinary()->getArch() == Triple::aarch64))
6609467b48Spatrick     return make_error<StringError>(
67097a140dSpatrick         "File format not supported (only does ELF and Mach-O little endian "
68097a140dSpatrick         "64-bit).",
6909467b48Spatrick         std::make_error_code(std::errc::not_supported));
7009467b48Spatrick 
7109467b48Spatrick   StringRef Contents = "";
7209467b48Spatrick   const auto &Sections = ObjFile.getBinary()->sections();
73097a140dSpatrick   uint64_t Address = 0;
7409467b48Spatrick   auto I = llvm::find_if(Sections, [&](object::SectionRef Section) {
7509467b48Spatrick     Expected<StringRef> NameOrErr = Section.getName();
76097a140dSpatrick     if (NameOrErr) {
77097a140dSpatrick       Address = Section.getAddress();
7809467b48Spatrick       return *NameOrErr == "xray_instr_map";
79097a140dSpatrick     }
8009467b48Spatrick     consumeError(NameOrErr.takeError());
8109467b48Spatrick     return false;
8209467b48Spatrick   });
8309467b48Spatrick 
8409467b48Spatrick   if (I == Sections.end())
8509467b48Spatrick     return make_error<StringError>(
8609467b48Spatrick         "Failed to find XRay instrumentation map.",
8709467b48Spatrick         std::make_error_code(std::errc::executable_format_error));
8809467b48Spatrick 
89*d415bd75Srobert   if (Error E = I->getContents().moveInto(Contents))
90*d415bd75Srobert     return E;
9109467b48Spatrick 
9209467b48Spatrick   RelocMap Relocs;
9309467b48Spatrick   if (ObjFile.getBinary()->isELF()) {
9409467b48Spatrick     uint32_t RelativeRelocation = [](object::ObjectFile *ObjFile) {
9509467b48Spatrick       if (const auto *ELFObj = dyn_cast<object::ELF32LEObjectFile>(ObjFile))
9673471bf0Spatrick         return ELFObj->getELFFile().getRelativeRelocationType();
97097a140dSpatrick       else if (const auto *ELFObj =
98097a140dSpatrick                    dyn_cast<object::ELF32BEObjectFile>(ObjFile))
9973471bf0Spatrick         return ELFObj->getELFFile().getRelativeRelocationType();
100097a140dSpatrick       else if (const auto *ELFObj =
101097a140dSpatrick                    dyn_cast<object::ELF64LEObjectFile>(ObjFile))
10273471bf0Spatrick         return ELFObj->getELFFile().getRelativeRelocationType();
103097a140dSpatrick       else if (const auto *ELFObj =
104097a140dSpatrick                    dyn_cast<object::ELF64BEObjectFile>(ObjFile))
10573471bf0Spatrick         return ELFObj->getELFFile().getRelativeRelocationType();
10609467b48Spatrick       else
10709467b48Spatrick         return static_cast<uint32_t>(0);
10809467b48Spatrick     }(ObjFile.getBinary());
10909467b48Spatrick 
11073471bf0Spatrick     object::SupportsRelocation Supports;
11109467b48Spatrick     object::RelocationResolver Resolver;
11273471bf0Spatrick     std::tie(Supports, Resolver) =
11309467b48Spatrick         object::getRelocationResolver(*ObjFile.getBinary());
11409467b48Spatrick 
11509467b48Spatrick     for (const object::SectionRef &Section : Sections) {
11609467b48Spatrick       for (const object::RelocationRef &Reloc : Section.relocations()) {
117097a140dSpatrick         if (ObjFile.getBinary()->getArch() == Triple::arm) {
11873471bf0Spatrick           if (Supports && Supports(Reloc.getType())) {
119097a140dSpatrick             Expected<uint64_t> ValueOrErr = Reloc.getSymbol()->getValue();
120097a140dSpatrick             if (!ValueOrErr)
121097a140dSpatrick               return ValueOrErr.takeError();
12273471bf0Spatrick             Relocs.insert(
12373471bf0Spatrick                 {Reloc.getOffset(),
12473471bf0Spatrick                  object::resolveRelocation(Resolver, Reloc, *ValueOrErr, 0)});
125097a140dSpatrick           }
12673471bf0Spatrick         } else if (Supports && Supports(Reloc.getType())) {
12709467b48Spatrick           auto AddendOrErr = object::ELFRelocationRef(Reloc).getAddend();
12809467b48Spatrick           auto A = AddendOrErr ? *AddendOrErr : 0;
129097a140dSpatrick           Expected<uint64_t> ValueOrErr = Reloc.getSymbol()->getValue();
130097a140dSpatrick           if (!ValueOrErr)
131097a140dSpatrick             // TODO: Test this error.
132097a140dSpatrick             return ValueOrErr.takeError();
13373471bf0Spatrick           Relocs.insert(
13473471bf0Spatrick               {Reloc.getOffset(),
13573471bf0Spatrick                object::resolveRelocation(Resolver, Reloc, *ValueOrErr, A)});
13609467b48Spatrick         } else if (Reloc.getType() == RelativeRelocation) {
13709467b48Spatrick           if (auto AddendOrErr = object::ELFRelocationRef(Reloc).getAddend())
13809467b48Spatrick             Relocs.insert({Reloc.getOffset(), *AddendOrErr});
13909467b48Spatrick         }
14009467b48Spatrick       }
14109467b48Spatrick     }
14209467b48Spatrick   }
14309467b48Spatrick 
14409467b48Spatrick   // Copy the instrumentation map data into the Sleds data structure.
14509467b48Spatrick   auto C = Contents.bytes_begin();
146097a140dSpatrick   bool Is32Bit = ObjFile.getBinary()->makeTriple().isArch32Bit();
147097a140dSpatrick   size_t ELFSledEntrySize = Is32Bit ? 16 : 32;
14809467b48Spatrick 
149097a140dSpatrick   if ((C - Contents.bytes_end()) % ELFSledEntrySize != 0)
15009467b48Spatrick     return make_error<StringError>(
15109467b48Spatrick         Twine("Instrumentation map entries not evenly divisible by size of "
152097a140dSpatrick               "an XRay sled entry."),
15309467b48Spatrick         std::make_error_code(std::errc::executable_format_error));
15409467b48Spatrick 
15509467b48Spatrick   auto RelocateOrElse = [&](uint64_t Offset, uint64_t Address) {
15609467b48Spatrick     if (!Address) {
15709467b48Spatrick       uint64_t A = I->getAddress() + C - Contents.bytes_begin() + Offset;
15809467b48Spatrick       RelocMap::const_iterator R = Relocs.find(A);
15909467b48Spatrick       if (R != Relocs.end())
16009467b48Spatrick         return R->second;
16109467b48Spatrick     }
16209467b48Spatrick     return Address;
16309467b48Spatrick   };
16409467b48Spatrick 
165097a140dSpatrick   const int WordSize = Is32Bit ? 4 : 8;
16609467b48Spatrick   int32_t FuncId = 1;
16709467b48Spatrick   uint64_t CurFn = 0;
168097a140dSpatrick   for (; C != Contents.bytes_end(); C += ELFSledEntrySize) {
16909467b48Spatrick     DataExtractor Extractor(
170097a140dSpatrick         StringRef(reinterpret_cast<const char *>(C), ELFSledEntrySize), true,
17109467b48Spatrick         8);
17209467b48Spatrick     Sleds.push_back({});
17309467b48Spatrick     auto &Entry = Sleds.back();
17409467b48Spatrick     uint64_t OffsetPtr = 0;
17509467b48Spatrick     uint64_t AddrOff = OffsetPtr;
176097a140dSpatrick     if (Is32Bit)
177097a140dSpatrick       Entry.Address = RelocateOrElse(AddrOff, Extractor.getU32(&OffsetPtr));
178097a140dSpatrick     else
17909467b48Spatrick       Entry.Address = RelocateOrElse(AddrOff, Extractor.getU64(&OffsetPtr));
18009467b48Spatrick     uint64_t FuncOff = OffsetPtr;
181097a140dSpatrick     if (Is32Bit)
182097a140dSpatrick       Entry.Function = RelocateOrElse(FuncOff, Extractor.getU32(&OffsetPtr));
183097a140dSpatrick     else
18409467b48Spatrick       Entry.Function = RelocateOrElse(FuncOff, Extractor.getU64(&OffsetPtr));
18509467b48Spatrick     auto Kind = Extractor.getU8(&OffsetPtr);
18609467b48Spatrick     static constexpr SledEntry::FunctionKinds Kinds[] = {
18709467b48Spatrick         SledEntry::FunctionKinds::ENTRY, SledEntry::FunctionKinds::EXIT,
18809467b48Spatrick         SledEntry::FunctionKinds::TAIL,
18909467b48Spatrick         SledEntry::FunctionKinds::LOG_ARGS_ENTER,
19009467b48Spatrick         SledEntry::FunctionKinds::CUSTOM_EVENT};
191*d415bd75Srobert     if (Kind >= std::size(Kinds))
19209467b48Spatrick       return errorCodeToError(
19309467b48Spatrick           std::make_error_code(std::errc::executable_format_error));
19409467b48Spatrick     Entry.Kind = Kinds[Kind];
19509467b48Spatrick     Entry.AlwaysInstrument = Extractor.getU8(&OffsetPtr) != 0;
196097a140dSpatrick     Entry.Version = Extractor.getU8(&OffsetPtr);
197097a140dSpatrick     if (Entry.Version >= 2) {
198097a140dSpatrick       Entry.Address += C - Contents.bytes_begin() + Address;
199097a140dSpatrick       Entry.Function += C - Contents.bytes_begin() + WordSize + Address;
200097a140dSpatrick     }
20109467b48Spatrick 
20209467b48Spatrick     // We do replicate the function id generation scheme implemented in the
20309467b48Spatrick     // XRay runtime.
20409467b48Spatrick     // FIXME: Figure out how to keep this consistent with the XRay runtime.
20509467b48Spatrick     if (CurFn == 0) {
20609467b48Spatrick       CurFn = Entry.Function;
20709467b48Spatrick       FunctionAddresses[FuncId] = Entry.Function;
20809467b48Spatrick       FunctionIds[Entry.Function] = FuncId;
20909467b48Spatrick     }
21009467b48Spatrick     if (Entry.Function != CurFn) {
21109467b48Spatrick       ++FuncId;
21209467b48Spatrick       CurFn = Entry.Function;
21309467b48Spatrick       FunctionAddresses[FuncId] = Entry.Function;
21409467b48Spatrick       FunctionIds[Entry.Function] = FuncId;
21509467b48Spatrick     }
21609467b48Spatrick   }
21709467b48Spatrick   return Error::success();
21809467b48Spatrick }
21909467b48Spatrick 
22009467b48Spatrick static Error
loadYAML(sys::fs::file_t Fd,size_t FileSize,StringRef Filename,InstrumentationMap::SledContainer & Sleds,InstrumentationMap::FunctionAddressMap & FunctionAddresses,InstrumentationMap::FunctionAddressReverseMap & FunctionIds)22109467b48Spatrick loadYAML(sys::fs::file_t Fd, size_t FileSize, StringRef Filename,
22209467b48Spatrick          InstrumentationMap::SledContainer &Sleds,
22309467b48Spatrick          InstrumentationMap::FunctionAddressMap &FunctionAddresses,
22409467b48Spatrick          InstrumentationMap::FunctionAddressReverseMap &FunctionIds) {
22509467b48Spatrick   std::error_code EC;
22609467b48Spatrick   sys::fs::mapped_file_region MappedFile(
22709467b48Spatrick       Fd, sys::fs::mapped_file_region::mapmode::readonly, FileSize, 0, EC);
22809467b48Spatrick   sys::fs::closeFile(Fd);
22909467b48Spatrick   if (EC)
23009467b48Spatrick     return make_error<StringError>(
23109467b48Spatrick         Twine("Failed memory-mapping file '") + Filename + "'.", EC);
23209467b48Spatrick 
23309467b48Spatrick   std::vector<YAMLXRaySledEntry> YAMLSleds;
23409467b48Spatrick   yaml::Input In(StringRef(MappedFile.data(), MappedFile.size()));
23509467b48Spatrick   In >> YAMLSleds;
23609467b48Spatrick   if (In.error())
23709467b48Spatrick     return make_error<StringError>(
23809467b48Spatrick         Twine("Failed loading YAML document from '") + Filename + "'.",
23909467b48Spatrick         In.error());
24009467b48Spatrick 
24109467b48Spatrick   Sleds.reserve(YAMLSleds.size());
24209467b48Spatrick   for (const auto &Y : YAMLSleds) {
24309467b48Spatrick     FunctionAddresses[Y.FuncId] = Y.Function;
24409467b48Spatrick     FunctionIds[Y.Function] = Y.FuncId;
245097a140dSpatrick     Sleds.push_back(SledEntry{Y.Address, Y.Function, Y.Kind, Y.AlwaysInstrument,
246097a140dSpatrick                               Y.Version});
24709467b48Spatrick   }
24809467b48Spatrick   return Error::success();
24909467b48Spatrick }
25009467b48Spatrick 
25109467b48Spatrick // FIXME: Create error types that encapsulate a bit more information than what
25209467b48Spatrick // StringError instances contain.
25309467b48Spatrick Expected<InstrumentationMap>
loadInstrumentationMap(StringRef Filename)25409467b48Spatrick llvm::xray::loadInstrumentationMap(StringRef Filename) {
25509467b48Spatrick   // At this point we assume the file is an object file -- and if that doesn't
25609467b48Spatrick   // work, we treat it as YAML.
25709467b48Spatrick   // FIXME: Extend to support non-ELF and non-x86_64 binaries.
25809467b48Spatrick 
25909467b48Spatrick   InstrumentationMap Map;
26009467b48Spatrick   auto ObjectFileOrError = object::ObjectFile::createObjectFile(Filename);
26109467b48Spatrick   if (!ObjectFileOrError) {
26209467b48Spatrick     auto E = ObjectFileOrError.takeError();
26309467b48Spatrick     // We try to load it as YAML if the ELF load didn't work.
264097a140dSpatrick     Expected<sys::fs::file_t> FdOrErr =
265097a140dSpatrick         sys::fs::openNativeFileForRead(Filename);
26609467b48Spatrick     if (!FdOrErr) {
26709467b48Spatrick       // Report the ELF load error if YAML failed.
26809467b48Spatrick       consumeError(FdOrErr.takeError());
26909467b48Spatrick       return std::move(E);
27009467b48Spatrick     }
27109467b48Spatrick 
27209467b48Spatrick     uint64_t FileSize;
27309467b48Spatrick     if (sys::fs::file_size(Filename, FileSize))
27409467b48Spatrick       return std::move(E);
27509467b48Spatrick 
27609467b48Spatrick     // If the file is empty, we return the original error.
27709467b48Spatrick     if (FileSize == 0)
27809467b48Spatrick       return std::move(E);
27909467b48Spatrick 
28009467b48Spatrick     // From this point on the errors will be only for the YAML parts, so we
28109467b48Spatrick     // consume the errors at this point.
28209467b48Spatrick     consumeError(std::move(E));
28309467b48Spatrick     if (auto E = loadYAML(*FdOrErr, FileSize, Filename, Map.Sleds,
28409467b48Spatrick                           Map.FunctionAddresses, Map.FunctionIds))
28509467b48Spatrick       return std::move(E);
28609467b48Spatrick   } else if (auto E = loadObj(Filename, *ObjectFileOrError, Map.Sleds,
28709467b48Spatrick                               Map.FunctionAddresses, Map.FunctionIds)) {
28809467b48Spatrick     return std::move(E);
28909467b48Spatrick   }
29009467b48Spatrick   return Map;
29109467b48Spatrick }
292