diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -855,22 +855,64 @@ } } -template static void readCallGraphsFromObjectFiles() { - auto getIndex = [&](ObjFile *obj, uint32_t index) { - const Elf_Rel_Impl &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 +static bool +processCallGraphRelocations(SmallVector &symbolIndices, + ArrayRef &cgProfile, + ObjFile *inputObj) { + symbolIndices.clear(); + const ELFFile &obj = inputObj->getObj(); + ArrayRef> objSections = + CHECK(obj.sections(), "could not retrieve object sections"); + + if (inputObj->cgProfileSectionIndex == SHN_UNDEF) + return false; + cgProfile = + check(obj.template getSectionContentsAsArray( + objSections[inputObj->cgProfileSectionIndex])); + + for (size_t i = 0, e = objSections.size(); i < e; ++i) { + const Elf_Shdr_Impl &sec = objSections[i]; + if (sec.sh_info == inputObj->cgProfileSectionIndex) { + if (sec.sh_type == SHT_RELA) { + ArrayRef 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 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 static void readCallGraphsFromObjectFiles() { + SmallVector symbolIndices; + ArrayRef cgProfile; for (auto file : objectFiles) { auto *obj = cast>(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 &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 &cgpe = cgProfile[i]; + uint32_t fromIndex = symbolIndices[i * 2]; + uint32_t toIndex = symbolIndices[i * 2 + 1]; auto *fromSym = dyn_cast(&obj->getSymbol(fromIndex)); auto *toSym = dyn_cast(&obj->getSymbol(toIndex)); if (!fromSym || !toSym) diff --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h --- a/lld/ELF/InputFiles.h +++ b/lld/ELF/InputFiles.h @@ -249,10 +249,8 @@ // 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 cgProfile; - // SHT_LLVM_CALL_GRAPH_PROFILE relocations, always in the REL format. - ArrayRef cgProfileRel; + // SHT_LLVM_CALL_GRAPH_PROFILE section index. + uint32_t cgProfileSectionIndex = 0; // Get cached DWARF information. DWARFCache *getDwarf(); diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -571,19 +571,14 @@ CHECK(obj.getSectionStringTable(objSections), this); std::vector> 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(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. @@ -669,13 +664,8 @@ 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. diff --git a/lld/test/ELF/cgprofile-rela.test b/lld/test/ELF/cgprofile-rela.test new file mode 100644 --- /dev/null +++ b/lld/test/ELF/cgprofile-rela.test @@ -0,0 +1,117 @@ +## 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 diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-profile.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-profile.test --- a/llvm/test/tools/llvm-readobj/ELF/call-graph-profile.test +++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-profile.test @@ -40,13 +40,13 @@ 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: @@ -104,19 +104,19 @@ 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 @@ -153,7 +153,6 @@ Entries: - Weight: 89 - Weight: 98 - EntSize: [[ENTSIZE=]] Symbols: - Name: foo - Name: bar @@ -185,30 +184,29 @@ Entries: - Weight: 89 - Weight: 98 - EntSize: [[ENTSIZE=]] - 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 @@ -235,23 +233,120 @@ Entries: - Weight: 89 - Weight: 98 - EntSize: [[ENTSIZE=]] - 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 cant'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 diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -6702,6 +6702,55 @@ W.startLine() << "Hash Histogram not implemented!\n"; } +// Returns true if rel/rela section exists, and populates SymbolIndices. +// Otherwise returns false. +template +static bool getSymbolIndices(const typename ELFT::Shdr *CGRelSection, + const ELFFile &Obj, + const LLVMELFDumper *Dumper, + SmallVector &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 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 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 void LLVMELFDumper::printCGProfile() { llvm::MapVector SecToRelocMap; @@ -6723,40 +6772,22 @@ return; } - Elf_Rel_Range CGProfileRel; - bool UseReloc = (CGRelSection != nullptr); - if (UseReloc) { - Expected 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 SymbolIndices; + bool UseReloc = + getSymbolIndices(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 &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); }