Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SHT_LLVM_FUNC_MAP][llvm-readobj]Introduce function address map section and emit dynamic instruction count(readobj part) #124333

Open
wants to merge 1 commit into
base: users/wlei-llvm/spr/main.sht_llvm_func_mapllvm-readobjintroduce-function-address-map-section-and-emit-dynamic-instruction-countreadobj-part
Choose a base branch
from

Conversation

wlei-llvm
Copy link
Contributor

Test Plan: llvm/test/tools/llvm-readobj/ELF/func-map.test

Created using spr 1.3.6-beta.1
@llvmbot
Copy link
Member

llvmbot commented Jan 24, 2025

@llvm/pr-subscribers-llvm-binary-utilities

Author: Lei Wang (wlei-llvm)

Changes

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 --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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants