diff --git a/llvm/include/llvm/Object/ELF.h b/llvm/include/llvm/Object/ELF.h --- a/llvm/include/llvm/Object/ELF.h +++ b/llvm/include/llvm/Object/ELF.h @@ -392,7 +392,9 @@ Expected> getSectionContentsAsArray(const Elf_Shdr &Sec) const; Expected> getSectionContents(const Elf_Shdr &Sec) const; Expected> getSegmentContents(const Elf_Phdr &Phdr) const; - Expected> decodeBBAddrMap(const Elf_Shdr &Sec) const; + Expected> + decodeBBAddrMap(const Elf_Shdr &Sec, + const std::optional &RelaSec = {}) 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 diff --git a/llvm/lib/Object/ELF.cpp b/llvm/lib/Object/ELF.cpp --- a/llvm/lib/Object/ELF.cpp +++ b/llvm/lib/Object/ELF.cpp @@ -639,8 +639,25 @@ } template -Expected> -ELFFile::decodeBBAddrMap(const Elf_Shdr &Sec) const { +Expected> ELFFile::decodeBBAddrMap( + const Elf_Shdr &Sec, const std::optional &RelaSec) const { + bool IsRelocatable = getHeader().e_type == ELF::ET_REL; + assert(!IsRelocatable || (IsRelocatable && RelaSec)); + + // This DenseMap maps the offset of each function (the location of the + // reference to the function in the bbaddrmap section) to the addend + // (the location of the function in the text section). + llvm::DenseMap FunctionOffsetTranslations; + if (IsRelocatable && RelaSec) { + Expected Relas = this->relas(*RelaSec); + if (!Relas) + return createError("unable to read relocations for section " + + describe(*this, Sec) + ": " + + toString(Relas.takeError())); + for (Elf_Rela Rela : *Relas) { + FunctionOffsetTranslations[Rela.r_offset] = Rela.r_addend; + } + } Expected> ContentsOrErr = getSectionContents(Sec); if (!ContentsOrErr) return ContentsOrErr.takeError(); @@ -680,7 +697,17 @@ Twine(static_cast(Version))); Data.getU8(Cur); // Feature byte } + uint64_t SectionOffset = Cur.tell(); uintX_t Address = static_cast(Data.getAddress(Cur)); + if (IsRelocatable) { + assert(Address == 0); + auto FOTIterator = FunctionOffsetTranslations.find(SectionOffset); + if (FOTIterator == FunctionOffsetTranslations.end()) + return createError("failed to get relocation data for offset: " + + Twine(SectionOffset) + " in section " + + describe(*this, Sec)); + Address = FOTIterator->second; + } uint32_t NumBlocks = ReadULEB128AsUInt32(); std::vector BBEntries; uint32_t PrevBBEndOffset = 0; diff --git a/llvm/lib/Object/ELFObjectFile.cpp b/llvm/lib/Object/ELFObjectFile.cpp --- a/llvm/lib/Object/ELFObjectFile.cpp +++ b/llvm/lib/Object/ELFObjectFile.cpp @@ -681,12 +681,14 @@ Expected> static readBBAddrMapImpl( const ELFFile &EF, std::optional TextSectionIndex) { using Elf_Shdr = typename ELFT::Shdr; + bool IsRelocatable = EF.getHeader().e_type == ELF::ET_REL; std::vector BBAddrMaps; + const auto &Sections = cantFail(EF.sections()); - for (const Elf_Shdr &Sec : Sections) { + auto IsMatch = [&](const Elf_Shdr &Sec) -> Expected { if (Sec.sh_type != ELF::SHT_LLVM_BB_ADDR_MAP && Sec.sh_type != ELF::SHT_LLVM_BB_ADDR_MAP_V0) - continue; + return false; if (TextSectionIndex) { Expected TextSecOrErr = EF.getSection(Sec.sh_link); if (!TextSecOrErr) @@ -694,11 +696,24 @@ describe(EF, Sec) + ": " + toString(TextSecOrErr.takeError())); if (*TextSectionIndex != std::distance(Sections.begin(), *TextSecOrErr)) - continue; + return false; } - Expected> BBAddrMapOrErr = EF.decodeBBAddrMap(Sec); + return true; + }; + + Expected> SectionRelocMapOrErr = + EF.getSectionAndRelocations(IsMatch); + if (!SectionRelocMapOrErr) + return SectionRelocMapOrErr.takeError(); + + for (auto const &[Sec, RelocSec] : *SectionRelocMapOrErr) { + std::optional RelaSec; + if (IsRelocatable) + RelaSec = *RelocSec; + Expected> BBAddrMapOrErr = + EF.decodeBBAddrMap(*Sec, RelaSec); if (!BBAddrMapOrErr) - return createError("unable to read " + describe(EF, Sec) + ": " + + return createError("unable to read " + describe(EF, *Sec) + ": " + toString(BBAddrMapOrErr.takeError())); std::move(BBAddrMapOrErr->begin(), BBAddrMapOrErr->end(), std::back_inserter(BBAddrMaps)); diff --git a/llvm/test/tools/llvm-objdump/X86/elf-bbaddrmap-disassemble-symbolize-operands.yaml b/llvm/test/tools/llvm-objdump/X86/elf-bbaddrmap-disassemble-symbolize-operands.yaml --- a/llvm/test/tools/llvm-objdump/X86/elf-bbaddrmap-disassemble-symbolize-operands.yaml +++ b/llvm/test/tools/llvm-objdump/X86/elf-bbaddrmap-disassemble-symbolize-operands.yaml @@ -5,19 +5,12 @@ # UNSUPPORTED: system-windows ## Executable object file. -# RUN: yaml2obj --docnum=1 -DTYPE=ET_EXEC -DFOO_ADDR=0x4000 -DBAR_ADDR=0x5000 %s -o %t1 +# RUN: yaml2obj --docnum=1 -DFOO_ADDR=0x4000 -DBAR_ADDR=0x5000 %s -o %t1 # RUN: llvm-objdump %t1 -d --symbolize-operands -M intel --no-show-raw-insn --no-leading-addr | \ # RUN: FileCheck %s -DSYM=symbol --match-full-lines --check-prefixes=INTEL # RUN: llvm-objdump %t1 -d --symbolize-operands -M att --no-show-raw-insn --no-leading-addr | \ # RUN: FileCheck %s -DSYM=symbol --match-full-lines --check-prefixes=ATT -## Relocatable object file. -# RUN: yaml2obj --docnum=1 -DTYPE=ET_REL -DFOO_ADDR=0x0 -DBAR_ADDR=0x0 %s -o %t2 -# RUN: llvm-objdump %t2 -d --symbolize-operands -M intel --no-show-raw-insn --no-leading-addr | \ -# RUN: FileCheck %s -DSYM=foo+0x200c --match-full-lines --check-prefix=INTEL -# RUN: llvm-objdump %t2 -d --symbolize-operands -M att --no-show-raw-insn --no-leading-addr | \ -# RUN: FileCheck %s -DSYM=foo+0x200c --match-full-lines --check-prefix=ATT - ## Executable object file with a single SHT_LLVM_BB_ADDR_MAP for multiple text sections. # RUN: yaml2obj --docnum=2 %s -o %t3 # RUN: llvm-objdump %t3 -d --symbolize-operands -M intel --no-show-raw-insn --no-leading-addr | \ @@ -78,7 +71,7 @@ FileHeader: Class: ELFCLASS64 Data: ELFDATA2LSB - Type: [[TYPE]] + Type: ET_EXEC Machine: EM_X86_64 Sections: - Name: .text.foo diff --git a/llvm/test/tools/llvm-objdump/X86/elf-bbaddrmap-symbolize-relocatable.yaml b/llvm/test/tools/llvm-objdump/X86/elf-bbaddrmap-symbolize-relocatable.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/X86/elf-bbaddrmap-symbolize-relocatable.yaml @@ -0,0 +1,68 @@ +## Test that in the presence of SHT_LLVM_BB_ADDR_MAP sections, +## --symbolize-operands can display labels properly in a relocatable +## object file. + +## Fails on windows (https://github.com/llvm/llvm-project/issues/60013). +# UNSUPPORTED: system-windows + +## Relocatable Object file. +# RUN: yaml2obj %s -o %t1 +# RUN: llvm-objdump %t1 -d --symbolize-operands -M att --no-show-raw-insn --no-leading-addr | \ +# RUN: FileCheck %s -DSYM=symbol --match-full-lines + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 + SectionHeaderStringTable: .strtab +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Content: FFFF + - Name: .llvm_bb_addr_map + Type: SHT_LLVM_BB_ADDR_MAP + Link: .text + Entries: + - Version: 2 + BBEntries: + - ID: 0 + AddressOffset: 0x0 + Size: 0x1 + Metadata: 0x1 + - Version: 2 + BBEntries: + - ID: 0 + AddressOffset: 0x0 + Size: 0x1 + Metadata: 0x1 + - Name: .rela.llvm_bb_addr_map + Type: SHT_RELA + Flags: [ SHF_INFO_LINK ] + Link: .symtab + Info: .llvm_bb_addr_map + Relocations: + - Offset: 0x2 + Symbol: .text + Type: R_X86_64_64 + - Offset: 0x11 + Symbol: .text + Type: R_X86_64_64 + Addend: 1 +Symbols: + - Name: a + Section: .text + Value: 0x0 + - Name: c + Section: .text + Value: 0x1 + - Name: .text + Type: STT_SECTION + Section: .text + +# CHECK: : +# CHECK-NEXT: : +# CHECK: : +# CHECK-NEXT: : diff --git a/llvm/test/tools/llvm-readobj/ELF/bb-addr-map-relocatable.test b/llvm/test/tools/llvm-readobj/ELF/bb-addr-map-relocatable.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-readobj/ELF/bb-addr-map-relocatable.test @@ -0,0 +1,131 @@ +## This test checks how we handle the --bb-addr-map option on relocatable +## object files. + +## Fails on windows (https://github.com/llvm/llvm-project/issues/60013). +# UNSUPPORTED: system-windows + +# RUN: yaml2obj %s -o %t1.o +# RUN: llvm-readobj %t1.o --bb-addr-map | FileCheck %s + +# CHECK: BBAddrMap [ +# CHECK-NEXT: Function { +# CHECK-NEXT: At: 0x0 +# CHECK-NEXT: Name: +# CHECK-NEXT: BB entries [ +# CHECK-NEXT: { +# CHECK-NEXT: ID: 0 +# CHECK-NEXT: Offset: 0x0 +# CHECK-NEXT: Size: 0xF +# CHECK-NEXT: HasReturn: Yes +# CHECK-NEXT: HasTailCall: No +# CHECK-NEXT: IsEHPad: No +# CHECK-NEXT: CanFallThrough: No +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: } +# CHECK-NEXT: Function { +# CHECK-NEXT: At: 0x10 +# CHECK-NEXT: Name: +# CHECK-NEXT: BB entries [ +# CHECK-NEXT: { +# CHECK-NEXT: ID: 0 +# CHECK-NEXT: Offset: 0x0 +# CHECK-NEXT: Size: 0x11 +# CHECK-NEXT: HasReturn: No +# CHECK-NEXT: HasTailCall: No +# CHECK-NEXT: IsEHPad: No +# CHECK-NEXT: CanFallThrough: Yes +# CHECK-NEXT: } +# CHECK-NEXT: { +# CHECK-NEXT: ID: 1 +# CHECK-NEXT: Offset: 0x11 +# CHECK-NEXT: Size: 0xE +# CHECK-NEXT: HasReturn: No +# CHECK-NEXT: HasTailCall: No +# CHECK-NEXT: IsEHPad: No +# CHECK-NEXT: CanFallThrough: No +# CHECK-NEXT: } +# CHECK-NEXT: { +# CHECK-NEXT: ID: 2 +# CHECK-NEXT: Offset: 0x1F +# CHECK-NEXT: Size: 0x6 +# CHECK-NEXT: HasReturn: No +# CHECK-NEXT: HasTailCall: No +# CHECK-NEXT: IsEHPad: No +# CHECK-NEXT: CanFallThrough: Yes +# CHECK-NEXT: } +# CHECK-NEXT: { +# CHECK-NEXT: ID: 3 +# CHECK-NEXT: Offset: 0x25 +# CHECK-NEXT: Size: 0x5 +# CHECK-NEXT: HasReturn: Yes +# CHECK-NEXT: HasTailCall: No +# CHECK-NEXT: IsEHPad: No +# CHECK-NEXT: CanFallThrough: No +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: } +# CHECK-NEXT: ] + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 + SectionHeaderStringTable: .strtab +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + - Name: .llvm_bb_addr_map + Type: SHT_LLVM_BB_ADDR_MAP + Link: .text + Entries: + - Version: 2 + BBEntries: + - ID: 0 + AddressOffset: 0x0 + Size: 0xF + Metadata: 0x1 + - Version: 2 + BBEntries: + - ID: 0 + AddressOffset: 0x0 + Size: 0x11 + Metadata: 0x8 + - ID: 1 + AddressOffset: 0x0 + Size: 0xE + Metadata: 0x0 + - ID: 2 + AddressOffset: 0x0 + Size: 0x6 + Metadata: 0x8 + - ID: 3 + AddressOffset: 0x0 + Size: 0x5 + Metadata: 0x1 + - Name: .rela.llvm_bb_addr_map + Type: SHT_RELA + Flags: [ SHF_INFO_LINK ] + Link: .symtab + Info: .llvm_bb_addr_map + Relocations: + - Offset: 0x2 + Symbol: .text + Type: R_X86_64_64 + - Offset: 0x11 + Symbol: .text + Type: R_X86_64_64 + Addend: 16 +Symbols: + - Name: a + Section: .text + Value: 0x0 + - Name: c + Section: .text + Value: 0x10 + - Name: .text + Type: STT_SECTION + Section: .text 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 @@ -7088,20 +7088,31 @@ template void LLVMELFDumper::printBBAddrMaps() { bool IsRelocatable = this->Obj.getHeader().e_type == ELF::ET_REL; - for (const Elf_Shdr &Sec : cantFail(this->Obj.sections())) { - if (Sec.sh_type != SHT_LLVM_BB_ADDR_MAP && - Sec.sh_type != SHT_LLVM_BB_ADDR_MAP_V0) { - continue; - } + using Elf_Shdr = typename ELFT::Shdr; + auto IsMatch = [&](const Elf_Shdr &Sec) -> bool { + return Sec.sh_type == ELF::SHT_LLVM_BB_ADDR_MAP || + Sec.sh_type == ELF::SHT_LLVM_BB_ADDR_MAP_V0; + }; + Expected> SecRelocMapOrErr = + this->Obj.getSectionAndRelocations(IsMatch); + if (!SecRelocMapOrErr) { + this->reportUniqueWarning("failed to get bbaddrmap section(s): " + + toString(SecRelocMapOrErr.takeError())); + return; + } + for (auto const& [Sec, RelocSec] : *SecRelocMapOrErr) { std::optional FunctionSec; if (IsRelocatable) FunctionSec = - unwrapOrError(this->FileName, this->Obj.getSection(Sec.sh_link)); + unwrapOrError(this->FileName, this->Obj.getSection(Sec->sh_link)); ListScope L(W, "BBAddrMap"); + std::optional RelaSec = {}; + if(IsRelocatable) + RelaSec = *RelocSec; Expected> BBAddrMapOrErr = - this->Obj.decodeBBAddrMap(Sec); + this->Obj.decodeBBAddrMap(*Sec, RelaSec); if (!BBAddrMapOrErr) { - this->reportUniqueWarning("unable to dump " + this->describe(Sec) + ": " + + this->reportUniqueWarning("unable to dump " + this->describe(*Sec) + ": " + toString(BBAddrMapOrErr.takeError())); continue; } @@ -7114,7 +7125,7 @@ if (FuncSymIndex.empty()) this->reportUniqueWarning( "could not identify function symbol for address (0x" + - Twine::utohexstr(AM.Addr) + ") in " + this->describe(Sec)); + Twine::utohexstr(AM.Addr) + ") in " + this->describe(*Sec)); else FuncName = this->getStaticSymbolName(FuncSymIndex.front()); W.printString("Name", FuncName);