[llvm] [SHT_LLVM_FUNC_ADDR_MAP] Introduce function address map section and emit dynamic instruction count (PR #123804)

Lei Wang via llvm-commits llvm-commits at lists.llvm.org
Tue Jan 21 10:52:43 PST 2025


https://github.com/wlei-llvm created https://github.com/llvm/llvm-project/pull/123804

None

>From 29e8cd21f841408f04bd9c81bfc6be180290dc17 Mon Sep 17 00:00:00 2001
From: wlei <wlei at fb.com>
Date: Mon, 20 Jan 2025 21:55:36 -0800
Subject: [PATCH] [SHT_LLVM_FUNC_ADDR_MAP] Introduce function address map
 section and emit dynamic instruction count feature

---
 llvm/include/llvm/BinaryFormat/ELF.h          |   1 +
 llvm/include/llvm/CodeGen/AsmPrinter.h        |   2 +
 llvm/include/llvm/MC/MCContext.h              |   5 +
 llvm/include/llvm/MC/MCObjectFileInfo.h       |   2 +
 llvm/include/llvm/Object/ELF.h                |   4 +
 llvm/include/llvm/Object/ELFTypes.h           |  38 +++++++
 llvm/include/llvm/ObjectYAML/ELFYAML.h        |  27 +++++
 llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp    |  57 +++++++++-
 llvm/lib/MC/MCObjectFileInfo.cpp              |  18 +++
 llvm/lib/MC/MCParser/ELFAsmParser.cpp         |   2 +
 llvm/lib/MC/MCSectionELF.cpp                  |   2 +
 llvm/lib/Object/ELF.cpp                       | 106 +++++++++++++++++-
 llvm/lib/ObjectYAML/ELFEmitter.cpp            |  23 ++++
 llvm/lib/ObjectYAML/ELFYAML.cpp               |  21 ++++
 .../function-address-map-function-sections.ll |  41 +++++++
 llvm/test/MC/AsmParser/llvm_section_types.s   |   5 +
 .../tools/llvm-readobj/ELF/func-addr-map.test |  96 ++++++++++++++++
 llvm/tools/llvm-readobj/ELFDumper.cpp         |  61 ++++++++++
 llvm/tools/llvm-readobj/ObjDumper.h           |   1 +
 llvm/tools/llvm-readobj/Opts.td               |   1 +
 llvm/tools/llvm-readobj/llvm-readobj.cpp      |   4 +
 llvm/tools/obj2yaml/elf2yaml.cpp              |  53 +++++++++
 22 files changed, 568 insertions(+), 2 deletions(-)
 create mode 100644 llvm/test/CodeGen/X86/function-address-map-function-sections.ll
 create mode 100644 llvm/test/tools/llvm-readobj/ELF/func-addr-map.test

diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h
index 1bc69f791bd84c..3bcc1248bf8269 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_ADDR_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/CodeGen/AsmPrinter.h b/llvm/include/llvm/CodeGen/AsmPrinter.h
index 5291369b3b9f1d..82ebf0361c13d8 100644
--- a/llvm/include/llvm/CodeGen/AsmPrinter.h
+++ b/llvm/include/llvm/CodeGen/AsmPrinter.h
@@ -414,6 +414,8 @@ class AsmPrinter : public MachineFunctionPass {
 
   void emitBBAddrMapSection(const MachineFunction &MF);
 
+  void emitFuncAddrMapSection(const MachineFunction &MF);
+
   void emitKCFITrapEntry(const MachineFunction &MF, const MCSymbol *Symbol);
   virtual void emitKCFITypeId(const MachineFunction &MF);
 
diff --git a/llvm/include/llvm/MC/MCContext.h b/llvm/include/llvm/MC/MCContext.h
index 57ba40f7ac26fc..8ba1c30d8a499d 100644
--- a/llvm/include/llvm/MC/MCContext.h
+++ b/llvm/include/llvm/MC/MCContext.h
@@ -177,6 +177,9 @@ class MCContext {
   /// LLVM_BB_ADDR_MAP version to emit.
   uint8_t BBAddrMapVersion = 2;
 
+  /// LLVM_FUNC_ADDR_MAP version to emit.
+  uint8_t FuncAddrMapVersion = 1;
+
   /// The file name of the log file from the environment variable
   /// AS_SECURE_LOG_FILE.  Which must be set before the .secure_log_unique
   /// directive is used or it is an error.
@@ -656,6 +659,8 @@ class MCContext {
 
   uint8_t getBBAddrMapVersion() const { return BBAddrMapVersion; }
 
+  uint8_t getFuncAddrMapVersion() const { return FuncAddrMapVersion; }
+
   /// @}
 
   /// \name Dwarf Management
diff --git a/llvm/include/llvm/MC/MCObjectFileInfo.h b/llvm/include/llvm/MC/MCObjectFileInfo.h
index fb575fe721015c..4bd34947762c01 100644
--- a/llvm/include/llvm/MC/MCObjectFileInfo.h
+++ b/llvm/include/llvm/MC/MCObjectFileInfo.h
@@ -364,6 +364,8 @@ class MCObjectFileInfo {
 
   MCSection *getBBAddrMapSection(const MCSection &TextSec) const;
 
+  MCSection *getFuncAddrMapSection(const MCSection &TextSec) const;
+
   MCSection *getKCFITrapSection(const MCSection &TextSec) const;
 
   MCSection *getPseudoProbeSection(const MCSection &TextSec) const;
diff --git a/llvm/include/llvm/Object/ELF.h b/llvm/include/llvm/Object/ELF.h
index 3aa1d7864fcb70..b179889d601252 100644
--- a/llvm/include/llvm/Object/ELF.h
+++ b/llvm/include/llvm/Object/ELF.h
@@ -513,6 +513,10 @@ class ELFFile {
   decodeBBAddrMap(const Elf_Shdr &Sec, const Elf_Shdr *RelaSec = nullptr,
                   std::vector<PGOAnalysisMap> *PGOAnalyses = nullptr) const;
 
+  Expected<std::vector<FuncAddrMap>>
+  decodeFuncAddrMap(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/include/llvm/Object/ELFTypes.h b/llvm/include/llvm/Object/ELFTypes.h
index 87e4dbe4480910..7d79fb82c046f9 100644
--- a/llvm/include/llvm/Object/ELFTypes.h
+++ b/llvm/include/llvm/Object/ELFTypes.h
@@ -1027,6 +1027,44 @@ struct PGOAnalysisMap {
   }
 };
 
+// Struct representing the FuncAddrMap for one function.
+struct FuncAddrMap {
+
+  // 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 FuncAddrMap::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..80d6a9ff83439a 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 FuncAddrMapEntry {
+  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,
+    FuncAddrMap,
 
     // Special chunks.
     SpecialChunksStart,
@@ -355,6 +363,20 @@ struct BBAddrMapSection : Section {
   }
 };
 
+struct FuncAddrMapSection : Section {
+  std::optional<std::vector<FuncAddrMapEntry>> Entries;
+
+  FuncAddrMapSection() : Section(ChunkKind::FuncAddrMap) {}
+
+  std::vector<std::pair<StringRef, bool>> getEntries() const override {
+    return {{"Entries", Entries.has_value()}};
+  };
+
+  static bool classof(const Chunk *S) {
+    return S->Kind == ChunkKind::FuncAddrMap;
+  }
+};
+
 struct StackSizesSection : Section {
   std::optional<std::vector<StackSizeEntry>> Entries;
 
@@ -762,6 +784,7 @@ bool shouldAllocateFileSpace(ArrayRef<ProgramHeader> Phdrs,
 } // end namespace llvm
 
 LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::StackSizeEntry)
+LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::FuncAddrMapEntry)
 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 +952,10 @@ template <> struct MappingTraits<ELFYAML::StackSizeEntry> {
   static void mapping(IO &IO, ELFYAML::StackSizeEntry &Rel);
 };
 
+template <> struct MappingTraits<ELFYAML::FuncAddrMapEntry> {
+  static void mapping(IO &IO, ELFYAML::FuncAddrMapEntry &E);
+};
+
 template <> struct MappingTraits<ELFYAML::BBAddrMapEntry> {
   static void mapping(IO &IO, ELFYAML::BBAddrMapEntry &E);
 };
diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
index b2a4721f37b268..4c56c306b5fc34 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -147,6 +147,11 @@ enum class PGOMapFeaturesEnum {
   BrProb,
   All,
 };
+
+enum class FuncAddrMapFeaturesEnum {
+  DynamicInstCount,
+};
+
 static cl::bits<PGOMapFeaturesEnum> PgoAnalysisMapFeatures(
     "pgo-analysis-map", cl::Hidden, cl::CommaSeparated,
     cl::values(
@@ -173,6 +178,13 @@ static cl::opt<bool> EmitJumpTableSizesSection(
     cl::desc("Emit a section containing jump table addresses and sizes"),
     cl::Hidden, cl::init(false));
 
+static cl::bits<FuncAddrMapFeaturesEnum> FuncAddrMapFeatures(
+    "func-addr-map", cl::Hidden, cl::CommaSeparated,
+    cl::values(clEnumValN(FuncAddrMapFeaturesEnum::DynamicInstCount,
+                          "dyn-inst-count", "Dynamic instruction count")),
+    cl::desc("Emit features of function address map in SHT_LLVM_FUNC_ADDR_MAP "
+             "section"));
+
 // This isn't turned on by default, since several of the scheduling models are
 // not completely accurate, and we don't want to be misleading.
 static cl::opt<bool> PrintLatency(
@@ -1390,6 +1402,14 @@ static uint32_t getBBAddrMapMetadata(const MachineBasicBlock &MBB) {
       .encode();
 }
 
+static llvm::object::FuncAddrMap::Features getFuncAddrMapFeature() {
+  return {FuncAddrMapFeatures.isSet(FuncAddrMapFeaturesEnum::DynamicInstCount)};
+}
+
+static bool isAnyFuncAddrMapFeature() {
+  return FuncAddrMapFeatures.getBits() != 0;
+}
+
 static llvm::object::BBAddrMap::Features
 getBBAddrMapFeature(const MachineFunction &MF, int NumMBBSectionRanges) {
   // Ensure that the user has not passed in additional options while also
@@ -1424,6 +1444,39 @@ getBBAddrMapFeature(const MachineFunction &MF, int NumMBBSectionRanges) {
           static_cast<bool>(BBAddrMapSkipEmitBBEntries)};
 }
 
+void AsmPrinter::emitFuncAddrMapSection(const MachineFunction &MF) {
+  if (!isAnyFuncAddrMapFeature())
+    return;
+
+  MCSection *FuncAddrMapSection =
+      getObjFileLowering().getFuncAddrMapSection(*MF.getSection());
+  assert(FuncAddrMapSection &&
+         ".llvm_func_addr_map section is not initialized.");
+  const MCSymbol *FunctionSymbol = getFunctionBegin();
+  OutStreamer->pushSection();
+  OutStreamer->switchSection(FuncAddrMapSection);
+  OutStreamer->AddComment("version");
+  uint8_t FuncAddrMapVersion =
+      OutStreamer->getContext().getFuncAddrMapVersion();
+  OutStreamer->emitInt8(FuncAddrMapVersion);
+  OutStreamer->AddComment("feature");
+  auto Features = getFuncAddrMapFeature();
+  OutStreamer->emitInt8(Features.encode());
+
+  OutStreamer->AddComment("function address");
+  OutStreamer->emitSymbolValue(FunctionSymbol, getPointerSize());
+  if (Features.DynamicInstCount) {
+    const MachineBlockFrequencyInfo *MBFI =
+        &getAnalysis<LazyMachineBlockFrequencyInfoPass>().getBFI();
+    uint64_t DynInstCount = 0;
+    for (const MachineBasicBlock &MBB : MF)
+      DynInstCount += MBFI->getBlockProfileCount(&MBB).value_or(0) * MBB.size();
+    OutStreamer->AddComment("Dynamic instruction count");
+    OutStreamer->emitULEB128IntValue(DynInstCount);
+  }
+  OutStreamer->popSection();
+}
+
 void AsmPrinter::emitBBAddrMapSection(const MachineFunction &MF) {
   MCSection *BBAddrMapSection =
       getObjFileLowering().getBBAddrMapSection(*MF.getSection());
@@ -2119,6 +2172,8 @@ void AsmPrinter::emitFunctionBody() {
       MF->getContext().reportWarning(
           SMLoc(), "pgo-analysis-map is enabled for function " + MF->getName() +
                        " but it does not have labels");
+
+    emitFuncAddrMapSection(*MF);
   }
 
   // Emit sections containing instruction and function PCs.
@@ -2749,7 +2804,7 @@ void AsmPrinter::SetupMachineFunction(MachineFunction &MF) {
       F.hasFnAttribute("xray-instruction-threshold") ||
       needFuncLabels(MF, *this) || NeedsLocalForSize ||
       MF.getTarget().Options.EmitStackSizeSection ||
-      MF.getTarget().Options.BBAddrMap) {
+      MF.getTarget().Options.BBAddrMap || isAnyFuncAddrMapFeature()) {
     CurrentFnBegin = createTempSymbol("func_begin");
     if (NeedsLocalForSize)
       CurrentFnSymForSize = CurrentFnBegin;
diff --git a/llvm/lib/MC/MCObjectFileInfo.cpp b/llvm/lib/MC/MCObjectFileInfo.cpp
index 150e38a94db6a6..47ed5d91c80849 100644
--- a/llvm/lib/MC/MCObjectFileInfo.cpp
+++ b/llvm/lib/MC/MCObjectFileInfo.cpp
@@ -1120,6 +1120,24 @@ MCObjectFileInfo::getBBAddrMapSection(const MCSection &TextSec) const {
                             cast<MCSymbolELF>(TextSec.getBeginSymbol()));
 }
 
+MCSection *
+MCObjectFileInfo::getFuncAddrMapSection(const MCSection &TextSec) const {
+  if (Ctx->getObjectFileType() != MCContext::IsELF)
+    return nullptr;
+
+  const MCSectionELF &ElfSec = static_cast<const MCSectionELF &>(TextSec);
+  unsigned Flags = ELF::SHF_LINK_ORDER;
+  StringRef GroupName;
+  if (const MCSymbol *Group = ElfSec.getGroup()) {
+    GroupName = Group->getName();
+    Flags |= ELF::SHF_GROUP;
+  }
+
+  return Ctx->getELFSection(".llvm_func_addr_map", ELF::SHT_LLVM_FUNC_ADDR_MAP,
+                            Flags, 0, GroupName, true, ElfSec.getUniqueID(),
+                            cast<MCSymbolELF>(TextSec.getBeginSymbol()));
+}
+
 MCSection *
 MCObjectFileInfo::getKCFITrapSection(const MCSection &TextSec) const {
   if (Ctx->getObjectFileType() != MCContext::IsELF)
diff --git a/llvm/lib/MC/MCParser/ELFAsmParser.cpp b/llvm/lib/MC/MCParser/ELFAsmParser.cpp
index b58210b3c268e9..044e9c19f4b777 100644
--- a/llvm/lib/MC/MCParser/ELFAsmParser.cpp
+++ b/llvm/lib/MC/MCParser/ELFAsmParser.cpp
@@ -679,6 +679,8 @@ bool ELFAsmParser::parseSectionArguments(bool IsPush, SMLoc loc) {
       Type = ELF::SHT_LLVM_LTO;
     else if (TypeName == "llvm_jt_sizes")
       Type = ELF::SHT_LLVM_JT_SIZES;
+    else if (TypeName == "llvm_func_addr_map")
+      Type = ELF::SHT_LLVM_FUNC_ADDR_MAP;
     else if (TypeName.getAsInteger(0, Type))
       return TokError("unknown section type");
   }
diff --git a/llvm/lib/MC/MCSectionELF.cpp b/llvm/lib/MC/MCSectionELF.cpp
index 25e62b70b5e2a0..8603ea43ede867 100644
--- a/llvm/lib/MC/MCSectionELF.cpp
+++ b/llvm/lib/MC/MCSectionELF.cpp
@@ -174,6 +174,8 @@ void MCSectionELF::printSwitchToSection(const MCAsmInfo &MAI, const Triple &T,
     OS << "llvm_lto";
   else if (Type == ELF::SHT_LLVM_JT_SIZES)
     OS << "llvm_jt_sizes";
+  else if (Type == ELF::SHT_LLVM_FUNC_ADDR_MAP)
+    OS << "llvm_func_addr_map";
   else
     OS << "0x" << Twine::utohexstr(Type);
 
diff --git a/llvm/lib/Object/ELF.cpp b/llvm/lib/Object/ELF.cpp
index b6d0699ee4fe08..bb5e6fa5970125 100644
--- a/llvm/lib/Object/ELF.cpp
+++ b/llvm/lib/Object/ELF.cpp
@@ -320,7 +320,8 @@ StringRef llvm::object::getELFSectionTypeName(uint32_t Machine, unsigned Type) {
     STRINGIFY_ENUM_CASE(ELF, SHT_LLVM_BB_ADDR_MAP);
     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_JT_SIZES);
+    STRINGIFY_ENUM_CASE(ELF, SHT_LLVM_FUNC_ADDR_MAP);
     STRINGIFY_ENUM_CASE(ELF, SHT_GNU_ATTRIBUTES);
     STRINGIFY_ENUM_CASE(ELF, SHT_GNU_HASH);
     STRINGIFY_ENUM_CASE(ELF, SHT_GNU_verdef);
@@ -731,6 +732,101 @@ static IntTy readULEB128As(DataExtractor &Data, DataExtractor::Cursor &Cur,
   return static_cast<IntTy>(Value);
 }
 
+template <typename ELFT>
+static Expected<std::vector<FuncAddrMap>>
+decodeFuncAddrMapImpl(const ELFFile<ELFT> &EF,
+                      const typename ELFFile<ELFT>::Elf_Shdr &Sec,
+                      const typename ELFFile<ELFT>::Elf_Shdr *RelaSec) {
+  bool IsRelocatable = EF.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 = EF.relas(*RelaSec);
+    if (!Relas)
+      return createError("unable to read relocations for section " +
+                         describe(EF, 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(EF, Sec));
+    }
+    return FOTIterator->second;
+  };
+  Expected<ArrayRef<uint8_t>> ContentsOrErr = EF.getSectionContents(Sec);
+  if (!ContentsOrErr)
+    return ContentsOrErr.takeError();
+  ArrayRef<uint8_t> Content = *ContentsOrErr;
+  DataExtractor Data(Content, EF.isLE(), ELFT::Is64Bits ? 8 : 4);
+  std::vector<FuncAddrMap> 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;
+  FuncAddrMap::Features FeatEnable{};
+  while (!ULEBSizeErr && Cur && Cur.tell() < Content.size()) {
+    if (Sec.sh_type == ELF::SHT_LLVM_FUNC_ADDR_MAP) {
+      Version = Data.getU8(Cur);
+      if (!Cur)
+        break;
+      Feature = Data.getU8(Cur); // Feature byte
+      if (!Cur)
+        break;
+      auto FeatEnableOrErr = FuncAddrMap::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 <typename ELFT>
 static Expected<std::vector<BBAddrMap>>
 decodeBBAddrMapImpl(const ELFFile<ELFT> &EF,
@@ -939,6 +1035,14 @@ ELFFile<ELFT>::decodeBBAddrMap(const Elf_Shdr &Sec, const Elf_Shdr *RelaSec,
   return std::move(AddrMapsOrErr);
 }
 
+template <class ELFT>
+Expected<std::vector<FuncAddrMap>>
+ELFFile<ELFT>::decodeFuncAddrMap(const Elf_Shdr &Sec,
+                                 const Elf_Shdr *RelaSec) const {
+  auto AddrMapsOrErr = decodeFuncAddrMapImpl(*this, Sec, RelaSec);
+  return std::move(AddrMapsOrErr);
+}
+
 template <class ELFT>
 Expected<
     MapVector<const typename ELFT::Shdr *, const typename ELFT::Shdr *>>
diff --git a/llvm/lib/ObjectYAML/ELFEmitter.cpp b/llvm/lib/ObjectYAML/ELFEmitter.cpp
index cc41bbe6bbde24..2a923515f7ef97 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::FuncAddrMapSection &Section,
+                           ContiguousBlobAccumulator &CBA);
   void writeSectionContent(Elf_Shdr &SHeader,
                            const ELFYAML::HashSection &Section,
                            ContiguousBlobAccumulator &CBA);
@@ -898,6 +901,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::FuncAddrMapSection>(Sec)) {
+      writeSectionContent(SHeader, *S, CBA);
     } else {
       llvm_unreachable("Unknown section type");
     }
@@ -1541,6 +1546,24 @@ void ELFState<ELFT>::writeSectionContent(
   }
 }
 
+template <class ELFT>
+void ELFState<ELFT>::writeSectionContent(
+    Elf_Shdr &SHeader, const ELFYAML::FuncAddrMapSection &Section,
+    ContiguousBlobAccumulator &CBA) {
+  for (const auto &[Idx, E] : llvm::enumerate(*Section.Entries)) {
+    // Write version and feature values.
+    if (Section.Type == llvm::ELF::SHT_LLVM_FUNC_ADDR_MAP) {
+      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 7e94d01a971534..1c7bbc5239a1b1 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_ADDR_MAP);
   ECase(SHT_LLVM_OFFLOADING);
   ECase(SHT_LLVM_LTO);
   ECase(SHT_GNU_ATTRIBUTES);
@@ -1425,6 +1426,12 @@ static void sectionMapping(IO &IO, ELFYAML::RawContentSection &Section) {
   IO.mapOptional("Info", Section.Info);
 }
 
+static void sectionMapping(IO &IO, ELFYAML::FuncAddrMapSection &Section) {
+  commonSectionMapping(IO, Section);
+  IO.mapOptional("Content", Section.Content);
+  IO.mapOptional("Entries", Section.Entries);
+}
+
 static void sectionMapping(IO &IO, ELFYAML::BBAddrMapSection &Section) {
   commonSectionMapping(IO, Section);
   IO.mapOptional("Content", Section.Content);
@@ -1725,6 +1732,11 @@ 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_ADDR_MAP:
+    if (!IO.outputting())
+      Section.reset(new ELFYAML::FuncAddrMapSection());
+    sectionMapping(IO, *cast<ELFYAML::FuncAddrMapSection>(Section.get()));
+    break;
   default:
     if (!IO.outputting()) {
       StringRef Name;
@@ -1846,6 +1858,15 @@ void MappingTraits<ELFYAML::StackSizeEntry>::mapping(
   IO.mapRequired("Size", E.Size);
 }
 
+void MappingTraits<ELFYAML::FuncAddrMapEntry>::mapping(
+    IO &IO, ELFYAML::FuncAddrMapEntry &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/CodeGen/X86/function-address-map-function-sections.ll b/llvm/test/CodeGen/X86/function-address-map-function-sections.ll
new file mode 100644
index 00000000000000..4590dfd5da2ff8
--- /dev/null
+++ b/llvm/test/CodeGen/X86/function-address-map-function-sections.ll
@@ -0,0 +1,41 @@
+; RUN: llc < %s -mtriple=x86_64 -function-sections -func-addr-map=dyn-inst-count| FileCheck %s
+
+$_Z4fooTIiET_v = comdat any
+
+define dso_local i32 @_Z3barv() {
+  ret i32 0
+}
+;; Check we add SHF_LINK_ORDER for .llvm_func_addr_map and link it with the corresponding .text sections.
+; CHECK:		.section .text._Z3barv,"ax", at progbits
+; CHECK-LABEL:	_Z3barv:
+; CHECK-NEXT:	[[BAR_BEGIN:.Lfunc_begin[0-9]+]]:
+; CHECK:		.section .llvm_func_addr_map,"o", at llvm_func_addr_map,.text._Z3barv{{$}}
+; CHECK-NEXT:		.byte 1			# version
+; CHECK-NEXT:		.byte 1			# feature
+; CHECK-NEXT:		.quad [[BAR_BEGIN]]	# function address
+
+
+define dso_local i32 @_Z3foov() {
+  %1 = call i32 @_Z4fooTIiET_v()
+  ret i32 %1
+}
+; CHECK:		.section .text._Z3foov,"ax", at progbits
+; CHECK-LABEL:	_Z3foov:
+; CHECK-NEXT:	[[FOO_BEGIN:.Lfunc_begin[0-9]+]]:
+; CHECK:		.section  .llvm_func_addr_map,"o", at llvm_func_addr_map,.text._Z3foov{{$}}
+; CHECK-NEXT:		.byte 1			# version
+; CHECK-NEXT:		.byte 1			# feature
+; CHECK-NEXT:		.quad [[FOO_BEGIN]]	# function address
+
+
+define linkonce_odr dso_local i32 @_Z4fooTIiET_v() comdat {
+  ret i32 0
+}
+;; Check we add .llvm_func_addr_map section to a COMDAT group with the corresponding .text section if such a COMDAT exists.
+; CHECK:		.section .text._Z4fooTIiET_v,"axG", at progbits,_Z4fooTIiET_v,comdat
+; CHECK-LABEL:	_Z4fooTIiET_v:
+; CHECK-NEXT:	[[FOOCOMDAT_BEGIN:.Lfunc_begin[0-9]+]]:
+; CHECK:		.section .llvm_func_addr_map,"oG", at llvm_func_addr_map,.text._Z4fooTIiET_v,_Z4fooTIiET_v,comdat{{$}}
+; CHECK-NEXT:		.byte 1				# version
+; CHECK-NEXT:		.byte 1				# feature
+; CHECK-NEXT:		.quad [[FOOCOMDAT_BEGIN]]	# function address
diff --git a/llvm/test/MC/AsmParser/llvm_section_types.s b/llvm/test/MC/AsmParser/llvm_section_types.s
index 147b1499d2b888..ab30a7565104e8 100644
--- a/llvm/test/MC/AsmParser/llvm_section_types.s
+++ b/llvm/test/MC/AsmParser/llvm_section_types.s
@@ -17,6 +17,9 @@
 .byte 1
 .section    .section8,"", at llvm_lto
 .byte 1
+.section    .section9,"", at llvm_func_addr_map
+.byte 1
+
 
 # CHECK:        Name: .section1
 # CHECK-NEXT:   Type: SHT_LLVM_BB_ADDR_MAP
@@ -34,3 +37,5 @@
 # CHECK-NEXT:   Type: SHT_LLVM_OFFLOADING
 # CHECK:        Name: .section8
 # CHECK-NEXT:   Type: SHT_LLVM_LTO
+# CHECK:        Name: .section9
+# CHECK-NEXT:   Type: SHT_LLVM_FUNC_ADDR_MAP
diff --git a/llvm/test/tools/llvm-readobj/ELF/func-addr-map.test b/llvm/test/tools/llvm-readobj/ELF/func-addr-map.test
new file mode 100644
index 00000000000000..cf88a5c2dfb62c
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/ELF/func-addr-map.test
@@ -0,0 +1,96 @@
+## This test checks how we handle the --func-addr-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-addr-map 2>&1 | FileCheck %s -DADDR=0x999999999 -DFILE=%t1.x64.o --check-prefix=CHECK
+# RUN: llvm-readelf %t1.x64.o --func-addr-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-addr-map 2>&1 | FileCheck -DADDR=0x11111 %s -DFILE=%t1.x32.o --check-prefix=CHECK
+# RUN: llvm-readelf %t1.x32.o --func-addr-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-addr-map 2>&1 | FileCheck %s -DOFFSET=0x3 -DFILE=%t2.o --check-prefix=TRUNCATED
+
+# CHECK:      FuncAddrMap [
+# CHECK-NEXT:   Function {
+# CHECK-NEXT:     At: [[ADDR]]
+# CHECK-NEXT: warning: '[[FILE]]': could not identify function symbol for address ([[ADDR]]) in SHT_LLVM_FUNC_ADDR_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: FuncAddrMap [
+# CHECK-NEXT:   Function {
+# CHECK-NEXT:     At: 0x33333
+# CHECK-NEXT:     Name: bar
+# CHECK-NEXT:     DynamicInstCount: 10
+# CHECK-NEXT:   }
+# CHECK-NEXT: ]
+
+# GNU: GNUStyle::printFuncAddrMaps not implemented
+
+# TRUNCATED:      FuncAddrMap [
+# TRUNCATED-NEXT:   warning: '[[FILE]]': unable to dump SHT_LLVM_FUNC_ADDR_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: FuncAddrMap [
+# 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_addr_map
+    Type:   SHT_LLVM_FUNC_ADDR_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_addr_map_2'
+    Type: SHT_LLVM_FUNC_ADDR_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..26b20733928050 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 printFuncAddrMaps() 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 printFuncAddrMaps() 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>::printFuncAddrMaps() {
+  OS << "GNUStyle::printFuncAddrMaps 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();
@@ -7784,6 +7790,61 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCGProfile() {
   }
 }
 
+template <class ELFT> void LLVMELFDumper<ELFT>::printFuncAddrMaps() {
+  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_ADDR_MAP;
+  };
+  Expected<MapVector<const Elf_Shdr *, const Elf_Shdr *>> SecRelocMapOrErr =
+      this->Obj.getSectionAndRelocations(IsMatch);
+  if (!SecRelocMapOrErr) {
+    this->reportUniqueWarning(
+        "failed to get SHT_LLVM_FUNC_ADDR_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, "FuncAddrMap");
+    if (IsRelocatable && !RelocSec) {
+      this->reportUniqueWarning("unable to get relocation section for " +
+                                this->describe(*Sec));
+      continue;
+    }
+    Expected<std::vector<FuncAddrMap>> FuncAddrMapOrErr =
+        this->Obj.decodeFuncAddrMap(*Sec, RelocSec);
+    if (!FuncAddrMapOrErr) {
+      this->reportUniqueWarning("unable to dump " + this->describe(*Sec) +
+                                ": " + toString(FuncAddrMapOrErr.takeError()));
+      continue;
+    }
+    for (const auto &AM : *FuncAddrMapOrErr) {
+      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>::printBBAddrMaps(bool PrettyPGOAnalysis) {
   bool IsRelocatable = this->Obj.getHeader().e_type == ELF::ET_REL;
diff --git a/llvm/tools/llvm-readobj/ObjDumper.h b/llvm/tools/llvm-readobj/ObjDumper.h
index cd744e3bbfb712..28e239f86db667 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 printFuncAddrMaps() {}
   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..1cd806d40f89c4 100644
--- a/llvm/tools/llvm-readobj/Opts.td
+++ b/llvm/tools/llvm-readobj/Opts.td
@@ -18,6 +18,7 @@ def all : FF<"all", "Equivalent to setting: --file-header, --program-headers, --
              "--symbols, --relocations, --dynamic-table, --notes, --version-info, --unwind, "
              "--section-groups and --histogram">;
 def arch_specific : FF<"arch-specific", "Display architecture-specific information">;
+def func_addr_map : FF<"func-addr-map", "Display the function address map section">;
 def bb_addr_map : FF<"bb-addr-map", "Display the BB 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">;
diff --git a/llvm/tools/llvm-readobj/llvm-readobj.cpp b/llvm/tools/llvm-readobj/llvm-readobj.cpp
index 2f77e5d350553d..588744dc8bf69f 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 FuncAddrMap;
 bool ExpandRelocs;
 static bool CGProfile;
 static bool Decompress;
@@ -219,6 +220,7 @@ static void parseOptions(const opt::InputArgList &Args) {
     WithColor::warning(errs(), ToolName)
         << "--bb-addr-map must be enabled for --pretty-pgo-analysis-map to "
            "have an effect\n";
+  opts::FuncAddrMap = Args.hasArg(OPT_func_addr_map);
   opts::CGProfile = Args.hasArg(OPT_cg_profile);
   opts::Decompress = Args.hasArg(OPT_decompress);
   opts::Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, false);
@@ -473,6 +475,8 @@ static void dumpObject(ObjectFile &Obj, ScopedPrinter &Writer,
       Dumper->printCGProfile();
     if (opts::BBAddrMap)
       Dumper->printBBAddrMaps(opts::PrettyPGOAnalysisMap);
+    if (opts::FuncAddrMap)
+      Dumper->printFuncAddrMaps();
     if (opts::Addrsig)
       Dumper->printAddrsig();
     if (opts::Notes)
diff --git a/llvm/tools/obj2yaml/elf2yaml.cpp b/llvm/tools/obj2yaml/elf2yaml.cpp
index b1c8032ea21929..95343351385448 100644
--- a/llvm/tools/obj2yaml/elf2yaml.cpp
+++ b/llvm/tools/obj2yaml/elf2yaml.cpp
@@ -97,6 +97,8 @@ class ELFDumper {
   dumpStackSizesSection(const Elf_Shdr *Shdr);
   Expected<ELFYAML::BBAddrMapSection *>
   dumpBBAddrMapSection(const Elf_Shdr *Shdr);
+  Expected<ELFYAML::FuncAddrMapSection *>
+  dumpFuncAddrMapSection(const Elf_Shdr *Shdr);
   Expected<ELFYAML::RawContentSection *>
   dumpPlaceholderSection(const Elf_Shdr *Shdr);
 
@@ -629,6 +631,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_ADDR_MAP:
+      return [this](const Elf_Shdr *S) { return dumpFuncAddrMapSection(S); };
     case ELF::SHT_STRTAB:
     case ELF::SHT_SYMTAB:
     case ELF::SHT_DYNSYM:
@@ -989,6 +993,55 @@ ELFDumper<ELFT>::dumpBBAddrMapSection(const Elf_Shdr *Shdr) {
   return S.release();
 }
 
+template <class ELFT>
+Expected<ELFYAML::FuncAddrMapSection *>
+ELFDumper<ELFT>::dumpFuncAddrMapSection(const Elf_Shdr *Shdr) {
+  auto S = std::make_unique<ELFYAML::FuncAddrMapSection>();
+  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::FuncAddrMapEntry> 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_ADDR_MAP) {
+      Version = Data.getU8(Cur);
+      Feature = Data.getU8(Cur);
+    }
+    auto FeatureOrErr = llvm::object::FuncAddrMap::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) {



More information about the llvm-commits mailing list