}
}
-template <class ELFT> static void readCallGraphsFromObjectFiles() {
- auto getIndex = [&](ObjFile<ELFT> *obj, uint32_t index) {
- const Elf_Rel_Impl<ELFT, false> &rel = obj->cgProfileRel[index];
- return rel.getSymbol(config->isMips64EL);
- };
+// If SHT_LLVM_CALL_GRAPH_PROFILE and its relocation section exist, returns
+// true and populates cgProfile and symbolIndices.
+template <class ELFT>
+static bool
+processCallGraphRelocations(SmallVector<uint32_t, 32> &symbolIndices,
+ ArrayRef<typename ELFT::CGProfile> &cgProfile,
+ ObjFile<ELFT> *inputObj) {
+ symbolIndices.clear();
+ const ELFFile<ELFT> &obj = inputObj->getObj();
+ ArrayRef<Elf_Shdr_Impl<ELFT>> objSections =
+ CHECK(obj.sections(), "could not retrieve object sections");
+
+ if (inputObj->cgProfileSectionIndex == SHN_UNDEF)
+ return false;
+ cgProfile =
+ check(obj.template getSectionContentsAsArray<typename ELFT::CGProfile>(
+ objSections[inputObj->cgProfileSectionIndex]));
+
+ for (size_t i = 0, e = objSections.size(); i < e; ++i) {
+ const Elf_Shdr_Impl<ELFT> &sec = objSections[i];
+ if (sec.sh_info == inputObj->cgProfileSectionIndex) {
+ if (sec.sh_type == SHT_RELA) {
+ ArrayRef<typename ELFT::Rela> relas =
+ CHECK(obj.relas(sec), "could not retrieve cg profile rela section");
+ for (const typename ELFT::Rela &rel : relas)
+ symbolIndices.push_back(rel.getSymbol(config->isMips64EL));
+ break;
+ }
+ if (sec.sh_type == SHT_REL) {
+ ArrayRef<typename ELFT::Rel> rels =
+ CHECK(obj.rels(sec), "could not retrieve cg profile rel section");
+ for (const typename ELFT::Rel &rel : rels)
+ symbolIndices.push_back(rel.getSymbol(config->isMips64EL));
+ break;
+ }
+ }
+ }
+ if (symbolIndices.empty())
+ warn("SHT_LLVM_CALL_GRAPH_PROFILE exists, but relocation section doesn't");
+ return !symbolIndices.empty();
+}
+
+template <class ELFT> static void readCallGraphsFromObjectFiles() {
+ SmallVector<uint32_t, 32> symbolIndices;
+ ArrayRef<typename ELFT::CGProfile> cgProfile;
for (auto file : objectFiles) {
auto *obj = cast<ObjFile<ELFT>>(file);
- if (obj->cgProfileRel.empty())
+ if (!processCallGraphRelocations(symbolIndices, cgProfile, obj))
continue;
- if (obj->cgProfileRel.size() != obj->cgProfile.size() * 2)
+
+ if (symbolIndices.size() != cgProfile.size() * 2)
fatal("number of relocations doesn't match Weights");
- for (uint32_t i = 0, size = obj->cgProfile.size(); i < size; ++i) {
- const Elf_CGProfile_Impl<ELFT> &cgpe = obj->cgProfile[i];
- uint32_t fromIndex = getIndex(obj, i * 2);
- uint32_t toIndex = getIndex(obj, i * 2 + 1);
+
+ for (uint32_t i = 0, size = cgProfile.size(); i < size; ++i) {
+ const Elf_CGProfile_Impl<ELFT> &cgpe = cgProfile[i];
+ uint32_t fromIndex = symbolIndices[i * 2];
+ uint32_t toIndex = symbolIndices[i * 2 + 1];
auto *fromSym = dyn_cast<Defined>(&obj->getSymbol(fromIndex));
auto *toSym = dyn_cast<Defined>(&obj->getSymbol(toIndex));
if (!fromSym || !toSym)
CHECK(obj.getSectionStringTable(objSections), this);
std::vector<ArrayRef<Elf_Word>> selectedGroups;
- // SHT_LLVM_CALL_GRAPH_PROFILE Section Index.
- size_t cgProfileSectionIndex = 0;
for (size_t i = 0, e = objSections.size(); i < e; ++i) {
if (this->sections[i] == &InputSection::discarded)
continue;
const Elf_Shdr &sec = objSections[i];
- if (sec.sh_type == ELF::SHT_LLVM_CALL_GRAPH_PROFILE) {
- cgProfile =
- check(obj.template getSectionContentsAsArray<Elf_CGProfile>(sec));
+ if (sec.sh_type == ELF::SHT_LLVM_CALL_GRAPH_PROFILE)
cgProfileSectionIndex = i;
- }
// SHF_EXCLUDE'ed sections are discarded by the linker. However,
// if -r is given, we'll let the final link discard such sections.
continue;
const Elf_Shdr &sec = objSections[i];
- if (sec.sh_type == SHT_REL || sec.sh_type == SHT_RELA) {
+ if (sec.sh_type == SHT_REL || sec.sh_type == SHT_RELA)
this->sections[i] = createInputSection(sec);
- if (cgProfileSectionIndex && sec.sh_info == cgProfileSectionIndex) {
- if (sec.sh_type == SHT_REL)
- cgProfileRel = CHECK(getObj().rels(sec), this);
- }
- }
// A SHF_LINK_ORDER section with sh_link=0 is handled as if it did not have
// the flag.
// Pointer to this input file's .llvm_addrsig section, if it has one.
const Elf_Shdr *addrsigSec = nullptr;
- // SHT_LLVM_CALL_GRAPH_PROFILE table.
- ArrayRef<Elf_CGProfile> cgProfile;
- // SHT_LLVM_CALL_GRAPH_PROFILE relocations, always in the REL format.
- ArrayRef<Elf_Rel> cgProfileRel;
+ // SHT_LLVM_CALL_GRAPH_PROFILE section index.
+ uint32_t cgProfileSectionIndex = 0;
// Get cached DWARF information.
DWARFCache *getDwarf();
--- /dev/null
+## Under some circumstances, GNU tools strip/objcopy change REL to RELA. https://sourceware.org/bugzilla/show_bug.cgi?id=28035
+## Test that LLD can handle call graph profile data relocated with RELA relocations.
+# REQUIRES: x86
+
+# RUN: yaml2obj %s -o %t.o
+# RUN: ld.lld %t.o -o %t
+# RUN: llvm-nm --no-sort %t | FileCheck %s
+# RUN: ld.lld --no-call-graph-profile-sort %t.o -o %t
+# RUN: llvm-nm --no-sort %t | FileCheck %s --check-prefix=NO-CG
+
+# CHECK: 0000000000201124 t D
+# CHECK: 0000000000201122 t C
+# CHECK: 0000000000201128 t B
+# CHECK: 0000000000201120 t A
+# CHECK: 0000000000201126 T _start
+
+# NO-CG: 0000000000201120 t D
+# NO-CG: 0000000000201122 t C
+# NO-CG: 0000000000201124 t B
+# NO-CG: 0000000000201126 t A
+# NO-CG: 0000000000201128 T _start
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_REL
+ Machine: EM_X86_64
+Sections:
+ - Name: .text.D
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ Size: 2
+ - Name: .text.C
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ Size: 2
+ - Name: .text.B
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ Size: 2
+ - Name: .text.A
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ Size: 2
+ - Name: .text._start
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ Size: 2
+ - Name: .llvm.call-graph-profile
+ Type: SHT_LLVM_CALL_GRAPH_PROFILE
+ Flags: [ SHF_EXCLUDE ]
+ Link: .symtab
+ AddressAlign: 0x1
+ Entries:
+ - Weight: 10
+ - Weight: 10
+ - Weight: 80
+ - Weight: 40
+ - Weight: 30
+ - Weight: 90
+ - Name: .rela.llvm.call-graph-profile
+ Type: SHT_RELA
+ Info: .llvm.call-graph-profile
+ Relocations:
+ - Offset: 0x0
+ Symbol: A
+ Type: R_X86_64_NONE
+ - Offset: 0x0
+ Symbol: B
+ Type: R_X86_64_NONE
+ - Offset: 0x8
+ Symbol: A
+ Type: R_X86_64_NONE
+ - Offset: 0x8
+ Symbol: B
+ Type: R_X86_64_NONE
+ - Offset: 0x10
+ Symbol: _start
+ Type: R_X86_64_NONE
+ - Offset: 0x10
+ Symbol: B
+ Type: R_X86_64_NONE
+ - Offset: 0x18
+ Symbol: A
+ Type: R_X86_64_NONE
+ - Offset: 0x18
+ Symbol: C
+ Type: R_X86_64_NONE
+ - Offset: 0x20
+ Symbol: B
+ Type: R_X86_64_NONE
+ - Offset: 0x20
+ Symbol: C
+ Type: R_X86_64_NONE
+ - Offset: 0x28
+ Symbol: C
+ Type: R_X86_64_NONE
+ - Offset: 0x28
+ Symbol: D
+ Type: R_X86_64_NONE
+Symbols:
+ - Name: D
+ Type: STT_FUNC
+ Section: .text.D
+ - Name: C
+ Type: STT_FUNC
+ Section: .text.C
+ - Name: B
+ Type: STT_FUNC
+ Section: .text.B
+ - Name: A
+ Type: STT_FUNC
+ Section: .text.A
+ - Name: _start
+ Binding: STB_GLOBAL
+ Section: .text._start
Relocations:
- Symbol: foo
Type: R_X86_64_NONE
- - Offset: 0x1
+ - Offset: 0x0
Symbol: bar
Type: R_X86_64_NONE
- - Offset: 0x2
+ - Offset: 0x8
Symbol: bar
Type: R_X86_64_NONE
- - Offset: 0x3
+ - Offset: 0x8
Symbol: foo
Type: R_X86_64_NONE
Symbols:
Relocations:
- Symbol: 1
Type: R_X86_64_NONE
- - Offset: 0x1
+ - Offset: 0x0
Symbol: 2
Type: R_X86_64_NONE
- - Offset: 0x2
+ - Offset: 0x8
Symbol: 2
Type: R_X86_64_NONE
- - Offset: 0x3
+ - Offset: 0x8
Symbol: 3
Type: R_X86_64_NONE
- - Offset: 0x4
+ - Offset: 0x10
Symbol: 0x0 ## Null symbol.
Type: R_X86_64_NONE
- - Offset: 0x5
+ - Offset: 0x10
Symbol: 0x4 ## This index goes past the end of the symbol table.
Type: R_X86_64_NONE
- Name: .strtab
Entries:
- Weight: 89
- Weight: 98
- EntSize: [[ENTSIZE=<none>]]
Symbols:
- Name: foo
- Name: bar
Entries:
- Weight: 89
- Weight: 98
- EntSize: [[ENTSIZE=<none>]]
- Name: .rel.llvm.call-graph-profile
Type: SHT_REL
Info: .llvm.call-graph-profile
Relocations:
- Symbol: foo
Type: R_X86_64_NONE
- - Offset: 0x1
+ - Offset: 0x0
Symbol: bar
Type: R_X86_64_NONE
- - Offset: 0x2
+ - Offset: 0x8
Symbol: bar
Type: R_X86_64_NONE
- - Offset: 0x3
+ - Offset: 0x8
Symbol: foo
Type: R_X86_64_NONE
- - Offset: 0x4
+ - Offset: 0x10
Symbol: foo
Type: R_X86_64_NONE
Symbols:
- Name: foo
- Name: bar
-## Check we report a warning when a relocation section cant't be loaded.
+## Check we report a warning when a REL relocation section can't be loaded.
# RUN: yaml2obj %s --docnum=5 -o %t6.o
# RUN: llvm-readobj %t6.o --cg-profile 2>&1 | FileCheck %s -DFILE=%t6.o --check-prefix=LLVM-RELOC-WRONG-SIZE
# RUN: llvm-readobj %t6.o --elf-cg-profile 2>&1 | FileCheck %s -DFILE=%t6.o --check-prefix=LLVM-RELOC-WRONG-SIZE
Entries:
- Weight: 89
- Weight: 98
- EntSize: [[ENTSIZE=<none>]]
- Name: .rel.llvm.call-graph-profile
Type: SHT_REL
Info: .llvm.call-graph-profile
Relocations:
- Symbol: foo
Type: R_X86_64_NONE
- - Offset: 0x1
+ - Offset: 0x0
Symbol: bar
Type: R_X86_64_NONE
- - Offset: 0x2
+ - Offset: 0x8
Symbol: bar
Type: R_X86_64_NONE
- - Offset: 0x3
+ - Offset: 0x8
Symbol: foo
Type: R_X86_64_NONE
EntSize: 24
Symbols:
- Name: foo
- Name: bar
+
+## GNU strip may convert SHT_REL to SHT_RELA. Test we can handle SHT_RELA.
+# RUN: yaml2obj %s --docnum=6 -o %t7.o
+# RUN: llvm-readobj %t7.o --cg-profile | FileCheck %s --check-prefix=LLVM-RELA
+# RUN: llvm-readelf %t7.o --cg-profile | FileCheck %s --check-prefix=GNU-RELA
+
+# LLVM-RELA: CGProfile [
+# LLVM-RELA-NEXT: CGProfileEntry {
+# LLVM-RELA-NEXT: From: foo (1)
+# LLVM-RELA-NEXT: To: bar (2)
+# LLVM-RELA-NEXT: Weight: 89
+# LLVM-RELA-NEXT: }
+# LLVM-RELA-NEXT: CGProfileEntry {
+# LLVM-RELA-NEXT: From: bar (2)
+# LLVM-RELA-NEXT: To: foo (1)
+# LLVM-RELA-NEXT: Weight: 98
+# LLVM-RELA-NEXT: }
+# LLVM-RELA-NEXT: ]
+
+# GNU-RELA: GNUStyle::printCGProfile not implemented
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_DYN
+ Machine: EM_X86_64
+Sections:
+ - Name: .llvm.call-graph-profile
+ Type: SHT_LLVM_CALL_GRAPH_PROFILE
+ Entries:
+ - Weight: 89
+ - Weight: 98
+ - Name: .rela.llvm.call-graph-profile
+ Type: SHT_RELA
+ Info: .llvm.call-graph-profile
+ Relocations:
+ - Symbol: foo
+ Type: R_X86_64_NONE
+ - Offset: 0x0
+ Symbol: bar
+ Type: R_X86_64_NONE
+ - Offset: 0x8
+ Symbol: bar
+ Type: R_X86_64_NONE
+ - Offset: 0x8
+ Symbol: foo
+ Type: R_X86_64_NONE
+Symbols:
+ - Name: foo
+ - Name: bar
+
+## Check we report a warning when a RELA relocation section can't be loaded.
+# RUN: yaml2obj %s --docnum=7 -o %t8.o
+# RUN: llvm-readobj %t8.o --cg-profile 2>&1 | FileCheck %s -DFILE=%t8.o --check-prefix=LLVM-RELOC-WRONG-SIZE-RELA
+# RUN: llvm-readobj %t8.o --elf-cg-profile 2>&1 | FileCheck %s -DFILE=%t8.o --check-prefix=LLVM-RELOC-WRONG-SIZE-RELA
+
+# LLVM-RELOC-WRONG-SIZE-RELA: warning: '[[FILE]]': unable to load relocations for SHT_LLVM_CALL_GRAPH_PROFILE section: section [index 2] has invalid sh_entsize: expected 24, but got 16
+# LLVM-RELOC-WRONG-SIZE-RELA-NEXT: CGProfile [
+# LLVM-RELOC-WRONG-SIZE-RELA-NEXT: CGProfileEntry {
+# LLVM-RELOC-WRONG-SIZE-RELA-NEXT: Weight: 89
+# LLVM-RELOC-WRONG-SIZE-RELA-NEXT: }
+# LLVM-RELOC-WRONG-SIZE-RELA-NEXT: CGProfileEntry {
+# LLVM-RELOC-WRONG-SIZE-RELA-NEXT: Weight: 98
+# LLVM-RELOC-WRONG-SIZE-RELA-NEXT: }
+# LLVM-RELOC-WRONG-SIZE-RELA-NEXT: ]
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_DYN
+ Machine: EM_X86_64
+Sections:
+ - Name: .llvm.call-graph-profile
+ Type: SHT_LLVM_CALL_GRAPH_PROFILE
+ Entries:
+ - Weight: 89
+ - Weight: 98
+ - Name: .rela.llvm.call-graph-profile
+ Type: SHT_RELA
+ Info: .llvm.call-graph-profile
+ Relocations:
+ - Symbol: foo
+ Type: R_X86_64_NONE
+ - Offset: 0x0
+ Symbol: bar
+ Type: R_X86_64_NONE
+ - Offset: 0x8
+ Symbol: bar
+ Type: R_X86_64_NONE
+ - Offset: 0x8
+ Symbol: foo
+ Type: R_X86_64_NONE
+ EntSize: 16
+Symbols:
+ - Name: foo
+ - Name: bar
W.startLine() << "Hash Histogram not implemented!\n";
}
+// Returns true if rel/rela section exists, and populates SymbolIndices.
+// Otherwise returns false.
+template <class ELFT>
+static bool getSymbolIndices(const typename ELFT::Shdr *CGRelSection,
+ const ELFFile<ELFT> &Obj,
+ const LLVMELFDumper<ELFT> *Dumper,
+ SmallVector<uint32_t, 128> &SymbolIndices) {
+ if (!CGRelSection) {
+ Dumper->reportUniqueWarning(
+ "relocation section for a call graph section doesn't exist");
+ return false;
+ }
+
+ if (CGRelSection->sh_type == SHT_REL) {
+ typename ELFT::RelRange CGProfileRel;
+ Expected<typename ELFT::RelRange> CGProfileRelOrError =
+ Obj.rels(*CGRelSection);
+ if (!CGProfileRelOrError) {
+ Dumper->reportUniqueWarning("unable to load relocations for "
+ "SHT_LLVM_CALL_GRAPH_PROFILE section: " +
+ toString(CGProfileRelOrError.takeError()));
+ return false;
+ }
+
+ CGProfileRel = *CGProfileRelOrError;
+ for (const typename ELFT::Rel &Rel : CGProfileRel)
+ SymbolIndices.push_back(Rel.getSymbol(Obj.isMips64EL()));
+ } else {
+ // MC unconditionally produces SHT_REL, but GNU strip/objcopy may convert
+ // the format to SHT_RELA
+ // (https://sourceware.org/bugzilla/show_bug.cgi?id=28035)
+ typename ELFT::RelaRange CGProfileRela;
+ Expected<typename ELFT::RelaRange> CGProfileRelaOrError =
+ Obj.relas(*CGRelSection);
+ if (!CGProfileRelaOrError) {
+ Dumper->reportUniqueWarning("unable to load relocations for "
+ "SHT_LLVM_CALL_GRAPH_PROFILE section: " +
+ toString(CGProfileRelaOrError.takeError()));
+ return false;
+ }
+
+ CGProfileRela = *CGProfileRelaOrError;
+ for (const typename ELFT::Rela &Rela : CGProfileRela)
+ SymbolIndices.push_back(Rela.getSymbol(Obj.isMips64EL()));
+ }
+
+ return true;
+}
+
template <class ELFT> void LLVMELFDumper<ELFT>::printCGProfile() {
llvm::MapVector<const Elf_Shdr *, const Elf_Shdr *> SecToRelocMap;
return;
}
- Elf_Rel_Range CGProfileRel;
- bool UseReloc = (CGRelSection != nullptr);
- if (UseReloc) {
- Expected<Elf_Rel_Range> CGProfileRelaOrError =
- this->Obj.rels(*CGRelSection);
- if (!CGProfileRelaOrError) {
- this->reportUniqueWarning("unable to load relocations for "
- "SHT_LLVM_CALL_GRAPH_PROFILE section: " +
- toString(CGProfileRelaOrError.takeError()));
- UseReloc = false;
- } else
- CGProfileRel = *CGProfileRelaOrError;
-
- if (UseReloc && CGProfileRel.size() != (CGProfileOrErr->size() * 2)) {
- this->reportUniqueWarning(
- "number of from/to pairs does not match number of frequencies");
- UseReloc = false;
- }
- } else
+ SmallVector<uint32_t, 128> SymbolIndices;
+ bool UseReloc =
+ getSymbolIndices<ELFT>(CGRelSection, this->Obj, this, SymbolIndices);
+ if (UseReloc && SymbolIndices.size() != CGProfileOrErr->size() * 2) {
this->reportUniqueWarning(
- "relocation section for a call graph section doesn't exist");
-
- auto GetIndex = [&](uint32_t Index) {
- const Elf_Rel_Impl<ELFT, false> &Rel = CGProfileRel[Index];
- return Rel.getSymbol(this->Obj.isMips64EL());
- };
+ "number of from/to pairs does not match number of frequencies");
+ UseReloc = false;
+ }
ListScope L(W, "CGProfile");
for (uint32_t I = 0, Size = CGProfileOrErr->size(); I != Size; ++I) {
const Elf_CGProfile &CGPE = (*CGProfileOrErr)[I];
DictScope D(W, "CGProfileEntry");
if (UseReloc) {
- uint32_t From = GetIndex(I * 2);
- uint32_t To = GetIndex(I * 2 + 1);
+ uint32_t From = SymbolIndices[I * 2];
+ uint32_t To = SymbolIndices[I * 2 + 1];
W.printNumber("From", this->getStaticSymbolName(From), From);
W.printNumber("To", this->getStaticSymbolName(To), To);
}