10e8ababfSDean Michael Berris //===- InstrumentationMap.cpp - XRay Instrumentation Map ------------------===//
20e8ababfSDean Michael Berris //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60e8ababfSDean Michael Berris //
70e8ababfSDean Michael Berris //===----------------------------------------------------------------------===//
80e8ababfSDean Michael Berris //
90e8ababfSDean Michael Berris // Implementation of the InstrumentationMap type for XRay sleds.
100e8ababfSDean Michael Berris //
110e8ababfSDean Michael Berris //===----------------------------------------------------------------------===//
120e8ababfSDean Michael Berris
136bda14b3SChandler Carruth #include "llvm/XRay/InstrumentationMap.h"
14493a0824SPetr Hosek #include "llvm/ADT/DenseMap.h"
1544d95122SEugene Zelenko #include "llvm/ADT/None.h"
1644d95122SEugene Zelenko #include "llvm/ADT/STLExtras.h"
1744d95122SEugene Zelenko #include "llvm/ADT/StringRef.h"
1844d95122SEugene Zelenko #include "llvm/ADT/Triple.h"
1944d95122SEugene Zelenko #include "llvm/ADT/Twine.h"
2044d95122SEugene Zelenko #include "llvm/Object/Binary.h"
21493a0824SPetr Hosek #include "llvm/Object/ELFObjectFile.h"
220e8ababfSDean Michael Berris #include "llvm/Object/ObjectFile.h"
231d321434SAditya Kumar #include "llvm/Object/RelocationResolver.h"
240e8ababfSDean Michael Berris #include "llvm/Support/DataExtractor.h"
2544d95122SEugene Zelenko #include "llvm/Support/Error.h"
260e8ababfSDean Michael Berris #include "llvm/Support/FileSystem.h"
2744d95122SEugene Zelenko #include "llvm/Support/YAMLTraits.h"
2844d95122SEugene Zelenko #include <algorithm>
2944d95122SEugene Zelenko #include <cstddef>
3044d95122SEugene Zelenko #include <cstdint>
310e8ababfSDean Michael Berris #include <system_error>
3244d95122SEugene Zelenko #include <vector>
330e8ababfSDean Michael Berris
3444d95122SEugene Zelenko using namespace llvm;
3544d95122SEugene Zelenko using namespace xray;
360e8ababfSDean Michael Berris
getFunctionId(uint64_t Addr) const370e8ababfSDean Michael Berris Optional<int32_t> InstrumentationMap::getFunctionId(uint64_t Addr) const {
380e8ababfSDean Michael Berris auto I = FunctionIds.find(Addr);
390e8ababfSDean Michael Berris if (I != FunctionIds.end())
400e8ababfSDean Michael Berris return I->second;
410e8ababfSDean Michael Berris return None;
420e8ababfSDean Michael Berris }
430e8ababfSDean Michael Berris
getFunctionAddr(int32_t FuncId) const440e8ababfSDean Michael Berris Optional<uint64_t> InstrumentationMap::getFunctionAddr(int32_t FuncId) const {
450e8ababfSDean Michael Berris auto I = FunctionAddresses.find(FuncId);
460e8ababfSDean Michael Berris if (I != FunctionAddresses.end())
470e8ababfSDean Michael Berris return I->second;
480e8ababfSDean Michael Berris return None;
490e8ababfSDean Michael Berris }
500e8ababfSDean Michael Berris
51493a0824SPetr Hosek using RelocMap = DenseMap<uint64_t, uint64_t>;
52493a0824SPetr Hosek
5344d95122SEugene Zelenko static Error
loadObj(StringRef Filename,object::OwningBinary<object::ObjectFile> & ObjFile,InstrumentationMap::SledContainer & Sleds,InstrumentationMap::FunctionAddressMap & FunctionAddresses,InstrumentationMap::FunctionAddressReverseMap & FunctionIds)5407cc5a8dSDavid Carlier loadObj(StringRef Filename, object::OwningBinary<object::ObjectFile> &ObjFile,
550e8ababfSDean Michael Berris InstrumentationMap::SledContainer &Sleds,
560e8ababfSDean Michael Berris InstrumentationMap::FunctionAddressMap &FunctionAddresses,
570e8ababfSDean Michael Berris InstrumentationMap::FunctionAddressReverseMap &FunctionIds) {
580e8ababfSDean Michael Berris InstrumentationMap Map;
590e8ababfSDean Michael Berris
600e8ababfSDean Michael Berris // Find the section named "xray_instr_map".
6107cc5a8dSDavid Carlier if ((!ObjFile.getBinary()->isELF() && !ObjFile.getBinary()->isMachO()) ||
62918ed871STim Shen !(ObjFile.getBinary()->getArch() == Triple::x86_64 ||
631d321434SAditya Kumar ObjFile.getBinary()->getArch() == Triple::ppc64le ||
64e62d67f7SIan Levesque ObjFile.getBinary()->getArch() == Triple::arm ||
651d321434SAditya Kumar ObjFile.getBinary()->getArch() == Triple::aarch64))
660e8ababfSDean Michael Berris return make_error<StringError>(
6757f1c43cSIan Levesque "File format not supported (only does ELF and Mach-O little endian "
6857f1c43cSIan Levesque "64-bit).",
690e8ababfSDean Michael Berris std::make_error_code(std::errc::not_supported));
700e8ababfSDean Michael Berris
710e8ababfSDean Michael Berris StringRef Contents = "";
720e8ababfSDean Michael Berris const auto &Sections = ObjFile.getBinary()->sections();
7310bc1258SFangrui Song uint64_t Address = 0;
7444d95122SEugene Zelenko auto I = llvm::find_if(Sections, [&](object::SectionRef Section) {
75bcc00e1aSGeorge Rimar Expected<StringRef> NameOrErr = Section.getName();
7610bc1258SFangrui Song if (NameOrErr) {
7710bc1258SFangrui Song Address = Section.getAddress();
78bcc00e1aSGeorge Rimar return *NameOrErr == "xray_instr_map";
7910bc1258SFangrui Song }
80bcc00e1aSGeorge Rimar consumeError(NameOrErr.takeError());
810e8ababfSDean Michael Berris return false;
820e8ababfSDean Michael Berris });
830e8ababfSDean Michael Berris
840e8ababfSDean Michael Berris if (I == Sections.end())
850e8ababfSDean Michael Berris return make_error<StringError>(
860e8ababfSDean Michael Berris "Failed to find XRay instrumentation map.",
870e8ababfSDean Michael Berris std::make_error_code(std::errc::executable_format_error));
880e8ababfSDean Michael Berris
89*2410fb46SDuncan P. N. Exon Smith if (Error E = I->getContents().moveInto(Contents))
90*2410fb46SDuncan P. N. Exon Smith return E;
910e8ababfSDean Michael Berris
92493a0824SPetr Hosek RelocMap Relocs;
93493a0824SPetr Hosek if (ObjFile.getBinary()->isELF()) {
94d2ed5be8SFangrui Song uint32_t RelativeRelocation = [](object::ObjectFile *ObjFile) {
95493a0824SPetr Hosek if (const auto *ELFObj = dyn_cast<object::ELF32LEObjectFile>(ObjFile))
96ffbce65fSGeorgii Rymar return ELFObj->getELFFile().getRelativeRelocationType();
9757f1c43cSIan Levesque else if (const auto *ELFObj =
9857f1c43cSIan Levesque dyn_cast<object::ELF32BEObjectFile>(ObjFile))
99ffbce65fSGeorgii Rymar return ELFObj->getELFFile().getRelativeRelocationType();
10057f1c43cSIan Levesque else if (const auto *ELFObj =
10157f1c43cSIan Levesque dyn_cast<object::ELF64LEObjectFile>(ObjFile))
102ffbce65fSGeorgii Rymar return ELFObj->getELFFile().getRelativeRelocationType();
10357f1c43cSIan Levesque else if (const auto *ELFObj =
10457f1c43cSIan Levesque dyn_cast<object::ELF64BEObjectFile>(ObjFile))
105ffbce65fSGeorgii Rymar return ELFObj->getELFFile().getRelativeRelocationType();
106493a0824SPetr Hosek else
107493a0824SPetr Hosek return static_cast<uint32_t>(0);
108493a0824SPetr Hosek }(ObjFile.getBinary());
109493a0824SPetr Hosek
1109a99d23aSGeorgii Rymar object::SupportsRelocation Supports;
1111d321434SAditya Kumar object::RelocationResolver Resolver;
1129a99d23aSGeorgii Rymar std::tie(Supports, Resolver) =
1131d321434SAditya Kumar object::getRelocationResolver(*ObjFile.getBinary());
1141d321434SAditya Kumar
115493a0824SPetr Hosek for (const object::SectionRef &Section : Sections) {
116493a0824SPetr Hosek for (const object::RelocationRef &Reloc : Section.relocations()) {
117e62d67f7SIan Levesque if (ObjFile.getBinary()->getArch() == Triple::arm) {
1189a99d23aSGeorgii Rymar if (Supports && Supports(Reloc.getType())) {
119e62d67f7SIan Levesque Expected<uint64_t> ValueOrErr = Reloc.getSymbol()->getValue();
120e62d67f7SIan Levesque if (!ValueOrErr)
121e62d67f7SIan Levesque return ValueOrErr.takeError();
1229a99d23aSGeorgii Rymar Relocs.insert(
1239a99d23aSGeorgii Rymar {Reloc.getOffset(),
1249a99d23aSGeorgii Rymar object::resolveRelocation(Resolver, Reloc, *ValueOrErr, 0)});
125e62d67f7SIan Levesque }
1269a99d23aSGeorgii Rymar } else if (Supports && Supports(Reloc.getType())) {
1271d321434SAditya Kumar auto AddendOrErr = object::ELFRelocationRef(Reloc).getAddend();
1281d321434SAditya Kumar auto A = AddendOrErr ? *AddendOrErr : 0;
129ff6a0b6aSXing GUO Expected<uint64_t> ValueOrErr = Reloc.getSymbol()->getValue();
130ff6a0b6aSXing GUO if (!ValueOrErr)
131ff6a0b6aSXing GUO // TODO: Test this error.
132ff6a0b6aSXing GUO return ValueOrErr.takeError();
1339a99d23aSGeorgii Rymar Relocs.insert(
1349a99d23aSGeorgii Rymar {Reloc.getOffset(),
1359a99d23aSGeorgii Rymar object::resolveRelocation(Resolver, Reloc, *ValueOrErr, A)});
1361d321434SAditya Kumar } else if (Reloc.getType() == RelativeRelocation) {
137493a0824SPetr Hosek if (auto AddendOrErr = object::ELFRelocationRef(Reloc).getAddend())
138493a0824SPetr Hosek Relocs.insert({Reloc.getOffset(), *AddendOrErr});
139493a0824SPetr Hosek }
140493a0824SPetr Hosek }
141493a0824SPetr Hosek }
1421d321434SAditya Kumar }
143493a0824SPetr Hosek
1440e8ababfSDean Michael Berris // Copy the instrumentation map data into the Sleds data structure.
1450e8ababfSDean Michael Berris auto C = Contents.bytes_begin();
146e62d67f7SIan Levesque bool Is32Bit = ObjFile.getBinary()->makeTriple().isArch32Bit();
147e62d67f7SIan Levesque size_t ELFSledEntrySize = Is32Bit ? 16 : 32;
1480e8ababfSDean Michael Berris
149e62d67f7SIan Levesque if ((C - Contents.bytes_end()) % ELFSledEntrySize != 0)
1500e8ababfSDean Michael Berris return make_error<StringError>(
1510e8ababfSDean Michael Berris Twine("Instrumentation map entries not evenly divisible by size of "
152e62d67f7SIan Levesque "an XRay sled entry."),
1530e8ababfSDean Michael Berris std::make_error_code(std::errc::executable_format_error));
1540e8ababfSDean Michael Berris
155f26a70a5SIgor Kudrin auto RelocateOrElse = [&](uint64_t Offset, uint64_t Address) {
156493a0824SPetr Hosek if (!Address) {
157493a0824SPetr Hosek uint64_t A = I->getAddress() + C - Contents.bytes_begin() + Offset;
158493a0824SPetr Hosek RelocMap::const_iterator R = Relocs.find(A);
159493a0824SPetr Hosek if (R != Relocs.end())
160493a0824SPetr Hosek return R->second;
161493a0824SPetr Hosek }
162493a0824SPetr Hosek return Address;
163493a0824SPetr Hosek };
164493a0824SPetr Hosek
165e62d67f7SIan Levesque const int WordSize = Is32Bit ? 4 : 8;
1660e8ababfSDean Michael Berris int32_t FuncId = 1;
1670e8ababfSDean Michael Berris uint64_t CurFn = 0;
168e62d67f7SIan Levesque for (; C != Contents.bytes_end(); C += ELFSledEntrySize) {
1690e8ababfSDean Michael Berris DataExtractor Extractor(
170e62d67f7SIan Levesque StringRef(reinterpret_cast<const char *>(C), ELFSledEntrySize), true,
1710e8ababfSDean Michael Berris 8);
1720e8ababfSDean Michael Berris Sleds.push_back({});
1730e8ababfSDean Michael Berris auto &Entry = Sleds.back();
174f26a70a5SIgor Kudrin uint64_t OffsetPtr = 0;
175f26a70a5SIgor Kudrin uint64_t AddrOff = OffsetPtr;
176e62d67f7SIan Levesque if (Is32Bit)
177e62d67f7SIan Levesque Entry.Address = RelocateOrElse(AddrOff, Extractor.getU32(&OffsetPtr));
178e62d67f7SIan Levesque else
17927e2f201SPetr Hosek Entry.Address = RelocateOrElse(AddrOff, Extractor.getU64(&OffsetPtr));
180f26a70a5SIgor Kudrin uint64_t FuncOff = OffsetPtr;
181e62d67f7SIan Levesque if (Is32Bit)
182e62d67f7SIan Levesque Entry.Function = RelocateOrElse(FuncOff, Extractor.getU32(&OffsetPtr));
183e62d67f7SIan Levesque else
18427e2f201SPetr Hosek Entry.Function = RelocateOrElse(FuncOff, Extractor.getU64(&OffsetPtr));
1850e8ababfSDean Michael Berris auto Kind = Extractor.getU8(&OffsetPtr);
1860e8ababfSDean Michael Berris static constexpr SledEntry::FunctionKinds Kinds[] = {
1870e8ababfSDean Michael Berris SledEntry::FunctionKinds::ENTRY, SledEntry::FunctionKinds::EXIT,
1880e8ababfSDean Michael Berris SledEntry::FunctionKinds::TAIL,
189c5caf3e9SDean Michael Berris SledEntry::FunctionKinds::LOG_ARGS_ENTER,
190c5caf3e9SDean Michael Berris SledEntry::FunctionKinds::CUSTOM_EVENT};
1913649fb14SDávid Bolvanský if (Kind >= sizeof(Kinds) / sizeof(Kinds[0]))
1920e8ababfSDean Michael Berris return errorCodeToError(
1930e8ababfSDean Michael Berris std::make_error_code(std::errc::executable_format_error));
1940e8ababfSDean Michael Berris Entry.Kind = Kinds[Kind];
1950e8ababfSDean Michael Berris Entry.AlwaysInstrument = Extractor.getU8(&OffsetPtr) != 0;
19610bc1258SFangrui Song Entry.Version = Extractor.getU8(&OffsetPtr);
19710bc1258SFangrui Song if (Entry.Version >= 2) {
19810bc1258SFangrui Song Entry.Address += C - Contents.bytes_begin() + Address;
19910bc1258SFangrui Song Entry.Function += C - Contents.bytes_begin() + WordSize + Address;
20010bc1258SFangrui Song }
2010e8ababfSDean Michael Berris
2020e8ababfSDean Michael Berris // We do replicate the function id generation scheme implemented in the
2030e8ababfSDean Michael Berris // XRay runtime.
2040e8ababfSDean Michael Berris // FIXME: Figure out how to keep this consistent with the XRay runtime.
2050e8ababfSDean Michael Berris if (CurFn == 0) {
2060e8ababfSDean Michael Berris CurFn = Entry.Function;
2070e8ababfSDean Michael Berris FunctionAddresses[FuncId] = Entry.Function;
2080e8ababfSDean Michael Berris FunctionIds[Entry.Function] = FuncId;
2090e8ababfSDean Michael Berris }
2100e8ababfSDean Michael Berris if (Entry.Function != CurFn) {
2110e8ababfSDean Michael Berris ++FuncId;
2120e8ababfSDean Michael Berris CurFn = Entry.Function;
2130e8ababfSDean Michael Berris FunctionAddresses[FuncId] = Entry.Function;
2140e8ababfSDean Michael Berris FunctionIds[Entry.Function] = FuncId;
2150e8ababfSDean Michael Berris }
2160e8ababfSDean Michael Berris }
2170e8ababfSDean Michael Berris return Error::success();
2180e8ababfSDean Michael Berris }
2190e8ababfSDean Michael Berris
22044d95122SEugene Zelenko static Error
loadYAML(sys::fs::file_t Fd,size_t FileSize,StringRef Filename,InstrumentationMap::SledContainer & Sleds,InstrumentationMap::FunctionAddressMap & FunctionAddresses,InstrumentationMap::FunctionAddressReverseMap & FunctionIds)221f002fcb2SReid Kleckner loadYAML(sys::fs::file_t Fd, size_t FileSize, StringRef Filename,
2220e8ababfSDean Michael Berris InstrumentationMap::SledContainer &Sleds,
2230e8ababfSDean Michael Berris InstrumentationMap::FunctionAddressMap &FunctionAddresses,
2240e8ababfSDean Michael Berris InstrumentationMap::FunctionAddressReverseMap &FunctionIds) {
2250e8ababfSDean Michael Berris std::error_code EC;
2260e8ababfSDean Michael Berris sys::fs::mapped_file_region MappedFile(
227f002fcb2SReid Kleckner Fd, sys::fs::mapped_file_region::mapmode::readonly, FileSize, 0, EC);
228f002fcb2SReid Kleckner sys::fs::closeFile(Fd);
2290e8ababfSDean Michael Berris if (EC)
2300e8ababfSDean Michael Berris return make_error<StringError>(
2310e8ababfSDean Michael Berris Twine("Failed memory-mapping file '") + Filename + "'.", EC);
2320e8ababfSDean Michael Berris
2330e8ababfSDean Michael Berris std::vector<YAMLXRaySledEntry> YAMLSleds;
2340e8ababfSDean Michael Berris yaml::Input In(StringRef(MappedFile.data(), MappedFile.size()));
2350e8ababfSDean Michael Berris In >> YAMLSleds;
2360e8ababfSDean Michael Berris if (In.error())
2370e8ababfSDean Michael Berris return make_error<StringError>(
2380e8ababfSDean Michael Berris Twine("Failed loading YAML document from '") + Filename + "'.",
2390e8ababfSDean Michael Berris In.error());
2400e8ababfSDean Michael Berris
2410e8ababfSDean Michael Berris Sleds.reserve(YAMLSleds.size());
2420e8ababfSDean Michael Berris for (const auto &Y : YAMLSleds) {
2430e8ababfSDean Michael Berris FunctionAddresses[Y.FuncId] = Y.Function;
2440e8ababfSDean Michael Berris FunctionIds[Y.Function] = Y.FuncId;
24510bc1258SFangrui Song Sleds.push_back(SledEntry{Y.Address, Y.Function, Y.Kind, Y.AlwaysInstrument,
24610bc1258SFangrui Song Y.Version});
2470e8ababfSDean Michael Berris }
2480e8ababfSDean Michael Berris return Error::success();
2490e8ababfSDean Michael Berris }
2500e8ababfSDean Michael Berris
2510e8ababfSDean Michael Berris // FIXME: Create error types that encapsulate a bit more information than what
2520e8ababfSDean Michael Berris // StringError instances contain.
25344d95122SEugene Zelenko Expected<InstrumentationMap>
loadInstrumentationMap(StringRef Filename)25444d95122SEugene Zelenko llvm::xray::loadInstrumentationMap(StringRef Filename) {
2550e8ababfSDean Michael Berris // At this point we assume the file is an object file -- and if that doesn't
2560e8ababfSDean Michael Berris // work, we treat it as YAML.
2570e8ababfSDean Michael Berris // FIXME: Extend to support non-ELF and non-x86_64 binaries.
2580e8ababfSDean Michael Berris
2590e8ababfSDean Michael Berris InstrumentationMap Map;
2600e8ababfSDean Michael Berris auto ObjectFileOrError = object::ObjectFile::createObjectFile(Filename);
2610e8ababfSDean Michael Berris if (!ObjectFileOrError) {
2620e8ababfSDean Michael Berris auto E = ObjectFileOrError.takeError();
2630e8ababfSDean Michael Berris // We try to load it as YAML if the ELF load didn't work.
26457f1c43cSIan Levesque Expected<sys::fs::file_t> FdOrErr =
26557f1c43cSIan Levesque sys::fs::openNativeFileForRead(Filename);
266f002fcb2SReid Kleckner if (!FdOrErr) {
267f002fcb2SReid Kleckner // Report the ELF load error if YAML failed.
268f002fcb2SReid Kleckner consumeError(FdOrErr.takeError());
269c55cf4afSBill Wendling return std::move(E);
270f002fcb2SReid Kleckner }
2710e8ababfSDean Michael Berris
2720e8ababfSDean Michael Berris uint64_t FileSize;
2730e8ababfSDean Michael Berris if (sys::fs::file_size(Filename, FileSize))
274c55cf4afSBill Wendling return std::move(E);
2750e8ababfSDean Michael Berris
2760e8ababfSDean Michael Berris // If the file is empty, we return the original error.
2770e8ababfSDean Michael Berris if (FileSize == 0)
278c55cf4afSBill Wendling return std::move(E);
2790e8ababfSDean Michael Berris
2800e8ababfSDean Michael Berris // From this point on the errors will be only for the YAML parts, so we
2810e8ababfSDean Michael Berris // consume the errors at this point.
2820e8ababfSDean Michael Berris consumeError(std::move(E));
283f002fcb2SReid Kleckner if (auto E = loadYAML(*FdOrErr, FileSize, Filename, Map.Sleds,
2840e8ababfSDean Michael Berris Map.FunctionAddresses, Map.FunctionIds))
285c55cf4afSBill Wendling return std::move(E);
28607cc5a8dSDavid Carlier } else if (auto E = loadObj(Filename, *ObjectFileOrError, Map.Sleds,
2870e8ababfSDean Michael Berris Map.FunctionAddresses, Map.FunctionIds)) {
288c55cf4afSBill Wendling return std::move(E);
2890e8ababfSDean Michael Berris }
2900e8ababfSDean Michael Berris return Map;
2910e8ababfSDean Michael Berris }
292