[lld][WebAssembly] Fix for PIC output + TLS + non-shared-memory

Prior to this change build with `-shared/-pie` and using TLS (but
without -shared-memory) would hit this assert:

  "Currenly only a single data segment is supported in PIC mode"

This is because we were not including TLS data when merging data
segments.  However, when we build without shared-memory (i.e.  without
threads) we effectively lower away TLS into a normal active data
segment.. so we were ending up with two active data segments: the merged
data, and the lowered TLS data.

To fix this problem we can instead avoid combining data segments at
all when running in shared memory mode (because in this case all
segment initialization is passive).  And then in non-shared memory
mode we know that TLS has been lowered and therefore we can can
and should combine all segments.

So with this new behavior we have two different modes:

1. With shared memory / mutli-threaded: Never combine data segments
   since it is not necessary.  (All data segments as passive already).

2. Wihout shared memory / single-threaded: Combine *all* data segments
   since we treat TLS as normal data.  (We end up with a single
   active data segment).

Differential Revision: https://reviews.llvm.org/D102937
This commit is contained in:
Sam Clegg 2021-05-21 09:58:21 -07:00
parent cc9c895d88
commit 8544b40b6e
6 changed files with 77 additions and 52 deletions

View File

@ -107,7 +107,7 @@
; PASSIVE-PIC: - Type: START
; PASSIVE-PIC-NEXT: StartFunction: 2
; PASSIVE-PIC-NEXT: - Type: DATACOUNT
; PASSIVE-PIC-NEXT: Count: 1
; PASSIVE-PIC-NEXT: Count: 2
; PASSIVE-PIC-NEXT: - Type: CODE
; PASSIVE-PIC-NEXT: Functions:
; PASSIVE-PIC-NEXT: - Index: 0
@ -121,17 +121,20 @@
; PASSIVE32-PIC-NEXT: - Type: I32
; PASSIVE64-PIC-NEXT: - Type: I64
; PASSIVE-PIC-NEXT: Count: 1
; PASSIVE32-PIC-NEXT: Body: 230141B4CE006A2100200041004101FE480200044020004101427FFE0102001A05410023016A410041B4CE00FC08000020004102FE1702002000417FFE0002001A0BFC09000B
; PASSIVE64-PIC-NEXT: Body: 230142B4CE006A2100200041004101FE480200044020004101427FFE0102001A05420023016A410041B4CE00FC08000020004102FE1702002000417FFE0002001A0BFC09000B
; PASSIVE32-PIC-NEXT: Body: 230141B4CE006A2100200041004101FE480200044020004101427FFE0102001A05410023016A4100410DFC080000411023016A41004114FC08010020004102FE1702002000417FFE0002001A0BFC0900FC09010B
; PASSIVE64-PIC-NEXT: Body: 230142B4CE006A2100200041004101FE480200044020004101427FFE0102001A05420023016A4100410DFC080000421023016A41004114FC08010020004102FE1702002000417FFE0002001A0BFC0900FC09010B
; PASSIVE-PIC-NEXT: - Index: 3
; PASSIVE-PIC-NEXT: Locals: []
; PASSIVE-PIC-NEXT: Body: 0B
; PASSIVE-PIC-NEXT: - Type: DATA
; PASSIVE-PIC-NEXT: Segments:
; PASSIVE-PIC-NEXT: - SectionOffset: 4
; PASSIVE-PIC-NEXT: - SectionOffset: 3
; PASSIVE-PIC-NEXT: InitFlags: 1
; PASSIVE-PIC: - Type: CUSTOM
; PASSIVE-PIC-NEXT: Content: 636F6E7374616E74000000002B
; PASSIVE-PIC-NEXT: - SectionOffset: 18
; PASSIVE-PIC-NEXT: InitFlags: 1
; PASSIVE-PIC-NEXT: Content: 68656C6C6F00676F6F646279650000002A000000
; PASSIVE-PIC-NEXT: - Type: CUSTOM
; PASSIVE-PIC-NEXT: Name: name
; PASSIVE-PIC-NEXT: FunctionNames:
; PASSIVE-PIC-NEXT: - Index: 0

View File

@ -1,5 +1,5 @@
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
# RUN: not wasm-ld %t.o -o %t.wasm 2>&1 | FileCheck %s
# RUN: not wasm-ld --shared-memory %t.o -o %t.wasm 2>&1 | FileCheck %s
.globl _start
_start:

View File

