Skip to content

Commit ef28379

Browse files
authored
[llvm-objcopy] Fix file offsets when PT_INTERP/PT_LOAD offsets are equal (#80562)
(#79887) When the offset of a PT_INTERP segment equals the offset of a PT_LOAD segment, we consider that the parent of the PT_LOAD segment is the PT_INTERP segment. In `layoutSegments`, we place both segments to be after the current `Offset`, ignoring the PT_LOAD alignment. This scenario is possible with fixed section addresses, but doesn't happen with default linker layouts (.interp precedes other sections and is part of a PT_LOAD segment containing the ELF header and program headers). ``` % cat a.s .globl _start; _start: ret .rodata; .byte 0 .tdata; .balign 4096; .byte 0 % clang -fuse-ld=lld a.s -o a -nostdlib -no-pie -z separate-loadable-segments -Wl,-Ttext=0x201000,--section-start=.interp=0x202000,--section-start=.rodata=0x202020,-z,nognustack % llvm-objcopy a a2 % llvm-readelf -l a2 # incorrect offset(PT_LOAD) Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align PHDR 0x000040 0x0000000000200040 0x0000000000200040 0x0001c0 0x0001c0 R 0x8 INTERP 0x001001 0x0000000000202000 0x0000000000202000 0x00001c 0x00001c R 0x1 [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2] LOAD 0x000000 0x0000000000200000 0x0000000000200000 0x000200 0x000200 R 0x1000 LOAD 0x001000 0x0000000000201000 0x0000000000201000 0x000001 0x000001 R E 0x1000 //// incorrect offset LOAD 0x001001 0x0000000000202000 0x0000000000202000 0x000021 0x000021 R 0x1000 LOAD 0x002000 0x0000000000203000 0x0000000000203000 0x000001 0x001000 RW 0x1000 TLS 0x002000 0x0000000000203000 0x0000000000203000 0x000001 0x000001 R 0x1000 GNU_RELRO 0x002000 0x0000000000203000 0x0000000000203000 0x000001 0x001000 R 0x1000 ``` The same issue occurs for PT_TLS/PT_GNU_RELRO if we PT_TLS's alignment is smaller and we place the PT_LOAD after PT_TLS/PT_GNU_RELRO segments (not linker default, but possible with a `PHDRS` linker script command). Fix #79887: when two segments have the same offset, order the one with a larger alignment first. In the previous case, the PT_LOAD segment will go before the PT_INTERP segment. In case of equal alignments, it doesn't matter which segment is treated as the parent segment.
1 parent 066773c commit ef28379

File tree

2 files changed

+124
-0
lines changed

2 files changed

+124
-0
lines changed

llvm/lib/ObjCopy/ELF/ELFObject.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1234,6 +1234,12 @@ static bool compareSegmentsByOffset(const Segment *A, const Segment *B) {
12341234
return true;
12351235
if (A->OriginalOffset > B->OriginalOffset)
12361236
return false;
1237+
// If alignments are different, the one with a smaller alignment cannot be the
1238+
// parent; otherwise, layoutSegments will not respect the larger alignment
1239+
// requirement. This rule ensures that PT_LOAD/PT_INTERP/PT_GNU_RELRO/PT_TLS
1240+
// segments at the same offset will be aligned correctly.
1241+
if (A->Align != B->Align)
1242+
return A->Align > B->Align;
12371243
return A->Index < B->Index;
12381244
}
12391245

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
## When the offset of a non-PT_LOAD segment (e.g. PT_INTERP) equals the offset
2+
## of a PT_LOAD segment, set the parent of the segment with a smaller alignment
3+
## to the segment with a larger alignment, ensuring that the offset is correctly
4+
## aligned.
5+
6+
# RUN: yaml2obj %s -o %t
7+
# RUN: llvm-objcopy %t %t2
8+
# RUN: llvm-readelf -Sl %t2 | FileCheck %s
9+
10+
# CHECK: [Nr] Name Type Address Off Size ES Flg Lk Inf Al
11+
# CHECK-NEXT: [ 0] NULL 0000000000000000 000000 000000 00 0 0 0
12+
# CHECK-NEXT: [ 1] .text PROGBITS 0000000000201000 001000 000001 00 0 0 4
13+
# CHECK-NEXT: [ 2] .interp PROGBITS 0000000000202000 002000 00001c 00 0 0 1
14+
# CHECK-NEXT: [ 3] .rodata PROGBITS 0000000000202020 002020 000001 00 0 0 1
15+
# CHECK-NEXT: [ 4] .tdata PROGBITS 0000000000203000 003000 000001 00 0 0 4096
16+
# CHECK-NEXT: [ 5] .relro_padding NOBITS 0000000000203001 003001 000fff 00 0 0 1
17+
# CHECK-NEXT: [ 6] .strtab STRTAB 0000000000000000 003001 000001 00 0 0 1
18+
# CHECK-NEXT: [ 7] .shstrtab STRTAB 0000000000000000 003002 00003f 00 0 0 1
19+
20+
# CHECK: Program Headers:
21+
# CHECK-NEXT: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
22+
# CHECK-NEXT: PHDR 0x000040 0x0000000000200040 0x0000000000200040 0x0001c0 0x0001c0 0x8
23+
# CHECK-NEXT: INTERP 0x002000 0x0000000000202000 0x0000000000202000 0x00001c 0x00001c 0x1
24+
# CHECK-NEXT: [Requesting program interpreter: ]
25+
# CHECK-NEXT: LOAD 0x000000 0x0000000000200000 0x0000000000200000 0x000200 0x000200 0x1000
26+
# CHECK-NEXT: LOAD 0x001000 0x0000000000201000 0x0000000000201000 0x000001 0x000001 0x1000
27+
# CHECK-NEXT: LOAD 0x002000 0x0000000000202000 0x0000000000202000 0x000021 0x000021 0x1000
28+
# CHECK-NEXT: TLS 0x003000 0x0000000000203000 0x0000000000203000 0x000001 0x001000 0x1000
29+
# CHECK-NEXT: GNU_RELRO 0x003000 0x0000000000203000 0x0000000000203000 0x000001 0x001000 0x1000
30+
# CHECK-NEXT: LOAD 0x003000 0x0000000000203000 0x0000000000203000 0x000001 0x001000 0x1000
31+
32+
--- !ELF
33+
FileHeader:
34+
Class: ELFCLASS64
35+
Data: ELFDATA2LSB
36+
Type: ET_EXEC
37+
Machine: EM_X86_64
38+
ProgramHeaders:
39+
- Type: PT_PHDR
40+
VAddr: 0x200040
41+
Align: 0x8
42+
Offset: 0x40
43+
FileSize: 0x1c0
44+
MemSize: 0x1c0
45+
- Type: PT_INTERP
46+
FirstSec: .interp
47+
LastSec: .interp
48+
## The address equals the address of its containing PT_LOAD.
49+
VAddr: 0x202000
50+
Offset: 0x2000
51+
- Type: PT_LOAD
52+
VAddr: 0x200000
53+
Align: 0x1000
54+
Offset: 0x0
55+
FileSize: 0x200
56+
MemSize: 0x200
57+
- Type: PT_LOAD
58+
FirstSec: .text
59+
LastSec: .text
60+
VAddr: 0x201000
61+
Align: 0x1000
62+
Offset: 0x1000
63+
- Type: PT_LOAD
64+
FirstSec: .interp
65+
LastSec: .rodata
66+
VAddr: 0x202000
67+
Align: 0x1000
68+
Offset: 0x2000
69+
## Intentionally place PT_TLS/PT_GNU_RELRO before PT_LOAD to test that we
70+
## correctly set parent segments.
71+
- Type: PT_TLS
72+
FirstSec: .tdata
73+
LastSec: .relro_padding
74+
VAddr: 0x203000
75+
Align: 0x1000
76+
Offset: 0x3000
77+
- Type: PT_GNU_RELRO
78+
FirstSec: .tdata
79+
LastSec: .relro_padding
80+
VAddr: 0x203000
81+
Align: 0x1000
82+
Offset: 0x3000
83+
- Type: PT_LOAD
84+
FirstSec: .tdata
85+
LastSec: .relro_padding
86+
VAddr: 0x203000
87+
Align: 0x1000
88+
Offset: 0x3000
89+
Sections:
90+
- Name: .text
91+
Type: SHT_PROGBITS
92+
Address: 0x201000
93+
AddressAlign: 0x4
94+
Offset: 0x1000
95+
Content: C3
96+
- Name: .interp
97+
Type: SHT_PROGBITS
98+
Address: 0x202000
99+
AddressAlign: 0x1
100+
Offset: 0x2000
101+
Size: 0x1C
102+
- Name: .rodata
103+
Type: SHT_PROGBITS
104+
Address: 0x202020
105+
AddressAlign: 0x1
106+
Offset: 0x2020
107+
Size: 1
108+
- Name: .tdata
109+
Type: SHT_PROGBITS
110+
Address: 0x203000
111+
AddressAlign: 0x1000
112+
Offset: 0x3000
113+
Size: 1
114+
- Name: .relro_padding
115+
Type: SHT_NOBITS
116+
Address: 0x203001
117+
AddressAlign: 0x1
118+
Size: 0xFFF

0 commit comments

Comments
 (0)