[llvm] [SHT_LLVM_FUNC_MAP][ObjectYaml]Introduce function address map section and emit dynamic instruction count(ObjectYaml part) (PR #124332)

via llvm-commits llvm-commits at lists.llvm.org
Fri Jan 24 11:41:37 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/obj2yaml/ELF/func-map.yaml


---
Full diff: https://github.com/llvm/llvm-project/pull/124332.diff


9 Files Affected:

- (modified) llvm/include/llvm/BinaryFormat/ELF.h (+1) 
- (modified) llvm/include/llvm/Object/ELFTypes.h (+38) 
- (modified) llvm/include/llvm/ObjectYAML/ELFYAML.h (+25) 
- (modified) llvm/lib/Object/ELF.cpp (+1) 
- (modified) llvm/lib/ObjectYAML/ELFEmitter.cpp (+30) 
- (modified) llvm/lib/ObjectYAML/ELFYAML.cpp (+22) 
- (added) llvm/test/tools/obj2yaml/ELF/func-map.yaml (+139) 
- (added) llvm/test/tools/yaml2obj/ELF/func-map.yaml (+116) 
- (modified) llvm/tools/obj2yaml/elf2yaml.cpp (+52) 


``````````diff
diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h
index 48ae0db80f43ee..46837c402d88b6 100644
--- a/llvm/include/llvm/BinaryFormat/ELF.h
+++ b/llvm/include/llvm/BinaryFormat/ELF.h
@@ -1139,6 +1139,7 @@ enum : unsigned {
   SHT_LLVM_OFFLOADING = 0x6fff4c0b,         // LLVM device offloading data.
   SHT_LLVM_LTO = 0x6fff4c0c,                // .llvm.lto for fat LTO.
   SHT_LLVM_JT_SIZES = 0x6fff4c0d,           // LLVM jump tables sizes.
+  SHT_LLVM_FUNC_MAP = 0x6fff4c0e,           // LLVM function address map.
   // Android's experimental support for SHT_RELR sections.
   // https://android.googlesource.com/platform/bionic/+/b7feec74547f84559a1467aca02708ff61346d2a/libc/include/elf.h#512
   SHT_ANDROID_RELR = 0x6fffff00,   // Relocation entries; only offsets.
diff --git a/llvm/include/llvm/Object/ELFTypes.h b/llvm/include/llvm/Object/ELFTypes.h
index 87e4dbe4480910..749a3635ebf14b 100644
--- a/llvm/include/llvm/Object/ELFTypes.h
+++ b/llvm/include/llvm/Object/ELFTypes.h
@@ -1027,6 +1027,44 @@ struct PGOAnalysisMap {
   }
 };
 
+// Struct representing the FuncMap for one function.
+struct FuncMap {
+
+  // Bitfield of optional features to control the extra information
+  // emitted/encoded in the the section.
+  struct Features {
+    bool DynamicInstCount : 1;
+
+    // Encodes to minimum bit width representation.
+    uint8_t encode() const {
+      return (static_cast<uint8_t>(DynamicInstCount) << 0);
+    }
+
+    // Decodes from minimum bit width representation and validates no
+    // unnecessary bits are used.
+    static Expected<Features> decode(uint8_t Val) {
+      Features Feat{static_cast<bool>(Val & (1 << 0))};
+      if (Feat.encode() != Val)
+        return createStringError(std::error_code(),
+                                 "invalid encoding for FuncMap::Features: 0x%x",
+                                 Val);
+      return Feat;
+    }
+
+    bool operator==(const Features &Other) const {
+      return DynamicInstCount == Other.DynamicInstCount;
+    }
+  };
+
+  uint64_t FunctionAddress = 0;  // Function entry address.
+  uint64_t DynamicInstCount = 0; // Dynamic instruction count for this function
+
+  // Flags to indicate if each feature was enabled in this function
+  Features FeatEnable;
+
+  uint64_t getFunctionAddress() const { return FunctionAddress; }
+};
+
 } // end namespace object.
 } // end namespace llvm.
 
