Skip to content

[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

Open
wants to merge 4 commits into
base: users/wlei-llvm/spr/main.sht_llvm_func_mapllvm-readobjintroduce-function-address-map-section-and-emit-dynamic-instruction-countreadobj-part
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions llvm/include/llvm/Object/ELF.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
86 changes: 86 additions & 0 deletions llvm/lib/Object/ELF.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -940,6 +940,92 @@ 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;
Copy link
Collaborator

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?


// This DenseMap maps the offset of each function (the location of the
// reference to the function in the SHT_LLVM_FUNC_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;
Comment on lines +963 to +964
Copy link
Collaborator

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

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

// 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 (Cur && Cur.tell() < Content.size()) {
if (Sec.sh_type == ELF::SHT_LLVM_FUNC_MAP) {
Copy link
Collaborator

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?

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 = Data.getU64(Cur);
if (!Cur)
break;
FunctionEntries.push_back({FunctionAddress, DynamicInstCount});
}

if (!Cur)
return Cur.takeError();
return FunctionEntries;
}

template <class ELFT>
Expected<
MapVector<const typename ELFT::Shdr *, const typename ELFT::Shdr *>>
Expand Down
93 changes: 93 additions & 0 deletions llvm/test/tools/llvm-readobj/ELF/func-map.test
Original file line number Diff line number Diff line change
@@ -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
59 changes: 59 additions & 0 deletions llvm/tools/llvm-readobj/ELFDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -7922,6 +7928,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;
};
Comment on lines +7934 to +7936
Copy link
Collaborator

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

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));
Copy link
Collaborator

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.

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)
Expand Down
1 change: 1 addition & 0 deletions llvm/tools/llvm-readobj/ObjDumper.h
Original file line number Diff line number Diff line change
Expand Up @@ -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() {}
Expand Down
1 change: 1 addition & 0 deletions llvm/tools/llvm-readobj/Opts.td
Original file line number Diff line number Diff line change
Expand Up @@ -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">;
Expand Down
4 changes: 4 additions & 0 deletions llvm/tools/llvm-readobj/llvm-readobj.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Expand Down