-
Notifications
You must be signed in to change notification settings - Fork 13.3k
[SHT_LLVM_FUNC_MAP][llvm-readobj]Introduce function address map section and emit dynamic instruction count(readobj part) #124333
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: users/wlei-llvm/spr/main.sht_llvm_func_mapllvm-readobjintroduce-function-address-map-section-and-emit-dynamic-instruction-countreadobj-part
Are you sure you want to change the base?
[SHT_LLVM_FUNC_MAP][llvm-readobj]Introduce function address map section and emit dynamic instruction count(readobj part) #124333
Conversation
Created using spr 1.3.6-beta.1
@llvm/pr-subscribers-llvm-binary-utilities Author: Lei Wang (wlei-llvm) ChangesTest Plan: llvm/test/tools/llvm-readobj/ELF/func-map.test Full diff: https://github.com/llvm/llvm-project/pull/124333.diff 7 Files Affected:
diff --git a/llvm/include/llvm/Object/ELF.h b/llvm/include/llvm/Object/ELF.h
index 3aa1d7864fcb70..a688672a3e5190 100644
--- a/llvm/include/llvm/Object/ELF.h
+++ b/llvm/include/llvm/Object/ELF.h
@@ -513,6 +513,13 @@ class ELFFile {
decodeBBAddrMap(const Elf_Shdr &Sec, const Elf_Shdr *RelaSec = nullptr,
std::vector<PGOAnalysisMap> *PGOAnalyses = nullptr) const;
+ /// Returns a vector of FuncMap structs corresponding to each function
+ /// within the text section that the SHT_LLVM_FUNC_MAP section \p Sec
+ /// is associated with. If the current ELFFile is relocatable, a corresponding
+ /// \p RelaSec must be passed in as an argument.
+ Expected<std::vector<FuncMap>>
+ decodeFuncMap(const Elf_Shdr &Sec, const Elf_Shdr *RelaSec = nullptr) const;
+
/// Returns a map from every section matching \p IsMatch to its relocation
/// section, or \p nullptr if it has no relocation section. This function
/// returns an error if any of the \p IsMatch calls fail or if it fails to
diff --git a/llvm/lib/Object/ELF.cpp b/llvm/lib/Object/ELF.cpp
index 41c3fb4cc5e406..87a9e5469f46d2 100644
--- a/llvm/lib/Object/ELF.cpp
+++ b/llvm/lib/Object/ELF.cpp
@@ -940,6 +940,104 @@ ELFFile<ELFT>::decodeBBAddrMap(const Elf_Shdr &Sec, const Elf_Shdr *RelaSec,
return std::move(AddrMapsOrErr);
}
+template <class ELFT>
+Expected<std::vector<FuncMap>>
+ELFFile<ELFT>::decodeFuncMap(const Elf_Shdr &Sec,
+ const Elf_Shdr *RelaSec) const {
+ bool IsRelocatable = this->getHeader().e_type == ELF::ET_REL;
+
+ // This DenseMap maps the offset of each function (the location of the
+ // reference to the function in the SHT_LLVM_FUNC_ADDR_MAP section) to the
+ // addend (the location of the function in the text section).
+ llvm::DenseMap<uint64_t, uint64_t> FunctionOffsetTranslations;
+ if (IsRelocatable && RelaSec) {
+ assert(RelaSec &&
+ "Can't read a SHT_LLVM_FUNC_ADDR_MAP section in a relocatable "
+ "object file without providing a relocation section.");
+ Expected<typename ELFFile<ELFT>::Elf_Rela_Range> Relas =
+ this->relas(*RelaSec);
+ if (!Relas)
+ return createError("unable to read relocations for section " +
+ describe(*this, Sec) + ": " +
+ toString(Relas.takeError()));
+ for (typename ELFFile<ELFT>::Elf_Rela Rela : *Relas)
+ FunctionOffsetTranslations[Rela.r_offset] = Rela.r_addend;
+ }
+ auto GetAddressForRelocation =
+ [&](unsigned RelocationOffsetInSection) -> Expected<unsigned> {
+ auto FOTIterator =
+ FunctionOffsetTranslations.find(RelocationOffsetInSection);
+ if (FOTIterator == FunctionOffsetTranslations.end()) {
+ return createError("failed to get relocation data for offset: " +
+ Twine::utohexstr(RelocationOffsetInSection) +
+ " in section " + describe(*this, Sec));
+ }
+ return FOTIterator->second;
+ };
+ Expected<ArrayRef<uint8_t>> ContentsOrErr = this->getSectionContents(Sec);
+ if (!ContentsOrErr)
+ return ContentsOrErr.takeError();
+ ArrayRef<uint8_t> Content = *ContentsOrErr;
+ DataExtractor Data(Content, this->isLE(), ELFT::Is64Bits ? 8 : 4);
+ std::vector<FuncMap> FunctionEntries;
+
+ DataExtractor::Cursor Cur(0);
+ Error ULEBSizeErr = Error::success();
+
+ // Helper lampda to extract the (possiblly relocatable) address stored at Cur.
+ auto ExtractAddress = [&]() -> Expected<typename ELFFile<ELFT>::uintX_t> {
+ uint64_t RelocationOffsetInSection = Cur.tell();
+ auto Address =
+ static_cast<typename ELFFile<ELFT>::uintX_t>(Data.getAddress(Cur));
+ if (!Cur)
+ return Cur.takeError();
+ if (!IsRelocatable)
+ return Address;
+ assert(Address == 0);
+ Expected<unsigned> AddressOrErr =
+ GetAddressForRelocation(RelocationOffsetInSection);
+ if (!AddressOrErr)
+ return AddressOrErr.takeError();
+ return *AddressOrErr;
+ };
+
+ uint8_t Version = 0;
+ uint8_t Feature = 0;
+ FuncMap::Features FeatEnable{};
+ while (!ULEBSizeErr && Cur && Cur.tell() < Content.size()) {
+ if (Sec.sh_type == ELF::SHT_LLVM_FUNC_MAP) {
+ Version = Data.getU8(Cur);
+ if (!Cur)
+ break;
+ if (Version > 1)
+ return createError("unsupported SHT_LLVM_FUNC_MAP version: " +
+ Twine(static_cast<int>(Version)));
+ Feature = Data.getU8(Cur); // Feature byte
+ if (!Cur)
+ break;
+ auto FeatEnableOrErr = FuncMap::Features::decode(Feature);
+ if (!FeatEnableOrErr)
+ return FeatEnableOrErr.takeError();
+ FeatEnable = *FeatEnableOrErr;
+ }
+ typename ELFFile<ELFT>::uintX_t FunctionAddress = 0;
+ auto AddressOrErr = ExtractAddress();
+ if (!AddressOrErr)
+ return AddressOrErr.takeError();
+ FunctionAddress = *AddressOrErr;
+ uint64_t DynamicInstCount =
+ FeatEnable.DynamicInstCount
+ ? readULEB128As<uint64_t>(Data, Cur, ULEBSizeErr)
+ : 0;
+ FunctionEntries.push_back({FunctionAddress, DynamicInstCount, FeatEnable});
+ }
+ // Either Cur is in the error state, or we have an error in ULEBSizeErr, but
+ // we join all errors here to be safe.
+ if (!Cur || ULEBSizeErr)
+ return joinErrors(Cur.takeError(), std::move(ULEBSizeErr));
+ return FunctionEntries;
+}
+
template <class ELFT>
Expected<
MapVector<const typename ELFT::Shdr *, const typename ELFT::Shdr *>>
diff --git a/llvm/test/tools/llvm-readobj/ELF/func-map.test b/llvm/test/tools/llvm-readobj/ELF/func-map.test
new file mode 100644
index 00000000000000..eb768ea21b8e1e
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/ELF/func-map.test
@@ -0,0 +1,96 @@
+## This test checks how we handle the --func-map option.
+
+## Check 64-bit:
+# RUN: yaml2obj --docnum=1 %s -DBITS=64 -DADDR=0x999999999 -o %t1.x64.o
+# RUN: llvm-readobj %t1.x64.o --func-map 2>&1 | FileCheck %s -DADDR=0x999999999 -DFILE=%t1.x64.o --check-prefix=CHECK
+# RUN: llvm-readelf %t1.x64.o --func-map | FileCheck %s --check-prefix=GNU
+
+## Check 32-bit:
+# RUN: yaml2obj --docnum=1 %s -DBITS=32 -o %t1.x32.o
+# RUN: llvm-readobj %t1.x32.o --func-map 2>&1 | FileCheck -DADDR=0x11111 %s -DFILE=%t1.x32.o --check-prefix=CHECK
+# RUN: llvm-readelf %t1.x32.o --func-map | FileCheck %s --check-prefix=GNU
+
+## Check that a malformed section can be handled.
+# RUN: yaml2obj --docnum=1 %s -DBITS=32 -DSIZE=3 -o %t2.o
+# RUN: llvm-readobj %t2.o --func-map 2>&1 | FileCheck %s -DOFFSET=0x3 -DFILE=%t2.o --check-prefix=TRUNCATED
+
+# CHECK: FuncMap [
+# CHECK-NEXT: Function {
+# CHECK-NEXT: At: [[ADDR]]
+# CHECK-NEXT: warning: '[[FILE]]': could not identify function symbol for address ([[ADDR]]) in SHT_LLVM_FUNC_MAP section with index 3
+# CHECK-NEXT: Name: <?>
+# CHECK-NEXT: DynamicInstCount: 10
+# CHECK-NEXT: }
+# CHECK-NEXT: Function {
+# CHECK-NEXT: At: 0x22222
+# CHECK-NEXT: Name: foo
+# CHECK-NEXT: DynamicInstCount: 16
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+# CHECK-NEXT: FuncMap [
+# CHECK-NEXT: Function {
+# CHECK-NEXT: At: 0x33333
+# CHECK-NEXT: Name: bar
+# CHECK-NEXT: DynamicInstCount: 10
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+
+# GNU: GNUStyle::printFuncMaps not implemented
+
+# TRUNCATED: FuncMap [
+# TRUNCATED-NEXT: warning: '[[FILE]]': unable to dump SHT_LLVM_FUNC_MAP section with index 3: unexpected end of data at offset [[OFFSET]]
+# TRUNCATED-NEXT: ]
+## Check that the other valid section is properly dumped.
+# TRUNCATED-NEXT: FuncMap [
+# TRUNCATED-NEXT: Function {
+# TRUNCATED-NEXT: At: 0x33333
+# TRUNCATED-NEXT: Name: bar
+# TRUNCATED-NEXT: DynamicInstCount: 10
+# TRUNCATED-NEXT: }
+# TRUNCATED-NEXT: ]
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS[[BITS]]
+ Data: ELFDATA2LSB
+ Type: ET_EXEC
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [SHF_ALLOC]
+ - Name: .text.bar
+ Type: SHT_PROGBITS
+ Flags: [SHF_ALLOC]
+ - Name: .llvm_func_map
+ Type: SHT_LLVM_FUNC_MAP
+ ShSize: [[SIZE=<none>]]
+ Link: .text
+ Entries:
+ - Version: 1
+ Feature: 0x1
+ Address: [[ADDR=0x11111]]
+ DynInstCnt: 0xA
+ - Version: 1
+ Feature: 0x1
+ Address: 0x22222
+ DynInstCnt: 0x10
+ - Name: dummy_section
+ Type: SHT_PROGBITS
+ Size: 16
+ - Name: '.llvm_func_map_2'
+ Type: SHT_LLVM_FUNC_MAP
+ Link: .text.bar
+ Entries:
+ - Version: 1
+ Feature: 0x1
+ Address: 0x33333
+ DynInstCnt: 0xA
+Symbols:
+ - Name: foo
+ Section: .text
+ Type: STT_FUNC
+ Value: 0x22222
+ - Name: bar
+ Section: .text.bar
+ Type: STT_FUNC
+ Value: 0x33333
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index bfca65aad52b44..1595ad935e9bc0 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -606,6 +606,7 @@ template <typename ELFT> class GNUELFDumper : public ELFDumper<ELFT> {
void printVersionDependencySection(const Elf_Shdr *Sec) override;
void printCGProfile() override;
void printBBAddrMaps(bool PrettyPGOAnalysis) override;
+ void printFuncMaps() override;
void printAddrsig() override;
void printNotes() override;
void printELFLinkerOptions() override;
@@ -717,6 +718,7 @@ template <typename ELFT> class LLVMELFDumper : public ELFDumper<ELFT> {
void printVersionDependencySection(const Elf_Shdr *Sec) override;
void printCGProfile() override;
void printBBAddrMaps(bool PrettyPGOAnalysis) override;
+ void printFuncMaps() override;
void printAddrsig() override;
void printNotes() override;
void printELFLinkerOptions() override;
@@ -5199,6 +5201,10 @@ void GNUELFDumper<ELFT>::printBBAddrMaps(bool /*PrettyPGOAnalysis*/) {
OS << "GNUStyle::printBBAddrMaps not implemented\n";
}
+template <class ELFT> void GNUELFDumper<ELFT>::printFuncMaps() {
+ OS << "GNUStyle::printFuncMaps not implemented\n";
+}
+
static Expected<std::vector<uint64_t>> toULEB128Array(ArrayRef<uint8_t> Data) {
std::vector<uint64_t> Ret;
const uint8_t *Cur = Data.begin();
@@ -7895,6 +7901,60 @@ void LLVMELFDumper<ELFT>::printBBAddrMaps(bool PrettyPGOAnalysis) {
}
}
+template <class ELFT> void LLVMELFDumper<ELFT>::printFuncMaps() {
+ bool IsRelocatable = this->Obj.getHeader().e_type == ELF::ET_REL;
+ using Elf_Shdr = typename ELFT::Shdr;
+ auto IsMatch = [](const Elf_Shdr &Sec) -> bool {
+ return Sec.sh_type == ELF::SHT_LLVM_FUNC_MAP;
+ };
+ Expected<MapVector<const Elf_Shdr *, const Elf_Shdr *>> SecRelocMapOrErr =
+ this->Obj.getSectionAndRelocations(IsMatch);
+ if (!SecRelocMapOrErr) {
+ this->reportUniqueWarning("failed to get SHT_LLVM_FUNC_MAP section(s): " +
+ toString(SecRelocMapOrErr.takeError()));
+ return;
+ }
+
+ for (auto const &[Sec, RelocSec] : *SecRelocMapOrErr) {
+ std::optional<const Elf_Shdr *> FunctionSec;
+ if (IsRelocatable)
+ FunctionSec =
+ unwrapOrError(this->FileName, this->Obj.getSection(Sec->sh_link));
+ ListScope L(W, "FuncMap");
+ if (IsRelocatable && !RelocSec) {
+ this->reportUniqueWarning("unable to get relocation section for " +
+ this->describe(*Sec));
+ continue;
+ }
+ Expected<std::vector<FuncMap>> FuncMapOrErr =
+ this->Obj.decodeFuncMap(*Sec, RelocSec);
+ if (!FuncMapOrErr) {
+ this->reportUniqueWarning("unable to dump " + this->describe(*Sec) +
+ ": " + toString(FuncMapOrErr.takeError()));
+ continue;
+ }
+ for (const auto &AM : *FuncMapOrErr) {
+ DictScope D(W, "Function");
+ W.printHex("At", AM.getFunctionAddress());
+ SmallVector<uint32_t> FuncSymIndex =
+ this->getSymbolIndexesForFunctionAddress(AM.getFunctionAddress(),
+ FunctionSec);
+ std::string FuncName = "<?>";
+ if (FuncSymIndex.empty())
+ this->reportUniqueWarning(
+ "could not identify function symbol for address (0x" +
+ Twine::utohexstr(AM.getFunctionAddress()) + ") in " +
+ this->describe(*Sec));
+ else
+ FuncName = this->getStaticSymbolName(FuncSymIndex.front());
+
+ W.printString("Name", FuncName);
+ if (AM.FeatEnable.DynamicInstCount)
+ W.printNumber("DynamicInstCount", AM.DynamicInstCount);
+ }
+ }
+}
+
template <class ELFT> void LLVMELFDumper<ELFT>::printAddrsig() {
ListScope L(W, "Addrsig");
if (!this->DotAddrsigSec)
diff --git a/llvm/tools/llvm-readobj/ObjDumper.h b/llvm/tools/llvm-readobj/ObjDumper.h
index cd744e3bbfb712..2c2341e0468153 100644
--- a/llvm/tools/llvm-readobj/ObjDumper.h
+++ b/llvm/tools/llvm-readobj/ObjDumper.h
@@ -132,6 +132,7 @@ class ObjDumper {
// If PrettyPGOAnalysis is true, prints BFI as relative frequency and BPI as
// percentage. Otherwise raw values are displayed.
virtual void printBBAddrMaps(bool PrettyPGOAnalysis) {}
+ virtual void printFuncMaps() {}
virtual void printAddrsig() {}
virtual void printNotes() {}
virtual void printELFLinkerOptions() {}
diff --git a/llvm/tools/llvm-readobj/Opts.td b/llvm/tools/llvm-readobj/Opts.td
index 7d574d875d22ea..17c65a6feb7896 100644
--- a/llvm/tools/llvm-readobj/Opts.td
+++ b/llvm/tools/llvm-readobj/Opts.td
@@ -19,6 +19,7 @@ def all : FF<"all", "Equivalent to setting: --file-header, --program-headers, --
"--section-groups and --histogram">;
def arch_specific : FF<"arch-specific", "Display architecture-specific information">;
def bb_addr_map : FF<"bb-addr-map", "Display the BB address map section">;
+def func_map : FF<"func-map", "Display the function address map section">;
def pretty_pgo_analysis_map : FF<"pretty-pgo-analysis-map", "Display PGO analysis values with formatting rather than raw numbers">;
def cg_profile : FF<"cg-profile", "Display call graph profile section">;
def decompress : FF<"decompress", "Dump decompressed section content when used with -x or -p">;
diff --git a/llvm/tools/llvm-readobj/llvm-readobj.cpp b/llvm/tools/llvm-readobj/llvm-readobj.cpp
index 2f77e5d350553d..91465a631cba37 100644
--- a/llvm/tools/llvm-readobj/llvm-readobj.cpp
+++ b/llvm/tools/llvm-readobj/llvm-readobj.cpp
@@ -98,6 +98,7 @@ static bool All;
static bool ArchSpecificInfo;
static bool BBAddrMap;
static bool PrettyPGOAnalysisMap;
+static bool FuncMap;
bool ExpandRelocs;
static bool CGProfile;
static bool Decompress;
@@ -220,6 +221,7 @@ static void parseOptions(const opt::InputArgList &Args) {
<< "--bb-addr-map must be enabled for --pretty-pgo-analysis-map to "
"have an effect\n";
opts::CGProfile = Args.hasArg(OPT_cg_profile);
+ opts::FuncMap = Args.hasArg(OPT_func_map);
opts::Decompress = Args.hasArg(OPT_decompress);
opts::Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, false);
opts::DependentLibraries = Args.hasArg(OPT_dependent_libraries);
@@ -473,6 +475,8 @@ static void dumpObject(ObjectFile &Obj, ScopedPrinter &Writer,
Dumper->printCGProfile();
if (opts::BBAddrMap)
Dumper->printBBAddrMaps(opts::PrettyPGOAnalysisMap);
+ if (opts::FuncMap)
+ Dumper->printFuncMaps();
if (opts::Addrsig)
Dumper->printAddrsig();
if (opts::Notes)
|
Created using spr 1.3.6-beta.1
Created using spr 1.3.6-beta.1
Created using spr 1.3.6-beta.1
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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need this check? Would not the value of RelaSec
(nullptr
or otherwise) be sufficient?
for (typename ELFFile<ELFT>::Elf_Rela Rela : *Relas) | ||
FunctionOffsetTranslations[Rela.r_offset] = Rela.r_addend; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not all relocations function in the same way. Naively assuming that the r_addend
and r_offset
work like this is not going to be correct in some cases. The ELF gABI only describes r_addend
as a "constant addend used to compute the value".
Have you looked into the Object/RelocationResolver.h? It's used elsewhere by llvm-readobj to calculate the values of relocations and may be of some use (see printRelocatableStackSizes
for an example usage).
|
||
uint8_t Version = 0; | ||
while (Cur && Cur.tell() < Content.size()) { | ||
if (Sec.sh_type == ELF::SHT_LLVM_FUNC_MAP) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this check needed?
auto IsMatch = [](const Elf_Shdr &Sec) -> bool { | ||
return Sec.sh_type == ELF::SHT_LLVM_FUNC_MAP; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please define this inline, since it's only used once, and delete the trailing return type (since that is automatically derived from the result of the return expression, of which there is only one).
std::optional<const Elf_Shdr *> FunctionSec; | ||
if (IsRelocatable) | ||
FunctionSec = | ||
unwrapOrError(this->FileName, this->Obj.getSection(Sec->sh_link)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do not use unwrapOrError
in new code. The dumper should be tolerant of slightly dodgy looking object files, as the dumper is often the only way of finding out what's gone wrong (short of decoding the bytes be hand).
A warning is fine here and you can then either continue as if the relocation section didn't exist or bail out. See my comments elsewhere about the requirement for a relocation section.
llvm-readobj part of #124332
PR stack:
#124332 [SHT_LLVM_FUNC_MAP][ObjectYaml]Introduce function address map section and emit dynamic instruction count(ObjectYaml part)
(This one) #124333 [SHT_LLVM_FUNC_MAP][llvm-readobj]Introduce function address map section and emit dynamic instruction count(readobj part)
#124334 [SHT_LLVM_FUNC_MAP][CodeGen]Introduce function address map section and emit dynamic instruction count(CodeGen part)
Test Plan: llvm/test/tools/llvm-readobj/ELF/func-map.test