Skip to content

Commit 20de182

Browse files
committed
[llvm-objcopy] --only-keep-debug: place zero-size segment according to its parent segment
Alternative to D74755. sectionWithinSegment() treats an empty section as having a size of 1. Due to the rule, an empty .tdata will not be attributed to an empty PT_TLS. (The empty p_align=64 PT_TLS is for Android Bionic's TCB compatibility (ELF-TLS). See https://reviews.llvm.org/D62055#1507426) Currently --only-keep-debug will not layout a segment with no section (layoutSegmentsForOnlyKeepDebug()), thus p_offset of PT_TLS can go past the end of the file. The strange p_offset can trigger validation errors for subsequent tools, e.g. llvm-objcopy errors when reading back the separate debug file (readProgramHeaders()). This patch places such an empty segment according to its parent segment. This special cases works for the empty PT_TLS used in Android. For a non-empty segment, it should have at least one non-empty section and will be handled by the normal code. Note, p_memsz PT_LOAD is rejected by both Linux and FreeBSD. Reviewed By: jhenderson Differential Revision: https://reviews.llvm.org/D90897
1 parent f0580c7 commit 20de182

File tree

2 files changed

+71
-2
lines changed

2 files changed

+71
-2
lines changed

llvm/test/tools/llvm-objcopy/ELF/only-keep-debug.test

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,3 +216,65 @@ Sections:
216216
Flags: [ SHF_ALLOC ]
217217
DynamicSymbols: []
218218
Symbols: []
219+
220+
## PT_TLS and .tdata are empty. Test that we set its p_offset to the parent
221+
## segment's p_offset. If we don't rewrite the p_offset of PT_TLS and the deleted
222+
## bytes are large, p_offset can be larger than the file size, and trigger
223+
## validation errors with subsequent tools.
224+
# RUN: yaml2obj --docnum=4 %s -o %t4
225+
# RUN: llvm-objcopy --only-keep-debug %t4 %t4.dbg
226+
# RUN: llvm-readelf -S -l %t4.dbg | FileCheck --check-prefix=CHECK4 %s
227+
228+
# CHECK4: [Nr] Name Type Address Off Size ES Flg Lk Inf Al
229+
# CHECK4: [ 1] .text NOBITS 0000000000000200 000200 000001 00 AX 0 0 0
230+
# CHECK4-NEXT: [ 2] .tdata NOBITS 0000000000001240 000240 000000 00 WAT 0 0 64
231+
# CHECK4-NEXT: [ 3] .got NOBITS 0000000000001240 000240 000008 00 WA 0 0 0
232+
233+
# CHECK4: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
234+
# CHECK4-NEXT: LOAD 0x000200 0x0000000000000200 0x0000000000000200 0x000000 0x000001 R E 0x1000
235+
# CHECK4-NEXT: LOAD 0x000240 0x0000000000001240 0x0000000000001240 0x000000 0x000008 RW 0x1000
236+
# CHECK4-NEXT: TLS 0x000240 0x0000000000001240 0x0000000000001240 0x000000 0x000000 R 0x40
237+
238+
--- !ELF
239+
FileHeader:
240+
Class: ELFCLASS64
241+
Data: ELFDATA2LSB
242+
Type: ET_DYN
243+
Machine: EM_X86_64
244+
Sections:
245+
- Name: .text
246+
Type: SHT_PROGBITS
247+
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
248+
Address: 0x200
249+
Size: 1
250+
- Name: .tdata
251+
Type: SHT_PROGBITS
252+
Flags: [ SHF_ALLOC, SHF_WRITE, SHF_TLS ]
253+
Address: 0x1240 # Ensure Address=0x1000+Offset
254+
AddressAlign: 0x40
255+
- Name: .got
256+
Type: SHT_PROGBITS
257+
Flags: [ SHF_ALLOC, SHF_WRITE ]
258+
Size: 8
259+
ProgramHeaders:
260+
- Type: PT_LOAD
261+
Flags: [ PF_R, PF_X ]
262+
VAddr: 0x200
263+
Align: 0x1000
264+
FirstSec: .text
265+
LastSec: .text
266+
## Add .got so that the PT_LOAD does not have zero p_memsz. We don't add
267+
## sections to zero-sized segments so zero-sized segments may have strange
268+
## offsets. In practice, the Linux kernel errors when mmapping a p_memsz
269+
## PT_LOAD,so for practical so this assumption can generally be made.
270+
- Type: PT_LOAD
271+
Flags: [ PF_R, PF_W ]
272+
VAddr: 0x1240
273+
Align: 0x1000
274+
FirstSec: .tdata
275+
LastSec: .got
276+
- Type: PT_TLS
277+
Flags: [ PF_R ]
278+
VAddr: 0x1240
279+
FirstSec: .tdata
280+
LastSec: .tdata

llvm/tools/llvm-objcopy/ELF/Object.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2304,12 +2304,19 @@ static uint64_t layoutSectionsForOnlyKeepDebug(Object &Obj, uint64_t Off) {
23042304
return Off;
23052305
}
23062306

2307-
// Rewrite p_offset and p_filesz of non-empty non-PT_PHDR segments after
2308-
// sh_offset values have been updated.
2307+
// Rewrite p_offset and p_filesz of non-PT_PHDR segments after sh_offset values
2308+
// have been updated.
23092309
static uint64_t layoutSegmentsForOnlyKeepDebug(std::vector<Segment *> &Segments,
23102310
uint64_t HdrEnd) {
23112311
uint64_t MaxOffset = 0;
23122312
for (Segment *Seg : Segments) {
2313+
// An empty segment contains no section (see sectionWithinSegment). If it
2314+
// has a parent segment, copy the parent segment's offset field. This works
2315+
// for empty PT_TLS. We don't handle empty segments without a parent for
2316+
// now.
2317+
if (Seg->ParentSegment != nullptr && Seg->MemSize == 0)
2318+
Seg->Offset = Seg->ParentSegment->Offset;
2319+
23132320
const SectionBase *FirstSec = Seg->firstSection();
23142321
if (Seg->Type == PT_PHDR || !FirstSec)
23152322
continue;

0 commit comments

Comments
 (0)