@ -9,7 +9,7 @@
get_tls1:
.functype get_tls1 () -> (i32)
global.get __tls_base
i32.const tls1
i32.const tls1@TLSREL
i32.add
end_function
@ -39,6 +39,9 @@ tls1:
# RUN: wasm-ld --no-gc-sections --no-entry -o %t.wasm %t.o
# RUN: obj2yaml %t.wasm | FileCheck %s
# RUN: wasm-ld --experimental-pic -shared -o %t.so %t.o
# RUN: obj2yaml %t.so | FileCheck %s --check-prefix=PIC
# CHECK: - Type: GLOBAL
# __stack_pointer
# CHECK-NEXT: Globals:
@ -73,3 +76,31 @@ tls1:
# CHECK-NEXT: Opcode: I32_CONST
# CHECK-NEXT: Value: 1028
# CHECK-NEXT: Content: 2A000000
# CHECK-NEXT: - Type: CUSTOM
# In PIC mode we expect TLS data and non-TLS data to be merged into
# a single segment which is initialized via the __memory_base import
# PIC: - Type: IMPORT
# PIC-NEXT: Imports:
# PIC-NEXT: - Module: env
# PIC-NEXT: Field: memory
# PIC-NEXT: Kind: MEMORY
# PIC-NEXT: Memory:
# PIC-NEXT: Minimum: 0x1
# PIC-NEXT: - Module: env
# PIC-NEXT: Field: __memory_base
# PIC-NEXT: Kind: GLOBAL
# PIC-NEXT: GlobalType: I32
# .tdata and .data are combined into single segment in PIC mode.
# PIC: - Type: DATA
# PIC-NEXT: Segments:
# PIC-NEXT: - SectionOffset: 6
# PIC-NEXT: InitFlags: 0
# PIC-NEXT: Offset:
# PIC-NEXT: Opcode: GLOBAL_GET
# PIC-NEXT: Index: 0
# PIC-NEXT: Content: 2B0000002A000000
# PIC-NEXT: - Type: CUSTOM

View File

@ -144,8 +144,8 @@ void DataSection::finalizeContents() {
});
#endif
assert((!config->isPic || activeCount <= 1) &&
"Currenly only a single data segment is supported in PIC mode");
assert((config->sharedMemory || !config->isPic || activeCount <= 1) &&
"output segments should have been combined by now");
writeUleb128(os, segmentCount, "data segment count");
os.flush();

View File

@ -115,12 +115,17 @@ void scanRelocations(InputChunk *chunk) {
addGOTEntry(sym);
break;
case R_WASM_MEMORY_ADDR_TLS_SLEB:
if (auto *D = dyn_cast<DefinedData>(sym)) {
if (!D->segment->outputSeg->isTLS()) {
error(toString(file) + ": relocation " +
relocTypeToString(reloc.Type) + " cannot be used against `" +
toString(*sym) +
"` in non-TLS section: " + D->segment->outputSeg->name);
// In single-threaded builds TLS is lowered away and TLS data can be
// merged with normal data and allowing TLS relocation in non-TLS
// segments.
if (config->sharedMemory) {
if (auto *D = dyn_cast<DefinedData>(sym)) {
if (!D->segment->outputSeg->isTLS()) {
error(toString(file) + ": relocation " +
relocTypeToString(reloc.Type) + " cannot be used against `" +
toString(*sym) +
"` in non-TLS section: " + D->segment->outputSeg->name);
}
}
}
break;

View File

@ -895,53 +895,37 @@ void Writer::createOutputSegments() {
}
void Writer::combineOutputSegments() {
// With PIC code we currently only support a single data segment since
// we only have a single __memory_base to use as our base address.
// This pass combines all non-TLS data segments into a single .data
// segment.
// With PIC code we currently only support a single active data segment since
// we only have a single __memory_base to use as our base address. This pass
// combines all data segments into a single .data segment.
// This restructions can be relaxed once we have extended constant
// expressions available:
// https://github.com/WebAssembly/extended-const
assert(config->isPic);
assert(config->isPic && !config->sharedMemory);
if (segments.size() <= 1)
return;
OutputSegment *combined = nullptr;
std::vector<OutputSegment *> new_segments;
OutputSegment *combined = make<OutputSegment>(".data");
combined->startVA = segments[0]->startVA;
for (OutputSegment *s : segments) {
if (s->isTLS()) {
new_segments.push_back(s);
} else {
if (!combined) {
LLVM_DEBUG(dbgs() << "created combined output segment: .data\n");
combined = make<OutputSegment>(".data");
combined->startVA = s->startVA;
if (config->sharedMemory)
combined->initFlags = WASM_DATA_SEGMENT_IS_PASSIVE;
}
bool first = true;
for (InputChunk *inSeg : s->inputSegments) {
if (first)
inSeg->alignment = std::max(inSeg->alignment, s->alignment);
first = false;
bool first = true;
for (InputChunk *inSeg : s->inputSegments) {
if (first)
inSeg->alignment = std::max(inSeg->alignment, s->alignment);
first = false;
#ifndef NDEBUG
uint64_t oldVA = inSeg->getVA();
uint64_t oldVA = inSeg->getVA();
#endif
combined->addInputSegment(inSeg);
combined->addInputSegment(inSeg);
#ifndef NDEBUG
uint64_t newVA = inSeg->getVA();
LLVM_DEBUG(dbgs() << "added input segment. name=" << inSeg->getName()
<< " oldVA=" << oldVA << " newVA=" << newVA << "\n");
assert(oldVA == newVA);
uint64_t newVA = inSeg->getVA();
LLVM_DEBUG(dbgs() << "added input segment. name=" << inSeg->getName()
<< " oldVA=" << oldVA << " newVA=" << newVA << "\n");
assert(oldVA == newVA);
#endif
}
}
}
if (combined) {
new_segments.push_back(combined);
segments = new_segments;
for (size_t i = 0; i < segments.size(); ++i)
segments[i]->index = i;
}
segments = {combined};
}
static void createFunction(DefinedFunction *func, StringRef bodyContent) {
@ -1440,7 +1424,9 @@ void Writer::run() {
}
}
if (config->isPic) {
if (config->isPic && !config->sharedMemory) {
// In shared memory mode all data segments are passive and initilized
// via __wasm_init_memory.
log("-- combineOutputSegments");
combineOutputSegments();
}