[lld] fbf41b5 - [ELF] Simplify sh_addr computation and warn if sh_addr is not a multiple of sh_addralign

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Wed Mar 11 09:36:22 PDT 2020


Author: Fangrui Song
Date: 2020-03-11T09:35:42-07:00
New Revision: fbf41b52677d9e64a888be755b4fdaef89cf519e

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

LOG: [ELF] Simplify sh_addr computation and warn if sh_addr is not a multiple of sh_addralign

See `docs/ELF/linker_script.rst` for the new computation for sh_addr and sh_addralign.
`ALIGN(section_align)` now means: "increase alignment to section_align"
(like yet another input section requirement).

The "start of section .foo changes from 0x11 to 0x20" warning no longer
makes sense. Change it to warn if sh_addr%sh_addralign!=0.

To decrease the alignment from the default max_input_align,
use `.output ALIGN(8) : {}` instead of `.output : ALIGN(8) {}`
See linkerscript/section-address-align.test as an example.

When both an output section address and ALIGN are set (can be seen as an
"undefined behavior" https://sourceware.org/ml/binutils/2020-03/msg00115.html),
lld may align more than GNU ld, but it makes a linker script working
with GNU ld hard to break with lld.

This patch can be considered as restoring part of the behavior before D74736.

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

Added: 
    lld/test/ELF/linkerscript/section-address-align.test

Modified: 
    lld/ELF/LinkerScript.cpp
    lld/ELF/LinkerScript.h
    lld/ELF/Writer.cpp
    lld/docs/ELF/linker_script.rst
    lld/test/ELF/linkerscript/lma-align.test
    lld/test/ELF/linkerscript/section-align2.test

Removed: 
    


################################################################################
diff  --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp
index 2c71280d67c2..ef767700ccd9 100644
--- a/lld/ELF/LinkerScript.cpp
+++ b/lld/ELF/LinkerScript.cpp
@@ -777,18 +777,13 @@ void LinkerScript::switchTo(OutputSection *sec) {
   ctx->outSec = sec;
 
   uint64_t pos = advance(0, 1);
-  if (sec->addrExpr && !sec->alignExpr) {
+  if (sec->addrExpr && script->hasSectionsCommand) {
     // The alignment is ignored.
     ctx->outSec->addr = pos;
   } else {
-    // If ALIGN is specified, advance sh_addr according to ALIGN and ignore the
-    // maximum of input section alignments.
-    //
-    // When no SECTIONS command is given, sec->alignExpr is set to the maximum
-    // of input section alignments.
-    uint32_t align =
-        sec->alignExpr ? sec->alignExpr().getValue() : ctx->outSec->alignment;
-    ctx->outSec->addr = advance(0, align);
+    // ctx->outSec->alignment is the max of ALIGN and the maximum of input
+    // section alignments.
+    ctx->outSec->addr = advance(0, ctx->outSec->alignment);
     expandMemoryRegions(ctx->outSec->addr - pos);
   }
 }
@@ -854,10 +849,7 @@ void LinkerScript::assignOffsets(OutputSection *sec) {
     expandMemoryRegion(ctx->memRegion, dot - ctx->memRegion->curPos,
                        ctx->memRegion->name, sec->name);
 
-  uint64_t oldDot = dot;
   switchTo(sec);
-  if (sec->addrExpr && oldDot != dot)
-    changedSectionAddresses.push_back({sec, oldDot});
 
   ctx->lmaOffset = 0;
 
@@ -1129,7 +1121,6 @@ const Defined *LinkerScript::assignAddresses() {
   auto deleter = std::make_unique<AddressState>();
   ctx = deleter.get();
   errorOnMissingSection = true;
-  changedSectionAddresses.clear();
   switchTo(aether);
 
   SymbolAssignmentMap oldValues = getSymbolAssignmentValues(sectionCommands);

diff  --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h
index 2eb98b694de7..66ff79c32678 100644
--- a/lld/ELF/LinkerScript.h
+++ b/lld/ELF/LinkerScript.h
@@ -324,10 +324,6 @@ class LinkerScript final {
 
   // Sections that will be warned/errored by --orphan-handling.
   std::vector<const InputSectionBase *> orphanSections;
-
-  // Sections whose addresses are not equal to their addrExpr values.
-  std::vector<std::pair<const OutputSection *, uint64_t>>
-      changedSectionAddresses;
 };
 
 extern LinkerScript *script;

diff  --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index b41aceb8634b..d6678ac2548d 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -1638,16 +1638,14 @@ template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() {
     }
   }
 
-  // If a SECTIONS command is given, addrExpr, if set, is the specified output
-  // section address. Warn if the computed value is 
diff erent from the actual
-  // address.
-  if (!script->hasSectionsCommand)
-    return;
-  for (auto changed : script->changedSectionAddresses) {
-    const OutputSection *os = changed.first;
-    warn("start of section " + os->name + " changes from 0x" +
-         utohexstr(changed.second) + " to 0x" + utohexstr(os->addr));
-  }
+  // If addrExpr is set, the address may not be a multiple of the alignment.
+  // Warn because this is error-prone.
+  for (BaseCommand *cmd : script->sectionCommands)
+    if (auto *os = dyn_cast<OutputSection>(cmd))
+      if (os->addr % os->alignment != 0)
+        warn("address (0x" + Twine::utohexstr(os->addr) + ") of section " +
+             os->name + " is not a multiple of alignment (" +
+             Twine(os->alignment) + ")");
 }
 
 static void finalizeSynthetic(SyntheticSection *sec) {

diff  --git a/lld/docs/ELF/linker_script.rst b/lld/docs/ELF/linker_script.rst
index aebefeca9d5e..5b904bb3a1e1 100644
--- a/lld/docs/ELF/linker_script.rst
+++ b/lld/docs/ELF/linker_script.rst
@@ -16,3 +16,38 @@ defined by the documentation is to follow the GNU ld implementation wherever
 possible. We reserve the right to make 
diff erent implementation choices where
 it is appropriate for LLD. Intentional deviations will be documented in this
 file.
+
+Output section description
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The description of an output section looks like:
+
+::
+
+  section [address] [(type)] : [AT(lma)] [ALIGN(section_align)] [SUBALIGN](subsection_align)] {
+    output-section-command
+    ...
+  } [>region] [AT>lma_region] [:phdr ...] [=fillexp] [,]
+
+Output section address
+----------------------
+
+When an *OutputSection* *S* has ``address``, LLD will set sh_addr to ``address``.
+
+The ELF specification says:
+
+> The value of sh_addr must be congruent to 0, modulo the value of sh_addralign.
+
+The presence of ``address`` can cause the condition unsatisfied. LLD will warn.
+GNU ld from Binutils 2.35 onwards will reduce sh_addralign so that
+sh_addr=0 (modulo sh_addralign).
+
+Output section alignment
+------------------------
+
+sh_addralign of an *OutputSection* *S* is the maximum of
+``ALIGN(section_align)`` and the maximum alignment of the input sections in
+*S*.
+
+When an *OutputSection* *S* has both ``address`` and ``ALIGN(section_align)``,
+GNU ld will set sh_addralign to ``ALIGN(section_align)``.

diff  --git a/lld/test/ELF/linkerscript/lma-align.test b/lld/test/ELF/linkerscript/lma-align.test
index 37d7becf9e76..9994ff516934 100644
--- a/lld/test/ELF/linkerscript/lma-align.test
+++ b/lld/test/ELF/linkerscript/lma-align.test
@@ -5,21 +5,21 @@
 # RUN: ld.lld -T %s %t.o -o %t 2>&1 | FileCheck --check-prefix=WARN %s --implicit-check-not=warning:
 # RUN: llvm-readelf -S -l %t | FileCheck %s
 
-# WARN: warning: start of section .data changes from 0x11001 to 0x11010
-# WARN: warning: start of section .bss changes from 0x11021 to 0x11040
+# WARN: warning: address (0x11001) of section .data is not a multiple of alignment (32)
+# WARN: warning: address (0x11021) of section .bss is not a multiple of alignment (64)
 
 # CHECK:      Name         Type     Address          Off    Size   ES Flg Lk Inf Al
 # CHECK-NEXT:              NULL     0000000000000000 000000 000000 00      0   0  0
 # CHECK-NEXT: .text        PROGBITS 0000000000001000 001000 000001 00  AX  0   0  4
 # CHECK-NEXT: .data.rel.ro PROGBITS 0000000000011000 002000 000001 00  WA  0   0 16
-# CHECK-NEXT: .data        PROGBITS 0000000000011010 002010 000011 00  WA  0   0 32
-# CHECK-NEXT: .bss         NOBITS   0000000000011040 002040 000001 00  WA  0   0 64
+# CHECK-NEXT: .data        PROGBITS 0000000000011001 002001 000020 00  WA  0   0 32
+# CHECK-NEXT: .bss         NOBITS   0000000000011021 002021 000001 00  WA  0   0 64
 
 # CHECK:      Type  Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
 # CHECK-NEXT: LOAD  0x001000 0x0000000000001000 0x0000000000001000 0x000001 0x000001 R E 0x1000
 # CHECK-NEXT: LOAD  0x002000 0x0000000000011000 0x0000000000001010 0x000001 0x000001 RW  0x1000
-# CHECK-NEXT: LOAD  0x002010 0x0000000000011010 0x0000000000001020 0x000011 0x000011 RW  0x1000
-# CHECK-NEXT: LOAD  0x002040 0x0000000000011040 0x0000000000011040 0x000000 0x000001 RW  0x1000
+# CHECK-NEXT: LOAD  0x002001 0x0000000000011001 0x0000000000001020 0x000020 0x000020 RW  0x1000
+# CHECK-NEXT: LOAD  0x002021 0x0000000000011021 0x0000000000011021 0x000000 0x000001 RW  0x1000
 
 MEMORY {
   ROM : ORIGIN = 0x1000, LENGTH = 1K

diff  --git a/lld/test/ELF/linkerscript/section-address-align.test b/lld/test/ELF/linkerscript/section-address-align.test
new file mode 100644
index 000000000000..cb048a987585
--- /dev/null
+++ b/lld/test/ELF/linkerscript/section-address-align.test
@@ -0,0 +1,34 @@
+# REQUIRES: x86
+## Test ALIGN when specifying the output section address.
+
+# RUN: echo '.globl _start; _start: ret; \
+# RUN:   .data.rel.ro; .balign 8; .byte 0; \
+# RUN:   .data; .byte 0; \
+# RUN:   .bss; .balign 32; .byte 0' | \
+# RUN:   llvm-mc -filetype=obj -triple=x86_64 - -o %t.o
+# RUN: ld.lld -T %s %t.o -o %t 2>&1 | FileCheck --check-prefix=WARN %s --implicit-check-not=warning:
+# RUN: llvm-readelf -S %t | FileCheck %s
+
+# WARN: warning: address (0x10004) of section .data.rel.ro is not a multiple of alignment (8)
+# WARN: warning: address (0x20008) of section .bss is not a multiple of alignment (32)
+
+# CHECK:      Name         Type     Address          Off    Size   ES Flg Lk Inf Al
+# CHECK-NEXT:              NULL     0000000000000000 000000 000000 00      0   0  0
+# CHECK-NEXT: .text        PROGBITS 0000000000010000 001000 000001 00  AX  0   0  4
+# CHECK-NEXT: .data.rel.ro PROGBITS 0000000000010004 001004 000005 00  WA  0   0  8
+# CHECK-NEXT: .data        PROGBITS 0000000000020000 002000 000001 00  WA  0   0  1
+# CHECK-NEXT: .bss         NOBITS   0000000000020008 002001 000019 00  WA  0   0 32
+
+SECTIONS {
+  .text 0x10000 : { *(.text) }
+  ## The output .data.rel.ro starts at 0x10004.
+  ## The input .data.rel.ro starts at 0x10008 and ends at 0x10009.
+  ## sh_size(.data.rel.ro) = 0x10009-0x10004 = 0x5.
+  .data.rel.ro ALIGN(4) : { *(.data.rel.ro) }
+
+  .data 0x20000 : { *(.data) }
+  ## The output .bss starts at 0x20008.
+  ## The input .bss starts at 0x20020 and ends at 0x20021.
+  ## sh_size(.bss) = 0x20021-0x20008 = 0x19.
+  .bss ALIGN(8) : { *(.bss) }
+}

diff  --git a/lld/test/ELF/linkerscript/section-align2.test b/lld/test/ELF/linkerscript/section-align2.test
index 0c48c9c7397a..9400af98e3d1 100644
--- a/lld/test/ELF/linkerscript/section-align2.test
+++ b/lld/test/ELF/linkerscript/section-align2.test
@@ -2,7 +2,9 @@
 ## Test ALIGN and its interaction with explicit output section addresses.
 
 # RUN: echo '.globl _start; _start: ret; .data.rel.ro; .balign 8; .byte 0; .data; .byte 0; \
-# RUN:   .section .data2,"aw"; .balign 8; .byte 0; .bss; .balign 32; .byte 0' | \
+# RUN:   .section .data2,"aw"; .balign 8; .byte 0; \
+# RUN:   .section .data3,"aw"; .balign 32; .byte 0; \
+# RUN:   .bss; .balign 32; .byte 0' | \
 # RUN:   llvm-mc -filetype=obj -triple=aarch64 - -o %t.o
 # RUN: ld.lld -T %s %t.o -o %t 2>&1 | FileCheck --check-prefix=WARN %s --implicit-check-not=warning:
 # RUN: llvm-readelf -S %t | FileCheck %s
@@ -10,16 +12,18 @@
 ## Check we don't warn in the absence of SECTIONS.
 # RUN: ld.lld --fatal-warnings -Ttext=0x10000 %t.o -o /dev/null
 
-# WARN: warning: start of section .data.rel.ro changes from 0x10004 to 0x10010
-# WARN: warning: start of section .bss changes from 0x20009 to 0x20010
+# WARN: warning: address (0x10004) of section .data.rel.ro is not a multiple of alignment (16)
+# WARN: warning: address (0x20001) of section .data2 is not a multiple of alignment (8)
+# WARN: warning: address (0x20021) of section .bss is not a multiple of alignment (32)
 
 # CHECK:      Name         Type     Address          Off    Size   ES Flg Lk Inf Al
 # CHECK-NEXT:              NULL     0000000000000000 000000 000000 00      0   0  0
 # CHECK-NEXT: .text        PROGBITS 0000000000010000 010000 000004 00  AX  0   0  4
-# CHECK-NEXT: .data.rel.ro PROGBITS 0000000000010010 010010 000001 00  WA  0   0 16
+# CHECK-NEXT: .data.rel.ro PROGBITS 0000000000010004 010004 000005 00  WA  0   0 16
 # CHECK-NEXT: .data        PROGBITS 0000000000020000 020000 000001 00  WA  0   0  1
 # CHECK-NEXT: .data2       PROGBITS 0000000000020001 020001 000008 00  WA  0   0  8
-# CHECK-NEXT: .bss         NOBITS   0000000000020010 020009 000011 00  WA  0   0 32
+# CHECK-NEXT: .data3       PROGBITS 0000000000020020 020020 000001 00  WA  0   0 32
+# CHECK-NEXT: .bss         NOBITS   0000000000020021 020021 000020 00  WA  0   0 32
 
 SECTIONS {
   .text 0x10000 : { *(.text) }
@@ -30,6 +34,9 @@ SECTIONS {
   ## The output section address is set without ALIGN. sh_addr is set to Dot, ignoring alignment.
   ## sh_addralign is the maximum of input section alignments, 8.
   .data2 . : { *(.data2) }
+  ## sh_addr is aligned to 32.
+  ## The input section has a larger alignment and is thus preceded by a gap.
+  .data3 : ALIGN(16) { *(.data3) }
   ## sh_addr is aligned to 16.
   ## The input section has a larger alignment and is thus preceded by a gap.
   .bss . : ALIGN(16) { *(.bss) }


        


More information about the llvm-commits mailing list