Skip to content

Commit d28c6d5

Browse files
committed
[llvm-objcopy][ELF] -O binary: use LMA instead of sh_offset to decide where to write section contents
.text sh_address=0x1000 sh_offset=0x1000 .data sh_address=0x3000 sh_offset=0x2000 In an objcopy -O binary output, the distance between two sections equal their LMA differences (0x3000-0x1000), instead of their sh_offset differences (0x2000-0x1000). This patch changes our behavior to match GNU. This rule gets more complex when the containing PT_LOAD has p_vaddr!=p_paddr. GNU objcopy essentially computes sh_offset-p_offset+p_paddr for each candidate section, and removes the gap before the first address. Added tests to binary-paddr.test to catch the compatibility problem. Reviewed By: jhenderson Differential Revision: https://reviews.llvm.org/D71035
1 parent 9e119ad commit d28c6d5

File tree

2 files changed

+118
-41
lines changed

2 files changed

+118
-41
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,21 @@
1-
# RUN: yaml2obj %s -o %t
2-
# RUN: llvm-objcopy -O binary %t %t2
3-
# RUN: od -t x2 %t2 | FileCheck %s --ignore-case
4-
# RUN: wc -c < %t2 | FileCheck %s --check-prefix=SIZE
1+
## The computed LMA of a section in a PT_LOAD equals sh_offset-p_offset+p_paddr.
2+
## The byte offset difference between two sections equals the difference between their LMAs.
53

6-
!ELF
4+
## Corollary: if two sections are in the same PT_LOAD, the byte offset
5+
## difference equals the difference between their sh_addr fields.
6+
7+
# RUN: yaml2obj --docnum=1 %s -o %t1
8+
# RUN: llvm-objcopy -O binary %t1 %t1.out
9+
# RUN: od -A x -t x2 %t1.out | FileCheck %s --check-prefix=CHECK1 --ignore-case
10+
# RUN: wc -c %t1.out | FileCheck %s --check-prefix=SIZE1
11+
12+
# CHECK1: 000000 c3c3 c3c3 0000 0000 0000 0000 0000 0000
13+
# CHECK1-NEXT: 000010 0000 0000 0000 0000 0000 0000 0000 0000
14+
# CHECK1-NEXT: *
15+
# CHECK1-NEXT: 001000 3232
16+
# SIZE1: 4098
17+
18+
--- !ELF
719
FileHeader:
820
Class: ELFCLASS64
921
Data: ELFDATA2LSB
@@ -14,32 +26,107 @@ Sections:
1426
Type: SHT_PROGBITS
1527
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
1628
Address: 0x1000
17-
AddressAlign: 0x0000000000001000
29+
AddressAlign: 0x1000
1830
Content: "c3c3c3c3"
1931
- Name: .data
2032
Type: SHT_PROGBITS
21-
Flags: [ SHF_ALLOC ]
33+
Flags: [ SHF_ALLOC, SHF_WRITE ]
2234
Address: 0x2000
23-
AddressAlign: 0x0000000000001000
35+
AddressAlign: 0x1000
2436
Content: "3232"
2537
ProgramHeaders:
2638
- Type: PT_LOAD
27-
Flags: [ PF_X, PF_R ]
28-
VAddr: 0x1000
29-
PAddr: 0x1000
30-
Align: 0x1000
39+
Flags: [ PF_R, PF_W ]
3140
Sections:
3241
- Section: .text
42+
- Section: .data
43+
44+
## The computed LMA of a section not in a PT_LOAD equals its sh_addr.
45+
46+
# RUN: yaml2obj --docnum=2 %s -o %t2
47+
# RUN: llvm-objcopy -O binary %t2 %t2.out
48+
# RUN: od -A x -t x2 %t2.out | FileCheck %s --check-prefix=CHECK2 --ignore-case
49+
# RUN: wc -c %t2.out | FileCheck %s --check-prefix=SIZE2
50+
51+
## The computed LMA of .data is 0x4000. The minimum LMA of all sections is 0x1000.
52+
## The content of .data will be written at 0x4000-0x1000 = 0x3000.
53+
# CHECK2: 000000 c3c3 c3c3 0000 0000 0000 0000 0000 0000
54+
# CHECK2-NEXT: 000010 0000 0000 0000 0000 0000 0000 0000 0000
55+
# CHECK2-NEXT: *
56+
# CHECK2-NEXT: 003000 3232
57+
# SIZE2: 12290
58+
59+
--- !ELF
60+
FileHeader:
61+
Class: ELFCLASS64
62+
Data: ELFDATA2LSB
63+
Type: ET_EXEC
64+
Machine: EM_X86_64
65+
Sections:
66+
- Name: .text
67+
Type: SHT_PROGBITS
68+
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
69+
## Not in a PT_LOAD. LMA = sh_addr = 0x1000.
70+
Address: 0x1000
71+
AddressAlign: 0x1000
72+
Content: "c3c3c3c3"
73+
- Name: .data
74+
Type: SHT_PROGBITS
75+
Flags: [ SHF_ALLOC, SHF_WRITE ]
76+
## LMA = sh_offset-p_offset+p_paddr = 0x2000-0x2000+0x4000 = 0x4000.
77+
Address: 0x2000
78+
AddressAlign: 0x1000
79+
Content: "3232"
80+
ProgramHeaders:
3381
- Type: PT_LOAD
3482
Flags: [ PF_R, PF_W ]
3583
VAddr: 0x2000
84+
## p_vaddr is increased from 0x2000 to 0x4000.
3685
PAddr: 0x4000
37-
Align: 0x1000
3886
Sections:
3987
- Section: .data
4088

