1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-22 18:54:02 +01:00

[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
This commit is contained in:
Fangrui Song 2020-11-11 09:21:10 -08:00
parent ea9b12d4bc
commit 64b86c783b
2 changed files with 71 additions and 2 deletions

View File

@ -216,3 +216,65 @@ Sections:
Flags: [ SHF_ALLOC ]
DynamicSymbols: []
Symbols: []
## PT_TLS and .tdata are empty. Test that we set its p_offset to the parent
## segment's p_offset. If we don't rewrite the p_offset of PT_TLS and the deleted
## bytes are large, p_offset can be larger than the file size, and trigger
## validation errors with subsequent tools.
# RUN: yaml2obj --docnum=4 %s -o %t4
# RUN: llvm-objcopy --only-keep-debug %t4 %t4.dbg
# RUN: llvm-readelf -S -l %t4.dbg | FileCheck --check-prefix=CHECK4 %s
# CHECK4: [Nr] Name Type Address Off Size ES Flg Lk Inf Al
# CHECK4: [ 1] .text NOBITS 0000000000000200 000200 000001 00 AX 0 0 0
# CHECK4-NEXT: [ 2] .tdata NOBITS 0000000000001240 000240 000000 00 WAT 0 0 64
# CHECK4-NEXT: [ 3] .got NOBITS 0000000000001240 000240 000008 00 WA 0 0 0
# CHECK4: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
# CHECK4-NEXT: LOAD 0x000200 0x0000000000000200 0x0000000000000200 0x000000 0x000001 R E 0x1000
# CHECK4-NEXT: LOAD 0x000240 0x0000000000001240 0x0000000000001240 0x000000 0x000008 RW 0x1000
# CHECK4-NEXT: TLS 0x000240 0x0000000000001240 0x0000000000001240 0x000000 0x000000 R 0x40
--- !ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_DYN
Machine: EM_X86_64
Sections:
- Name: .text
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
Address: 0x200
Size: 1
- Name: .tdata
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC, SHF_WRITE, SHF_TLS ]
Address: 0x1240 # Ensure Address=0x1000+Offset
AddressAlign: 0x40
- Name: .got
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC, SHF_WRITE ]
Size: 8
ProgramHeaders:
- Type: PT_LOAD
Flags: [ PF_R, PF_X ]
VAddr: 0x200
Align: 0x1000
FirstSec: .text
LastSec: .text
## Add .got so that the PT_LOAD does not have zero p_memsz. We don't add
## sections to zero-sized segments so zero-sized segments may have strange
## offsets. In practice, the Linux kernel errors when mmapping a p_memsz
## PT_LOAD,so for practical so this assumption can generally be made.
- Type: PT_LOAD
Flags: [ PF_R, PF_W ]
VAddr: 0x1240
Align: 0x1000
FirstSec: .tdata
LastSec: .got
- Type: PT_TLS
Flags: [ PF_R ]
VAddr: 0x1240
FirstSec: .tdata
LastSec: .tdata

View File

@ -2304,12 +2304,19 @@ static uint64_t layoutSectionsForOnlyKeepDebug(Object &Obj, uint64_t Off) {
return Off;
}
// Rewrite p_offset and p_filesz of non-empty non-PT_PHDR segments after
// sh_offset values have been updated.
// Rewrite p_offset and p_filesz of non-PT_PHDR segments after sh_offset values
// have been updated.
static uint64_t layoutSegmentsForOnlyKeepDebug(std::vector<Segment *> &Segments,
uint64_t HdrEnd) {
uint64_t MaxOffset = 0;
for (Segment *Seg : Segments) {
// An empty segment contains no section (see sectionWithinSegment). If it
// has a parent segment, copy the parent segment's offset field. This works
// for empty PT_TLS. We don't handle empty segments without a parent for
// now.
if (Seg->ParentSegment != nullptr && Seg->MemSize == 0)
Seg->Offset = Seg->ParentSegment->Offset;
const SectionBase *FirstSec = Seg->firstSection();
if (Seg->Type == PT_PHDR || !FirstSec)
continue;