# CHECK-NEXT: Mutable: false
# CHECK-NEXT: InitExpr:
# CHECK-NEXT: Opcode: I32_CONST
-# CHECK-NEXT: Value: 1
+# CHECK-NEXT: Value: 0
--- /dev/null
+# Test that linking without shared memory causes __tls_base to be
+# interlized
+
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
+
+.globaltype __tls_base, i32
+
+.globl get_tls1
+get_tls1:
+ .functype get_tls1 () -> (i32)
+ global.get __tls_base
+ i32.const tls1
+ i32.add
+ end_function
+
+.section .data.no_tls,"",@
+.globl no_tls
+.p2align 2
+no_tls:
+ .int32 42
+ .size no_tls, 4
+
+.section .tdata.tls1,"",@
+.globl tls1
+.p2align 2
+tls1:
+ .int32 43
+ .size tls1, 2
+
+.section .custom_section.target_features,"",@
+ .int8 2
+ .int8 43
+ .int8 7
+ .ascii "atomics"
+ .int8 43
+ .int8 11
+ .ascii "bulk-memory"
+
+# RUN: wasm-ld --no-gc-sections --no-entry -o %t.wasm %t.o
+# RUN: obj2yaml %t.wasm | FileCheck %s
+
+# CHECK: - Type: GLOBAL
+# __stack_pointer
+# CHECK-NEXT: Globals:
+# CHECK-NEXT: - Index: 0
+# CHECK-NEXT: Type: I32
+# CHECK-NEXT: Mutable: true
+# CHECK-NEXT: InitExpr:
+# CHECK-NEXT: Opcode: I32_CONST
+# CHECK-NEXT: Value: 66576
+# __tls_base
+# CHECK-NEXT: - Index: 1
+# CHECK-NEXT: Type: I32
+# CHECK-NEXT: Mutable: false
+# CHECK-NEXT: InitExpr:
+# CHECK-NEXT: Opcode: I32_CONST
+# CHECK-NEXT: Value: 1028
+# CHECK-NEXT: - Type: EXPORT
+
+# CHECK: - Type: DATA
+# .data
+# CHECK-NEXT: Segments:
+# CHECK-NEXT: - SectionOffset: 7
+# CHECK-NEXT: InitFlags: 0
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: Opcode: I32_CONST
+# CHECK-NEXT: Value: 1024
+# CHECK-NEXT: Content: 2A000000
+# .tdata
+# CHECK-NEXT: - SectionOffset: 17
+# CHECK-NEXT: InitFlags: 0
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: Opcode: I32_CONST
+# CHECK-NEXT: Value: 1028
+# CHECK-NEXT: Content: 2B000000
return sym;
}
-static GlobalSymbol *createGlobalVariable(StringRef name, bool isMutable,
- int value) {
+static InputGlobal *createGlobal(StringRef name, bool isMutable) {
llvm::wasm::WasmGlobal wasmGlobal;
if (config->is64.getValueOr(false)) {
wasmGlobal.Type = {WASM_TYPE_I64, isMutable};
- wasmGlobal.InitExpr.Value.Int64 = value;
wasmGlobal.InitExpr.Opcode = WASM_OPCODE_I64_CONST;
+ wasmGlobal.InitExpr.Value.Int64 = 0;
} else {
wasmGlobal.Type = {WASM_TYPE_I32, isMutable};
- wasmGlobal.InitExpr.Value.Int32 = value;
wasmGlobal.InitExpr.Opcode = WASM_OPCODE_I32_CONST;
+ wasmGlobal.InitExpr.Value.Int32 = 0;
}
wasmGlobal.SymbolName = name;
- return symtab->addSyntheticGlobal(name, WASM_SYMBOL_VISIBILITY_HIDDEN,
- make<InputGlobal>(wasmGlobal, nullptr));
+ return make<InputGlobal>(wasmGlobal, nullptr);
+}
+
+static GlobalSymbol *createGlobalVariable(StringRef name, bool isMutable) {
+ InputGlobal *g = createGlobal(name, isMutable);
+ return symtab->addSyntheticGlobal(name, WASM_SYMBOL_VISIBILITY_HIDDEN, g);
+}
+
+static GlobalSymbol *createOptionalGlobal(StringRef name, bool isMutable) {
+ InputGlobal *g = createGlobal(name, isMutable);
+ return symtab->addOptionalGlobalSymbols(name, WASM_SYMBOL_VISIBILITY_HIDDEN,
+ g);
}
// Create ABI-defined synthetic symbols
WasmSym::tableBase->markLive();
} else {
// For non-PIC code
- WasmSym::stackPointer = createGlobalVariable("__stack_pointer", true, 0);
+ WasmSym::stackPointer = createGlobalVariable("__stack_pointer", true);
WasmSym::stackPointer->markLive();
}
WasmSym::initMemoryFlag = symtab->addSyntheticDataSymbol(
"__wasm_init_memory_flag", WASM_SYMBOL_VISIBILITY_HIDDEN);
assert(WasmSym::initMemoryFlag);
- WasmSym::tlsBase = createGlobalVariable("__tls_base", true, 0);
- WasmSym::tlsSize = createGlobalVariable("__tls_size", false, 0);
- WasmSym::tlsAlign = createGlobalVariable("__tls_align", false, 1);
+ WasmSym::tlsBase = createGlobalVariable("__tls_base", true);
+ WasmSym::tlsSize = createGlobalVariable("__tls_size", false);
+ WasmSym::tlsAlign = createGlobalVariable("__tls_align", false);
WasmSym::initTLS = symtab->addSyntheticFunction(
"__wasm_init_tls", WASM_SYMBOL_VISIBILITY_HIDDEN,
make<SyntheticFunction>(
WasmSym::definedMemoryBase = symtab->addOptionalDataSymbol("__memory_base");
WasmSym::definedTableBase = symtab->addOptionalDataSymbol("__table_base");
}
+
+ // For non-shared memory programs we still need to define __tls_base since we
+ // allow object files built with TLS to be linked into single threaded
+ // programs, and such object files can contains refernced to this symbol.
+ //
+ // However, in this case __tls_base is immutable and points directly to the
+ // start of the `.tdata` static segment.
+ //
+ // __tls_size and __tls_align are not needed in this case since they are only
+ // needed for __wasm_init_tls (which we do not create in this case).
+ if (!config->sharedMemory)
+ WasmSym::tlsBase = createOptionalGlobal("__tls_base", false);
}
// Reconstructs command line arguments so that so that you can re-run
flags, nullptr, function);
}
-// Adds an optional, linker generated, data symbols. The symbol will only be
+// Adds an optional, linker generated, data symbol. The symbol will only be
// added if there is an undefine reference to it, or if it is explicitly
// exported via the --export flag. Otherwise we don't add the symbol and return
// nullptr.
nullptr, global);
}
+DefinedGlobal *SymbolTable::addOptionalGlobalSymbols(StringRef name,
+ uint32_t flags,
+ InputGlobal *global) {
+ LLVM_DEBUG(dbgs() << "addOptionalGlobalSymbols: " << name << " -> " << global
+ << "\n");
+ Symbol *s = find(name);
+ if (!s || s->isDefined())
+ return nullptr;
+ syntheticGlobals.emplace_back(global);
+ return replaceSymbol<DefinedGlobal>(s, name, flags, nullptr, global);
+}
+
static bool shouldReplace(const Symbol *existing, InputFile *newFile,
uint32_t newFlags) {
// If existing symbol is undefined, replace it.
DefinedFunction *addSyntheticFunction(StringRef name, uint32_t flags,
InputFunction *function);
DefinedData *addOptionalDataSymbol(StringRef name, uint64_t value = 0);
+ DefinedGlobal *addOptionalGlobalSymbols(StringRef name, uint32_t flags,
+ InputGlobal *global);
void handleSymbolVariants();
void handleWeakUndefines();
});
}
+static void setGlobalPtr(DefinedGlobal *g, uint64_t memoryPtr) {
+ if (config->is64.getValueOr(false)) {
+ assert(g->global->global.InitExpr.Opcode == WASM_OPCODE_I64_CONST);
+ g->global->global.InitExpr.Value.Int64 = memoryPtr;
+ } else {
+ assert(g->global->global.InitExpr.Opcode == WASM_OPCODE_I32_CONST);
+ g->global->global.InitExpr.Value.Int32 = memoryPtr;
+ }
+}
+
// Fix the memory layout of the output binary. This assigns memory offsets
// to each of the input data sections as well as the explicit stack region.
// The default memory layout is as follows, from low to high.
seg->startVA = memoryPtr;
log(formatv("mem: {0,-15} offset={1,-8} size={2,-8} align={3}", seg->name,
memoryPtr, seg->size, seg->alignment));
- memoryPtr += seg->size;
- if (WasmSym::tlsSize && seg->name == ".tdata") {
- auto *tlsSize = cast<DefinedGlobal>(WasmSym::tlsSize);
- assert(tlsSize->global->global.InitExpr.Opcode == WASM_OPCODE_I32_CONST);
- tlsSize->global->global.InitExpr.Value.Int32 = seg->size;
+ if (seg->name == ".tdata") {
+ if (config->sharedMemory) {
+ auto *tlsSize = cast<DefinedGlobal>(WasmSym::tlsSize);
+ setGlobalPtr(tlsSize, seg->size);
- auto *tlsAlign = cast<DefinedGlobal>(WasmSym::tlsAlign);
- assert(tlsAlign->global->global.InitExpr.Opcode == WASM_OPCODE_I32_CONST);
- tlsAlign->global->global.InitExpr.Value.Int32 = int64_t{1}
- << seg->alignment;
+ auto *tlsAlign = cast<DefinedGlobal>(WasmSym::tlsAlign);
+ setGlobalPtr(tlsAlign, int64_t{1} << seg->alignment);
+ } else {
+ auto *tlsBase = cast<DefinedGlobal>(WasmSym::tlsBase);
+ setGlobalPtr(tlsBase, memoryPtr);
+ }
}
+
+ memoryPtr += seg->size;
}
// Make space for the memory initialization flag
if (s == nullptr) {
LLVM_DEBUG(dbgs() << "new segment: " << name << "\n");
s = make<OutputSegment>(name);
- if (config->sharedMemory || name == ".tdata")
+ if (config->sharedMemory)
s->initFlags = WASM_SEGMENT_IS_PASSIVE;
// Exported memories are guaranteed to be zero-initialized, so no need
// to emit data segments for bss sections.