41-
# CHECK: 0000000 c3c3 c3c3 0000 0000 0000 0000 0000 0000
42-
# CHECK-NEXT: 0000020 0000 0000 0000 0000 0000 0000 0000 0000
43-
# CHECK-NEXT: *
44-
# CHECK-NEXT: 0030000 3232
45-
# SIZE: 12290
89+
## Check that we use sh_offset instead of sh_addr to decide where to write section contents.
90+
91+
# RUN: yaml2obj --docnum=3 %s -o %t3
92+
# RUN: llvm-objcopy -O binary %t3 %t3.out
93+
# RUN: od -A x -t x2 %t3.out | FileCheck %s --check-prefix=CHECK3 --ignore-case
94+
# RUN: wc -c %t3.out | FileCheck %s --check-prefix=SIZE3
95+
96+
## The minimum LMA of all sections is 0x1000.
97+
## The content of .data will be written at 0x3000-0x1000 = 0x2000.
98+
# CHECK3: 000000 c3c3 c3c3 0000 0000 0000 0000 0000 0000
99+
# CHECK3-NEXT: 000010 0000 0000 0000 0000 0000 0000 0000 0000
100+
# CHECK3-NEXT: *
101+
# CHECK3-NEXT: 002000 3232
102+
# SIZE3: 8194
103+
104+
--- !ELF
105+
FileHeader:
106+
Class: ELFCLASS64
107+
Data: ELFDATA2LSB
108+
Type: ET_EXEC
109+
Machine: EM_X86_64
110+
Sections:
111+
- Name: .text
112+
Type: SHT_PROGBITS
113+
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
114+
## Not in a PT_LOAD. LMA = sh_addr = 0x1000.
115+
Address: 0x1000
116+
AddressAlign: 0x1000
117+
Content: "c3c3c3c3"
118+
- Name: .data
119+
Type: SHT_PROGBITS
120+
Flags: [ SHF_ALLOC, SHF_WRITE ]
121+
## sh_addr is increased from 0x2000 to 0x3000, but it is ignored.
122+
## LMA = sh_offset-p_offset+p_paddr = 0x2000-0x2000+0x3000 = 0x3000.
123+
Address: 0x3000
124+
AddressAlign: 0x1000
125+
Content: "3232"
126+
ProgramHeaders:
127+
- Type: PT_LOAD
128+
Flags: [ PF_R, PF_W ]
129+
VAddr: 0x3000
130+
PAddr: 0x3000
131+
Sections:
132+
- Section: .data

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

+13-23
Original file line numberDiff line numberDiff line change
@@ -2253,38 +2253,28 @@ Error BinaryWriter::finalize() {
22532253
std::unique(std::begin(OrderedSegments), std::end(OrderedSegments));
22542254
OrderedSegments.erase(End, std::end(OrderedSegments));
22552255

2256-
uint64_t Offset = 0;
2257-
2258-
// Modify the first segment so that there is no gap at the start. This allows
2259-
// our layout algorithm to proceed as expected while not writing out the gap
2260-
// at the start.
2261-
if (!OrderedSegments.empty()) {
2262-
Segment *Seg = OrderedSegments[0];
2263-
const SectionBase *Sec = Seg->firstSection();
2264-
auto Diff = Sec->OriginalOffset - Seg->OriginalOffset;
2265-
Seg->OriginalOffset += Diff;
2266-
// The size needs to be shrunk as well.
2267-
Seg->FileSize -= Diff;
2268-
// The PAddr needs to be increased to remove the gap before the first
2269-
// section.
2270-
Seg->PAddr += Diff;
2271-
uint64_t LowestPAddr = Seg->PAddr;
2272-
for (Segment *Segment : OrderedSegments) {
2273-
Segment->Offset = Segment->PAddr - LowestPAddr;
2274-
Offset = std::max(Offset, Segment->Offset + Segment->FileSize);
2275-
}
2256+
// Compute the section LMA based on its sh_offset and the containing segment's
2257+
// p_offset and p_paddr. Also compute the minimum LMA of all sections as
2258+
// MinAddr. In the output, the contents between address 0 and MinAddr will be
2259+
// skipped.
2260+
uint64_t MinAddr = UINT64_MAX;
2261+
for (SectionBase &Sec : Obj.allocSections()) {
2262+
if (Sec.ParentSegment != nullptr)
2263+
Sec.Addr =
2264+
Sec.Offset - Sec.ParentSegment->Offset + Sec.ParentSegment->PAddr;
2265+
MinAddr = std::min(MinAddr, Sec.Addr);
22762266
}
22772267

2278-
layoutSections(Obj.allocSections(), Offset);
2279-
22802268
// Now that every section has been laid out we just need to compute the total
22812269
// file size. This might not be the same as the offset returned by
22822270
// layoutSections, because we want to truncate the last segment to the end of
22832271
// its last section, to match GNU objcopy's behaviour.
22842272
TotalSize = 0;
2285-
for (const SectionBase &Sec : Obj.allocSections())
2273+
for (SectionBase &Sec : Obj.allocSections()) {
2274+
Sec.Offset = Sec.Addr - MinAddr;
22862275
if (Sec.Type != SHT_NOBITS)
22872276
TotalSize = std::max(TotalSize, Sec.Offset + Sec.Size);
2277+
}
22882278

22892279
if (Error E = Buf.allocate(TotalSize))
22902280
return E;

0 commit comments

Comments
 (0)