diff --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h --- a/lld/ELF/LinkerScript.h +++ b/lld/ELF/LinkerScript.h @@ -247,11 +247,11 @@ // 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 nameToOutputSection; diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp --- a/lld/ELF/LinkerScript.cpp +++ b/lld/ELF/LinkerScript.cpp @@ -849,17 +849,8 @@ } 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 @@ // 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 @@ 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 @@ // 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/test/ELF/linkerscript/tbss.s b/lld/test/ELF/linkerscript/tbss.s --- 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",@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",@nobits + .long 0 +.section second_tbss,"awT",@nobits + .byte 0 +.section bar, "aw" + .long 0