[llvm] [llvm-objdump] Fix lma display issue for non-bss sections (PR #72141)
Jason Liu via llvm-commits
llvm-commits at lists.llvm.org
Wed Nov 22 07:48:52 PST 2023
https://github.com/jasonliudev updated https://github.com/llvm/llvm-project/pull/72141
>From 046f1753534d9ea87e27569fd09c05a9fd86db82 Mon Sep 17 00:00:00 2001
From: Jason Liu <jasonliu.development at gmail.com>
Date: Mon, 13 Nov 2023 12:07:00 -0500
Subject: [PATCH 1/4] [llvm-objdump] Fix lma display issue for non-bss sections
llvm-readobj and llvm-objdump have inconsistent handling of display
lma for sections.
This patch tries to common code up and adapt the same approach for
both tools.
---
llvm/include/llvm/Object/ELF.h | 2 +
llvm/lib/Object/ELF.cpp | 43 +++++++++++++++
.../tools/llvm-objdump/X86/phdrs-lma3.test | 54 +++++++++++++++++++
llvm/tools/llvm-objdump/ELFDump.cpp | 6 ++-
llvm/tools/llvm-readobj/ELFDumper.cpp | 42 ++-------------
5 files changed, 106 insertions(+), 41 deletions(-)
create mode 100644 llvm/test/tools/llvm-objdump/X86/phdrs-lma3.test
diff --git a/llvm/include/llvm/Object/ELF.h b/llvm/include/llvm/Object/ELF.h
index 927deeea2cd6aef..e5201c9439e59ab 100644
--- a/llvm/include/llvm/Object/ELF.h
+++ b/llvm/include/llvm/Object/ELF.h
@@ -425,6 +425,8 @@ class ELFFile {
std::function<Expected<bool>(const Elf_Shdr &)> IsMatch) const;
void createFakeSections();
+
+ static bool isSectionInSegment(const Elf_Phdr &Phdr, const Elf_Shdr &Sec);
};
using ELF32LEFile = ELFFile<ELF32LE>;
diff --git a/llvm/lib/Object/ELF.cpp b/llvm/lib/Object/ELF.cpp
index 0d1862e573712f5..e3dc4329aad3268 100644
--- a/llvm/lib/Object/ELF.cpp
+++ b/llvm/lib/Object/ELF.cpp
@@ -799,6 +799,49 @@ ELFFile<ELFT>::getSectionAndRelocations(
return SecToRelocMap;
}
+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)
+ 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 ELFFile<ELFT>::isSectionInSegment(const Elf_Phdr &Phdr,
+ const Elf_Shdr &Sec) {
+ return checkOffsets<ELFT>(Phdr, Sec) && checkVMA<ELFT>(Phdr, Sec);
+}
+
template class llvm::object::ELFFile<ELF32LE>;
template class llvm::object::ELFFile<ELF32BE>;
template class llvm::object::ELFFile<ELF64LE>;
diff --git a/llvm/test/tools/llvm-objdump/X86/phdrs-lma3.test b/llvm/test/tools/llvm-objdump/X86/phdrs-lma3.test
new file mode 100644
index 000000000000000..e44d3f66310c0a8
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/X86/phdrs-lma3.test
@@ -0,0 +1,54 @@
+# RUN: yaml2obj %s > %t
+
+## Check when vma is the same, we could still print LMA correctly when dumping
+## section headers.
+# 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
diff --git a/llvm/tools/llvm-objdump/ELFDump.cpp b/llvm/tools/llvm-objdump/ELFDump.cpp
index 299a7e3cf3fa523..8951bbc52ac94ed 100644
--- a/llvm/tools/llvm-objdump/ELFDump.cpp
+++ b/llvm/tools/llvm-objdump/ELFDump.cpp
@@ -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) &&
+ (Obj.isSectionInSegment(
+ 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.
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 64f343a9b37a343..3e6fa1d159210af 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -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)
- 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) {
@@ -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 (this->Obj.isSectionInSegment(Phdr, Sec) &&
+ checkTLSSections<ELFT>(Phdr, Sec) &&
+ checkPTDynamic<ELFT>(Phdr, Sec)) {
Sections +=
unwrapOrError(this->FileName, this->Obj.getSectionName(Sec)).str() +
" ";
>From d45e1372e4529d0a61e1ab43deec74522274ff9b Mon Sep 17 00:00:00 2001
From: Jason Liu <47833311+jasonliudev at users.noreply.github.com>
Date: Wed, 15 Nov 2023 13:15:07 -0500
Subject: [PATCH 2/4] Address comments
---
llvm/include/llvm/Object/ELF.h | 43 +++++++++++++++++++
llvm/lib/Object/ELF.cpp | 43 -------------------
.../tools/llvm-objdump/X86/phdrs-lma3.test | 30 ++++++-------
llvm/tools/llvm-objdump/ELFDump.cpp | 2 +-
llvm/tools/llvm-readobj/ELFDumper.cpp | 2 +-
5 files changed, 60 insertions(+), 60 deletions(-)
diff --git a/llvm/include/llvm/Object/ELF.h b/llvm/include/llvm/Object/ELF.h
index e5201c9439e59ab..fbc1defa153776a 100644
--- a/llvm/include/llvm/Object/ELF.h
+++ b/llvm/include/llvm/Object/ELF.h
@@ -163,6 +163,49 @@ static inline Error defaultWarningHandler(const Twine &Msg) {
return createError(Msg);
}
+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)
+ 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 checkOffsets<ELFT>(Phdr, Sec) && checkVMA<ELFT>(Phdr, Sec);
+}
+
template <class ELFT>
class ELFFile {
public:
diff --git a/llvm/lib/Object/ELF.cpp b/llvm/lib/Object/ELF.cpp
index e3dc4329aad3268..0d1862e573712f5 100644
--- a/llvm/lib/Object/ELF.cpp
+++ b/llvm/lib/Object/ELF.cpp
@@ -799,49 +799,6 @@ ELFFile<ELFT>::getSectionAndRelocations(
return SecToRelocMap;
}
-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)
- 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 ELFFile<ELFT>::isSectionInSegment(const Elf_Phdr &Phdr,
- const Elf_Shdr &Sec) {
- return checkOffsets<ELFT>(Phdr, Sec) && checkVMA<ELFT>(Phdr, Sec);
-}
-
template class llvm::object::ELFFile<ELF32LE>;
template class llvm::object::ELFFile<ELF32BE>;
template class llvm::object::ELFFile<ELF64LE>;
diff --git a/llvm/test/tools/llvm-objdump/X86/phdrs-lma3.test b/llvm/test/tools/llvm-objdump/X86/phdrs-lma3.test
index e44d3f66310c0a8..663882ecd529029 100644
--- a/llvm/test/tools/llvm-objdump/X86/phdrs-lma3.test
+++ b/llvm/test/tools/llvm-objdump/X86/phdrs-lma3.test
@@ -1,7 +1,7 @@
-# RUN: yaml2obj %s > %t
+# RUN: yaml2obj %s -o %t
-## Check when vma is the same, we could still print LMA correctly when dumping
-## section headers.
+## .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:
@@ -34,21 +34,21 @@ Sections:
Content: "00000000"
Address: 0x00002000
ProgramHeaders:
- - Type: PT_LOAD
- Flags: [ PF_X, PF_R ]
- VAddr: 0x00001000
- PAddr: 0x00002000
+ - 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
+ - Type: PT_LOAD
+ Flags: [ PF_R ]
+ VAddr: 0x00002000
+ PAddr: 0x00003000
FirstSec: .data
LastSec: .data
- - Type: PT_LOAD
- Flags: [ PF_R ]
- VAddr: 0x00002000
- PAddr: 0x00004000
+ - Type: PT_LOAD
+ Flags: [ PF_R ]
+ VAddr: 0x00002000
+ PAddr: 0x00004000
FirstSec: .data_copy
LastSec: .data_copy
diff --git a/llvm/tools/llvm-objdump/ELFDump.cpp b/llvm/tools/llvm-objdump/ELFDump.cpp
index 8951bbc52ac94ed..34861ee92128fdd 100644
--- a/llvm/tools/llvm-objdump/ELFDump.cpp
+++ b/llvm/tools/llvm-objdump/ELFDump.cpp
@@ -182,7 +182,7 @@ static uint64_t getSectionLMA(const ELFFile<ELFT> &Obj,
// segment's p_addr to calculate the section's LMA.
for (const typename ELFT::Phdr &Phdr : *PhdrRangeOrErr)
if ((Phdr.p_type == ELF::PT_LOAD) &&
- (Obj.isSectionInSegment(
+ (isSectionInSegment<ELFT>(
Phdr, *cast<const ELFObjectFile<ELFT>>(Sec.getObject())
->getSection(Sec.getRawDataRefImpl()))))
return Sec.getAddress() - Phdr.p_vaddr + Phdr.p_paddr;
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 3e6fa1d159210af..7a3c1381f8d8ca4 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -4638,7 +4638,7 @@ 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 (this->Obj.isSectionInSegment(Phdr, Sec) &&
+ if (isSectionInSegment<ELFT>(Phdr, Sec) &&
checkTLSSections<ELFT>(Phdr, Sec) &&
checkPTDynamic<ELFT>(Phdr, Sec)) {
Sections +=
>From 3b8a668803391a49de684ce891d8d399d4c85ff5 Mon Sep 17 00:00:00 2001
From: Jason Liu <47833311+jasonliudev at users.noreply.github.com>
Date: Wed, 15 Nov 2023 13:22:28 -0500
Subject: [PATCH 3/4] Remove extra lines.
---
llvm/include/llvm/Object/ELF.h | 2 --
1 file changed, 2 deletions(-)
diff --git a/llvm/include/llvm/Object/ELF.h b/llvm/include/llvm/Object/ELF.h
index fbc1defa153776a..ce1d9c0b3745ffd 100644
--- a/llvm/include/llvm/Object/ELF.h
+++ b/llvm/include/llvm/Object/ELF.h
@@ -468,8 +468,6 @@ class ELFFile {
std::function<Expected<bool>(const Elf_Shdr &)> IsMatch) const;
void createFakeSections();
-
- static bool isSectionInSegment(const Elf_Phdr &Phdr, const Elf_Shdr &Sec);
};
using ELF32LEFile = ELFFile<ELF32LE>;
>From fb82e5b4ef86213e7b22df5a81950ae47f3c6baa Mon Sep 17 00:00:00 2001
From: Jason Liu <jasonliu.development at gmail.com>
Date: Wed, 22 Nov 2023 10:44:02 -0500
Subject: [PATCH 4/4] Resolve conflict with bolt
---
llvm/include/llvm/Object/ELF.h | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/llvm/include/llvm/Object/ELF.h b/llvm/include/llvm/Object/ELF.h
index ce1d9c0b3745ffd..3fca00592f73b4c 100644
--- a/llvm/include/llvm/Object/ELF.h
+++ b/llvm/include/llvm/Object/ELF.h
@@ -164,7 +164,7 @@ static inline Error defaultWarningHandler(const Twine &Msg) {
}
template <class ELFT>
-static bool checkOffsets(const typename ELFT::Phdr &Phdr,
+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)
@@ -182,7 +182,7 @@ static bool checkOffsets(const typename ELFT::Phdr &Phdr,
// 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,
+bool checkSectionVMA(const typename ELFT::Phdr &Phdr,
const typename ELFT::Shdr &Sec) {
if (!(Sec.sh_flags & ELF::SHF_ALLOC))
return true;
@@ -203,7 +203,8 @@ static bool checkVMA(const typename ELFT::Phdr &Phdr,
template <class ELFT>
bool isSectionInSegment(const typename ELFT::Phdr &Phdr,
const typename ELFT::Shdr &Sec) {
- return checkOffsets<ELFT>(Phdr, Sec) && checkVMA<ELFT>(Phdr, Sec);
+ return checkSectionOffsets<ELFT>(Phdr, Sec) &&
+ checkSectionVMA<ELFT>(Phdr, Sec);
}
template <class ELFT>
More information about the llvm-commits
mailing list