[lld] r371957 - [ELF] Map the ELF header at imageBase

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Mon Sep 16 00:04:16 PDT 2019


Author: maskray
Date: Mon Sep 16 00:04:16 2019
New Revision: 371957

URL: http://llvm.org/viewvc/llvm-project?rev=371957&view=rev
Log:
[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

Modified:
    lld/trunk/ELF/LinkerScript.cpp
    lld/trunk/ELF/Writer.cpp
    lld/trunk/test/ELF/basic-aarch64.s
    lld/trunk/test/ELF/basic-i386.s
    lld/trunk/test/ELF/basic-ppc.s
    lld/trunk/test/ELF/basic-sparcv9.s
    lld/trunk/test/ELF/ttext-tdata-tbss.s

Modified: lld/trunk/ELF/LinkerScript.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/LinkerScript.cpp?rev=371957&r1=371956&r2=371957&view=diff
==============================================================================
--- lld/trunk/ELF/LinkerScript.cpp (original)
+++ lld/trunk/ELF/LinkerScript.cpp Mon Sep 16 00:04:16 2019
@@ -1006,17 +1006,13 @@ static uint64_t computeBase(uint64_t min
   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();

Modified: lld/trunk/ELF/Writer.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Writer.cpp?rev=371957&r1=371956&r2=371957&view=diff
==============================================================================
--- lld/trunk/ELF/Writer.cpp (original)
+++ lld/trunk/ELF/Writer.cpp Mon Sep 16 00:04:16 2019
@@ -555,7 +555,8 @@ template <class ELFT> void Writer<ELFT>:
   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

Modified: lld/trunk/test/ELF/basic-aarch64.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/basic-aarch64.s?rev=371957&r1=371956&r2=371957&view=diff
==============================================================================
--- lld/trunk/test/ELF/basic-aarch64.s (original)
+++ lld/trunk/test/ELF/basic-aarch64.s Mon Sep 16 00:04:16 2019
@@ -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 [

Modified: lld/trunk/test/ELF/basic-i386.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/basic-i386.s?rev=371957&r1=371956&r2=371957&view=diff
==============================================================================
--- lld/trunk/test/ELF/basic-i386.s (original)
+++ lld/trunk/test/ELF/basic-i386.s Mon Sep 16 00:04:16 2019
@@ -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 [

Modified: lld/trunk/test/ELF/basic-ppc.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/basic-ppc.s?rev=371957&r1=371956&r2=371957&view=diff
==============================================================================
--- lld/trunk/test/ELF/basic-ppc.s (original)
+++ lld/trunk/test/ELF/basic-ppc.s Mon Sep 16 00:04:16 2019
@@ -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)

Modified: lld/trunk/test/ELF/basic-sparcv9.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/basic-sparcv9.s?rev=371957&r1=371956&r2=371957&view=diff
==============================================================================
--- lld/trunk/test/ELF/basic-sparcv9.s (original)
+++ lld/trunk/test/ELF/basic-sparcv9.s Mon Sep 16 00:04:16 2019
@@ -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 [

Modified: lld/trunk/test/ELF/ttext-tdata-tbss.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/ttext-tdata-tbss.s?rev=371957&r1=371956&r2=371957&view=diff
==============================================================================
--- lld/trunk/test/ELF/ttext-tdata-tbss.s (original)
+++ lld/trunk/test/ELF/ttext-tdata-tbss.s Mon Sep 16 00:04:16 2019
@@ -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




More information about the llvm-commits mailing list