aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorwlei <wlei@fb.com>2025-02-03 23:50:02 -0800
committerwlei <wlei@fb.com>2025-02-03 23:50:02 -0800
commitb5edcb2abe2e7c0d6869d15e947f01630b66adb3 (patch)
tree05af9c822d77c0200d10c08eee72fd2bee49ed87
parent95d993a838863269dc1b90de3808c1e40ac6d5f2 (diff)
parent7d4830352bc089cbc82043c6365821f9c1e7f2b6 (diff)
downloadllvm-users/wlei-llvm/spr/tmp-add-func-map-section-header.zip
llvm-users/wlei-llvm/spr/tmp-add-func-map-section-header.tar.gz
llvm-users/wlei-llvm/spr/tmp-add-func-map-section-header.tar.bz2
Created using spr 1.3.6-beta.1
-rw-r--r--llvm/docs/Extensions.rst22
-rw-r--r--llvm/include/llvm/BinaryFormat/ELF.h1
-rw-r--r--llvm/include/llvm/CodeGen/AsmPrinter.h9
-rw-r--r--llvm/include/llvm/MC/MCContext.h5
-rw-r--r--llvm/include/llvm/MC/MCObjectFileInfo.h2
-rw-r--r--llvm/include/llvm/Object/ELF.h7
-rw-r--r--llvm/include/llvm/Object/ELFTypes.h8
-rw-r--r--llvm/include/llvm/ObjectYAML/ELFYAML.h24
-rw-r--r--llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp74
-rw-r--r--llvm/lib/MC/MCObjectFileInfo.cpp17
-rw-r--r--llvm/lib/MC/MCParser/ELFAsmParser.cpp2
-rw-r--r--llvm/lib/MC/MCSectionELF.cpp2
-rw-r--r--llvm/lib/Object/ELF.cpp87
-rw-r--r--llvm/lib/ObjectYAML/ELFEmitter.cpp27
-rw-r--r--llvm/lib/ObjectYAML/ELFYAML.cpp21
-rw-r--r--llvm/test/CodeGen/X86/function-address-map-dyn-inst-count.ll108
-rw-r--r--llvm/test/CodeGen/X86/function-address-map-function-sections.ll38
-rw-r--r--llvm/test/MC/AsmParser/llvm_section_types.s4
-rw-r--r--llvm/test/tools/llvm-readobj/ELF/func-map.test93
-rw-r--r--llvm/test/tools/obj2yaml/ELF/func-map.yaml130
-rw-r--r--llvm/test/tools/yaml2obj/ELF/func-map.yaml115
-rw-r--r--llvm/tools/llvm-readobj/ELFDumper.cpp59
-rw-r--r--llvm/tools/llvm-readobj/ObjDumper.h1
-rw-r--r--llvm/tools/llvm-readobj/Opts.td1
-rw-r--r--llvm/tools/llvm-readobj/llvm-readobj.cpp4
-rw-r--r--llvm/tools/obj2yaml/elf2yaml.cpp48
26 files changed, 907 insertions, 2 deletions
diff --git a/llvm/docs/Extensions.rst b/llvm/docs/Extensions.rst
index ea26784..1ebea4a 100644
--- a/llvm/docs/Extensions.rst
+++ b/llvm/docs/Extensions.rst
@@ -535,6 +535,27 @@ Example of BBAddrMap with PGO data:
.uleb128 1000 # BB_3 basic block frequency (only when enabled)
.uleb128 0 # BB_3 successors count (only enabled with branch probabilities)
+``SHT_LLVM_FUNC_MAP`` Section (function address map)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+This section stores the mapping from the binary address of function to its
+related metadata features. It is used to emit function-level analysis data and
+can be enabled through ``--func-map`` option. The fields are encoded in the
+following format:
+
+#. A version number byte used for backward compatibility.
+#. The function's entry address.
+#. Dynamic Instruction Count, which is calculated as the total PGO counts for all
+ instructions within the function.
+
+Example:
+
+.. code-block:: gas
+
+ .section ".llvm_func_map","",@llvm_func_map
+ .byte 1 # version number
+ .quad .Lfunc_begin1 # function address
+ .uleb128 1000 # dynamic instruction count
+
``SHT_LLVM_OFFLOADING`` Section (offloading data)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This section stores the binary data used to perform offloading device linking
@@ -725,4 +746,3 @@ follows:
add x16, x16, :lo12:__chkstk
blr x16
sub sp, sp, x15, lsl #4
-
diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h
index 48ae0db..46837c4 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/CodeGen/AsmPrinter.h b/llvm/include/llvm/CodeGen/AsmPrinter.h
index 5291369..b58c027 100644
--- a/llvm/include/llvm/CodeGen/AsmPrinter.h
+++ b/llvm/include/llvm/CodeGen/AsmPrinter.h
@@ -132,6 +132,9 @@ public:
/// default, this is equal to CurrentFnSym.
MCSymbol *CurrentFnSymForSize = nullptr;
+ MCSymbol *FMapEnd = nullptr;
+ MCSymbol *FMapStart = nullptr;
+
/// Map a basic block section ID to the begin and end symbols of that section
/// which determine the section's range.
struct MBBSectionRange {
@@ -414,6 +417,12 @@ public:
void emitBBAddrMapSection(const MachineFunction &MF);
+ void emitFuncMapStart();
+
+ void emitFuncMapEnd();
+
+ void emitFuncMapSection(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 57ba40f..6fc9eaa 100644
--- a/llvm/include/llvm/MC/MCContext.h
+++ b/llvm/include/llvm/MC/MCContext.h
@@ -177,6 +177,9 @@ private:
/// LLVM_BB_ADDR_MAP version to emit.
uint8_t BBAddrMapVersion = 2;
+ /// LLVM_FUNC_MAP version to emit.
+ uint8_t FuncMapVersion = 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 @@ public:
uint8_t getBBAddrMapVersion() const { return BBAddrMapVersion; }
+ uint8_t getFuncMapVersion() const { return FuncMapVersion; }
+
/// @}
/// \name Dwarf Management
diff --git a/llvm/include/llvm/MC/MCObjectFileInfo.h b/llvm/include/llvm/MC/MCObjectFileInfo.h
index fb575fe..e344d47 100644
--- a/llvm/include/llvm/MC/MCObjectFileInfo.h
+++ b/llvm/include/llvm/MC/MCObjectFileInfo.h
@@ -364,6 +364,8 @@ public:
MCSection *getBBAddrMapSection(const MCSection &TextSec) const;
+ MCSection *getFuncMapSection(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 3aa1d78..a688672 100644
--- a/llvm/include/llvm/Object/ELF.h
+++ b/llvm/include/llvm/Object/ELF.h
@@ -513,6 +513,13 @@ public:
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/include/llvm/Object/ELFTypes.h b/llvm/include/llvm/Object/ELFTypes.h
index 87e4dbe..4ff4ec5 100644
--- a/llvm/include/llvm/Object/ELFTypes.h
+++ b/llvm/include/llvm/Object/ELFTypes.h
@@ -1027,6 +1027,14 @@ struct PGOAnalysisMap {
}
};
+// Struct representing the FuncMap for one function.
+struct FuncMap {
+ uint64_t FunctionAddress = 0; // Function entry address.
+ uint64_t DynamicInstCount = 0; // Dynamic instruction count for this function.
+
+ 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 dfdfa05..f302bb0 100644
--- a/llvm/include/llvm/ObjectYAML/ELFYAML.h
+++ b/llvm/include/llvm/ObjectYAML/ELFYAML.h
@@ -195,6 +195,12 @@ struct PGOAnalysisMapEntry {
std::optional<std::vector<PGOBBEntry>> PGOBBEntries;
};
+struct FuncMapEntry {
+ uint8_t Version;
+ llvm::yaml::Hex64 Address;
+ uint64_t DynamicInstCount;
+};
+
struct StackSizeEntry {
llvm::yaml::Hex64 Address;
llvm::yaml::Hex64 Size;
@@ -229,6 +235,7 @@ struct Chunk {
DependentLibraries,
CallGraphProfile,
BBAddrMap,
+ FuncMap,
// Special chunks.
SpecialChunksStart,
@@ -355,6 +362,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 +781,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 +949,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/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
index b2a4721..26fe134 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -147,6 +147,7 @@ enum class PGOMapFeaturesEnum {
BrProb,
All,
};
+
static cl::bits<PGOMapFeaturesEnum> PgoAnalysisMapFeatures(
"pgo-analysis-map", cl::Hidden, cl::CommaSeparated,
cl::values(
@@ -161,6 +162,13 @@ static cl::bits<PGOMapFeaturesEnum> PgoAnalysisMapFeatures(
"Enable extended information within the SHT_LLVM_BB_ADDR_MAP that is "
"extracted from PGO related analysis."));
+static cl::opt<bool> EmitFuncMap(
+ "func-map",
+ cl::desc(
+ "Emit features of function address map in SHT_LLVM_FUNC_MAP "
+ "section(Currently only support dynamic instruction count feature)."),
+ cl::Hidden, cl::init(false));
+
static cl::opt<bool> BBAddrMapSkipEmitBBEntries(
"basic-block-address-map-skip-bb-entries",
cl::desc("Skip emitting basic block entries in the SHT_LLVM_BB_ADDR_MAP "
@@ -456,6 +464,36 @@ void AsmPrinter::getAnalysisUsage(AnalysisUsage &AU) const {
AU.addRequired<MachineBranchProbabilityInfoWrapperPass>();
}
+void AsmPrinter::emitFuncMapStart() {
+ MCSection *TextSection =
+ OutStreamer->getContext().getObjectFileInfo()->getTextSection();
+ MCSection *FuncMapSection =
+ getObjFileLowering().getFuncMapSection(*TextSection);
+ assert(FuncMapSection && ".llvm_func_map section is not initialized.");
+ OutStreamer->pushSection();
+ OutStreamer->switchSection(FuncMapSection);
+ FMapStart = OutStreamer->getContext().createTempSymbol("func_map_start");
+ FMapEnd = OutStreamer->getContext().createTempSymbol("func_map_end");
+ OutStreamer->emitAbsoluteSymbolDiff(FMapEnd, FMapStart, 4);
+ OutStreamer->emitLabel(FMapStart);
+ OutStreamer->AddComment("version");
+ uint8_t FuncMapVersion = OutStreamer->getContext().getFuncMapVersion();
+ OutStreamer->emitInt8(FuncMapVersion);
+ OutStreamer->popSection();
+}
+
+void AsmPrinter::emitFuncMapEnd() {
+ MCSection *TextSection =
+ OutStreamer->getContext().getObjectFileInfo()->getTextSection();
+ MCSection *FuncMapSection =
+ getObjFileLowering().getFuncMapSection(*TextSection);
+ assert(FuncMapSection && ".llvm_func_map section is not initialized.");
+ OutStreamer->pushSection();
+ OutStreamer->switchSection(FuncMapSection);
+ OutStreamer->emitLabel(FMapEnd);
+ OutStreamer->popSection();
+}
+
bool AsmPrinter::doInitialization(Module &M) {
auto *MMIWP = getAnalysisIfAvailable<MachineModuleInfoWrapperPass>();
MMI = MMIWP ? &MMIWP->getMMI() : nullptr;
@@ -639,6 +677,7 @@ bool AsmPrinter::doInitialization(Module &M) {
for (auto &Handler : EHHandlers)
Handler->beginModule(&M);
+ emitFuncMapStart();
return false;
}
@@ -1548,6 +1587,36 @@ void AsmPrinter::emitBBAddrMapSection(const MachineFunction &MF) {
OutStreamer->popSection();
}
+void AsmPrinter::emitFuncMapSection(const MachineFunction &MF) {
+ if (!EmitFuncMap)
+ return;
+
+ MCSection *FuncMapSection =
+ getObjFileLowering().getFuncMapSection(*MF.getSection());
+ assert(FuncMapSection && ".llvm_func_map section is not initialized.");
+ const MCSymbol *FunctionSymbol = getFunctionBegin();
+ OutStreamer->pushSection();
+ OutStreamer->switchSection(FuncMapSection);
+
+ OutStreamer->AddComment("function address");
+ OutStreamer->emitSymbolValue(FunctionSymbol, getPointerSize());
+
+ const MachineBlockFrequencyInfo *MBFI =
+ &getAnalysis<LazyMachineBlockFrequencyInfoPass>().getBFI();
+ uint64_t DynInstCount = 0;
+ for (const MachineBasicBlock &MBB : MF) {
+ for (const MachineInstr &MI : MBB) {
+ if (MI.isDebugValue() || MI.isPseudoProbe())
+ continue;
+ DynInstCount += MBFI->getBlockProfileCount(&MBB).value_or(0);
+ }
+ }
+
+ OutStreamer->AddComment("dynamic instruction count");
+ OutStreamer->emitULEB128IntValue(DynInstCount);
+ OutStreamer->popSection();
+}
+
void AsmPrinter::emitKCFITrapEntry(const MachineFunction &MF,
const MCSymbol *Symbol) {
MCSection *Section =
@@ -2119,6 +2188,7 @@ void AsmPrinter::emitFunctionBody() {
MF->getContext().reportWarning(
SMLoc(), "pgo-analysis-map is enabled for function " + MF->getName() +
" but it does not have labels");
+ emitFuncMapSection(*MF);
}
// Emit sections containing instruction and function PCs.
@@ -2558,6 +2628,8 @@ bool AsmPrinter::doFinalization(Module &M) {
// through user plugins.
emitStackMaps();
+ emitFuncMapEnd();
+
// Print aliases in topological order, that is, for each alias a = b,
// b must be printed before a.
// This is because on some targets (e.g. PowerPC) linker expects aliases in
@@ -2749,7 +2821,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 || EmitFuncMap) {
CurrentFnBegin = createTempSymbol("func_begin");
if (NeedsLocalForSize)
CurrentFnSymForSize = CurrentFnBegin;
diff --git a/llvm/lib/MC/MCObjectFileInfo.cpp b/llvm/lib/MC/MCObjectFileInfo.cpp
index 150e38a..02cc207 100644
--- a/llvm/lib/MC/MCObjectFileInfo.cpp
+++ b/llvm/lib/MC/MCObjectFileInfo.cpp
@@ -1120,6 +1120,23 @@ MCObjectFileInfo::getBBAddrMapSection(const MCSection &TextSec) const {
cast<MCSymbolELF>(TextSec.getBeginSymbol()));
}
+MCSection *MCObjectFileInfo::getFuncMapSection(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_map", ELF::SHT_LLVM_FUNC_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 b58210b..c9df8a3 100644
--- a/llvm/lib/MC/MCParser/ELFAsmParser.cpp
+++ b/llvm/lib/MC/MCParser/ELFAsmParser.cpp
@@ -679,6 +679,8 @@ EndStmt:
Type = ELF::SHT_LLVM_LTO;
else if (TypeName == "llvm_jt_sizes")
Type = ELF::SHT_LLVM_JT_SIZES;
+ else if (TypeName == "llvm_func_map")
+ Type = ELF::SHT_LLVM_FUNC_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 25e62b7..fa1a5eb 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_MAP)
+ OS << "llvm_func_map";
else
OS << "0x" << Twine::utohexstr(Type);
diff --git a/llvm/lib/Object/ELF.cpp b/llvm/lib/Object/ELF.cpp
index b6d0699..00b08b8 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);
@@ -940,6 +941,92 @@ ELFFile<ELFT>::decodeBBAddrMap(const Elf_Shdr &Sec, const Elf_Shdr *RelaSec,
}
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;
+ 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)));
+ }
+ typename ELFFile<ELFT>::uintX_t FunctionAddress = 0;
+ auto AddressOrErr = ExtractAddress();
+ if (!AddressOrErr)
+ return AddressOrErr.takeError();
+ FunctionAddress = *AddressOrErr;
+ uint64_t DynamicInstCount = readULEB128As<uint64_t>(Data, Cur, ULEBSizeErr);
+ FunctionEntries.push_back({FunctionAddress, DynamicInstCount});
+ }
+ // 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 *>>
ELFFile<ELFT>::getSectionAndRelocations(
diff --git a/llvm/lib/ObjectYAML/ELFEmitter.cpp b/llvm/lib/ObjectYAML/ELFEmitter.cpp
index 9ae76a7..06efac7 100644
--- a/llvm/lib/ObjectYAML/ELFEmitter.cpp
+++ b/llvm/lib/ObjectYAML/ELFEmitter.cpp
@@ -288,6 +288,9 @@ template <class ELFT> class ELFState {
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);
void writeSectionContent(Elf_Shdr &SHeader,
@@ -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");
}
@@ -1538,6 +1543,28 @@ 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)) {
+ 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);
+ SHeader.sh_size += 1;
+ }
+ CBA.write<uintX_t>(E.Address, ELFT::Endianness);
+ SHeader.sh_size += sizeof(uintX_t);
+ SHeader.sh_size += CBA.writeULEB128(E.DynamicInstCount);
+ }
+}
+
+template <class ELFT>
void ELFState<ELFT>::writeSectionContent(
Elf_Shdr &SHeader, const ELFYAML::LinkerOptionsSection &Section,
ContiguousBlobAccumulator &CBA) {
diff --git a/llvm/lib/ObjectYAML/ELFYAML.cpp b/llvm/lib/ObjectYAML/ELFYAML.cpp
index 539834f..737ce96 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,14 @@ 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("Address", E.Address, Hex64(0));
+ IO.mapOptional("DynInstCnt", E.DynamicInstCount, 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-dyn-inst-count.ll b/llvm/test/CodeGen/X86/function-address-map-dyn-inst-count.ll
new file mode 100644
index 0000000..7762103
--- /dev/null
+++ b/llvm/test/CodeGen/X86/function-address-map-dyn-inst-count.ll
@@ -0,0 +1,108 @@
+; Test emitting dynamic instruction count feature in .llvm_func_map section.
+; RUN: llc < %s -mtriple=x86_64 -function-sections -func-map | FileCheck %s
+
+;; Check we add SHF_LINK_ORDER for .llvm_func_map and link it with the corresponding .text sections.
+; CHECK: .section .text.foo,"ax",@progbits
+; CHECK-LABEL: foo:
+; CHECK-NEXT: [[FOO_BEGIN:.Lfunc_begin[0-9]+]]:
+; CHECK: .section .llvm_func_map,"o",@llvm_func_map,.text.foo{{$}}
+; CHECK-NEXT: .byte 1 # version
+; CHECK-NEXT: .quad [[FOO_BEGIN]] # function address
+; CHECK-NEXT: .ascii "\252\001" # dynamic instruction count
+
+
+; CHECK: .section .text.main,"ax",@progbits
+; CHECK-LABEL: main:
+; CHECK-NEXT: [[MAIN_BEGIN:.Lfunc_begin[0-9]+]]:
+; CHECK: .section .llvm_func_map,"o",@llvm_func_map,.text.main{{$}}
+; CHECK-NEXT: .byte 1 # version
+; CHECK-NEXT: .quad [[MAIN_BEGIN]] # function address
+; CHECK-NEXT: .ascii "\265\003" # dynamic instruction count
+
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+define i32 @foo(i32 %x) !dbg !4 !prof !6 {
+entry:
+ #dbg_value(i32 %x, !7, !DIExpression(), !9)
+ call void @llvm.pseudoprobe(i64 6699318081062747564, i64 1, i32 0, i64 -1)
+ %rem = mul i32 %x, 5
+ %tobool.not = icmp eq i32 %rem, 0, !dbg !10
+ br i1 %tobool.not, label %if.end, label %if.then, !prof !12
+
+if.then: ; preds = %entry
+ %inc = add i32 0, 0
+ call void @llvm.pseudoprobe(i64 6699318081062747564, i64 2, i32 0, i64 -1)
+ #dbg_value(i32 %inc, !7, !DIExpression(), !9)
+ br label %if.end
+
+if.end: ; preds = %if.then, %entry
+ %x.addr.0 = phi i32 [ 0, %if.then ], [ 1, %entry ]
+ #dbg_value(i32 %x.addr.0, !7, !DIExpression(), !9)
+ ret i32 %x.addr.0
+}
+
+define i32 @main() #0 !dbg !13 !prof !15 {
+entry:
+ #dbg_value(i32 0, !16, !DIExpression(), !17)
+ br label %while.cond
+
+while.cond: ; preds = %if.then, %if.else, %entry
+ %i.0 = phi i32 [ 0, %entry ], [ %inc, %if.else ], [ %inc, %if.then ]
+ #dbg_value(i32 %i.0, !16, !DIExpression(), !17)
+ %inc = add i32 %i.0, 1
+ #dbg_value(i32 %inc, !16, !DIExpression(), !17)
+ %cmp = icmp ult i32 %i.0, 1600000
+ br i1 %cmp, label %while.body, label %while.end, !prof !18
+
+while.body: ; preds = %while.cond
+ %rem = urem i32 %inc, 11
+ %tobool.not = icmp eq i32 %rem, 0
+ br i1 %tobool.not, label %if.else, label %if.then, !prof !19
+
+if.then: ; preds = %while.body
+ %call = call i32 @foo(i32 0), !dbg !20
+ %0 = load volatile i32, ptr null, align 4
+ br label %while.cond
+
+if.else: ; preds = %while.body
+ store i32 0, ptr null, align 4
+ br label %while.cond
+
+while.end: ; preds = %while.cond
+ ret i32 0
+
+; uselistorder directives
+ uselistorder label %while.cond, { 1, 0, 2 }
+ uselistorder i32 %inc, { 2, 1, 0 }
+}
+
+attributes #0 = { "target-cpu"="x86-64" }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!3}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 20.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !2, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "test.c", directory: "/home", checksumkind: CSK_MD5, checksum: "920887ee2258042655d8340f78e732e9")
+!2 = !{}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 3, type: !5, scopeLine: 3, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2)
+!5 = distinct !DISubroutineType(types: !2)
+!6 = !{!"function_entry_count", i64 20}
+!7 = !DILocalVariable(name: "x", arg: 1, scope: !4, file: !1, line: 3, type: !8)
+!8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!9 = !DILocation(line: 0, scope: !4)
+!10 = !DILocation(line: 4, column: 9, scope: !11)
+!11 = distinct !DILexicalBlock(scope: !4, file: !1, line: 4, column: 7)
+!12 = !{!"branch_weights", i32 15, i32 5}
+!13 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 9, type: !14, scopeLine: 9, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2)
+!14 = !DISubroutineType(types: !2)
+!15 = !{!"function_entry_count", i64 1}
+!16 = !DILocalVariable(name: "i", scope: !13, file: !1, line: 10, type: !8)
+!17 = !DILocation(line: 0, scope: !13)
+!18 = !{!"branch_weights", i32 22, i32 1}
+!19 = !{!"branch_weights", i32 2, i32 20}
+!20 = !DILocation(line: 12, column: 22, scope: !21)
+!21 = !DILexicalBlockFile(scope: !22, file: !1, discriminator: 455082031)
+!22 = distinct !DILexicalBlock(scope: !13, file: !1, line: 12, column: 9)
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 0000000..0c5eb2d
--- /dev/null
+++ b/llvm/test/CodeGen/X86/function-address-map-function-sections.ll
@@ -0,0 +1,38 @@
+; RUN: llc < %s -mtriple=x86_64 -function-sections -func-map | FileCheck %s
+
+$_Z4fooTIiET_v = comdat any
+
+define dso_local i32 @_Z3barv() {
+ ret i32 0
+}
+;; Check we add SHF_LINK_ORDER for .llvm_func_map and link it with the corresponding .text sections.
+; CHECK: .section .text._Z3barv,"ax",@progbits
+; CHECK-LABEL: _Z3barv:
+; CHECK-NEXT: [[BAR_BEGIN:.Lfunc_begin[0-9]+]]:
+; CHECK: .section .llvm_func_map,"o",@llvm_func_map,.text._Z3barv{{$}}
+; CHECK-NEXT: .byte 1 # version
+; 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",@progbits
+; CHECK-LABEL: _Z3foov:
+; CHECK-NEXT: [[FOO_BEGIN:.Lfunc_begin[0-9]+]]:
+; CHECK: .section .llvm_func_map,"o",@llvm_func_map,.text._Z3foov{{$}}
+; CHECK-NEXT: .byte 1 # version
+; CHECK-NEXT: .quad [[FOO_BEGIN]] # function address
+
+
+define linkonce_odr dso_local i32 @_Z4fooTIiET_v() comdat {
+ ret i32 0
+}
+;; Check we add .llvm_func_map section to a COMDAT group with the corresponding .text section if such a COMDAT exists.
+; CHECK: .section .text._Z4fooTIiET_v,"axG",@progbits,_Z4fooTIiET_v,comdat
+; CHECK-LABEL: _Z4fooTIiET_v:
+; CHECK-NEXT: [[FOOCOMDAT_BEGIN:.Lfunc_begin[0-9]+]]:
+; CHECK: .section .llvm_func_map,"oG",@llvm_func_map,.text._Z4fooTIiET_v,_Z4fooTIiET_v,comdat{{$}}
+; CHECK-NEXT: .byte 1 # version
+; 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 147b1499..f3f3150 100644
--- a/llvm/test/MC/AsmParser/llvm_section_types.s
+++ b/llvm/test/MC/AsmParser/llvm_section_types.s
@@ -17,6 +17,8 @@
.byte 1
.section .section8,"",@llvm_lto
.byte 1
+.section .section9,"",@llvm_func_map
+.byte 1
# CHECK: Name: .section1
# CHECK-NEXT: Type: SHT_LLVM_BB_ADDR_MAP
@@ -34,3 +36,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_MAP
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 0000000..9677f9f
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/ELF/func-map.test
@@ -0,0 +1,93 @@
+## 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: 100
+# CHECK-NEXT: }
+# CHECK-NEXT: Function {
+# CHECK-NEXT: At: 0x22222
+# CHECK-NEXT: Name: foo
+# CHECK-NEXT: DynamicInstCount: 200
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+# CHECK-NEXT: FuncMap [
+# CHECK-NEXT: Function {
+# CHECK-NEXT: At: 0x33333
+# CHECK-NEXT: Name: bar
+# CHECK-NEXT: DynamicInstCount: 300
+# 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: 300
+# 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
+ Address: [[ADDR=0x11111]]
+ DynInstCnt: 100
+ - Version: 1
+ Address: 0x22222
+ DynInstCnt: 200
+ - Name: dummy_section
+ Type: SHT_PROGBITS
+ Size: 16
+ - Name: '.llvm_func_map_2'
+ Type: SHT_LLVM_FUNC_MAP
+ Link: .text.bar
+ Entries:
+ - Version: 1
+ Address: 0x33333
+ DynInstCnt: 300
+Symbols:
+ - Name: foo
+ Section: .text
+ Type: STT_FUNC
+ Value: 0x22222
+ - Name: bar
+ Section: .text.bar
+ Type: STT_FUNC
+ Value: 0x33333
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 0000000..4b0476b
--- /dev/null
+++ b/llvm/test/tools/obj2yaml/ELF/func-map.yaml
@@ -0,0 +1,130 @@
+## 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
+## The 'Address' field is omitted when it's zero.
+# VALID-NEXT: DynInstCnt: 16
+## The 'DynInstCnt' field is omitted when it's zero.
+# VALID-NEXT: - Version: 1
+# VALID-NEXT: Address: 0x1
+# VALID-NEXT: - Version: 1
+# VALID-NEXT: Address: 0xFFFFFFFFFFFFFFF1
+# VALID-NEXT: DynInstCnt: 100001
+
+--- !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
+ Address: 0x0
+ DynInstCnt: 16
+ - Version: 1
+ Address: 0x1
+ DynInstCnt: 0
+ - Version: 1
+ Address: 0xFFFFFFFFFFFFFFF1
+ DynInstCnt: 100001
+
+## 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: Address: 0x2
+# MULTI-NEXT: DynInstCnt: 3
+# MULTI-NEXT: - Name: '.llvm_func_map (1)'
+# MULTI-NEXT: Type: SHT_LLVM_FUNC_MAP
+# MULTI-NEXT: Entries:
+# MULTI-NEXT: - Version: 1
+# MULTI-NEXT: Address: 0xA
+# MULTI-NEXT: DynInstCnt: 100
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_EXEC
+Sections:
+ - Name: .llvm_func_map
+ Type: SHT_LLVM_FUNC_MAP
+ Entries:
+ - Version: 1
+ Address: 0x2
+ DynInstCnt: 3
+ - Name: '.llvm_func_map (1)'
+ Type: SHT_LLVM_FUNC_MAP
+ Entries:
+ - Version: 1
+ Address: 0xA
+ DynInstCnt: 100
+
+## 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
+# 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 0000000..2a51593
--- /dev/null
+++ b/llvm/test/tools/yaml2obj/ELF/func-map.yaml
@@ -0,0 +1,115 @@
+## 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: 01222202 00000000 0010
+# 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
+ 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/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index bfca65a..bdc1b20 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -606,6 +606,7 @@ public:
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 @@ public:
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,59 @@ 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);
+ 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 cd744e3..2c2341e 100644
--- a/llvm/tools/llvm-readobj/ObjDumper.h
+++ b/llvm/tools/llvm-readobj/ObjDumper.h
@@ -132,6 +132,7 @@ public:
// 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 7d574d8..17c65a6 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 2f77e5d..91465a6 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)
diff --git a/llvm/tools/obj2yaml/elf2yaml.cpp b/llvm/tools/obj2yaml/elf2yaml.cpp
index b1c8032..0c7e6a4 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:
@@ -990,6 +993,51 @@ ELFDumper<ELFT>::dumpBBAddrMapSection(const Elf_Shdr *Shdr) {
}
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;
+ uint64_t Address = 0;
+ while (Cur && Cur.tell() < Content.size()) {
+ if (Shdr->sh_type == ELF::SHT_LLVM_FUNC_MAP) {
+ Version = Data.getU8(Cur);
+ if (Cur && Version > 1)
+ return createStringError(errc::invalid_argument,
+ "invalid SHT_LLVM_FUNC_MAP section version: " +
+ Twine(static_cast<int>(Version)));
+ }
+ Address = Data.getAddress(Cur);
+ uint64_t DynamicInstCount = Data.getULEB128(Cur);
+ Entries.push_back({Version, 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) {
auto S = std::make_unique<ELFYAML::AddrsigSection>();