[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
This commit is contained in:
Fangrui Song 2021-08-04 08:58:50 -07:00
parent b4a1eab941
commit 9bd29a73d1
3 changed files with 56 additions and 51 deletions

View File

@ -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) {

View File

@ -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;

View File

@ -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