diff --git a/llvm/include/llvm/ObjectYAML/ELFYAML.h b/llvm/include/llvm/ObjectYAML/ELFYAML.h
index dfdfa055d65fa6..9180135683b65c 100644
--- a/llvm/include/llvm/ObjectYAML/ELFYAML.h
+++ b/llvm/include/llvm/ObjectYAML/ELFYAML.h
@@ -195,6 +195,13 @@ struct PGOAnalysisMapEntry {
   std::optional<std::vector<PGOBBEntry>> PGOBBEntries;
 };
 
+struct FuncMapEntry {
+  uint8_t Version;
+  llvm::yaml::Hex8 Feature;
+  llvm::yaml::Hex64 Address;
+  llvm::yaml::Hex64 DynamicInstCount;
+};
+
 struct StackSizeEntry {
   llvm::yaml::Hex64 Address;
   llvm::yaml::Hex64 Size;
@@ -229,6 +236,7 @@ struct Chunk {
     DependentLibraries,
     CallGraphProfile,
     BBAddrMap,
+    FuncMap,
 
     // Special chunks.
     SpecialChunksStart,
@@ -355,6 +363,18 @@ struct BBAddrMapSection : Section {
   }
 };
 
+struct FuncMapSection : Section {
+  std::optional<std::vector<FuncMapEntry>> Entries;
+
+  FuncMapSection() : Section(ChunkKind::FuncMap) {}
+
+  std::vector<std::pair<StringRef, bool>> getEntries() const override {
+    return {{"Entries", Entries.has_value()}};
+  };
+
+  static bool classof(const Chunk *S) { return S->Kind == ChunkKind::FuncMap; }
+};
+
 struct StackSizesSection : Section {
   std::optional<std::vector<StackSizeEntry>> Entries;
 
@@ -762,6 +782,7 @@ bool shouldAllocateFileSpace(ArrayRef<ProgramHeader> Phdrs,
 } // end namespace llvm
 
 LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::StackSizeEntry)
+LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::FuncMapEntry)
 LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::BBAddrMapEntry)
 LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::BBAddrMapEntry::BBEntry)
 LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::BBAddrMapEntry::BBRangeEntry)
@@ -929,6 +950,10 @@ template <> struct MappingTraits<ELFYAML::StackSizeEntry> {
   static void mapping(IO &IO, ELFYAML::StackSizeEntry &Rel);
 };
 
+template <> struct MappingTraits<ELFYAML::FuncMapEntry> {
+  static void mapping(IO &IO, ELFYAML::FuncMapEntry &E);
+};
+
 template <> struct MappingTraits<ELFYAML::BBAddrMapEntry> {
   static void mapping(IO &IO, ELFYAML::BBAddrMapEntry &E);
 };
