[llvm-branch-commits] [SHT_LLVM_FUNC_MAP][llvm-readobj]Introduce function address map section and emit dynamic instruction count(readobj part) (PR #124333)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Fri Jan 24 11:42:04 PST 2025
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
More information about the llvm-branch-commits
mailing list