[lld] bb4a36e - [ELF] Propagate LMA offset to sections with neither AT() nor AT>

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 1 08:19:17 PDT 2020


Author: Fangrui Song
Date: 2020-04-01T08:19:06-07:00
New Revision: bb4a36ea280283cb980cff07a81601d1ff3b81ba

URL: https://github.com/llvm/llvm-project/commit/bb4a36ea280283cb980cff07a81601d1ff3b81ba
DIFF: https://github.com/llvm/llvm-project/commit/bb4a36ea280283cb980cff07a81601d1ff3b81ba.diff

LOG: [ELF] Propagate LMA offset to sections with neither AT() nor AT>

Fixes https://bugs.llvm.org/show_bug.cgi?id=45313
Also fixes linkerscript/{at4.s,overlay.test} LMA address issues exposed by
011b785505b1f6d315a93fd0a0409576ad8d1805.
Related: D74297

This patch improves emulation of GNU ld's heuristics on the difference
between the LMA and the VMA:
https://sourceware.org/binutils/docs/ld/Output-Section-LMA.html#Output-Section-LMA

New test linkerscript/lma-offset.s (based on at4.s) demonstrates some behaviors.

Reviewed By: psmith

Differential Revision: https://reviews.llvm.org/D76995

Added: 
    lld/test/ELF/linkerscript/lma-offset.s

Modified: 
    lld/ELF/LinkerScript.cpp
    lld/docs/ELF/linker_script.rst
    lld/test/ELF/linkerscript/loadaddr.s
    lld/test/ELF/linkerscript/map-file2.test
    lld/test/ELF/linkerscript/overlay.test

Removed: 
    lld/test/ELF/linkerscript/at4.s


