Skip to content

[llvm-objdump] Fix lma display issue for non-bss sections #72141

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

Merged
merged 1 commit into from
Nov 27, 2023
Merged
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
44 changes: 44 additions & 0 deletions llvm/include/llvm/Object/ELF.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,50 @@ static inline Error defaultWarningHandler(const Twine &Msg) {
return createError(Msg);
}

template <class ELFT>
bool checkSectionOffsets(const typename ELFT::Phdr &Phdr,
const typename ELFT::Shdr &Sec) {
// SHT_NOBITS sections don't need to have an offset inside the segment.
if (Sec.sh_type == ELF::SHT_NOBITS)
return true;

if (Sec.sh_offset < Phdr.p_offset)
return false;

// Only non-empty sections can be at the end of a segment.
if (Sec.sh_size == 0)
return (Sec.sh_offset + 1 <= Phdr.p_offset + Phdr.p_filesz);
return Sec.sh_offset + Sec.sh_size <= Phdr.p_offset + Phdr.p_filesz;
}

// Check that an allocatable section belongs to a virtual address
// space of a segment.
template <class ELFT>
bool checkSectionVMA(const typename ELFT::Phdr &Phdr,
const typename ELFT::Shdr &Sec) {
if (!(Sec.sh_flags & ELF::SHF_ALLOC))
return true;

if (Sec.sh_addr < Phdr.p_vaddr)
return false;

bool IsTbss =
(Sec.sh_type == ELF::SHT_NOBITS) && ((Sec.sh_flags & ELF::SHF_TLS) != 0);
// .tbss is special, it only has memory in PT_TLS and has NOBITS properties.
bool IsTbssInNonTLS = IsTbss && Phdr.p_type != ELF::PT_TLS;
// Only non-empty sections can be at the end of a segment.
if (Sec.sh_size == 0 || IsTbssInNonTLS)
return Sec.sh_addr + 1 <= Phdr.p_vaddr + Phdr.p_memsz;
return Sec.sh_addr + Sec.sh_size <= Phdr.p_vaddr + Phdr.p_memsz;
}

template <class ELFT>
bool isSectionInSegment(const typename ELFT::Phdr &Phdr,
const typename ELFT::Shdr &Sec) {
return checkSectionOffsets<ELFT>(Phdr, Sec) &&
checkSectionVMA<ELFT>(Phdr, Sec);
}

