[lld] 9bd29a7 - [ELF] Make dot in .tbss correct

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 4 08:58:54 PDT 2021


Author: Fangrui Song
Date: 2021-08-04T08:58:50-07:00
New Revision: 9bd29a73d17add45234a35de5f6ad7ca8321f7f9

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

LOG: [ELF] Make dot in .tbss correct

GNU ld doesn't support multiple SHF_TLS SHT_NOBITS output sections (it restores
the address after an SHF_TLS SHT_NOBITS section, so consecutive SHF_TLS
SHT_NOBITS sections will have conflicting address ranges).

That said, `threadBssOffset` implements limited support for consecutive SHF_TLS
SHT_NOBITS sections. (SHF_TLS SHT_PROGBITS following a SHF_TLS SHT_NOBITS can still be
incorrect.)

`.` in an output section description of an SHF_TLS SHT_NOBITS section is
incorrect. (https://lists.llvm.org/pipermail/llvm-dev/2021-July/151974.html)

This patch saves the end address of the previous tbss section in
`ctx->tbssAddr`, changes `dot` in the beginning of `assignOffset` so
that `.` evaluation will be correct.

Reviewed By: peter.smith

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

Added: 
    

Modified: 
    lld/ELF/LinkerScript.cpp
    lld/ELF/LinkerScript.h
    lld/test/ELF/linkerscript/tbss.s

Removed: 
    


################################################################################
diff  --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp
index a938984ad945e..01785f39ed759 100644
--- a/lld/ELF/LinkerScript.cpp
+++ b/lld/ELF/LinkerScript.cpp
@@ -849,17 +849,8 @@ void LinkerScript::diagnoseOrphanHandling() const {
 }
 
 uint64_t LinkerScript::advance(uint64_t size, unsigned alignment) {
-  bool isTbss =
-      (ctx->outSec->flags & SHF_TLS) && ctx->outSec->type == SHT_NOBITS;
-  uint64_t start = isTbss ? dot + ctx->threadBssOffset : dot;
-  start = alignTo(start, alignment);
-  uint64_t end = start + size;
-
-  if (isTbss)
-    ctx->threadBssOffset = end - dot;
-  else
-    dot = end;
-  return end;
+  dot = alignTo(dot, alignment) + size;
+  return dot;
 }
 
 void LinkerScript::output(InputSection *s) {
@@ -931,13 +922,24 @@ static OutputSection *findFirstSection(PhdrEntry *load) {
 // This function assigns offsets to input sections and an output section
 // for a single sections command (e.g. ".text { *(.text); }").
 void LinkerScript::assignOffsets(OutputSection *sec) {
+  const bool isTbss = (sec->flags & SHF_TLS) && sec->type == SHT_NOBITS;
   const bool sameMemRegion = ctx->memRegion == sec->memRegion;
   const bool prevLMARegionIsDefault = ctx->lmaRegion == nullptr;
   const uint64_t savedDot = dot;
   ctx->memRegion = sec->memRegion;
   ctx->lmaRegion = sec->lmaRegion;
 
-  if (sec->flags & SHF_ALLOC) {
+  if (!(sec->flags & SHF_ALLOC)) {
+    // Non-SHF_ALLOC sections have zero addresses.
+    dot = 0;
+  } else if (isTbss) {
+    // Allow consecutive SHF_TLS SHT_NOBITS output sections. The address range
+    // starts from the end address of the previous tbss section.
+    if (ctx->tbssAddr == 0)
+      ctx->tbssAddr = dot;
+    else
+      dot = ctx->tbssAddr;
+  } else {
     if (ctx->memRegion)
       dot = ctx->memRegion->curPos;
     if (sec->addrExpr)
@@ -950,9 +952,6 @@ void LinkerScript::assignOffsets(OutputSection *sec) {
     if (ctx->memRegion && ctx->memRegion->curPos < dot)
       expandMemoryRegion(ctx->memRegion, dot - ctx->memRegion->curPos,
                          ctx->memRegion->name, sec->name);
-  } else {
-    // Non-SHF_ALLOC sections have zero addresses.
-    dot = 0;
   }
 
   switchTo(sec);
@@ -1008,8 +1007,13 @@ void LinkerScript::assignOffsets(OutputSection *sec) {
 
   // Non-SHF_ALLOC sections do not affect the addresses of other OutputSections
   // as they are not part of the process image.
-  if (!(sec->flags & SHF_ALLOC))
+  if (!(sec->flags & SHF_ALLOC)) {
     dot = savedDot;
+  } else if (isTbss) {
+    // NOBITS TLS sections are similar. Additionally save the end address.
+    ctx->tbssAddr = dot;
+    dot = savedDot;
+  }
 }
 
 static bool isDiscardable(OutputSection &sec) {

diff  --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h
index 0592c52acb84d..d2487ae0f9d28 100644
--- a/lld/ELF/LinkerScript.h
+++ b/lld/ELF/LinkerScript.h
@@ -247,11 +247,11 @@ class LinkerScript final {
   // not be used outside of the scope of a call to the above functions.
   struct AddressState {
     AddressState();
-    uint64_t threadBssOffset = 0;
     OutputSection *outSec = nullptr;
     MemoryRegion *memRegion = nullptr;
     MemoryRegion *lmaRegion = nullptr;
     uint64_t lmaOffset = 0;
+    uint64_t tbssAddr = 0;
   };
 
   llvm::DenseMap<StringRef, OutputSection *> nameToOutputSection;

diff  --git a/lld/test/ELF/linkerscript/tbss.s b/lld/test/ELF/linkerscript/tbss.s
index 1560ad5d039a2..1113a797b9170 100644
--- a/lld/test/ELF/linkerscript/tbss.s
+++ b/lld/test/ELF/linkerscript/tbss.s
@@ -1,42 +1,43 @@
 # REQUIRES: x86
 # RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
-# RUN: echo "SECTIONS { \
+# RUN: echo 'SECTIONS { \
 # RUN:   . = SIZEOF_HEADERS; \
 # RUN:   .text : { *(.text) } \
-# RUN:   foo : { *(foo) } \
+# RUN:   .tbss : { __tbss_start = .; *(.tbss) __tbss_end = .; } \
+# RUN:   second_tbss : { second_tbss_start = .; *(second_tbss) second_tbss_end = .; } \
 # RUN:   bar : { *(bar) } \
-# RUN: }" > %t.script
-# RUN: ld.lld -T %t.script %t.o -o %t
-# RUN: llvm-readobj -S %t | FileCheck %s
+# RUN: }' > %t.lds
+# RUN: ld.lld -T %t.lds %t.o -o %t1
+# RUN: llvm-readelf -S -s %t1 | FileCheck %s
 
-# test that a tbss section doesn't use address space.
+# RUN: echo 'PHDRS { text PT_LOAD; }' > %th.lds
+# RUN: cat %th.lds %t.lds > %t2.lds
+# RUN: ld.lld -T %t.lds %t.o -o %t2
+# RUN: llvm-readelf -S -s %t2 | FileCheck %s
 
-# CHECK:        Name: foo
-# CHECK-NEXT:   Type: SHT_NOBITS
-# CHECK-NEXT:   Flags [
-# CHECK-NEXT:     SHF_ALLOC
-# CHECK-NEXT:     SHF_TLS
-# CHECK-NEXT:     SHF_WRITE
-# CHECK-NEXT:   ]
-# CHECK-NEXT:   Address: 0x[[ADDR:.*]]
-# CHECK-NEXT:   Offset: 0x[[ADDR]]
-# CHECK-NEXT:   Size: 4
-# CHECK-NEXT:   Link: 0
-# CHECK-NEXT:   Info: 0
-# CHECK-NEXT:   AddressAlignment: 1
-# CHECK-NEXT:   EntrySize: 0
-# CHECK-NEXT: }
-# CHECK-NEXT: Section {
-# CHECK-NEXT:   Index:
-# CHECK-NEXT:   Name: bar
-# CHECK-NEXT:   Type: SHT_PROGBITS
-# CHECK-NEXT:   Flags [
-# CHECK-NEXT:     SHF_ALLOC
-# CHECK-NEXT:     SHF_WRITE
-# CHECK-NEXT:   ]
-# CHECK-NEXT:   Address: 0x[[ADDR]]
+## Test that a tbss section doesn't affect the start address of the next section.
 
-        .section foo,"awT", at nobits
-        .long   0
-        .section bar, "aw"
-        .long 0
+# CHECK: Name        Type     Address              Off                Size   ES Flg
+# CHECK: .tbss       NOBITS   [[#%x,ADDR:]]        [[#%x,OFF:]]       000004 00 WAT
+# CHECK: second_tbss NOBITS   {{0+}}[[#%x,ADDR+4]] {{0+}}[[#%x,OFF]]  000001 00 WAT
+# CHECK: bar         PROGBITS {{0+}}[[#%x,ADDR]]   {{0+}}[[#%x,OFF]]  000004 00  WA
+
+## Test that . in a tbss section represents the current location, even if the
+## address will be reset.
+
+# CHECK: Value                {{.*}} Name
+# CHECK: {{0+}}[[#%x,ADDR]]   {{.*}} __tbss_start
+# CHECK: {{0+}}[[#%x,ADDR+4]] {{.*}} __tbss_end
+# CHECK: {{0+}}[[#%x,ADDR+4]] {{.*}} second_tbss_start
+# CHECK: {{0+}}[[#%x,ADDR+5]] {{.*}} second_tbss_end
+
+.globl _start
+_start:
+  nop
+
+.section .tbss,"awT", at nobits
+  .long 0
+.section second_tbss,"awT", at nobits
+  .byte 0
+.section bar, "aw"
+  .long 0


        


More information about the llvm-commits mailing list