################################################################################
diff  --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp
index f52dc701541f..4d507ee9c2c9 100644
--- a/lld/ELF/LinkerScript.cpp
+++ b/lld/ELF/LinkerScript.cpp
@@ -833,6 +833,7 @@ void LinkerScript::assignOffsets(OutputSection *sec) {
   if (!(sec->flags & SHF_ALLOC))
     dot = 0;
 
+  bool prevLMARegionIsDefault = ctx->lmaRegion == nullptr;
   ctx->memRegion = sec->memRegion;
   ctx->lmaRegion = sec->lmaRegion;
   if (ctx->memRegion)
@@ -851,19 +852,19 @@ void LinkerScript::assignOffsets(OutputSection *sec) {
 
   switchTo(sec);
 
-  ctx->lmaOffset = 0;
-
+  // ctx->lmaOffset is LMA minus VMA. If LMA is explicitly specified via AT() or
+  // AT>, recompute ctx->lmaOffset; otherwise, if both previous/current LMA
+  // region is the default, reuse previous lmaOffset; otherwise, reset lmaOffset
+  // to 0. This emulates heuristics described in
+  // https://sourceware.org/binutils/docs/ld/Output-Section-LMA.html
   if (sec->lmaExpr)
     ctx->lmaOffset = sec->lmaExpr().getValue() - dot;
-  if (MemoryRegion *mr = sec->lmaRegion)
+  else if (MemoryRegion *mr = sec->lmaRegion)
     ctx->lmaOffset = alignTo(mr->curPos, sec->alignment) - dot;
+  else if (!prevLMARegionIsDefault)
+    ctx->lmaOffset = 0;
 
-  // If neither AT nor AT> is specified for an allocatable section, the linker
-  // will set the LMA such that the 
diff erence between VMA and LMA for the
-  // section is the same as the preceding output section in the same region
-  // https://sourceware.org/binutils/docs-2.20/ld/Output-Section-LMA.html
-  // This, however, should only be done by the first "non-header" section
-  // in the segment.
+  // Propagate ctx->lmaOffset to the first "non-header" section.
   if (PhdrEntry *l = ctx->outSec->ptLoad)
     if (sec == findFirstSection(l))
       l->lmaOffset = ctx->lmaOffset;

diff  --git a/lld/docs/ELF/linker_script.rst b/lld/docs/ELF/linker_script.rst
index 5b904bb3a1e1..c5115c1c9d6f 100644
--- a/lld/docs/ELF/linker_script.rst
+++ b/lld/docs/ELF/linker_script.rst
@@ -51,3 +51,27 @@ sh_addralign of an *OutputSection* *S* is the maximum of
 
 When an *OutputSection* *S* has both ``address`` and ``ALIGN(section_align)``,
 GNU ld will set sh_addralign to ``ALIGN(section_align)``.
+
+Output section LMA
+------------------
+
+A load address (LMA) can be specified by ``AT(lma)`` or ``AT>lma_region``.
+
+- ``AT(lma)`` specifies the exact load address. If the linker script does not
+  have a PHDRS command, then a new loadable segment will be generated.
+- ``AT>lma_region`` specifies the LMA region. The lack of ``AT>lma_region``
+  means the default region is used. Note, GNU ld propagates the previous LMA
+  memory region when ``address`` is not specified. The LMA is set to the
+  current location of the memory region aligned to the section alignment.
+  If the linker script does not have a PHDRS command, then if
+  ``lma_region`` is 
diff erent from the ``lma_region`` for
+  the previous OutputSection a new loadable segment will be generated.
+
+The two keywords cannot be specified at the same time.
+
+If neither ``AT(lma)`` nor ``AT>lma_region`` is specified:
+
+- If the previous section is also in the default LMA region, the 
diff erence
+  between the LMA and the VMA is computed to be the same as the previous
+  
diff erence.
+- Otherwise, the LMA is set to the VMA.

diff  --git a/lld/test/ELF/linkerscript/at4.s b/lld/test/ELF/linkerscript/at4.s
deleted file mode 100644
index a52a33e5cee4..000000000000
--- a/lld/test/ELF/linkerscript/at4.s
+++ /dev/null
@@ -1,28 +0,0 @@
-# REQUIRES: x86
-# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
-# RUN: echo "SECTIONS { \
-# RUN:  . = 0x1000; \
-# RUN:  .aaa : { *(.aaa) } \
-# RUN:  .bbb : AT(0x2008) { *(.bbb) } \
-# RUN:  .ccc : { *(.ccc) } \
-# RUN: }" > %t.script
-# RUN: ld.lld %t.o --script %t.script -o %t
-# RUN: llvm-readelf -l %t | FileCheck %s
-
-# CHECK:      Type  Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
-# CHECK-NEXT: LOAD  0x001000 0x0000000000001000 0x0000000000001000 0x000008 0x000008 R   0x1000
-# CHECK-NEXT: LOAD  0x001008 0x0000000000001008 0x0000000000002008 0x000010 0x000010 R   0x1000
-# CHECK-NEXT: LOAD  0x001018 0x0000000000001018 0x0000000000001018 0x000001 0x000001 R E 0x1000
-
-.global _start
-_start:
- nop
-
-.section .aaa, "a"
-.quad 0
-
-.section .bbb, "a"
-.quad 0
-
-.section .ccc, "a"
-.quad 0

diff  --git a/lld/test/ELF/linkerscript/lma-offset.s b/lld/test/ELF/linkerscript/lma-offset.s
new file mode 100644
index 000000000000..3c739724538e
--- /dev/null
+++ b/lld/test/ELF/linkerscript/lma-offset.s
@@ -0,0 +1,39 @@
+# REQUIRES: x86
+## Test the 
diff erence between the VMA and the LMA for sections with AT().
+
+# RUN: echo '.globl _start; _start: ret; \
+# RUN:   .section .a,"a"; .byte 0; \
+# RUN:   .section .b,"a"; .byte 0; \
+# RUN:   .section .c,"a"; .byte 0; \
+# RUN:   .section .d,"a"; .byte 0; \
+# RUN:   .data; .byte 0' | \
+# RUN:   llvm-mc -filetype=obj -triple=x86_64 - -o %t.o
+# RUN: ld.lld -T %s %t.o -o %t
+# RUN: llvm-readelf -l %t | FileCheck %s
+
+# CHECK:      Type  Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
+# CHECK-NEXT: LOAD  0x001000 0x0000000000001000 0x0000000000001000 0x000001 0x000001 R   0x1000
+
+## .b has AT(). It starts a PT_LOAD segment which also includes .c
+# CHECK-NEXT: LOAD  0x001001 0x0000000000001001 0x0000000000002005 0x000002 0x000002 R   0x1000
+
+## .d has AT(). It starts a PT_LOAD segment, even if the 
diff erence between
+## LMA and VMA (0x2007-0x1003) is the same as the previous one.
+# CHECK-NEXT: LOAD  0x001003 0x0000000000001003 0x0000000000002007 0x000001 0x000001 R   0x1000
+
+## The orphan section .text starts a PT_LOAD segment. The 
diff erence between
+## LMA and VMA (0x2008-0x1004) remains the same
+# CHECK-NEXT: LOAD  0x001004 0x0000000000001004 0x0000000000002008 0x000001 0x000001 R E 0x1000
+
+## .data starts a PT_LOAD segment. The 
diff erence remains the same.
+# CHECK-NEXT: LOAD  0x001005 0x0000000000001005 0x0000000000002009 0x000001 0x000001 RW  0x1000
+
+SECTIONS {
+  . = 0x1000;
+  .a : { *(.a) }
+  .b : AT(0x2005) { *(.b) }
+  .c : { *(.c) }
+  .d : AT(0x2007) { *(.d) }
+  ## Orphan section .text will be inserted here.
+  .data : { *(.data) }
+}

diff  --git a/lld/test/ELF/linkerscript/loadaddr.s b/lld/test/ELF/linkerscript/loadaddr.s
index e2c82fc6c8cb..055b7422baeb 100644
--- a/lld/test/ELF/linkerscript/loadaddr.s
+++ b/lld/test/ELF/linkerscript/loadaddr.s
@@ -22,7 +22,7 @@
 # CHECK-NEXT: 0000000000002008 g       *ABS*  0000000000000000 bbb_lma
 # CHECK-NEXT: 0000000000003000 g       *ABS*  0000000000000000 ccc_lma
 # CHECK-NEXT: 0000000000004000 g       *ABS*  0000000000000000 ddd_lma
-# CHECK-NEXT: 0000000000001020 g       *ABS*  0000000000000000 txt_lma
+# CHECK-NEXT: 0000000000004008 g       *ABS*  0000000000000000 txt_lma
 # ERROR: {{.*}}.script:1: undefined section .zzz
 
 .global _start

diff  --git a/lld/test/ELF/linkerscript/map-file2.test b/lld/test/ELF/linkerscript/map-file2.test
index f527e8ecdf80..535043282249 100644
--- a/lld/test/ELF/linkerscript/map-file2.test
+++ b/lld/test/ELF/linkerscript/map-file2.test
@@ -32,10 +32,10 @@ SECTIONS {
 # CHECK-NEXT:       1219             3209        8     1         {{.*}}{{/|\\}}map-file2.test.tmp.o:(.ddd)
 # CHECK-NEXT:       1228             3218       34     8 .eh_frame
 # CHECK-NEXT:       1228             3218       30     1         {{.*}}{{/|\\}}map-file2.test.tmp.o:(.eh_frame+0x0)
-# CHECK-NEXT:       125c             125c        1     4 .text
-# CHECK-NEXT:       125c             125c        1     4         {{.*}}{{/|\\}}map-file2.test.tmp.o:(.text)
-# CHECK-NEXT:       125c             125c        0     1                 f(int)
-# CHECK-NEXT:       125c             125c        0     1                 _start
+# CHECK-NEXT:       125c             324c        1     4 .text
+# CHECK-NEXT:       125c             324c        1     4         {{.*}}{{/|\\}}map-file2.test.tmp.o:(.text)
+# CHECK-NEXT:       125c             324c        0     1                 f(int)
+# CHECK-NEXT:       125c             324c        0     1                 _start
 # CHECK-NEXT:          0                0        8     1 .comment
 # CHECK-NEXT:          0                0        8     1         <internal>:(.comment)
 # CHECK-NEXT:          0                0       48     8 .symtab

diff  --git a/lld/test/ELF/linkerscript/overlay.test b/lld/test/ELF/linkerscript/overlay.test
index 85e140d60ab0..2d3c88759c63 100644
--- a/lld/test/ELF/linkerscript/overlay.test
+++ b/lld/test/ELF/linkerscript/overlay.test
@@ -28,4 +28,4 @@ SECTIONS {
 # CHECK:      Type Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
 # CHECK-NEXT: LOAD 0x001000 0x0000000000001000 0x0000000000004000 0x000008 0x000008 R   0x1000
 # CHECK-NEXT: LOAD 0x002000 0x0000000000001000 0x0000000000004008 0x000004 0x000004 R   0x1000
-# CHECK-NEXT: LOAD 0x002008 0x0000000000001008 0x0000000000001008 0x000001 0x000001 R E 0x1000
+# CHECK-NEXT: LOAD 0x002008 0x0000000000001008 0x0000000000004010 0x000001 0x000001 R E 0x1000


        


More information about the llvm-commits mailing list