Skip to content

Commit

Permalink
[WebAssembly] Add new relocation type for TLS data symbols
Browse files Browse the repository at this point in the history
These relocations represent offsets from the __tls_base symbol.

Previously we were just using normal MEMORY_ADDR relocations and relying
on the linker to select a segment-offset rather and absolute value in
Symbol::getVirtualAddress().  Using an explicit relocation type allows
allow us to clearly distinguish absolute from relative relocations based
on the relocation information alone.

One place this is useful is being able to reject absolute relocation in
the PIC case, but still accept TLS relocations.

Differential Revision: https://reviews.llvm.org/D91276
  • Loading branch information
sbc100 committed Nov 13, 2020
1 parent 0fd6a04 commit a28a466
Show file tree
Hide file tree
Showing 23 changed files with 152 additions and 36 deletions.
24 changes: 24 additions & 0 deletions lld/test/wasm/relocation-bad-tls.s
@@ -0,0 +1,24 @@
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
# RUN: not wasm-ld %t.o -o out.wasm 2>&1 | FileCheck %s

.globl _start
_start:
.functype _start () -> ()
i32.const foo@TLSREL
i32.const bar@TLSREL
end_function

.section .data,"",@
.globl foo
foo:
.int32 0
.size foo, 4

.section .bss,"",@
.globl bar
bar:
.int32 0
.size bar, 4

# CHECK: relocation R_WASM_MEMORY_ADDR_TLS_SLEB cannot be used against `foo` in non-TLS section: .data
# CHECK: relocation R_WASM_MEMORY_ADDR_TLS_SLEB cannot be used against `bar` in non-TLS section: .bss
4 changes: 2 additions & 2 deletions lld/test/wasm/tls.s
Expand Up @@ -7,15 +7,15 @@
tls1_addr:
.functype tls1_addr () -> (i32)
global.get __tls_base
i32.const tls1
i32.const tls1@TLSREL
i32.add
end_function

.globl tls2_addr
tls2_addr:
.functype tls2_addr () -> (i32)
global.get __tls_base
i32.const tls2
i32.const tls2@TLSREL
i32.add
end_function

