Path: blob/main/contrib/llvm-project/llvm/lib/XRay/InstrumentationMap.cpp
35234 views
//===- InstrumentationMap.cpp - XRay Instrumentation Map ------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// Implementation of the InstrumentationMap type for XRay sleds.9//10//===----------------------------------------------------------------------===//1112#include "llvm/XRay/InstrumentationMap.h"13#include "llvm/ADT/DenseMap.h"14#include "llvm/ADT/STLExtras.h"15#include "llvm/ADT/StringRef.h"16#include "llvm/ADT/Twine.h"17#include "llvm/Object/Binary.h"18#include "llvm/Object/ELFObjectFile.h"19#include "llvm/Object/ObjectFile.h"20#include "llvm/Object/RelocationResolver.h"21#include "llvm/Support/DataExtractor.h"22#include "llvm/Support/Error.h"23#include "llvm/Support/FileSystem.h"24#include "llvm/Support/YAMLTraits.h"25#include "llvm/TargetParser/Triple.h"26#include <algorithm>27#include <cstddef>28#include <cstdint>29#include <system_error>30#include <vector>3132using namespace llvm;33using namespace xray;3435std::optional<int32_t> InstrumentationMap::getFunctionId(uint64_t Addr) const {36auto I = FunctionIds.find(Addr);37if (I != FunctionIds.end())38return I->second;39return std::nullopt;40}4142std::optional<uint64_t>43InstrumentationMap::getFunctionAddr(int32_t FuncId) const {44auto I = FunctionAddresses.find(FuncId);45if (I != FunctionAddresses.end())46return I->second;47return std::nullopt;48}4950using RelocMap = DenseMap<uint64_t, uint64_t>;5152static Error53loadObj(StringRef Filename, object::OwningBinary<object::ObjectFile> &ObjFile,54InstrumentationMap::SledContainer &Sleds,55InstrumentationMap::FunctionAddressMap &FunctionAddresses,56InstrumentationMap::FunctionAddressReverseMap &FunctionIds) {57InstrumentationMap Map;5859// Find the section named "xray_instr_map".60if ((!ObjFile.getBinary()->isELF() && !ObjFile.getBinary()->isMachO()) ||61!(ObjFile.getBinary()->getArch() == Triple::x86_64 ||62ObjFile.getBinary()->getArch() == Triple::loongarch64 ||63ObjFile.getBinary()->getArch() == Triple::ppc64le ||64ObjFile.getBinary()->getArch() == Triple::arm ||65ObjFile.getBinary()->getArch() == Triple::aarch64))66return make_error<StringError>(67"File format not supported (only does ELF and Mach-O little endian "68"64-bit).",69std::make_error_code(std::errc::not_supported));7071StringRef Contents = "";72const auto &Sections = ObjFile.getBinary()->sections();73uint64_t Address = 0;74auto I = llvm::find_if(Sections, [&](object::SectionRef Section) {75Expected<StringRef> NameOrErr = Section.getName();76if (NameOrErr) {77Address = Section.getAddress();78return *NameOrErr == "xray_instr_map";79}80consumeError(NameOrErr.takeError());81return false;82});8384if (I == Sections.end())85return make_error<StringError>(86"Failed to find XRay instrumentation map.",87std::make_error_code(std::errc::executable_format_error));8889if (Error E = I->getContents().moveInto(Contents))90return E;9192RelocMap Relocs;93if (ObjFile.getBinary()->isELF()) {94uint32_t RelativeRelocation = [](object::ObjectFile *ObjFile) {95if (const auto *ELFObj = dyn_cast<object::ELF32LEObjectFile>(ObjFile))96return ELFObj->getELFFile().getRelativeRelocationType();97else if (const auto *ELFObj =98dyn_cast<object::ELF32BEObjectFile>(ObjFile))99return ELFObj->getELFFile().getRelativeRelocationType();100else if (const auto *ELFObj =101dyn_cast<object::ELF64LEObjectFile>(ObjFile))102return ELFObj->getELFFile().getRelativeRelocationType();103else if (const auto *ELFObj =104dyn_cast<object::ELF64BEObjectFile>(ObjFile))105return ELFObj->getELFFile().getRelativeRelocationType();106else107return static_cast<uint32_t>(0);108}(ObjFile.getBinary());109110object::SupportsRelocation Supports;111object::RelocationResolver Resolver;112std::tie(Supports, Resolver) =113object::getRelocationResolver(*ObjFile.getBinary());114115for (const object::SectionRef &Section : Sections) {116for (const object::RelocationRef &Reloc : Section.relocations()) {117if (ObjFile.getBinary()->getArch() == Triple::arm) {118if (Supports && Supports(Reloc.getType())) {119Expected<uint64_t> ValueOrErr = Reloc.getSymbol()->getValue();120if (!ValueOrErr)121return ValueOrErr.takeError();122Relocs.insert(123{Reloc.getOffset(),124object::resolveRelocation(Resolver, Reloc, *ValueOrErr, 0)});125}126} else if (Supports && Supports(Reloc.getType())) {127auto AddendOrErr = object::ELFRelocationRef(Reloc).getAddend();128auto A = AddendOrErr ? *AddendOrErr : 0;129Expected<uint64_t> ValueOrErr = Reloc.getSymbol()->getValue();130if (!ValueOrErr)131// TODO: Test this error.132return ValueOrErr.takeError();133Relocs.insert(134{Reloc.getOffset(),135object::resolveRelocation(Resolver, Reloc, *ValueOrErr, A)});136} else if (Reloc.getType() == RelativeRelocation) {137if (auto AddendOrErr = object::ELFRelocationRef(Reloc).getAddend())138Relocs.insert({Reloc.getOffset(), *AddendOrErr});139}140}141}142}143144// Copy the instrumentation map data into the Sleds data structure.145auto C = Contents.bytes_begin();146bool Is32Bit = ObjFile.getBinary()->makeTriple().isArch32Bit();147size_t ELFSledEntrySize = Is32Bit ? 16 : 32;148149if ((C - Contents.bytes_end()) % ELFSledEntrySize != 0)150return make_error<StringError>(151Twine("Instrumentation map entries not evenly divisible by size of "152"an XRay sled entry."),153std::make_error_code(std::errc::executable_format_error));154155auto RelocateOrElse = [&](uint64_t Offset, uint64_t Address) {156if (!Address) {157uint64_t A = I->getAddress() + C - Contents.bytes_begin() + Offset;158RelocMap::const_iterator R = Relocs.find(A);159if (R != Relocs.end())160return R->second;161}162return Address;163};164165const int WordSize = Is32Bit ? 4 : 8;166int32_t FuncId = 1;167uint64_t CurFn = 0;168for (; C != Contents.bytes_end(); C += ELFSledEntrySize) {169DataExtractor Extractor(170StringRef(reinterpret_cast<const char *>(C), ELFSledEntrySize), true,1718);172Sleds.push_back({});173auto &Entry = Sleds.back();174uint64_t OffsetPtr = 0;175uint64_t AddrOff = OffsetPtr;176if (Is32Bit)177Entry.Address = RelocateOrElse(AddrOff, Extractor.getU32(&OffsetPtr));178else179Entry.Address = RelocateOrElse(AddrOff, Extractor.getU64(&OffsetPtr));180uint64_t FuncOff = OffsetPtr;181if (Is32Bit)182Entry.Function = RelocateOrElse(FuncOff, Extractor.getU32(&OffsetPtr));183else184Entry.Function = RelocateOrElse(FuncOff, Extractor.getU64(&OffsetPtr));185auto Kind = Extractor.getU8(&OffsetPtr);186static constexpr SledEntry::FunctionKinds Kinds[] = {187SledEntry::FunctionKinds::ENTRY, SledEntry::FunctionKinds::EXIT,188SledEntry::FunctionKinds::TAIL,189SledEntry::FunctionKinds::LOG_ARGS_ENTER,190SledEntry::FunctionKinds::CUSTOM_EVENT};191if (Kind >= std::size(Kinds))192return errorCodeToError(193std::make_error_code(std::errc::executable_format_error));194Entry.Kind = Kinds[Kind];195Entry.AlwaysInstrument = Extractor.getU8(&OffsetPtr) != 0;196Entry.Version = Extractor.getU8(&OffsetPtr);197if (Entry.Version >= 2) {198Entry.Address += C - Contents.bytes_begin() + Address;199Entry.Function += C - Contents.bytes_begin() + WordSize + Address;200}201202// We do replicate the function id generation scheme implemented in the203// XRay runtime.204// FIXME: Figure out how to keep this consistent with the XRay runtime.205if (CurFn == 0) {206CurFn = Entry.Function;207FunctionAddresses[FuncId] = Entry.Function;208FunctionIds[Entry.Function] = FuncId;209}210if (Entry.Function != CurFn) {211++FuncId;212CurFn = Entry.Function;213FunctionAddresses[FuncId] = Entry.Function;214FunctionIds[Entry.Function] = FuncId;215}216}217return Error::success();218}219220static Error221loadYAML(sys::fs::file_t Fd, size_t FileSize, StringRef Filename,222InstrumentationMap::SledContainer &Sleds,223InstrumentationMap::FunctionAddressMap &FunctionAddresses,224InstrumentationMap::FunctionAddressReverseMap &FunctionIds) {225std::error_code EC;226sys::fs::mapped_file_region MappedFile(227Fd, sys::fs::mapped_file_region::mapmode::readonly, FileSize, 0, EC);228sys::fs::closeFile(Fd);229if (EC)230return make_error<StringError>(231Twine("Failed memory-mapping file '") + Filename + "'.", EC);232233std::vector<YAMLXRaySledEntry> YAMLSleds;234yaml::Input In(StringRef(MappedFile.data(), MappedFile.size()));235In >> YAMLSleds;236if (In.error())237return make_error<StringError>(238Twine("Failed loading YAML document from '") + Filename + "'.",239In.error());240241Sleds.reserve(YAMLSleds.size());242for (const auto &Y : YAMLSleds) {243FunctionAddresses[Y.FuncId] = Y.Function;244FunctionIds[Y.Function] = Y.FuncId;245Sleds.push_back(SledEntry{Y.Address, Y.Function, Y.Kind, Y.AlwaysInstrument,246Y.Version});247}248return Error::success();249}250251// FIXME: Create error types that encapsulate a bit more information than what252// StringError instances contain.253Expected<InstrumentationMap>254llvm::xray::loadInstrumentationMap(StringRef Filename) {255// At this point we assume the file is an object file -- and if that doesn't256// work, we treat it as YAML.257// FIXME: Extend to support non-ELF and non-x86_64 binaries.258259InstrumentationMap Map;260auto ObjectFileOrError = object::ObjectFile::createObjectFile(Filename);261if (!ObjectFileOrError) {262auto E = ObjectFileOrError.takeError();263// We try to load it as YAML if the ELF load didn't work.264Expected<sys::fs::file_t> FdOrErr =265sys::fs::openNativeFileForRead(Filename);266if (!FdOrErr) {267// Report the ELF load error if YAML failed.268consumeError(FdOrErr.takeError());269return std::move(E);270}271272uint64_t FileSize;273if (sys::fs::file_size(Filename, FileSize))274return std::move(E);275276// If the file is empty, we return the original error.277if (FileSize == 0)278return std::move(E);279280// From this point on the errors will be only for the YAML parts, so we281// consume the errors at this point.282consumeError(std::move(E));283if (auto E = loadYAML(*FdOrErr, FileSize, Filename, Map.Sleds,284Map.FunctionAddresses, Map.FunctionIds))285return std::move(E);286} else if (auto E = loadObj(Filename, *ObjectFileOrError, Map.Sleds,287Map.FunctionAddresses, Map.FunctionIds)) {288return std::move(E);289}290return Map;291}292293294