diff --git a/llvm/test/tools/llvm-objcopy/ELF/binary-paddr.test b/llvm/test/tools/llvm-objcopy/ELF/binary-paddr.test --- a/llvm/test/tools/llvm-objcopy/ELF/binary-paddr.test +++ b/llvm/test/tools/llvm-objcopy/ELF/binary-paddr.test @@ -1,9 +1,19 @@ -# RUN: yaml2obj %s -o %t -# RUN: llvm-objcopy -O binary %t %t2 -# RUN: od -t x2 %t2 | FileCheck %s --ignore-case -# RUN: wc -c < %t2 | FileCheck %s --check-prefix=SIZE +## Check that we apply VMA->LMA adjustment when the PT_LOAD segment has p_vaddr != p_paddr. -!ELF +# RUN: yaml2obj --docnum=1 %s -o %t1 +# RUN: llvm-objcopy -O binary %t1 %t1.out +# RUN: od -A x -t x2 %t1.out | FileCheck %s --check-prefix=CHECK1 --ignore-case +# RUN: wc -c %t1.out | FileCheck %s --check-prefix=SIZE1 + +## The computed LMA of .data is 0x4000. The minimum LMA of all sections is 0x1000. +## The content of .data will be written at 0x4000-0x1000 = 0x3000. +# CHECK1: 000000 c3c3 c3c3 0000 0000 0000 0000 0000 0000 +# CHECK1-NEXT: 000010 0000 0000 0000 0000 0000 0000 0000 0000 +# CHECK1-NEXT: * +# CHECK1-NEXT: 003000 3232 +# SIZE1: 12290 + +--- !ELF FileHeader: Class: ELFCLASS64 Data: ELFDATA2LSB @@ -19,6 +29,7 @@ - Name: .data Type: SHT_PROGBITS Flags: [ SHF_ALLOC ] + # The computed LMA is sh_address-p_vaddr+p_paddr = 0x2000-0x2000+0x4000 = 0x4000. Address: 0x2000 AddressAlign: 0x0000000000001000 Content: "3232" @@ -33,13 +44,112 @@ - Type: PT_LOAD Flags: [ PF_R, PF_W ] VAddr: 0x2000 + # p_vaddr is increased from 0x2000 to 0x4000. PAddr: 0x4000 Align: 0x1000 Sections: - Section: .data -# CHECK: 0000000 c3c3 c3c3 0000 0000 0000 0000 0000 0000 -# CHECK-NEXT: 0000020 0000 0000 0000 0000 0000 0000 0000 0000 -# CHECK-NEXT: * -# CHECK-NEXT: 0030000 3232 -# SIZE: 12290 +## Check that we use LMA instead of sh_offset to decide where to write section contents. + +# RUN: yaml2obj --docnum=2 %s -o %t2 +# RUN: llvm-objcopy -O binary %t2 %t2.out +# RUN: od -A x -t x2 %t2.out | FileCheck %s --check-prefix=CHECK2 --ignore-case +# RUN: wc -c %t2.out | FileCheck %s --check-prefix=SIZE2 + +## The minimum LMA of all sections is 0x1000. +## The content of .data will be written at 0x2000-0x1000 = 0x3000. +# CHECK2: 000000 c3c3 c3c3 0000 0000 0000 0000 0000 0000 +# CHECK2-NEXT: 000010 0000 0000 0000 0000 0000 0000 0000 0000 +# CHECK2-NEXT: * +# CHECK2-NEXT: 002000 3232 +# SIZE2: 8194 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x1000 + AddressAlign: 0x0000000000001000 + Content: "c3c3c3c3" + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + # sh_address is increased from 0x2000 to 0x3000. + Address: 0x3000 + AddressAlign: 0x0000000000001000 + Content: "3232" +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_X, PF_R ] + VAddr: 0x1000 + PAddr: 0x1000 + Align: 0x1000 + Sections: + - Section: .text + - Type: PT_LOAD + Flags: [ PF_R, PF_W ] + VAddr: 0x3000 + PAddr: 0x3000 + Align: 0x1000 + Sections: + - Section: .data + +## Check the combination of previous two cases. +## We use LMA (after VMA->LMA adjustment) to decide where to write section contents. + +# RUN: yaml2obj --docnum=3 %s -o %t3 +# RUN: llvm-objcopy -O binary %t3 %t3.out +# RUN: od -A x -t x2 %t3.out | FileCheck %s --check-prefix=CHECK3 --ignore-case +# RUN: wc -c %t3.out | FileCheck %s --check-prefix=SIZE3 + +## The computed LMA of .data is 0x4000. The minimum LMA of all sections is 0x1000. +## The content of .data will be written at 0x4000-0x1000 = 0x3000. +# CHECK3: 000000 c3c3 c3c3 0000 0000 0000 0000 0000 0000 +# CHECK3-NEXT: 000010 0000 0000 0000 0000 0000 0000 0000 0000 +# CHECK3-NEXT: * +# CHECK3-NEXT: 003000 3232 +# SIZE3: 12290 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x1000 + AddressAlign: 0x0000000000001000 + Content: "c3c3c3c3" + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + # sh_address is increased from 0x2000 to 0x3000. + # The computed LMA is sh_address-p_vaddr+p_paddr = 0x3000-0x3000+0x4000 = 0x4000. + Address: 0x3000 + AddressAlign: 0x0000000000001000 + Content: "3232" +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_X, PF_R ] + VAddr: 0x1000 + PAddr: 0x1000 + Align: 0x1000 + Sections: + - Section: .text + - Type: PT_LOAD + Flags: [ PF_R, PF_W ] + VAddr: 0x3000 + PAddr: 0x4000 + Align: 0x1000 + Sections: + - Section: .data diff --git a/llvm/tools/llvm-objcopy/ELF/Object.cpp b/llvm/tools/llvm-objcopy/ELF/Object.cpp --- a/llvm/tools/llvm-objcopy/ELF/Object.cpp +++ b/llvm/tools/llvm-objcopy/ELF/Object.cpp @@ -2253,38 +2253,26 @@ std::unique(std::begin(OrderedSegments), std::end(OrderedSegments)); OrderedSegments.erase(End, std::end(OrderedSegments)); - uint64_t Offset = 0; - - // Modify the first segment so that there is no gap at the start. This allows - // our layout algorithm to proceed as expected while not writing out the gap - // at the start. - if (!OrderedSegments.empty()) { - Segment *Seg = OrderedSegments[0]; - const SectionBase *Sec = Seg->firstSection(); - auto Diff = Sec->OriginalOffset - Seg->OriginalOffset; - Seg->OriginalOffset += Diff; - // The size needs to be shrunk as well. - Seg->FileSize -= Diff; - // The PAddr needs to be increased to remove the gap before the first - // section. - Seg->PAddr += Diff; - uint64_t LowestPAddr = Seg->PAddr; - for (Segment *Segment : OrderedSegments) { - Segment->Offset = Segment->PAddr - LowestPAddr; - Offset = std::max(Offset, Segment->Offset + Segment->FileSize); - } + // Compute the section LMA based on its VMA and the segment LMA. Also compute + // the minimum LMA. The contents between address 0 and MinAddr will be + // skipped. + uint64_t MinAddr = UINT64_MAX; + for (SectionBase &Sec : Obj.allocSections()) { + if (Sec.ParentSegment != nullptr) + Sec.Addr += Sec.ParentSegment->PAddr - Sec.ParentSegment->VAddr; + MinAddr = std::min(MinAddr, Sec.Addr); } - layoutSections(Obj.allocSections(), Offset); - // Now that every section has been laid out we just need to compute the total // file size. This might not be the same as the offset returned by // layoutSections, because we want to truncate the last segment to the end of // its last section, to match GNU objcopy's behaviour. TotalSize = 0; - for (const SectionBase &Sec : Obj.allocSections()) + for (SectionBase &Sec : Obj.allocSections()) { + Sec.Offset = Sec.Addr - MinAddr; if (Sec.Type != SHT_NOBITS) TotalSize = std::max(TotalSize, Sec.Offset + Sec.Size); + } if (Error E = Buf.allocate(TotalSize)) return E;