[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