template <class ELFT>
class ELFFile {
public:
Expand Down
54 changes: 54 additions & 0 deletions llvm/test/tools/llvm-objdump/X86/phdrs-lma3.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# RUN: yaml2obj %s -o %t

## .data and .data_copy have the same VMA but different sh_offset values.
## Check that we can still print LMA correctly.
# RUN: llvm-objdump --section-headers %t | FileCheck %s

# CHECK: Sections:
# CHECK-NEXT: Idx Name Size VMA LMA Type
# CHECK-NEXT: 0 00000000 0000000000000000 0000000000000000
# CHECK-NEXT: 1 .text 00000004 0000000000001000 0000000000002000 TEXT
# CHECK-NEXT: 2 .data 00000004 0000000000002000 0000000000003000 DATA
# CHECK-NEXT: 3 .data_copy 00000004 0000000000002000 0000000000004000 DATA

!ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_EXEC
Machine: EM_X86_64
Sections:
- Name: .text
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
Content: "00000000"
Address: 0x00001000
- Name: .data
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC ]
Content: "00000000"
Address: 0x00002000
- Name: .data_copy
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC ]
Content: "00000000"
Address: 0x00002000
ProgramHeaders:
- Type: PT_LOAD
Flags: [ PF_X, PF_R ]
VAddr: 0x00001000
PAddr: 0x00002000
FirstSec: .text
LastSec: .text
- Type: PT_LOAD
Flags: [ PF_R ]
VAddr: 0x00002000
PAddr: 0x00003000
FirstSec: .data
LastSec: .data
- Type: PT_LOAD
Flags: [ PF_R ]
VAddr: 0x00002000
PAddr: 0x00004000
FirstSec: .data_copy
LastSec: .data_copy
6 changes: 4 additions & 2 deletions llvm/tools/llvm-objdump/ELFDump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,10 @@ static uint64_t getSectionLMA(const ELFFile<ELFT> &Obj,
// Search for a PT_LOAD segment containing the requested section. Use this
// segment's p_addr to calculate the section's LMA.
for (const typename ELFT::Phdr &Phdr : *PhdrRangeOrErr)
if ((Phdr.p_type == ELF::PT_LOAD) && (Phdr.p_vaddr <= Sec.getAddress()) &&
(Phdr.p_vaddr + Phdr.p_memsz > Sec.getAddress()))
if ((Phdr.p_type == ELF::PT_LOAD) &&
(isSectionInSegment<ELFT>(
Phdr, *cast<const ELFObjectFile<ELFT>>(Sec.getObject())
->getSection(Sec.getRawDataRefImpl()))))
return Sec.getAddress() - Phdr.p_vaddr + Phdr.p_paddr;

// Return section's VMA if it isn't in a PT_LOAD segment.
Expand Down
42 changes: 3 additions & 39 deletions llvm/tools/llvm-readobj/ELFDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4507,43 +4507,6 @@ static bool checkTLSSections(const typename ELFT::Phdr &Phdr,
return Phdr.p_type != ELF::PT_TLS;
}

template <class ELFT>
static bool checkOffsets(const typename ELFT::Phdr &Phdr,
const typename ELFT::Shdr &Sec) {
// SHT_NOBITS sections don't need to have an offset inside the segment.
if (Sec.sh_type == ELF::SHT_NOBITS)
return true;

if (Sec.sh_offset < Phdr.p_offset)
return false;

// Only non-empty sections can be at the end of a segment.
if (Sec.sh_size == 0)
return (Sec.sh_offset + 1 <= Phdr.p_offset + Phdr.p_filesz);
return Sec.sh_offset + Sec.sh_size <= Phdr.p_offset + Phdr.p_filesz;
}

// Check that an allocatable section belongs to a virtual address
// space of a segment.
template <class ELFT>
static bool checkVMA(const typename ELFT::Phdr &Phdr,
const typename ELFT::Shdr &Sec) {
if (!(Sec.sh_flags & ELF::SHF_ALLOC))
return true;

if (Sec.sh_addr < Phdr.p_vaddr)
return false;

bool IsTbss =
(Sec.sh_type == ELF::SHT_NOBITS) && ((Sec.sh_flags & ELF::SHF_TLS) != 0);
// .tbss is special, it only has memory in PT_TLS and has NOBITS properties.
bool IsTbssInNonTLS = IsTbss && Phdr.p_type != ELF::PT_TLS;
// Only non-empty sections can be at the end of a segment.
if (Sec.sh_size == 0 || IsTbssInNonTLS)
Copy link
Member

@MaskRay MaskRay Nov 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I change the condition to if (0), no test will fail, which means that we don't have coverage for an empty sectio nat the end of a segment (llvm-readelf -S). But this is a pre-existing issue that you can ignore.

return Sec.sh_addr + 1 <= Phdr.p_vaddr + Phdr.p_memsz;
return Sec.sh_addr + Sec.sh_size <= Phdr.p_vaddr + Phdr.p_memsz;
}

template <class ELFT>
static bool checkPTDynamic(const typename ELFT::Phdr &Phdr,
const typename ELFT::Shdr &Sec) {
Expand Down Expand Up @@ -4675,8 +4638,9 @@ template <class ELFT> void GNUELFDumper<ELFT>::printSectionMapping() {
// readelf additionally makes sure it does not print zero sized sections
// at end of segments and for PT_DYNAMIC both start and end of section
// .tbss must only be shown in PT_TLS section.
if (checkTLSSections<ELFT>(Phdr, Sec) && checkOffsets<ELFT>(Phdr, Sec) &&
checkVMA<ELFT>(Phdr, Sec) && checkPTDynamic<ELFT>(Phdr, Sec)) {
if (isSectionInSegment<ELFT>(Phdr, Sec) &&
checkTLSSections<ELFT>(Phdr, Sec) &&
checkPTDynamic<ELFT>(Phdr, Sec)) {
Sections +=
unwrapOrError(this->FileName, this->Obj.getSectionName(Sec)).str() +
" ";
Expand Down