Expand Down
2 changes: 2 additions & 0 deletions lld/wasm/InputChunks.cpp
Expand Up @@ -79,6 +79,7 @@ void InputChunk::verifyRelocTargets() const {
case R_WASM_TABLE_INDEX_REL_SLEB:
case R_WASM_MEMORY_ADDR_SLEB:
case R_WASM_MEMORY_ADDR_REL_SLEB:
case R_WASM_MEMORY_ADDR_TLS_SLEB:
existingValue = static_cast<uint64_t>(decodeSLEB128(loc, &bytesRead));
break;
case R_WASM_TABLE_INDEX_SLEB64:
Expand Down Expand Up @@ -158,6 +159,7 @@ void InputChunk::writeTo(uint8_t *buf) const {
case R_WASM_TABLE_INDEX_REL_SLEB:
case R_WASM_MEMORY_ADDR_SLEB:
case R_WASM_MEMORY_ADDR_REL_SLEB:
case R_WASM_MEMORY_ADDR_TLS_SLEB:
encodeSLEB128(static_cast<int32_t>(value), loc, 5);
break;
case R_WASM_TABLE_INDEX_SLEB64:
Expand Down
22 changes: 19 additions & 3 deletions lld/wasm/InputFiles.cpp
Expand Up @@ -11,6 +11,7 @@
#include "InputChunks.h"
#include "InputEvent.h"
#include "InputGlobal.h"
#include "OutputSegment.h"
#include "SymbolTable.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h"
Expand Down Expand Up @@ -154,7 +155,8 @@ uint64_t ObjFile::calcExpectedValue(const WasmRelocation &reloc) const {
case R_WASM_MEMORY_ADDR_REL_SLEB:
case R_WASM_MEMORY_ADDR_REL_SLEB64:
case R_WASM_MEMORY_ADDR_I32:
case R_WASM_MEMORY_ADDR_I64: {
case R_WASM_MEMORY_ADDR_I64:
case R_WASM_MEMORY_ADDR_TLS_SLEB: {
const WasmSymbol &sym = wasmObj->syms()[reloc.Index];
if (sym.isUndefined())
return 0;
Expand Down Expand Up @@ -227,10 +229,24 @@ uint64_t ObjFile::calcNewValue(const WasmRelocation &reloc) const {
case R_WASM_MEMORY_ADDR_REL_SLEB:
case R_WASM_MEMORY_ADDR_REL_SLEB64:
case R_WASM_MEMORY_ADDR_I32:
case R_WASM_MEMORY_ADDR_I64:
case R_WASM_MEMORY_ADDR_I64: {
if (isa<UndefinedData>(sym) || sym->isUndefWeak())
return 0;
auto D = cast<DefinedData>(sym);
// Treat non-TLS relocation against symbols that live in the TLS segment
// like TLS relocations. This beaviour exists to support older object
// files created before we introduced TLS relocations.
// TODO(sbc): Remove this legacy behaviour one day. This will break
// backward compat with old object files built with `-fPIC`.
if (D->segment && D->segment->outputSeg->name == ".tdata")
return D->getOutputSegmentOffset() + reloc.Addend;
return D->getVirtualAddress() + reloc.Addend;
}
case R_WASM_MEMORY_ADDR_TLS_SLEB:
if (isa<UndefinedData>(sym) || sym->isUndefWeak())
return 0;
return cast<DefinedData>(sym)->getVirtualAddress() + reloc.Addend;
// TLS relocations are relative to the start of the TLS output segment
return cast<DefinedData>(sym)->getOutputSegmentOffset() + reloc.Addend;
case R_WASM_TYPE_INDEX_LEB:
return typeMap[reloc.Index];
case R_WASM_FUNCTION_INDEX_LEB:
Expand Down
13 changes: 10 additions & 3 deletions lld/wasm/OutputSections.cpp
Expand Up @@ -133,13 +133,20 @@ void DataSection::finalizeContents() {
std::count_if(segments.begin(), segments.end(),
[](OutputSegment *segment) { return !segment->isBss; });

#ifndef NDEBUG
unsigned activeCount = std::count_if(
segments.begin(), segments.end(), [](OutputSegment *segment) {
return (segment->initFlags & WASM_SEGMENT_IS_PASSIVE) == 0;
});
#endif

assert((!config->isPic || activeCount <= 1) &&
"Currenly only a single data segment is supported in PIC mode");

writeUleb128(os, segmentCount, "data segment count");
os.flush();
bodySize = dataSectionHeader.size();

assert((!config->isPic || segments.size() <= 1) &&
"Currenly only a single data segment is supported in PIC mode");

for (OutputSegment *segment : segments) {
if (segment->isBss)
continue;
Expand Down
11 changes: 11 additions & 0 deletions lld/wasm/Relocations.cpp
Expand Up @@ -9,6 +9,7 @@
#include "Relocations.h"

#include "InputChunks.h"
#include "OutputSegment.h"
#include "SyntheticSections.h"

using namespace llvm;
Expand Down Expand Up @@ -88,6 +89,16 @@ void scanRelocations(InputChunk *chunk) {
if (!isa<GlobalSymbol>(sym))
addGOTEntry(sym);
break;
case R_WASM_MEMORY_ADDR_TLS_SLEB:
if (auto *D = dyn_cast<DefinedData>(sym)) {
if (D->segment->outputSeg->name != ".tdata") {
error(toString(file) + ": relocation " +
relocTypeToString(reloc.Type) + " cannot be used against `" +
toString(*sym) +
"` in non-TLS section: " + D->segment->outputSeg->name);
}
}
break;
}

if (config->isPic) {
Expand Down
8 changes: 1 addition & 7 deletions lld/wasm/Symbols.cpp
Expand Up @@ -257,14 +257,8 @@ DefinedFunction::DefinedFunction(StringRef name, uint32_t flags, InputFile *f,

uint64_t DefinedData::getVirtualAddress() const {
LLVM_DEBUG(dbgs() << "getVirtualAddress: " << getName() << "\n");
if (segment) {
// For thread local data, the symbol location is relative to the start of
// the .tdata section, since they are used as offsets from __tls_base.
// Hence, we do not add in segment->outputSeg->startVA.
if (segment->outputSeg->name == ".tdata")
return segment->outputSegmentOffset + offset;
if (segment)
return segment->outputSeg->startVA + segment->outputSegmentOffset + offset;
}
return offset;
}

Expand Down
12 changes: 6 additions & 6 deletions lld/wasm/Writer.cpp
Expand Up @@ -749,15 +749,15 @@ void Writer::assignIndexes() {
}

static StringRef getOutputDataSegmentName(StringRef name) {
// 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.
if (config->isPic)
return ".data";
// We only support one thread-local segment, so we must merge the segments
// despite --no-merge-data-segments.
// We also need to merge .tbss into .tdata so they share the same offsets.
if (name.startswith(".tdata") || name.startswith(".tbss"))
return ".tdata";
// 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.
if (config->isPic)
return ".data";
if (!config->mergeDataSegments)
return name;
if (name.startswith(".text."))
Expand Down Expand Up @@ -1199,10 +1199,10 @@ void Writer::run() {

if (!config->relocatable) {
// Create linker synthesized functions
if (config->sharedMemory)
createInitMemoryFunction();
if (config->isPic)
createApplyRelocationsFunction();
else if (config->sharedMemory)
createInitMemoryFunction();
createCallCtorsFunction();

// Create export wrappers for commands if needed.
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/BinaryFormat/WasmRelocs.def
Expand Up @@ -23,3 +23,4 @@ WASM_RELOC(R_WASM_MEMORY_ADDR_REL_SLEB64, 17)
WASM_RELOC(R_WASM_TABLE_INDEX_SLEB64, 18)
WASM_RELOC(R_WASM_TABLE_INDEX_I64, 19)
WASM_RELOC(R_WASM_TABLE_NUMBER_LEB, 20)
WASM_RELOC(R_WASM_MEMORY_ADDR_TLS_SLEB, 21)
5 changes: 3 additions & 2 deletions llvm/include/llvm/MC/MCExpr.h
Expand Up @@ -321,8 +321,9 @@ class MCSymbolRefExpr : public MCExpr {
VK_Hexagon_IE_GOT,

VK_WASM_TYPEINDEX, // Reference to a symbol's type (signature)
VK_WASM_MBREL, // Memory address relative to memory base
VK_WASM_TBREL, // Table index relative to table bare
VK_WASM_TLSREL, // Memory address relative to __tls_base
VK_WASM_MBREL, // Memory address relative to __memory_base
VK_WASM_TBREL, // Table index relative to __table_base

VK_AMDGPU_GOTPCREL32_LO, // symbol@gotpcrel32@lo
VK_AMDGPU_GOTPCREL32_HI, // symbol@gotpcrel32@hi
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/BinaryFormat/Wasm.cpp
Expand Up @@ -48,6 +48,7 @@ bool llvm::wasm::relocTypeHasAddend(uint32_t Type) {
case R_WASM_MEMORY_ADDR_REL_SLEB64:
case R_WASM_MEMORY_ADDR_I32:
case R_WASM_MEMORY_ADDR_I64:
case R_WASM_MEMORY_ADDR_TLS_SLEB:
case R_WASM_FUNCTION_OFFSET_I32:
case R_WASM_SECTION_OFFSET_I32:
return true;
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/MC/MCExpr.cpp
Expand Up @@ -350,6 +350,7 @@ StringRef MCSymbolRefExpr::getVariantKindName(VariantKind Kind) {
case VK_Hexagon_IE_GOT: return "IEGOT";
case VK_WASM_TYPEINDEX: return "TYPEINDEX";
case VK_WASM_MBREL: return "MBREL";
case VK_WASM_TLSREL: return "TLSREL";
case VK_WASM_TBREL: return "TBREL";
case VK_AMDGPU_GOTPCREL32_LO: return "gotpcrel32@lo";
case VK_AMDGPU_GOTPCREL32_HI: return "gotpcrel32@hi";
Expand Down Expand Up @@ -490,6 +491,7 @@ MCSymbolRefExpr::getVariantKindForName(StringRef Name) {
.Case("typeindex", VK_WASM_TYPEINDEX)
.Case("tbrel", VK_WASM_TBREL)
.Case("mbrel", VK_WASM_MBREL)
.Case("tlsrel", VK_WASM_TLSREL)
.Case("gotpcrel32@lo", VK_AMDGPU_GOTPCREL32_LO)
.Case("gotpcrel32@hi", VK_AMDGPU_GOTPCREL32_HI)
.Case("rel32@lo", VK_AMDGPU_REL32_LO)
Expand Down
4 changes: 3 additions & 1 deletion llvm/lib/MC/WasmObjectWriter.cpp
Expand Up @@ -576,7 +576,8 @@ WasmObjectWriter::getProvisionalValue(const WasmRelocationEntry &RelEntry,
case wasm::R_WASM_MEMORY_ADDR_REL_SLEB:
case wasm::R_WASM_MEMORY_ADDR_REL_SLEB64:
case wasm::R_WASM_MEMORY_ADDR_I32:
case wasm::R_WASM_MEMORY_ADDR_I64: {
case wasm::R_WASM_MEMORY_ADDR_I64:
case wasm::R_WASM_MEMORY_ADDR_TLS_SLEB: {
// Provisional value is address of the global plus the offset
const MCSymbolWasm *Base =
cast<MCSymbolWasm>(Layout.getBaseSymbol(*RelEntry.Symbol));
Expand Down Expand Up @@ -685,6 +686,7 @@ void WasmObjectWriter::applyRelocations(
case wasm::R_WASM_TABLE_INDEX_REL_SLEB:
case wasm::R_WASM_MEMORY_ADDR_SLEB:
case wasm::R_WASM_MEMORY_ADDR_REL_SLEB:
case wasm::R_WASM_MEMORY_ADDR_TLS_SLEB:
writePatchableSLEB<5>(Stream, Value, Offset);
break;
case wasm::R_WASM_TABLE_INDEX_SLEB64:
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Object/WasmObjectFile.cpp
Expand Up @@ -855,6 +855,7 @@ Error WasmObjectFile::parseRelocSection(StringRef Name, ReadContext &Ctx) {
case wasm::R_WASM_MEMORY_ADDR_SLEB:
case wasm::R_WASM_MEMORY_ADDR_I32:
case wasm::R_WASM_MEMORY_ADDR_REL_SLEB:
case wasm::R_WASM_MEMORY_ADDR_TLS_SLEB:
if (!isValidDataSymbol(Reloc.Index))
return make_error<GenericBinaryError>("Bad relocation data index",
object_error::parse_failed);
Expand Down
Expand Up @@ -100,6 +100,11 @@ enum TOF {
// Only applicable to data symbols.
MO_MEMORY_BASE_REL,

// On a symbol operand this indicates that the immediate is the symbol
// address relative the __tls_base wasm global.
// Only applicable to data symbols.
MO_TLS_BASE_REL,

// On a symbol operand this indicates that the immediate is the symbol
// address relative the __table_base wasm global.
// Only applicable to function symbols.
Expand Down
Expand Up @@ -76,6 +76,8 @@ unsigned WebAssemblyWasmObjectWriter::getRelocType(const MCValue &Target,
case MCSymbolRefExpr::VK_WASM_TBREL:
assert(SymA.isFunction());
return wasm::R_WASM_TABLE_INDEX_REL_SLEB;
case MCSymbolRefExpr::VK_WASM_TLSREL:
return wasm::R_WASM_MEMORY_ADDR_TLS_SLEB;
case MCSymbolRefExpr::VK_WASM_MBREL:
assert(SymA.isData());
return is64Bit() ? wasm::R_WASM_MEMORY_ADDR_REL_SLEB64
Expand Down
3 changes: 2 additions & 1 deletion llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp
Expand Up @@ -149,7 +149,8 @@ void WebAssemblyDAGToDAGISel::Select(SDNode *Node) {

SDValue TLSBaseSym = CurDAG->getTargetExternalSymbol("__tls_base", PtrVT);
SDValue TLSOffsetSym = CurDAG->getTargetGlobalAddress(
GA->getGlobal(), DL, PtrVT, GA->getOffset(), 0);
GA->getGlobal(), DL, PtrVT, GA->getOffset(),
WebAssemblyII::MO_TLS_BASE_REL);

MachineSDNode *TLSBase =
CurDAG->getMachineNode(GlobalGetIns, DL, PtrVT, TLSBaseSym);
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h
Expand Up @@ -106,6 +106,7 @@ class WebAssemblyTargetLowering final : public TargetLowering {
SDValue LowerRETURNADDR(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerGlobalTLSAddress(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerExternalSymbol(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerBR_JT(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerJumpTable(SDValue Op, SelectionDAG &DAG) const;
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp
Expand Up @@ -139,6 +139,9 @@ MCOperand WebAssemblyMCInstLower::lowerSymbolOperand(const MachineOperand &MO,
case WebAssemblyII::MO_MEMORY_BASE_REL:
Kind = MCSymbolRefExpr::VK_WASM_MBREL;
break;
case WebAssemblyII::MO_TLS_BASE_REL:
Kind = MCSymbolRefExpr::VK_WASM_TLSREL;
break;
case WebAssemblyII::MO_TABLE_BASE_REL:
Kind = MCSymbolRefExpr::VK_WASM_TBREL;
break;
Expand Down
5 changes: 2 additions & 3 deletions llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
Expand Up @@ -277,10 +277,9 @@ class CoalesceFeaturesAndStripAtomics final : public ModulePass {
bool stripThreadLocals(Module &M) {
bool Stripped = false;
for (auto &GV : M.globals()) {
if (GV.getThreadLocalMode() !=
GlobalValue::ThreadLocalMode::NotThreadLocal) {
if (GV.isThreadLocal()) {
Stripped = true;
GV.setThreadLocalMode(GlobalValue::ThreadLocalMode::NotThreadLocal);
GV.setThreadLocal(false);
}
}
return Stripped;
Expand Down
8 changes: 4 additions & 4 deletions llvm/test/CodeGen/WebAssembly/tls-general-dynamic.ll
Expand Up @@ -12,7 +12,7 @@ target triple = "wasm32-unknown-unknown"
; CHECK-NEXT: .functype address_of_tls () -> (i32)
define i32 @address_of_tls() {
; TLS-DAG: global.get __tls_base
; TLS-DAG: i32.const tls
; TLS-DAG: i32.const tls@TLSREL
; TLS-NEXT: i32.add
; TLS-NEXT: return

Expand All @@ -25,7 +25,7 @@ define i32 @address_of_tls() {
; CHECK-NEXT: .functype ptr_to_tls () -> (i32)
define i32* @ptr_to_tls() {
; TLS-DAG: global.get __tls_base
; TLS-DAG: i32.const tls
; TLS-DAG: i32.const tls@TLSREL
; TLS-NEXT: i32.add
; TLS-NEXT: return

Expand All @@ -38,7 +38,7 @@ define i32* @ptr_to_tls() {
; CHECK-NEXT: .functype tls_load () -> (i32)
define i32 @tls_load() {
; TLS-DAG: global.get __tls_base
; TLS-DAG: i32.const tls
; TLS-DAG: i32.const tls@TLSREL
; TLS-NEXT: i32.add
; TLS-NEXT: i32.load 0
; TLS-NEXT: return
Expand All @@ -54,7 +54,7 @@ define i32 @tls_load() {
; CHECK-NEXT: .functype tls_store (i32) -> ()
define void @tls_store(i32 %x) {
; TLS-DAG: global.get __tls_base
; TLS-DAG: i32.const tls
; TLS-DAG: i32.const tls@TLSREL
; TLS-NEXT: i32.add
; TLS-NEXT: i32.store 0
; TLS-NEXT: return
Expand Down

0 comments on commit a28a466

Please sign in to comment.