-
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?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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; | ||
|
||
// 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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not all relocations function in the same way. Naively assuming that the 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 |
||
} | ||
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) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 *>> | ||
|
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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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(); | ||
|
@@ -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
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do not use 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) | ||
|
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?