| commit 06bb7dfbd445fe928b0ae0263ba9df1acd861c41 |
| Author: Fangrui Song <maskray@google.com> |
| Date: Mon Sep 16 07:04:16 2019 +0000 |
| |
| [ELF] Map the ELF header at imageBase |
| |
| If there is no readonly section, we map: |
| |
| * The ELF header at imageBase+maxPageSize |
| * Program headers at imageBase+maxPageSize+sizeof(Ehdr) |
| * The first section .text at imageBase+maxPageSize+sizeof(Ehdr)+sizeof(program headers) |
| |
| Due to the interaction between Writer<ELFT>::fixSectionAlignments and |
| LinkerScript::allocateHeaders, |
| `alignDown(p_vaddr(R PT_LOAD)) = alignDown(p_vaddr(RX PT_LOAD))`. |
| The RX PT_LOAD will override the R PT_LOAD at runtime, which is not ideal: |
| |
| ``` |
| // PHDR at 0x401034, should be 0x400034 |
| PHDR 0x000034 0x00401034 0x00401034 0x000a0 0x000a0 R 0x4 |
| // R PT_LOAD contains just Ehdr and program headers. |
| // At 0x401000, should be 0x400000 |
| LOAD 0x000000 0x00401000 0x00401000 0x000d4 0x000d4 R 0x1000 |
| LOAD 0x0000d4 0x004010d4 0x004010d4 0x00001 0x00001 R E 0x1000 |
| ``` |
| |
| * createPhdrs allocates the headers to the R PT_LOAD. |
| * fixSectionAlignments assigns `imageBase+maxPageSize+sizeof(Ehdr)+sizeof(program headers)` (formula: `alignTo(dot, maxPageSize) + dot % config->maxPageSize`) to addrExpr of .text |
| * allocateHeaders computes the minimum address among SHF_ALLOC sections, i.e. addr(.text) |
| * allocateHeaders sets address of ELF header to `addr(.text)-sizeof(Ehdr)-sizeof(program headers) = imageBase+maxPageSize` |
| |
| The main observation is that when the SECTIONS command is not used, we |
| don't have to call allocateHeaders. This requires an assumption that |
| the presence of PT_PHDR and addresses of headers can be decided |
| regardless of address information. |
| |
| This may seem natural because dot is not manipulated by a linker script. |
| The other thing is that we have to drop the special rule for -T<section> |
| in `getInitialDot`. If -Ttext is smaller than the image base, the headers |
| will not be allocated with the old behavior (allocateHeaders is called) |
| but always allocated with the new behavior. |
| |
| The behavior change is not a problem. Whether and where headers are |
| allocated can vary among linkers, or ld.bfd across different versions |
| (--enable-separate-code or not). It is thus advised to use a linker |
| script with the PHDRS command to have a consistent behavior across |
| linkers. If PT_PHDR is needed, an explicit --image-base can be a simpler |
| alternative. |
| |
| Differential Revision: https://reviews.llvm.org/D67325 |
| |
| llvm-svn: 371957 |
| |
| diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp |
| index 8d8f3b596fa..cd02e779e7a 100644 |
| --- a/lld/ELF/LinkerScript.cpp |
| +++ b/lld/ELF/LinkerScript.cpp |
| @@ -1006,17 +1006,13 @@ static uint64_t computeBase(uint64_t min, bool allocateHeaders) { |
| return alignDown(min, config->maxPageSize); |
| } |
| |
| -// Try to find an address for the file and program headers output sections, |
| -// which were unconditionally added to the first PT_LOAD segment earlier. |
| +// When the SECTIONS command is used, try to find an address for the file and |
| +// program headers output sections, which can be added to the first PT_LOAD |
| +// segment when program headers are created. |
| // |
| -// When using the default layout, we check if the headers fit below the first |
| -// allocated section. When using a linker script, we also check if the headers |
| -// are covered by the output section. This allows omitting the headers by not |
| -// leaving enough space for them in the linker script; this pattern is common |
| -// in embedded systems. |
| -// |
| -// If there isn't enough space for these sections, we'll remove them from the |
| -// PT_LOAD segment, and we'll also remove the PT_PHDR segment. |
| +// We check if the headers fit below the first allocated section. If there isn't |
| +// enough space for these sections, we'll remove them from the PT_LOAD segment, |
| +// and we'll also remove the PT_PHDR segment. |
| void LinkerScript::allocateHeaders(std::vector<PhdrEntry *> &phdrs) { |
| uint64_t min = std::numeric_limits<uint64_t>::max(); |
| for (OutputSection *sec : outputSections) |
| @@ -1062,28 +1058,23 @@ LinkerScript::AddressState::AddressState() { |
| } |
| } |
| |
| -static uint64_t getInitialDot() { |
| - // By default linker scripts use an initial value of 0 for '.', |
| - // but prefer -image-base if set. |
| - if (script->hasSectionsCommand) |
| - return config->imageBase ? *config->imageBase : 0; |
| - |
| - uint64_t startAddr = UINT64_MAX; |
| - // The sections with -T<section> have been sorted in order of ascending |
| - // address. We must lower startAddr if the lowest -T<section address> as |
| - // calls to setDot() must be monotonically increasing. |
| - for (auto &kv : config->sectionStartMap) |
| - startAddr = std::min(startAddr, kv.second); |
| - return std::min(startAddr, target->getImageBase() + elf::getHeaderSize()); |
| -} |
| - |
| // Here we assign addresses as instructed by linker script SECTIONS |
| // sub-commands. Doing that allows us to use final VA values, so here |
| // we also handle rest commands like symbol assignments and ASSERTs. |
| // Returns a symbol that has changed its section or value, or nullptr if no |
| // symbol has changed. |
| const Defined *LinkerScript::assignAddresses() { |
| - dot = getInitialDot(); |
| + if (script->hasSectionsCommand) { |
| + // With a linker script, assignment of addresses to headers is covered by |
| + // allocateHeaders(). |
| + dot = config->imageBase.getValueOr(0); |
| + } else { |
| + // Assign addresses to headers right now. |
| + dot = target->getImageBase(); |
| + Out::elfHeader->addr = dot; |
| + Out::programHeaders->addr = dot + Out::elfHeader->size; |
| + dot += getHeaderSize(); |
| + } |
| |
| auto deleter = std::make_unique<AddressState>(); |
| ctx = deleter.get(); |
| diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp |
| index 68a16b2a79b..cb0ac5adc6a 100644 |
| --- a/lld/ELF/Writer.cpp |
| +++ b/lld/ELF/Writer.cpp |
| @@ -555,7 +555,8 @@ template <class ELFT> void Writer<ELFT>::run() { |
| for (OutputSection *sec : outputSections) |
| sec->maybeCompress<ELFT>(); |
| |
| - script->allocateHeaders(mainPart->phdrs); |
| + if (script->hasSectionsCommand) |
| + script->allocateHeaders(mainPart->phdrs); |
| |
| // Remove empty PT_LOAD to avoid causing the dynamic linker to try to mmap a |
| // 0 sized region. This has to be done late since only after assignAddresses |
| diff --git a/lld/test/ELF/basic-aarch64.s b/lld/test/ELF/basic-aarch64.s |
| index 9882f9d0e65..2e3e5da45f1 100644 |
| --- a/lld/test/ELF/basic-aarch64.s |
| +++ b/lld/test/ELF/basic-aarch64.s |
| @@ -159,8 +159,8 @@ _start: |
| # CHECK-NEXT: ProgramHeader { |
| # CHECK-NEXT: Type: PT_PHDR (0x6) |
| # CHECK-NEXT: Offset: 0x40 |
| -# CHECK-NEXT: VirtualAddress: 0x210040 |
| -# CHECK-NEXT: PhysicalAddress: 0x210040 |
| +# CHECK-NEXT: VirtualAddress: 0x200040 |
| +# CHECK-NEXT: PhysicalAddress: 0x200040 |
| # CHECK-NEXT: FileSize: 224 |
| # CHECK-NEXT: MemSize: 224 |
| # CHECK-NEXT: Flags [ (0x4) |
| @@ -171,8 +171,8 @@ _start: |
| # CHECK-NEXT: ProgramHeader { |
| # CHECK-NEXT: Type: PT_LOAD (0x1) |
| # CHECK-NEXT: Offset: 0x0 |
| -# CHECK-NEXT: VirtualAddress: 0x210000 |
| -# CHECK-NEXT: PhysicalAddress: 0x210000 |
| +# CHECK-NEXT: VirtualAddress: 0x200000 |
| +# CHECK-NEXT: PhysicalAddress: 0x200000 |
| # CHECK-NEXT: FileSize: 288 |
| # CHECK-NEXT: MemSize: 288 |
| # CHECK-NEXT: Flags [ |
| diff --git a/lld/test/ELF/basic-i386.s b/lld/test/ELF/basic-i386.s |
| index b0f2cda9279..24d4ff45182 100644 |
| --- a/lld/test/ELF/basic-i386.s |
| +++ b/lld/test/ELF/basic-i386.s |
| @@ -129,8 +129,8 @@ _start: |
| # CHECK-NEXT: ProgramHeader { |
| # CHECK-NEXT: Type: PT_PHDR (0x6) |
| # CHECK-NEXT: Offset: 0x34 |
| -# CHECK-NEXT: VirtualAddress: 0x401034 |
| -# CHECK-NEXT: PhysicalAddress: 0x401034 |
| +# CHECK-NEXT: VirtualAddress: 0x400034 |
| +# CHECK-NEXT: PhysicalAddress: 0x400034 |
| # CHECK-NEXT: FileSize: 128 |
| # CHECK-NEXT: MemSize: 128 |
| # CHECK-NEXT: Flags [ (0x4) |
| @@ -141,8 +141,8 @@ _start: |
| # CHECK-NEXT: ProgramHeader { |
| # CHECK-NEXT: Type: PT_LOAD (0x1) |
| # CHECK-NEXT: Offset: 0x0 |
| -# CHECK-NEXT: VirtualAddress: 0x401000 |
| -# CHECK-NEXT: PhysicalAddress: 0x401000 |
| +# CHECK-NEXT: VirtualAddress: 0x400000 |
| +# CHECK-NEXT: PhysicalAddress: 0x400000 |
| # CHECK-NEXT: FileSize: 180 |
| # CHECK-NEXT: MemSize: 180 |
| # CHECK-NEXT: Flags [ |
| diff --git a/lld/test/ELF/basic-ppc.s b/lld/test/ELF/basic-ppc.s |
| index 567b55c13a1..ff753c8ceba 100644 |
| --- a/lld/test/ELF/basic-ppc.s |
| +++ b/lld/test/ELF/basic-ppc.s |
| @@ -143,8 +143,8 @@ |
| // CHECK-NEXT: ProgramHeader { |
| // CHECK-NEXT: Type: PT_PHDR (0x6) |
| // CHECK-NEXT: Offset: 0x34 |
| -// CHECK-NEXT: VirtualAddress: 0x10010034 |
| -// CHECK-NEXT: PhysicalAddress: 0x10010034 |
| +// CHECK-NEXT: VirtualAddress: 0x10000034 |
| +// CHECK-NEXT: PhysicalAddress: 0x10000034 |
| // CHECK-NEXT: FileSize: 128 |
| // CHECK-NEXT: MemSize: 128 |
| // CHECK-NEXT: Flags [ (0x4) |
| @@ -155,8 +155,8 @@ |
| // CHECK-NEXT: ProgramHeader { |
| // CHECK-NEXT: Type: PT_LOAD (0x1) |
| // CHECK-NEXT: Offset: 0x0 |
| -// CHECK-NEXT: VirtualAddress: 0x10010000 |
| -// CHECK-NEXT: PhysicalAddress: 0x10010000 |
| +// CHECK-NEXT: VirtualAddress: 0x10000000 |
| +// CHECK-NEXT: PhysicalAddress: 0x10000000 |
| // CHECK-NEXT: FileSize: 180 |
| // CHECK-NEXT: MemSize: 180 |
| // CHECK-NEXT: Flags [ (0x4) |
| diff --git a/lld/test/ELF/basic-sparcv9.s b/lld/test/ELF/basic-sparcv9.s |
| index 820dba556f5..43f3670ea6c 100644 |
| --- a/lld/test/ELF/basic-sparcv9.s |
| +++ b/lld/test/ELF/basic-sparcv9.s |
| @@ -150,8 +150,8 @@ _start: |
| # CHECK-NEXT: ProgramHeader { |
| # CHECK-NEXT: Type: PT_PHDR (0x6) |
| # CHECK-NEXT: Offset: 0x40 |
| -# CHECK-NEXT: VirtualAddress: 0x200040 |
| -# CHECK-NEXT: PhysicalAddress: 0x200040 |
| +# CHECK-NEXT: VirtualAddress: 0x100040 |
| +# CHECK-NEXT: PhysicalAddress: 0x100040 |
| # CHECK-NEXT: FileSize: 224 |
| # CHECK-NEXT: MemSize: 224 |
| # CHECK-NEXT: Flags [ (0x4) |
| @@ -162,8 +162,8 @@ _start: |
| # CHECK-NEXT: ProgramHeader { |
| # CHECK-NEXT: Type: PT_LOAD (0x1) |
| # CHECK-NEXT: Offset: 0x0 |
| -# CHECK-NEXT: VirtualAddress: 0x200000 |
| -# CHECK-NEXT: PhysicalAddress: 0x200000 |
| +# CHECK-NEXT: VirtualAddress: 0x100000 |
| +# CHECK-NEXT: PhysicalAddress: 0x100000 |
| # CHECK-NEXT: FileSize: 288 |
| # CHECK-NEXT: MemSize: 288 |
| # CHECK-NEXT: Flags [ |
| diff --git a/lld/test/ELF/ttext-tdata-tbss.s b/lld/test/ELF/ttext-tdata-tbss.s |
| index 9637bdd55d0..f00933454b1 100644 |
| --- a/lld/test/ELF/ttext-tdata-tbss.s |
| +++ b/lld/test/ELF/ttext-tdata-tbss.s |
| @@ -13,7 +13,9 @@ |
| # CHECK-NEXT: PHDR |
| # CHECK-NEXT: LOAD 0x000000 0x0000000000200000 |
| |
| -## With .text at 0 there is no space to allocate the headers. |
| +## If -Ttext is smaller than the image base (which defaults to 0x200000 for -no-pie), |
| +## the headers will still be allocated, but mapped at a higher address, |
| +## which may look strange. |
| # RUN: ld.lld -Ttext 0x0 -Tdata 0x4000 -Tbss 0x8000 %t.o -o %t2 |
| # RUN: llvm-readelf -S -l %t2 | FileCheck %s --check-prefix=USER1 |
| # USER1: .text PROGBITS 0000000000000000 001000 000001 |
| @@ -22,10 +24,12 @@ |
| # USER1-NEXT: .rodata PROGBITS 0000000000009000 003000 000008 |
| # USER1-NEXT: .aw PROGBITS 000000000000a000 004000 000008 |
| # USER1: Type |
| +# USER1-NEXT: PHDR 0x000040 0x0000000000200040 |
| +# USER1-NEXT: LOAD 0x000000 0x0000000000200000 |
| # USER1-NEXT: LOAD 0x001000 0x0000000000000000 |
| |
| -## With .text at 0x1000 there is space to allocate the headers. |
| -# RUN: ld.lld -Ttext 0x1000 -Tdata 0x4000 -Tbss 0x8000 %t.o -o %t3 |
| +## Specify --image-base to make program headers look normal. |
| +# RUN: ld.lld --image-base=0 -Ttext 0x1000 -Tdata 0x4000 -Tbss 0x8000 %t.o -o %t3 |
| # RUN: llvm-readelf -S -l %t3 | FileCheck %s --check-prefix=USER2 |
| # USER2: .text PROGBITS 0000000000001000 001000 000001 |
| # USER2-NEXT: .data PROGBITS 0000000000004000 002000 000008 |
| @@ -33,8 +37,9 @@ |
| # USER2-NEXT: .rodata PROGBITS 0000000000009000 003000 000008 |
| # USER2-NEXT: .aw PROGBITS 000000000000a000 004000 000008 |
| # USER2: Type |
| -# USER2-NEXT: PHDR |
| +# USER2-NEXT: PHDR 0x000040 0x0000000000000040 |
| # USER2-NEXT: LOAD 0x000000 0x0000000000000000 |
| +# USER2-NEXT: LOAD 0x001000 0x0000000000001000 |
| |
| ## With .text well above 200000 we don't need to change the image base |
| # RUN: ld.lld -Ttext 0x201000 %t.o -o %t4 |
| @@ -45,8 +50,9 @@ |
| # USER3-NEX: .data PROGBITS 0000000000203008 003008 000008 |
| # USER3-NEX: .bss NOBITS 0000000000203010 003010 000008 |
| # USER3: Type |
| -# USER3-NEXT: PHDR |
| +# USER3-NEXT: PHDR 0x000040 0x0000000000200040 |
| # USER3-NEXT: LOAD 0x000000 0x0000000000200000 |
| +# USER3-NEXT: LOAD 0x001000 0x0000000000201000 |
| |
| .text |
| .globl _start |