diff --git a/llvm/lib/Object/ELF.cpp b/llvm/lib/Object/ELF.cpp
index b6d0699ee4fe08..41c3fb4cc5e406 100644
--- a/llvm/lib/Object/ELF.cpp
+++ b/llvm/lib/Object/ELF.cpp
@@ -321,6 +321,7 @@ StringRef llvm::object::getELFSectionTypeName(uint32_t Machine, unsigned Type) {
     STRINGIFY_ENUM_CASE(ELF, SHT_LLVM_OFFLOADING);
     STRINGIFY_ENUM_CASE(ELF, SHT_LLVM_LTO);
     STRINGIFY_ENUM_CASE(ELF, SHT_LLVM_JT_SIZES)
+    STRINGIFY_ENUM_CASE(ELF, SHT_LLVM_FUNC_MAP);
     STRINGIFY_ENUM_CASE(ELF, SHT_GNU_ATTRIBUTES);
     STRINGIFY_ENUM_CASE(ELF, SHT_GNU_HASH);
     STRINGIFY_ENUM_CASE(ELF, SHT_GNU_verdef);
diff --git a/llvm/lib/ObjectYAML/ELFEmitter.cpp b/llvm/lib/ObjectYAML/ELFEmitter.cpp
index 9ae76a71ede5e0..7316003a90c141 100644
--- a/llvm/lib/ObjectYAML/ELFEmitter.cpp
+++ b/llvm/lib/ObjectYAML/ELFEmitter.cpp
@@ -287,6 +287,9 @@ template <class ELFT> class ELFState {
   void writeSectionContent(Elf_Shdr &SHeader,
                            const ELFYAML::BBAddrMapSection &Section,
                            ContiguousBlobAccumulator &CBA);
+  void writeSectionContent(Elf_Shdr &SHeader,
+                           const ELFYAML::FuncMapSection &Section,
+                           ContiguousBlobAccumulator &CBA);
   void writeSectionContent(Elf_Shdr &SHeader,
                            const ELFYAML::HashSection &Section,
                            ContiguousBlobAccumulator &CBA);
@@ -894,6 +897,8 @@ void ELFState<ELFT>::initSectionHeaders(std::vector<Elf_Shdr> &SHeaders,
       writeSectionContent(SHeader, *S, CBA);
     } else if (auto S = dyn_cast<ELFYAML::BBAddrMapSection>(Sec)) {
       writeSectionContent(SHeader, *S, CBA);
+    } else if (auto S = dyn_cast<ELFYAML::FuncMapSection>(Sec)) {
+      writeSectionContent(SHeader, *S, CBA);
     } else {
       llvm_unreachable("Unknown section type");
     }
@@ -1537,6 +1542,31 @@ void ELFState<ELFT>::writeSectionContent(
   }
 }
 
+template <class ELFT>
+void ELFState<ELFT>::writeSectionContent(Elf_Shdr &SHeader,
+                                         const ELFYAML::FuncMapSection &Section,
+                                         ContiguousBlobAccumulator &CBA) {
+  if (!Section.Entries)
+    return;
+
+  for (const auto &[Idx, E] : llvm::enumerate(*Section.Entries)) {
+    // Write version and feature values.
+    if (Section.Type == llvm::ELF::SHT_LLVM_FUNC_MAP) {
+      if (E.Version > 1)
+        WithColor::warning() << "unsupported SHT_LLVM_FUNC_MAP version: "
+                             << static_cast<int>(E.Version)
+                             << "; encoding using the most recent version";
+      CBA.write(E.Version);
+      CBA.write(E.Feature);
+      SHeader.sh_size += 2;
+    }
+    CBA.write<uintX_t>(E.Address, ELFT::Endianness);
+    SHeader.sh_size += sizeof(uintX_t);
+    if (E.DynamicInstCount)
+      SHeader.sh_size += CBA.writeULEB128(E.DynamicInstCount);
+  }
+}
+
 template <class ELFT>
 void ELFState<ELFT>::writeSectionContent(
     Elf_Shdr &SHeader, const ELFYAML::LinkerOptionsSection &Section,
diff --git a/llvm/lib/ObjectYAML/ELFYAML.cpp b/llvm/lib/ObjectYAML/ELFYAML.cpp
index 539834fc8d4dbf..077fd3aed2d242 100644
--- a/llvm/lib/ObjectYAML/ELFYAML.cpp
+++ b/llvm/lib/ObjectYAML/ELFYAML.cpp
@@ -723,6 +723,7 @@ void ScalarEnumerationTraits<ELFYAML::ELF_SHT>::enumeration(
   ECase(SHT_LLVM_PART_PHDR);
   ECase(SHT_LLVM_BB_ADDR_MAP_V0);
   ECase(SHT_LLVM_BB_ADDR_MAP);
+  ECase(SHT_LLVM_FUNC_MAP);
   ECase(SHT_LLVM_OFFLOADING);
   ECase(SHT_LLVM_LTO);
   ECase(SHT_GNU_ATTRIBUTES);
@@ -1432,6 +1433,12 @@ static void sectionMapping(IO &IO, ELFYAML::BBAddrMapSection &Section) {
   IO.mapOptional("PGOAnalyses", Section.PGOAnalyses);
 }
 
+static void sectionMapping(IO &IO, ELFYAML::FuncMapSection &Section) {
+  commonSectionMapping(IO, Section);
+  IO.mapOptional("Content", Section.Content);
+  IO.mapOptional("Entries", Section.Entries);
+}
+
 static void sectionMapping(IO &IO, ELFYAML::StackSizesSection &Section) {
   commonSectionMapping(IO, Section);
   IO.mapOptional("Entries", Section.Entries);
@@ -1725,6 +1732,12 @@ void MappingTraits<std::unique_ptr<ELFYAML::Chunk>>::mapping(
       Section.reset(new ELFYAML::BBAddrMapSection());
     sectionMapping(IO, *cast<ELFYAML::BBAddrMapSection>(Section.get()));
     break;
+  case ELF::SHT_LLVM_FUNC_MAP:
+    if (!IO.outputting())
+      Section.reset(new ELFYAML::FuncMapSection());
+    sectionMapping(IO, *cast<ELFYAML::FuncMapSection>(Section.get()));
+    break;
+
   default:
     if (!IO.outputting()) {
       StringRef Name;
@@ -1848,6 +1861,15 @@ void MappingTraits<ELFYAML::StackSizeEntry>::mapping(
   IO.mapRequired("Size", E.Size);
 }
 
+void MappingTraits<ELFYAML::FuncMapEntry>::mapping(IO &IO,
+                                                   ELFYAML::FuncMapEntry &E) {
+  assert(IO.getContext() && "The IO context is not initialized");
+  IO.mapRequired("Version", E.Version);
+  IO.mapOptional("Feature", E.Feature, Hex8(0));
+  IO.mapOptional("Address", E.Address, Hex64(0));
+  IO.mapOptional("DynInstCnt", E.DynamicInstCount, Hex64(0));
+}
+
 void MappingTraits<ELFYAML::BBAddrMapEntry>::mapping(
     IO &IO, ELFYAML::BBAddrMapEntry &E) {
   assert(IO.getContext() && "The IO context is not initialized");
diff --git a/llvm/test/tools/obj2yaml/ELF/func-map.yaml b/llvm/test/tools/obj2yaml/ELF/func-map.yaml
new file mode 100644
index 00000000000000..b21b637026f549
--- /dev/null
+++ b/llvm/test/tools/obj2yaml/ELF/func-map.yaml
@@ -0,0 +1,139 @@
+## Check how obj2yaml produces YAML .llvm_func_map descriptions.
+
+## Check that obj2yaml uses the "Entries" tag to describe an .llvm_func_map section.
+
+# RUN: yaml2obj --docnum=1 %s -o %t1
+# RUN: obj2yaml %t1 | FileCheck %s --check-prefix=VALID
+
+# VALID:      --- !ELF
+# VALID-NEXT: FileHeader:
+# VALID-NEXT:   Class: ELFCLASS64
+# VALID-NEXT:   Data:  ELFDATA2LSB
+# VALID-NEXT:   Type:  ET_EXEC
+# VALID-NEXT: Sections:
+# VALID-NEXT:   - Name: .llvm_func_map
+# VALID-NEXT:     Type: SHT_LLVM_FUNC_MAP
+# VALID-NEXT:     Entries:
+# VALID-NEXT:       - Version:         1
+# VALID-NEXT:         Feature:         0x1
+## The 'Address' field is omitted when it's zero.
+# VALID-NEXT:         DynInstCnt:      0x10
+# VALID-NEXT:       - Version:         1
+## The 'Feature' field is omitted when it's zero.
+# VALID-NEXT:         Address:         0x1
+# VALID-NEXT:       - Version:         1
+# VALID-NEXT:         Feature:         0x1
+# VALID-NEXT:         Address:         0xFFFFFFFFFFFFFFF1
+# VALID-NEXT:         DynInstCnt:      0xFFFFFFFFFFFFFFF2
+
+--- !ELF
+FileHeader:
+  Class: ELFCLASS64
+  Data:  ELFDATA2LSB
+  Type:  ET_EXEC
+Sections:
+  - Name:   .llvm_func_map
+    Type:   SHT_LLVM_FUNC_MAP
+    ShSize: [[SIZE=<none>]]
+    Entries:
+      - Version: 1
+        Feature: 0x1
+        Address: 0x0
+        DynInstCnt: 0x10
+      - Version: 1
+        Feature: 0x0
+        Address: 0x1
+      - Version: 1
+        Feature: 0x1
+        Address: 0xFFFFFFFFFFFFFFF1
+        DynInstCnt: 0xFFFFFFFFFFFFFFF2
+
+## Check obj2yaml can dump empty .llvm_func_map sections.
+
+# RUN: yaml2obj --docnum=2 %s -o %t2
+# RUN: obj2yaml %t2 | FileCheck %s --check-prefix=EMPTY
+
+# EMPTY:      --- !ELF
+# EMPTY-NEXT: FileHeader:
+# EMPTY-NEXT:   Class: ELFCLASS64
+# EMPTY-NEXT:   Data:  ELFDATA2LSB
+# EMPTY-NEXT:   Type:  ET_EXEC
+# EMPTY-NEXT: Sections:
+# EMPTY-NEXT:   - Name:    .llvm_func_map
+# EMPTY-NEXT:     Type:    SHT_LLVM_FUNC_MAP
+# EMPTY-NOT:      Content:
+
+--- !ELF
+FileHeader:
+  Class: ELFCLASS64
+  Data:  ELFDATA2LSB
+  Type:  ET_EXEC
+Sections:
+  - Name:    .llvm_func_map
+    Type:    SHT_LLVM_FUNC_MAP
+    Content: ""
+
+## Check obj2yaml can dump multiple .llvm_func_map sections.
+
+# RUN: yaml2obj --docnum=3 %s -o %t3
+# RUN: obj2yaml %t3 | FileCheck %s --check-prefix=MULTI
+
+# MULTI:      --- !ELF
+# MULTI-NEXT: FileHeader:
+# MULTI-NEXT:   Class: ELFCLASS64
+# MULTI-NEXT:   Data:  ELFDATA2LSB
+# MULTI-NEXT:   Type:  ET_EXEC
+# MULTI-NEXT: Sections:
+# MULTI-NEXT:   - Name: .llvm_func_map
+# MULTI-NEXT:     Type: SHT_LLVM_FUNC_MAP
+# MULTI-NEXT:     Entries:
+# MULTI-NEXT:       - Version: 1
+# MULTI-NEXT:         Feature: 0x1
+# MULTI-NEXT:         Address: 0x2
+# MULTI-NEXT:         DynInstCnt: 0x3
+# MULTI-NEXT:   - Name: '.llvm_func_map (1)'
+# MULTI-NEXT:     Type: SHT_LLVM_FUNC_MAP
+# MULTI-NEXT:     Entries:
+# MULTI-NEXT:       - Version: 1
+# MULTI-NEXT:         Feature: 0x1
+# MULTI-NEXT:         Address: 0xA
+# MULTI-NEXT:         DynInstCnt: 0xB
+
+--- !ELF
+FileHeader:
+  Class: ELFCLASS64
+  Data:  ELFDATA2LSB
+  Type:  ET_EXEC
+Sections:
+  - Name: .llvm_func_map
+    Type: SHT_LLVM_FUNC_MAP
+    Entries:
+      - Version: 1
+        Feature: 0x1
+        Address: 0x2
+        DynInstCnt: 0x3
+  - Name: '.llvm_func_map (1)'
+    Type:  SHT_LLVM_FUNC_MAP
+    Entries:
+      - Version: 1
+        Feature: 0x1
+        Address: 0xA
+        DynInstCnt: 0xB
+
+## Check that obj2yaml uses the "Content" tag to describe an .llvm_func_map section
+## when it can't extract the entries, for example, when the section is truncated.
+
+# RUN: yaml2obj --docnum=1 -DSIZE=0x8 %s -o %t4
+# RUN: obj2yaml %t4 | FileCheck %s --check-prefixes=TRUNCATED,INVALID
+
+
+# INVALID:           --- !ELF
+# INVALID-NEXT:      FileHeader:
+# INVALID-NEXT:        Class: ELFCLASS64
+# INVALID-NEXT:        Data:  ELFDATA2LSB
+# INVALID-NEXT:        Type:  ET_EXEC
+# INVALID-NEXT:      Sections:
+# INVALID-NEXT:        - Name:    .llvm_func_map
+# INVALID-NEXT:          Type:    SHT_LLVM_FUNC_MAP
+# BADNUM-NEXT:           Content: {{([[:xdigit:]]+)}}{{$}}
+# TRUNCATED-NEXT:        Content: '{{([[:xdigit:]]{16})}}'{{$}}
diff --git a/llvm/test/tools/yaml2obj/ELF/func-map.yaml b/llvm/test/tools/yaml2obj/ELF/func-map.yaml
new file mode 100644
index 00000000000000..9fee4038b79312
--- /dev/null
+++ b/llvm/test/tools/yaml2obj/ELF/func-map.yaml
@@ -0,0 +1,116 @@
+## Check how yaml2obj produces .llvm_func_map sections.
+
+# RUN: yaml2obj --docnum=1 %s -o %t1
+# RUN: llvm-readobj --sections --section-data %t1 | FileCheck %s
+
+## Case 1: Specify content.
+# CHECK:      Section {
+# CHECK:        Index: 1
+# CHECK-NEXT:   Name: .llvm_func_map (1)
+# CHECK-NEXT:   Type: SHT_LLVM_FUNC_MAP (0x6FFF4C0E)
+# CHECK-NEXT:   Flags [ (0x0)
+# CHECK-NEXT:   ]
+# CHECK-NEXT:   Address: 0x0
+# CHECK-NEXT:   Offset: 0x40
+# CHECK-NEXT:   Size: 13
+# CHECK-NEXT:   Link: 0
+# CHECK-NEXT:   Info: 0
+# CHECK-NEXT:   AddressAlignment: 0
+# CHECK-NEXT:   EntrySize: 0
+# CHECK-NEXT:   SectionData (
+# CHECK-NEXT:     0000: 00000000 00000000 01010203 04
+# CHECK-NEXT:   )
+# CHECK-NEXT: }
+
+## Case 2: Empty.
+# CHECK:        Name: .llvm_func_map (1)
+# CHECK:        Size:
+# CHECK-SAME:   {{^ 0$}}
+
+## Case 3: Specify Size only.
+# CHECK:        Name: .llvm_func_map (1)
+# CHECK:        SectionData (
+# CHECK-NEXT:     0000: 00000000 00000000
+# CHECK-NEXT:   )
+
+# Case 4: Specify Entries.
+# CHECK:        Name: .llvm_func_map (1)
+# CHECK:        SectionData (
+# CHECK-NEXT:     0000: 01012222 02000000 000010
+# CHECK-NEXT:   )
+
+
+
+
+--- !ELF
+FileHeader:
+  Class: ELFCLASS64
+  Data:  ELFDATA2LSB
+  Type:  ET_EXEC
+Sections:
+
+## Test the following cases:
+
+## 1) We can produce an .llvm_func_map section from a description with section
+##    content.
+##  Specify Content.
+  - Name:    '.llvm_func_map (1)'
+    Type:    SHT_LLVM_FUNC_MAP
+    Content: "00000000000000000101020304"
+
+# 2) We can produce an empty .llvm_func_map section from a description
+#    with empty section content.
+  - Name: '.llvm_func_map (2)'
+    Type: SHT_LLVM_FUNC_MAP
+
+## 3) We can produce a zero .llvm_func_map section of a specific size when
+##    we specify the size only.
+  - Name: '.llvm_func_map (3)'
+    Type: SHT_LLVM_FUNC_MAP
+    Size: 8
+
+## 4) We can produce an .llvm_func_map section from a description with
+##    Entries.
+  - Name: '.llvm_func_map (4)'
+    Type: SHT_LLVM_FUNC_MAP
+    Entries:
+      - Version: 1
+        Feature: 0x1
+        Address: 0x22222
+        DynInstCnt: 0x10
+
+## Check we can't use Entries at the same time as either Content or Size.
+# RUN: not yaml2obj --docnum=2 -DCONTENT="00" %s 2>&1 | FileCheck %s --check-prefix=INVALID
+# RUN: not yaml2obj --docnum=2 -DSIZE="0" %s 2>&1 | FileCheck %s --check-prefix=INVALID
+
+# INVALID: error: "Entries" cannot be used with "Content" or "Size"
+
+--- !ELF
+FileHeader:
+  Class: ELFCLASS64
+  Data:  ELFDATA2LSB
+  Type:  ET_EXEC
+Sections:
+##  Specify Content and Size
+  - Name:    '.llvm_func_map'
+    Type:    SHT_LLVM_FUNC_MAP
+    Entries: []
+    Content: [[CONTENT=<none>]]
+    Size:    [[SIZE=<none>]]
+
+## Check that yaml2obj generates a warning when we use unsupported versions.
+# RUN: yaml2obj --docnum=3  %s 2>&1 | FileCheck %s --check-prefix=INVALID-VERSION
+
+# INVALID-VERSION: warning: unsupported SHT_LLVM_FUNC_MAP version: 2; encoding using the most recent version
+
+--- !ELF
+FileHeader:
+  Class: ELFCLASS64
+  Data:  ELFDATA2LSB
+  Type:  ET_EXEC
+Sections:
+  - Name: '.llvm_func_map'
+    Type: SHT_LLVM_FUNC_MAP
+    Entries:
+#  Specify unsupported version
+      - Version: 2
diff --git a/llvm/tools/obj2yaml/elf2yaml.cpp b/llvm/tools/obj2yaml/elf2yaml.cpp
index b1c8032ea21929..ad2eb50937c210 100644
--- a/llvm/tools/obj2yaml/elf2yaml.cpp
+++ b/llvm/tools/obj2yaml/elf2yaml.cpp
@@ -97,6 +97,7 @@ class ELFDumper {
   dumpStackSizesSection(const Elf_Shdr *Shdr);
   Expected<ELFYAML::BBAddrMapSection *>
   dumpBBAddrMapSection(const Elf_Shdr *Shdr);
+  Expected<ELFYAML::FuncMapSection *> dumpFuncMapSection(const Elf_Shdr *Shdr);
   Expected<ELFYAML::RawContentSection *>
   dumpPlaceholderSection(const Elf_Shdr *Shdr);
 
@@ -629,6 +630,8 @@ ELFDumper<ELFT>::dumpSections() {
           [this](const Elf_Shdr *S) { return dumpCallGraphProfileSection(S); };
     case ELF::SHT_LLVM_BB_ADDR_MAP:
       return [this](const Elf_Shdr *S) { return dumpBBAddrMapSection(S); };
+    case ELF::SHT_LLVM_FUNC_MAP:
+      return [this](const Elf_Shdr *S) { return dumpFuncMapSection(S); };
     case ELF::SHT_STRTAB:
     case ELF::SHT_SYMTAB:
     case ELF::SHT_DYNSYM:
@@ -989,6 +992,55 @@ ELFDumper<ELFT>::dumpBBAddrMapSection(const Elf_Shdr *Shdr) {
   return S.release();
 }
 
+template <class ELFT>
+Expected<ELFYAML::FuncMapSection *>
+ELFDumper<ELFT>::dumpFuncMapSection(const Elf_Shdr *Shdr) {
+  auto S = std::make_unique<ELFYAML::FuncMapSection>();
+  if (Error E = dumpCommonSection(Shdr, *S))
+    return std::move(E);
+
+  auto ContentOrErr = Obj.getSectionContents(*Shdr);
+  if (!ContentOrErr)
+    return ContentOrErr.takeError();
+
+  ArrayRef<uint8_t> Content = *ContentOrErr;
+  if (Content.empty())
+    return S.release();
+
+  DataExtractor Data(Content, Obj.isLE(), ELFT::Is64Bits ? 8 : 4);
+
+  std::vector<ELFYAML::FuncMapEntry> Entries;
+  DataExtractor::Cursor Cur(0);
+  uint8_t Version = 0;
+  uint8_t Feature = 0;
+  uint64_t Address = 0;
+  while (Cur && Cur.tell() < Content.size()) {
+    if (Shdr->sh_type == ELF::SHT_LLVM_FUNC_MAP) {
+      Version = Data.getU8(Cur);
+      Feature = Data.getU8(Cur);
+    }
+    auto FeatureOrErr = llvm::object::FuncMap::Features::decode(Feature);
+    if (!FeatureOrErr)
+      return FeatureOrErr.takeError();
+
+    Address = Data.getAddress(Cur);
+
+    uint64_t DynamicInstCount =
+        FeatureOrErr->DynamicInstCount ? Data.getULEB128(Cur) : 0;
+    Entries.push_back({Version, Feature, Address, DynamicInstCount});
+  }
+
+  if (!Cur) {
+    // If the section cannot be decoded, we dump it as an array of bytes.
+    consumeError(Cur.takeError());
+    S->Content = yaml::BinaryRef(Content);
+  } else {
+    S->Entries = std::move(Entries);
+  }
+
+  return S.release();
+}
+
 template <class ELFT>
 Expected<ELFYAML::AddrsigSection *>
 ELFDumper<ELFT>::dumpAddrsigSection(const Elf_Shdr *Shdr) {

``````````

</details>


https://github.com/llvm/llvm-project/pull/124332


More information about the llvm-commits mailing list