llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-llvm-binary-utilities Author: Lei Wang (wlei-llvm) <details> <summary>Changes</summary> Test Plan: llvm/test/tools/llvm-readobj/ELF/func-map.test --- Full diff: https://github.com/llvm/llvm-project/pull/124333.diff 7 Files Affected: - (modified) llvm/include/llvm/Object/ELF.h (+7) - (modified) llvm/lib/Object/ELF.cpp (+98) - (added) llvm/test/tools/llvm-readobj/ELF/func-map.test (+96) - (modified) llvm/tools/llvm-readobj/ELFDumper.cpp (+60) - (modified) llvm/tools/llvm-readobj/ObjDumper.h (+1) - (modified) llvm/tools/llvm-readobj/Opts.td (+1) - (modified) llvm/tools/llvm-readobj/llvm-readobj.cpp (+4) ``````````diff diff --git a/llvm/include/llvm/Object/ELF.h b/llvm/include/llvm/Object/ELF.h index 3aa1d7864fcb70..a688672a3e5190 100644 --- a/llvm/include/llvm/Object/ELF.h +++ b/llvm/include/llvm/Object/ELF.h @@ -513,6 +513,13 @@ class ELFFile { decodeBBAddrMap(const Elf_Shdr &Sec, const Elf_Shdr *RelaSec = nullptr, std::vector<PGOAnalysisMap> *PGOAnalyses = nullptr) const; + /// Returns a vector of FuncMap structs corresponding to each function + /// within the text section that the SHT_LLVM_FUNC_MAP section \p Sec + /// is associated with. If the current ELFFile is relocatable, a corresponding + /// \p RelaSec must be passed in as an argument. + Expected<std::vector<FuncMap>> + decodeFuncMap(const Elf_Shdr &Sec, const Elf_Shdr *RelaSec = nullptr) const; + /// Returns a map from every section matching \p IsMatch to its relocation /// section, or \p nullptr if it has no relocation section. This function /// returns an error if any of the \p IsMatch calls fail or if it fails to diff --git a/llvm/lib/Object/ELF.cpp b/llvm/lib/Object/ELF.cpp index 41c3fb4cc5e406..87a9e5469f46d2 100644 --- a/llvm/lib/Object/ELF.cpp +++ b/llvm/lib/Object/ELF.cpp @@ -940,6 +940,104 @@ ELFFile<ELFT>::decodeBBAddrMap(const Elf_Shdr &Sec, const Elf_Shdr *RelaSec, return std::move(AddrMapsOrErr); } +template <class ELFT> +Expected<std::vector<FuncMap>> +ELFFile<ELFT>::decodeFuncMap(const Elf_Shdr &Sec, + const Elf_Shdr *RelaSec) const { + bool IsRelocatable = this->getHeader().e_type == ELF::ET_REL; + + // This DenseMap maps the offset of each function (the location of the + // reference to the function in the SHT_LLVM_FUNC_ADDR_MAP section) to the + // addend (the location of the function in the text section). + llvm::DenseMap<uint64_t, uint64_t> FunctionOffsetTranslations; + if (IsRelocatable && RelaSec) { + assert(RelaSec && + "Can't read a SHT_LLVM_FUNC_ADDR_MAP section in a relocatable " + "object file without providing a relocation section."); + Expected<typename ELFFile<ELFT>::Elf_Rela_Range> Relas = + this->relas(*RelaSec); + if (!Relas) + return createError("unable to read relocations for section " + + describe(*this, Sec) + ": " + + toString(Relas.takeError())); + for (typename ELFFile<ELFT>::Elf_Rela Rela : *Relas) + FunctionOffsetTranslations[Rela.r_offset] = Rela.r_addend; + } + auto GetAddressForRelocation = + [&](unsigned RelocationOffsetInSection) -> Expected<unsigned> { + auto FOTIterator = + FunctionOffsetTranslations.find(RelocationOffsetInSection); + if (FOTIterator == FunctionOffsetTranslations.end()) { + return createError("failed to get relocation data for offset: " + + Twine::utohexstr(RelocationOffsetInSection) + + " in section " + describe(*this, Sec)); + } + return FOTIterator->second; + }; + Expected<ArrayRef<uint8_t>> ContentsOrErr = this->getSectionContents(Sec); + if (!ContentsOrErr) + return ContentsOrErr.takeError(); + ArrayRef<uint8_t> Content = *ContentsOrErr; + DataExtractor Data(Content, this->isLE(), ELFT::Is64Bits ? 8 : 4); + std::vector<FuncMap> FunctionEntries; + + DataExtractor::Cursor Cur(0); + Error ULEBSizeErr = Error::success(); + + // Helper lampda to extract the (possiblly relocatable) address stored at Cur. + auto ExtractAddress = [&]() -> Expected<typename ELFFile<ELFT>::uintX_t> { + uint64_t RelocationOffsetInSection = Cur.tell(); + auto Address = + static_cast<typename ELFFile<ELFT>::uintX_t>(Data.getAddress(Cur)); + if (!Cur) + return Cur.takeError(); + if (!IsRelocatable) + return Address; + assert(Address == 0); + Expected<unsigned> AddressOrErr = + GetAddressForRelocation(RelocationOffsetInSection); + if (!AddressOrErr) + return AddressOrErr.takeError(); + return *AddressOrErr; + }; + + uint8_t Version = 0; + uint8_t Feature = 0; + FuncMap::Features FeatEnable{}; + while (!ULEBSizeErr && Cur && Cur.tell() < Content.size()) { + if (Sec.sh_type == ELF::SHT_LLVM_FUNC_MAP) { + Version = Data.getU8(Cur); + if (!Cur) + break; + if (Version > 1) + return createError("unsupported SHT_LLVM_FUNC_MAP version: " + + Twine(static_cast<int>(Version))); + Feature = Data.getU8(Cur); // Feature byte + if (!Cur) + break; + auto FeatEnableOrErr = FuncMap::Features::decode(Feature); + if (!FeatEnableOrErr) + return FeatEnableOrErr.takeError(); + FeatEnable = *FeatEnableOrErr; + } + typename ELFFile<ELFT>::uintX_t FunctionAddress = 0; + auto AddressOrErr = ExtractAddress(); + if (!AddressOrErr) + return AddressOrErr.takeError(); + FunctionAddress = *AddressOrErr; + uint64_t DynamicInstCount = + FeatEnable.DynamicInstCount + ? readULEB128As<uint64_t>(Data, Cur, ULEBSizeErr) + : 0; + FunctionEntries.push_back({FunctionAddress, DynamicInstCount, FeatEnable}); + } + // Either Cur is in the error state, or we have an error in ULEBSizeErr, but + // we join all errors here to be safe. + if (!Cur || ULEBSizeErr) + return joinErrors(Cur.takeError(), std::move(ULEBSizeErr)); + return FunctionEntries; +} + template <class ELFT> Expected< MapVector<const typename ELFT::Shdr *, const typename ELFT::Shdr *>> diff --git a/llvm/test/tools/llvm-readobj/ELF/func-map.test b/llvm/test/tools/llvm-readobj/ELF/func-map.test new file mode 100644 index 00000000000000..eb768ea21b8e1e --- /dev/null +++ b/llvm/test/tools/llvm-readobj/ELF/func-map.test @@ -0,0 +1,96 @@ +## This test checks how we handle the --func-map option. + +## Check 64-bit: +# RUN: yaml2obj --docnum=1 %s -DBITS=64 -DADDR=0x999999999 -o %t1.x64.o +# RUN: llvm-readobj %t1.x64.o --func-map 2>&1 | FileCheck %s -DADDR=0x999999999 -DFILE=%t1.x64.o --check-prefix=CHECK +# RUN: llvm-readelf %t1.x64.o --func-map | FileCheck %s --check-prefix=GNU + +## Check 32-bit: +# RUN: yaml2obj --docnum=1 %s -DBITS=32 -o %t1.x32.o +# RUN: llvm-readobj %t1.x32.o --func-map 2>&1 | FileCheck -DADDR=0x11111 %s -DFILE=%t1.x32.o --check-prefix=CHECK +# RUN: llvm-readelf %t1.x32.o --func-map | FileCheck %s --check-prefix=GNU + +## Check that a malformed section can be handled. +# RUN: yaml2obj --docnum=1 %s -DBITS=32 -DSIZE=3 -o %t2.o +# RUN: llvm-readobj %t2.o --func-map 2>&1 | FileCheck %s -DOFFSET=0x3 -DFILE=%t2.o --check-prefix=TRUNCATED + +# CHECK: FuncMap [ +# CHECK-NEXT: Function { +# CHECK-NEXT: At: [[ADDR]] +# CHECK-NEXT: warning: '[[FILE]]': could not identify function symbol for address ([[ADDR]]) in SHT_LLVM_FUNC_MAP section with index 3 +# CHECK-NEXT: Name: <?> +# CHECK-NEXT: DynamicInstCount: 10 +# CHECK-NEXT: } +# CHECK-NEXT: Function { +# CHECK-NEXT: At: 0x22222 +# CHECK-NEXT: Name: foo +# CHECK-NEXT: DynamicInstCount: 16 +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: FuncMap [ +# CHECK-NEXT: Function { +# CHECK-NEXT: At: 0x33333 +# CHECK-NEXT: Name: bar +# CHECK-NEXT: DynamicInstCount: 10 +# CHECK-NEXT: } +# CHECK-NEXT: ] + +# GNU: GNUStyle::printFuncMaps not implemented + +# TRUNCATED: FuncMap [ +# TRUNCATED-NEXT: warning: '[[FILE]]': unable to dump SHT_LLVM_FUNC_MAP section with index 3: unexpected end of data at offset [[OFFSET]] +# TRUNCATED-NEXT: ] +## Check that the other valid section is properly dumped. +# TRUNCATED-NEXT: FuncMap [ +# TRUNCATED-NEXT: Function { +# TRUNCATED-NEXT: At: 0x33333 +# TRUNCATED-NEXT: Name: bar +# TRUNCATED-NEXT: DynamicInstCount: 10 +# TRUNCATED-NEXT: } +# TRUNCATED-NEXT: ] + +--- !ELF +FileHeader: + Class: ELFCLASS[[BITS]] + Data: ELFDATA2LSB + Type: ET_EXEC +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [SHF_ALLOC] + - Name: .text.bar + Type: SHT_PROGBITS + Flags: [SHF_ALLOC] + - Name: .llvm_func_map + Type: SHT_LLVM_FUNC_MAP + ShSize: [[SIZE=<none>]] + Link: .text + Entries: + - Version: 1 + Feature: 0x1 + Address: [[ADDR=0x11111]] + DynInstCnt: 0xA + - Version: 1 + Feature: 0x1 + Address: 0x22222 + DynInstCnt: 0x10 + - Name: dummy_section + Type: SHT_PROGBITS + Size: 16 + - Name: '.llvm_func_map_2' + Type: SHT_LLVM_FUNC_MAP + Link: .text.bar + Entries: + - Version: 1 + Feature: 0x1 + Address: 0x33333 + DynInstCnt: 0xA +Symbols: + - Name: foo + Section: .text + Type: STT_FUNC + Value: 0x22222 + - Name: bar + Section: .text.bar + Type: STT_FUNC + Value: 0x33333 diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp index bfca65aad52b44..1595ad935e9bc0 100644 --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -606,6 +606,7 @@ template <typename ELFT> class GNUELFDumper : public ELFDumper<ELFT> { void printVersionDependencySection(const Elf_Shdr *Sec) override; void printCGProfile() override; void printBBAddrMaps(bool PrettyPGOAnalysis) override; + void printFuncMaps() override; void printAddrsig() override; void printNotes() override; void printELFLinkerOptions() override; @@ -717,6 +718,7 @@ template <typename ELFT> class LLVMELFDumper : public ELFDumper<ELFT> { void printVersionDependencySection(const Elf_Shdr *Sec) override; void printCGProfile() override; void printBBAddrMaps(bool PrettyPGOAnalysis) override; + void printFuncMaps() override; void printAddrsig() override; void printNotes() override; void printELFLinkerOptions() override; @@ -5199,6 +5201,10 @@ void GNUELFDumper<ELFT>::printBBAddrMaps(bool /*PrettyPGOAnalysis*/) { OS << "GNUStyle::printBBAddrMaps not implemented\n"; } +template <class ELFT> void GNUELFDumper<ELFT>::printFuncMaps() { + OS << "GNUStyle::printFuncMaps not implemented\n"; +} + static Expected<std::vector<uint64_t>> toULEB128Array(ArrayRef<uint8_t> Data) { std::vector<uint64_t> Ret; const uint8_t *Cur = Data.begin(); @@ -7895,6 +7901,60 @@ void LLVMELFDumper<ELFT>::printBBAddrMaps(bool PrettyPGOAnalysis) { } } +template <class ELFT> void LLVMELFDumper<ELFT>::printFuncMaps() { + bool IsRelocatable = this->Obj.getHeader().e_type == ELF::ET_REL; + using Elf_Shdr = typename ELFT::Shdr; + auto IsMatch = [](const Elf_Shdr &Sec) -> bool { + return Sec.sh_type == ELF::SHT_LLVM_FUNC_MAP; + }; + Expected<MapVector<const Elf_Shdr *, const Elf_Shdr *>> SecRelocMapOrErr = + this->Obj.getSectionAndRelocations(IsMatch); + if (!SecRelocMapOrErr) { + this->reportUniqueWarning("failed to get SHT_LLVM_FUNC_MAP section(s): " + + toString(SecRelocMapOrErr.takeError())); + return; + } + + for (auto const &[Sec, RelocSec] : *SecRelocMapOrErr) { + std::optional<const Elf_Shdr *> FunctionSec; + if (IsRelocatable) + FunctionSec = + unwrapOrError(this->FileName, this->Obj.getSection(Sec->sh_link)); + ListScope L(W, "FuncMap"); + if (IsRelocatable && !RelocSec) { + this->reportUniqueWarning("unable to get relocation section for " + + this->describe(*Sec)); + continue; + } + Expected<std::vector<FuncMap>> FuncMapOrErr = + this->Obj.decodeFuncMap(*Sec, RelocSec); + if (!FuncMapOrErr) { + this->reportUniqueWarning("unable to dump " + this->describe(*Sec) + + ": " + toString(FuncMapOrErr.takeError())); + continue; + } + for (const auto &AM : *FuncMapOrErr) { + DictScope D(W, "Function"); + W.printHex("At", AM.getFunctionAddress()); + SmallVector<uint32_t> FuncSymIndex = + this->getSymbolIndexesForFunctionAddress(AM.getFunctionAddress(), + FunctionSec); + std::string FuncName = "<?>"; + if (FuncSymIndex.empty()) + this->reportUniqueWarning( + "could not identify function symbol for address (0x" + + Twine::utohexstr(AM.getFunctionAddress()) + ") in " + + this->describe(*Sec)); + else + FuncName = this->getStaticSymbolName(FuncSymIndex.front()); + + W.printString("Name", FuncName); + if (AM.FeatEnable.DynamicInstCount) + W.printNumber("DynamicInstCount", AM.DynamicInstCount); + } + } +} + template <class ELFT> void LLVMELFDumper<ELFT>::printAddrsig() { ListScope L(W, "Addrsig"); if (!this->DotAddrsigSec) diff --git a/llvm/tools/llvm-readobj/ObjDumper.h b/llvm/tools/llvm-readobj/ObjDumper.h index cd744e3bbfb712..2c2341e0468153 100644 --- a/llvm/tools/llvm-readobj/ObjDumper.h +++ b/llvm/tools/llvm-readobj/ObjDumper.h @@ -132,6 +132,7 @@ class ObjDumper { // If PrettyPGOAnalysis is true, prints BFI as relative frequency and BPI as // percentage. Otherwise raw values are displayed. virtual void printBBAddrMaps(bool PrettyPGOAnalysis) {} + virtual void printFuncMaps() {} virtual void printAddrsig() {} virtual void printNotes() {} virtual void printELFLinkerOptions() {} diff --git a/llvm/tools/llvm-readobj/Opts.td b/llvm/tools/llvm-readobj/Opts.td index 7d574d875d22ea..17c65a6feb7896 100644 --- a/llvm/tools/llvm-readobj/Opts.td +++ b/llvm/tools/llvm-readobj/Opts.td @@ -19,6 +19,7 @@ def all : FF<"all", "Equivalent to setting: --file-header, --program-headers, -- "--section-groups and --histogram">; def arch_specific : FF<"arch-specific", "Display architecture-specific information">; def bb_addr_map : FF<"bb-addr-map", "Display the BB address map section">; +def func_map : FF<"func-map", "Display the function address map section">; def pretty_pgo_analysis_map : FF<"pretty-pgo-analysis-map", "Display PGO analysis values with formatting rather than raw numbers">; def cg_profile : FF<"cg-profile", "Display call graph profile section">; def decompress : FF<"decompress", "Dump decompressed section content when used with -x or -p">; diff --git a/llvm/tools/llvm-readobj/llvm-readobj.cpp b/llvm/tools/llvm-readobj/llvm-readobj.cpp index 2f77e5d350553d..91465a631cba37 100644 --- a/llvm/tools/llvm-readobj/llvm-readobj.cpp +++ b/llvm/tools/llvm-readobj/llvm-readobj.cpp @@ -98,6 +98,7 @@ static bool All; static bool ArchSpecificInfo; static bool BBAddrMap; static bool PrettyPGOAnalysisMap; +static bool FuncMap; bool ExpandRelocs; static bool CGProfile; static bool Decompress; @@ -220,6 +221,7 @@ static void parseOptions(const opt::InputArgList &Args) { << "--bb-addr-map must be enabled for --pretty-pgo-analysis-map to " "have an effect\n"; opts::CGProfile = Args.hasArg(OPT_cg_profile); + opts::FuncMap = Args.hasArg(OPT_func_map); opts::Decompress = Args.hasArg(OPT_decompress); opts::Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, false); opts::DependentLibraries = Args.hasArg(OPT_dependent_libraries); @@ -473,6 +475,8 @@ static void dumpObject(ObjectFile &Obj, ScopedPrinter &Writer, Dumper->printCGProfile(); if (opts::BBAddrMap) Dumper->printBBAddrMaps(opts::PrettyPGOAnalysisMap); + if (opts::FuncMap) + Dumper->printFuncMaps(); if (opts::Addrsig) Dumper->printAddrsig(); if (opts::Notes) `````````` </details> https://github.com/llvm/llvm-project/pull/124333 _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits