[WebAssembly] Fix PIC/GOT codegen for wasm64

__table_base is know 64-bit, since in LLVM it represents a function pointer offset
__table_base32 is a copy in wasm32 for use in elem init expr, since no truncation may be used there.
New reloc R_WASM_TABLE_INDEX_REL_SLEB64 added

Differential Revision: https://reviews.llvm.org/D101784
This commit is contained in:
Wouter van Oortmerssen 2021-04-22 16:54:58 -07:00
parent 5b6cae5524
commit 3a293cbf13
18 changed files with 529 additions and 60 deletions

View File

@ -1,12 +1,12 @@
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %p/Inputs/hello.s -o %t.hello32.o
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %s -o %t32.o
# RUN: wasm-ld -m wasm32 -no-gc-sections --export=__data_end --export=__heap_base --allow-undefined --no-entry -o %t32.wasm %t32.o %t.hello32.o
# RUN: obj2yaml %t32.wasm | FileCheck --check-prefixes CHECK,CHK32 %s
# RUN: obj2yaml %t32.wasm | FileCheck -DPTR=I32 %s
#
# RUN: llvm-mc -filetype=obj -triple=wasm64-unknown-unknown %p/Inputs/hello.s -o %t.hello64.o
# RUN: llvm-mc -filetype=obj -triple=wasm64-unknown-unknown %s -o %t64.o
# RUN: wasm-ld -m wasm64 -no-gc-sections --export=__data_end --export=__heap_base --allow-undefined --no-entry -o %t64.wasm %t64.o %t.hello64.o
# RUN: obj2yaml %t64.wasm | FileCheck --check-prefixes CHECK,CHK64 %s
# RUN: obj2yaml %t64.wasm | FileCheck --check-prefixes CHECK,CHK64 -DPTR=I64 %s
.section .data.foo,"",@
.globl foo
@ -59,24 +59,22 @@ local_struct_internal_ptr:
# CHECK-NEXT: - Type: GLOBAL
# CHECK-NEXT: Globals:
# CHECK-NEXT: - Index: 0
# CHK32-NEXT: Type: I32
# CHK64-NEXT: Type: I64
# CHECK-NEXT: Type: [[PTR]]
# CHECK-NEXT: Mutable: true
# CHECK-NEXT: InitExpr:
# CHK32-NEXT: Opcode: I32_CONST
# CHK64-NEXT: Opcode: I64_CONST
# CHECK-NEXT: Opcode: [[PTR]]_CONST
# CHECK-NEXT: Value: 66624
# CHECK-NEXT: - Index: 1
# CHECK-NEXT: Type: I32
# CHECK-NEXT: Type: [[PTR]]
# CHECK-NEXT: Mutable: false
# CHECK-NEXT: InitExpr:
# CHECK-NEXT: Opcode: I32_CONST
# CHECK-NEXT: Opcode: [[PTR]]_CONST
# CHECK-NEXT: Value: 1080
# CHECK-NEXT: - Index: 2
# CHECK-NEXT: Type: I32
# CHECK-NEXT: Type: [[PTR]]
# CHECK-NEXT: Mutable: false
# CHECK-NEXT: InitExpr:
# CHECK-NEXT: Opcode: I32_CONST
# CHECK-NEXT: Opcode: [[PTR]]_CONST
# CHECK-NEXT: Value: 66624
# CHECK: - Type: DATA
@ -84,15 +82,13 @@ local_struct_internal_ptr:
# CHECK-NEXT: - SectionOffset: 7
# CHECK-NEXT: InitFlags: 0
# CHECK-NEXT: Offset:
# CHK32-NEXT: Opcode: I32_CONST
# CHK64-NEXT: Opcode: I64_CONST
# CHECK-NEXT: Opcode: [[PTR]]_CONST
# CHECK-NEXT: Value: 1024
# CHECK-NEXT: Content: 68656C6C6F0A00
# CHECK-NEXT: - SectionOffset: 20
# CHECK-NEXT: InitFlags: 0
# CHECK-NEXT: Offset:
# CHK32-NEXT: Opcode: I32_CONST
# CHK64-NEXT: Opcode: I64_CONST
# CHECK-NEXT: Opcode: [[PTR]]_CONST
# CHECK-NEXT: Value: 1040

238
lld/test/wasm/shared64.s Normal file
View File

@ -0,0 +1,238 @@
# RUN: llvm-mc -filetype=obj -triple=wasm64-unknown-unknown -o %t.o %s
# RUN: wasm-ld -mwasm64 --experimental-pic -shared -o %t.wasm %t.o
# RUN: obj2yaml %t.wasm | FileCheck %s
.section .data.data,"",@
data:
.p2align 2
.int32 2
.size data, 4
.section .data.indirect_func_external,"",@
indirect_func_external:
.int32 func_external
.size indirect_func_external, 4
.section .data.indirect_func,"",@
indirect_func:
.int32 foo
.size indirect_func, 4
# Test data relocations
.section .data.data_addr,"",@
data_addr:
.int32 data
.size data_addr, 4
# .. against external symbols
.section .data.data_addr_external,"",@
data_addr_external:
.int32 data_external
.size data_addr_external, 4
# .. including addends
.section .data.extern_struct_internal_ptr,"",@
extern_struct_internal_ptr:
.int32 extern_struct + 4
.size extern_struct_internal_ptr, 4
# Test use of __stack_pointer
.section .text,"",@
foo:
# %ptr = alloca i32
# %0 = load i32, i32* @data, align 4
# %1 = load i32 ()*, i32 ()** @indirect_func, align 4
# call i32 %1()
# ret i32 %0
.functype foo () -> (i32)
.local i64, i32
global.get __stack_pointer
i64.const 16
i64.sub
local.tee 0
global.set __stack_pointer
global.get __memory_base
i64.const data@MBREL
i64.add
i32.load 0
local.set 1
global.get indirect_func@GOT
i32.load 0
call_indirect () -> (i32)
drop
local.get 0
i64.const 16
i64.add
global.set __stack_pointer
local.get 1
end_function
get_func_address:
.functype get_func_address () -> (i32)
global.get func_external@GOT
end_function
get_data_address:
.functype get_data_address () -> (i32)
global.get data_external@GOT
end_function
get_local_func_address:
# Verify that a function which is otherwise not address taken *is* added to
# the wasm table with referenced via R_WASM_TABLE_INDEX_REL_SLEB64
.functype get_local_func_address () -> (i64)
global.get __table_base
i64.const get_func_address@TBREL
i64.add
end_function
.globl foo
.globl data
.globl indirect_func
.globl indirect_func_external
.globl data_addr
.globl data_addr_external
.globl extern_struct_internal_ptr
.globl get_data_address
.globl get_func_address
.globl get_local_func_address
.hidden foo
.hidden data
.hidden get_data_address
.hidden get_func_address
# Without this linking will fail because we import __stack_pointer (a mutable
# global).
# TODO(sbc): We probably want a nicer way to specify target_features section
# in assembly.
.section .custom_section.target_features,"",@
.int8 1
.int8 43
.int8 15
.ascii "mutable-globals"
.functype func_external () -> ()
# Linker-synthesized globals
.globaltype __stack_pointer, i64
.globaltype __table_base, i64, immutable
.globaltype __memory_base, i64, immutable
# check for dylink section at start
# CHECK: Sections:
# CHECK-NEXT: - Type: CUSTOM
# CHECK-NEXT: Name: dylink
# CHECK-NEXT: MemorySize: 24
# CHECK-NEXT: MemoryAlignment: 2
# CHECK-NEXT: TableSize: 2
# CHECK-NEXT: TableAlignment: 0
# CHECK-NEXT: Needed: []
# CHECK-NEXT: - Type: TYPE
# check for import of __table_base and __memory_base globals
# CHECK: - Type: IMPORT
# CHECK-NEXT: Imports:
# CHECK-NEXT: - Module: env
# CHECK-NEXT: Field: memory
# CHECK-NEXT: Kind: MEMORY
# CHECK-NEXT: Memory:
# CHECK-NEXT: Flags: [ IS_64 ]
# CHECK-NEXT: Minimum: 0x1
# CHECK-NEXT: - Module: env
# CHECK-NEXT: Field: __indirect_function_table
# CHECK-NEXT: Kind: TABLE
# CHECK-NEXT: Table:
# CHECK-NEXT: Index: 0
# CHECK-NEXT: ElemType: FUNCREF
# CHECK-NEXT: Limits:
# CHECK-NEXT: Minimum: 0x2
# CHECK-NEXT: - Module: env
# CHECK-NEXT: Field: __stack_pointer
# CHECK-NEXT: Kind: GLOBAL
# CHECK-NEXT: GlobalType: I64
# CHECK-NEXT: GlobalMutable: true
# CHECK-NEXT: - Module: env
# CHECK-NEXT: Field: __memory_base
# CHECK-NEXT: Kind: GLOBAL
# CHECK-NEXT: GlobalType: I64
# CHECK-NEXT: GlobalMutable: false
# CHECK-NEXT: - Module: env
# CHECK-NEXT: Field: __table_base
# CHECK-NEXT: Kind: GLOBAL
# CHECK-NEXT: GlobalType: I64
# CHECK-NEXT: GlobalMutable: false
# CHECK-NEXT: - Module: env
# CHECK-NEXT: Field: __table_base32
# CHECK-NEXT: Kind: GLOBAL
# CHECK-NEXT: GlobalType: I32
# CHECK-NEXT: GlobalMutable: false
# CHECK-NEXT: - Module: env
# CHECK-NEXT: Field: func_external
# CHECK-NEXT: Kind: FUNCTION
# CHECK-NEXT: SigIndex: 1
# CHECK-NEXT: - Module: GOT.mem
# CHECK-NEXT: Field: indirect_func
# CHECK-NEXT: Kind: GLOBAL
# CHECK-NEXT: GlobalType: I32
# CHECK-NEXT: GlobalMutable: true
# CHECK-NEXT: - Module: GOT.func
# CHECK-NEXT: Field: func_external
# CHECK-NEXT: Kind: GLOBAL
# CHECK-NEXT: GlobalType: I32
# CHECK-NEXT: GlobalMutable: true
# CHECK-NEXT: - Module: GOT.mem
# CHECK-NEXT: Field: data_external
# CHECK-NEXT: Kind: GLOBAL
# CHECK-NEXT: GlobalType: I32
# CHECK-NEXT: GlobalMutable: true
# CHECK-NEXT: - Module: GOT.mem
# CHECK-NEXT: Field: extern_struct
# CHECK-NEXT: Kind: GLOBAL
# CHECK-NEXT: GlobalType: I32
# CHECK-NEXT: GlobalMutable: true
# CHECK-NEXT: - Type: FUNCTION
# CHECK: - Type: EXPORT
# CHECK-NEXT: Exports:
# CHECK-NEXT: - Name: __wasm_call_ctors
# CHECK-NEXT: Kind: FUNCTION
# CHECK-NEXT: Index: 1
# check for elem segment initialized with __table_base global as offset
# CHECK: - Type: ELEM
# CHECK-NEXT: Segments:
# CHECK-NEXT: - Offset:
# CHECK-NEXT: Opcode: GLOBAL_GET
# CHECK-NEXT: Index: 3
# CHECK-NEXT: Functions: [ 4, 3 ]
# check the generated code in __wasm_call_ctors and __wasm_apply_data_relocs functions
# TODO(sbc): Disassemble and verify instructions.
# CHECK: - Type: CODE
# CHECK-NEXT: Functions:
# CHECK-NEXT: - Index: 1
# CHECK-NEXT: Locals: []
# CHECK-NEXT: Body: 10020B
# CHECK-NEXT: - Index: 2
# CHECK-NEXT: Locals: []
# CHECK-NEXT: Body: 230142047C2305360200230142087C230241016A3602002301420C7C230141006A360200230142107C2306360200230142147C230741046A3602000B
# check the data segment initialized with __memory_base global as offset
# CHECK: - Type: DATA
# CHECK-NEXT: Segments:
# CHECK-NEXT: - SectionOffset: 6
# CHECK-NEXT: InitFlags: 0
# CHECK-NEXT: Offset:
# CHECK-NEXT: Opcode: GLOBAL_GET
# CHECK-NEXT: Index: 1
# CHECK-NEXT: Content: '020000000000000001000000000000000000000000000000'

View File

@ -584,15 +584,9 @@ createUndefinedGlobal(StringRef name, llvm::wasm::WasmGlobalType *type) {
static InputGlobal *createGlobal(StringRef name, bool isMutable) {
llvm::wasm::WasmGlobal wasmGlobal;
if (config->is64.getValueOr(false)) {
wasmGlobal.Type = {WASM_TYPE_I64, isMutable};
wasmGlobal.InitExpr.Opcode = WASM_OPCODE_I64_CONST;
wasmGlobal.InitExpr.Value.Int64 = 0;
} else {
wasmGlobal.Type = {WASM_TYPE_I32, isMutable};
wasmGlobal.InitExpr.Opcode = WASM_OPCODE_I32_CONST;
wasmGlobal.InitExpr.Value.Int32 = 0;
}
bool is64 = config->is64.getValueOr(false);
wasmGlobal.Type = {uint8_t(is64 ? WASM_TYPE_I64 : WASM_TYPE_I32), isMutable};
wasmGlobal.InitExpr = intConst(0, is64);
wasmGlobal.SymbolName = name;
return make<InputGlobal>(wasmGlobal, nullptr);
}
@ -635,12 +629,19 @@ static void createSyntheticSymbols() {
// which to load our static data and function table.
// See:
// https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md
WasmSym::memoryBase = createUndefinedGlobal(
"__memory_base",
config->is64.getValueOr(false) ? &globalTypeI64 : &globalTypeI32);
WasmSym::tableBase = createUndefinedGlobal("__table_base", &globalTypeI32);
bool is64 = config->is64.getValueOr(false);
auto *globalType = is64 ? &globalTypeI64 : &globalTypeI32;
WasmSym::memoryBase = createUndefinedGlobal("__memory_base", globalType);
WasmSym::tableBase = createUndefinedGlobal("__table_base", globalType);
WasmSym::memoryBase->markLive();
WasmSym::tableBase->markLive();
if (is64) {
WasmSym::tableBase32 =
createUndefinedGlobal("__table_base32", &globalTypeI32);
WasmSym::tableBase32->markLive();
} else {
WasmSym::tableBase32 = nullptr;
}
} else {
// For non-PIC code
WasmSym::stackPointer = createGlobalVariable("__stack_pointer", true);
@ -673,6 +674,9 @@ static void createOptionalSymbols() {
WasmSym::heapBase = symtab->addOptionalDataSymbol("__heap_base");
WasmSym::definedMemoryBase = symtab->addOptionalDataSymbol("__memory_base");
WasmSym::definedTableBase = symtab->addOptionalDataSymbol("__table_base");
if (config->is64.getValueOr(false))
WasmSym::definedTableBase32 =
symtab->addOptionalDataSymbol("__table_base32");
}
// For non-shared memory programs we still need to define __tls_base since we

View File

@ -134,6 +134,7 @@ void InputChunk::relocate(uint8_t *buf) const {
encodeSLEB128(static_cast<int32_t>(value), loc, 5);
break;
case R_WASM_TABLE_INDEX_SLEB64:
case R_WASM_TABLE_INDEX_REL_SLEB64:
case R_WASM_MEMORY_ADDR_SLEB64:
case R_WASM_MEMORY_ADDR_REL_SLEB64:
encodeSLEB128(static_cast<int64_t>(value), loc, 10);

View File

@ -42,6 +42,18 @@ protected:
llvm::Optional<uint32_t> assignedIndex;
};
inline WasmInitExpr intConst(uint64_t value, bool is64) {
WasmInitExpr ie;
if (is64) {
ie.Opcode = llvm::wasm::WASM_OPCODE_I64_CONST;
ie.Value.Int64 = static_cast<int64_t>(value);
} else {
ie.Opcode = llvm::wasm::WASM_OPCODE_I32_CONST;
ie.Value.Int32 = static_cast<int32_t>(value);
}
return ie;
}
class InputGlobal : public InputElement {
public:
InputGlobal(const WasmGlobal &g, ObjFile *f)
@ -51,13 +63,7 @@ public:
const WasmInitExpr &getInitExpr() const { return initExpr; }
void setPointerValue(uint64_t value) {
if (config->is64.getValueOr(false)) {
assert(initExpr.Opcode == llvm::wasm::WASM_OPCODE_I64_CONST);
initExpr.Value.Int64 = value;
} else {
assert(initExpr.Opcode == llvm::wasm::WASM_OPCODE_I32_CONST);
initExpr.Value.Int32 = value;
}
initExpr = intConst(value, config->is64.getValueOr(false));
}
private:

View File

@ -156,14 +156,15 @@ uint64_t ObjFile::calcNewValue(const WasmRelocation &reloc, uint64_t tombstone,
case R_WASM_TABLE_INDEX_I64:
case R_WASM_TABLE_INDEX_SLEB:
case R_WASM_TABLE_INDEX_SLEB64:
case R_WASM_TABLE_INDEX_REL_SLEB: {
case R_WASM_TABLE_INDEX_REL_SLEB:
case R_WASM_TABLE_INDEX_REL_SLEB64: {
if (!getFunctionSymbol(reloc.Index)->hasTableIndex())
return 0;
uint32_t index = getFunctionSymbol(reloc.Index)->getTableIndex();
if (reloc.Type == R_WASM_TABLE_INDEX_REL_SLEB)
if (reloc.Type == R_WASM_TABLE_INDEX_REL_SLEB ||
reloc.Type == R_WASM_TABLE_INDEX_REL_SLEB64)
index -= config->tableBase;
return index;
}
case R_WASM_MEMORY_ADDR_LEB:
case R_WASM_MEMORY_ADDR_LEB64:

View File

@ -8,6 +8,7 @@
#include "OutputSections.h"
#include "InputChunks.h"
#include "InputElement.h"
#include "InputFiles.h"
#include "OutputSegment.h"
#include "WriterUtils.h"
@ -162,12 +163,8 @@ void DataSection::finalizeContents() {
if (config->isPic) {
initExpr.Opcode = WASM_OPCODE_GLOBAL_GET;
initExpr.Value.Global = WasmSym::memoryBase->getGlobalIndex();
} else if (config->is64.getValueOr(false)) {
initExpr.Opcode = WASM_OPCODE_I64_CONST;
initExpr.Value.Int64 = static_cast<int64_t>(segment->startVA);
} else {
initExpr.Opcode = WASM_OPCODE_I32_CONST;
initExpr.Value.Int32 = static_cast<int32_t>(segment->startVA);
initExpr = intConst(segment->startVA, config->is64.getValueOr(false));
}
writeInitExpr(os, initExpr);
}

View File

@ -104,6 +104,7 @@ void scanRelocations(InputChunk *chunk) {
case R_WASM_TABLE_INDEX_SLEB:
case R_WASM_TABLE_INDEX_SLEB64:
case R_WASM_TABLE_INDEX_REL_SLEB:
case R_WASM_TABLE_INDEX_REL_SLEB64:
if (requiresGOTAccess(sym))
break;
out.elemSec->addEntry(cast<FunctionSymbol>(sym));

View File

@ -87,6 +87,8 @@ GlobalSymbol *WasmSym::tlsSize;
GlobalSymbol *WasmSym::tlsAlign;
UndefinedGlobal *WasmSym::tableBase;
DefinedData *WasmSym::definedTableBase;
UndefinedGlobal *WasmSym::tableBase32;
DefinedData *WasmSym::definedTableBase32;
UndefinedGlobal *WasmSym::memoryBase;
DefinedData *WasmSym::definedMemoryBase;
TableSymbol *WasmSym::indirectFunctionTable;

View File

@ -564,6 +564,11 @@ struct WasmSym {
// Used in PIC code for offset of indirect function table
static UndefinedGlobal *tableBase;
static DefinedData *definedTableBase;
// 32-bit copy in wasm64 to work around init expr limitations.
// These can potentially be removed again once we have
// https://github.com/WebAssembly/extended-const
static UndefinedGlobal *tableBase32;
static DefinedData *definedTableBase32;
// __memory_base
// Used in PIC code for offset of global data

View File

@ -362,33 +362,30 @@ void GlobalSection::writeBody() {
writeGlobalType(os, g->getType());
writeInitExpr(os, g->getInitExpr());
}
// TODO(wvo): when do these need I64_CONST?
bool is64 = config->is64.getValueOr(false);
uint8_t itype = is64 ? WASM_TYPE_I64 : WASM_TYPE_I32;
for (const Symbol *sym : internalGotSymbols) {
// In the case of dynamic linking, internal GOT entries
// need to be mutable since they get updated to the correct
// runtime value during `__wasm_apply_global_relocs`.
bool mutable_ = config->isPic & !sym->isStub;
WasmGlobalType type{WASM_TYPE_I32, mutable_};
WasmGlobalType type{itype, mutable_};
WasmInitExpr initExpr;
initExpr.Opcode = WASM_OPCODE_I32_CONST;
if (auto *d = dyn_cast<DefinedData>(sym))
initExpr.Value.Int32 = d->getVA();
initExpr = intConst(d->getVA(), is64);
else if (auto *f = dyn_cast<FunctionSymbol>(sym))
initExpr.Value.Int32 = f->isStub ? 0 : f->getTableIndex();
initExpr = intConst(f->isStub ? 0 : f->getTableIndex(), is64);
else {
assert(isa<UndefinedData>(sym));
initExpr.Value.Int32 = 0;
initExpr = intConst(0, is64);
}
writeGlobalType(os, type);
writeInitExpr(os, initExpr);
}
for (const DefinedData *sym : dataAddressGlobals) {
WasmGlobalType type{WASM_TYPE_I32, false};
WasmInitExpr initExpr;
initExpr.Opcode = WASM_OPCODE_I32_CONST;
initExpr.Value.Int32 = sym->getVA();
WasmGlobalType type{itype, false};
writeGlobalType(os, type);
writeInitExpr(os, initExpr);
writeInitExpr(os, intConst(sym->getVA(), is64));
}
}
@ -443,7 +440,10 @@ void ElemSection::writeBody() {
WasmInitExpr initExpr;
if (config->isPic) {
initExpr.Opcode = WASM_OPCODE_GLOBAL_GET;
initExpr.Value.Global = WasmSym::tableBase->getGlobalIndex();
initExpr.Value.Global =
(config->is64.getValueOr(false) ? WasmSym::tableBase32
: WasmSym::tableBase)
->getGlobalIndex();
} else {
initExpr.Opcode = WASM_OPCODE_I32_CONST;
initExpr.Value.Int32 = config->tableBase;

View File

@ -1403,6 +1403,8 @@ void Writer::run() {
config->tableBase = 1;
if (WasmSym::definedTableBase)
WasmSym::definedTableBase->setVA(config->tableBase);
if (WasmSym::definedTableBase32)
WasmSym::definedTableBase32->setVA(config->tableBase);
}
log("-- createOutputSegments");

View File

@ -26,3 +26,4 @@ WASM_RELOC(R_WASM_TABLE_NUMBER_LEB, 20)
WASM_RELOC(R_WASM_MEMORY_ADDR_TLS_SLEB, 21)
WASM_RELOC(R_WASM_FUNCTION_OFFSET_I64, 22)
WASM_RELOC(R_WASM_MEMORY_ADDR_LOCREL_I32, 23)
WASM_RELOC(R_WASM_TABLE_INDEX_REL_SLEB64, 24)

View File

@ -528,6 +528,7 @@ void WasmObjectWriter::recordRelocation(MCAssembler &Asm,
}
if (Type == wasm::R_WASM_TABLE_INDEX_REL_SLEB ||
Type == wasm::R_WASM_TABLE_INDEX_REL_SLEB64 ||
Type == wasm::R_WASM_TABLE_INDEX_SLEB ||
Type == wasm::R_WASM_TABLE_INDEX_SLEB64 ||
Type == wasm::R_WASM_TABLE_INDEX_I32 ||
@ -590,6 +591,7 @@ WasmObjectWriter::getProvisionalValue(const WasmRelocationEntry &RelEntry,
switch (RelEntry.Type) {
case wasm::R_WASM_TABLE_INDEX_REL_SLEB:
case wasm::R_WASM_TABLE_INDEX_REL_SLEB64:
case wasm::R_WASM_TABLE_INDEX_SLEB:
case wasm::R_WASM_TABLE_INDEX_SLEB64:
case wasm::R_WASM_TABLE_INDEX_I32:
@ -598,7 +600,8 @@ WasmObjectWriter::getProvisionalValue(const WasmRelocationEntry &RelEntry,
const MCSymbolWasm *Base =
cast<MCSymbolWasm>(Layout.getBaseSymbol(*RelEntry.Symbol));
assert(Base->isFunction());
if (RelEntry.Type == wasm::R_WASM_TABLE_INDEX_REL_SLEB)
if (RelEntry.Type == wasm::R_WASM_TABLE_INDEX_REL_SLEB ||
RelEntry.Type == wasm::R_WASM_TABLE_INDEX_REL_SLEB64)
return TableIndices[Base] - InitialTableOffset;
else
return TableIndices[Base];
@ -742,6 +745,7 @@ void WasmObjectWriter::applyRelocations(
writePatchableSLEB<5>(Stream, Value, Offset);
break;
case wasm::R_WASM_TABLE_INDEX_SLEB64:
case wasm::R_WASM_TABLE_INDEX_REL_SLEB64:
case wasm::R_WASM_MEMORY_ADDR_SLEB64:
case wasm::R_WASM_MEMORY_ADDR_REL_SLEB64:
writePatchableSLEB<10>(Stream, Value, Offset);
@ -1757,7 +1761,8 @@ uint64_t WasmObjectWriter::writeOneObject(MCAssembler &Asm,
Rel.Type != wasm::R_WASM_TABLE_INDEX_I64 &&
Rel.Type != wasm::R_WASM_TABLE_INDEX_SLEB &&
Rel.Type != wasm::R_WASM_TABLE_INDEX_SLEB64 &&
Rel.Type != wasm::R_WASM_TABLE_INDEX_REL_SLEB)
Rel.Type != wasm::R_WASM_TABLE_INDEX_REL_SLEB &&
Rel.Type != wasm::R_WASM_TABLE_INDEX_REL_SLEB64)
return;
assert(Rel.Symbol->isFunction());
const MCSymbolWasm *Base =

View File

@ -870,6 +870,7 @@ Error WasmObjectFile::parseRelocSection(StringRef Name, ReadContext &Ctx) {
case wasm::R_WASM_TABLE_INDEX_I32:
case wasm::R_WASM_TABLE_INDEX_I64:
case wasm::R_WASM_TABLE_INDEX_REL_SLEB:
case wasm::R_WASM_TABLE_INDEX_REL_SLEB64:
if (!isValidFunctionSymbol(Reloc.Index))
return make_error<GenericBinaryError>(
"invalid relocation function index", object_error::parse_failed);

View File

@ -76,7 +76,8 @@ unsigned WebAssemblyWasmObjectWriter::getRelocType(const MCValue &Target,
return wasm::R_WASM_GLOBAL_INDEX_LEB;
case MCSymbolRefExpr::VK_WASM_TBREL:
assert(SymA.isFunction());
return wasm::R_WASM_TABLE_INDEX_REL_SLEB;
return is64Bit() ? wasm::R_WASM_TABLE_INDEX_REL_SLEB64
: 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:

View File

@ -107,8 +107,7 @@ MCSymbol *WebAssemblyMCInstLower::GetExternalSymbolSymbol(
strcmp(Name, "__stack_pointer") == 0 || strcmp(Name, "__tls_base") == 0;
WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL);
WasmSym->setGlobalType(wasm::WasmGlobalType{
uint8_t(Subtarget.hasAddr64() && strcmp(Name, "__table_base") != 0
? wasm::WASM_TYPE_I64
uint8_t(Subtarget.hasAddr64() ? wasm::WASM_TYPE_I64
: wasm::WASM_TYPE_I32),
Mutable});
return WasmSym;

View File

@ -0,0 +1,209 @@
# RUN: llvm-mc -triple=wasm64-unknown-unknown -filetype=obj < %s | obj2yaml | FileCheck %s
# RUN: llvm-mc -triple=wasm64-unknown-unknown -mattr=+reference-types -filetype=obj < %s | obj2yaml | FileCheck --check-prefix=REF %s
# Verify that @GOT relocation entries result in R_WASM_GLOBAL_INDEX_LEB against
# against the corrsponding function or data symbol and that the corresponding
# data symbols are imported as a wasm globals.
load_default_data:
.functype load_default_data () -> (i32)
global.get default_data@GOT
i32.load 0
end_function
load_default_func:
.functype load_default_func () -> (i32)
global.get default_func@GOT
i32.load 0
end_function
load_hidden_data:
.functype load_hidden_data () -> (i64)
global.get __memory_base
i64.const .L.hidden_data@MBREL
i64.add
end_function
load_hidden_func:
.functype load_hidden_func () -> (i64)
global.get __table_base
i64.const hidden_func@TBREL
i64.add
end_function
hidden_func:
.functype hidden_func () -> (i32)
i32.const 0
end_function
.section .rodata.hidden_data,"",@
.L.hidden_data:
.int8 100
.size .L.hidden_data, 1
#.hidden hidden_func
#.hidden hidden_data
.size default_data, 4
.functype default_func () -> (i32)
# CHECK: --- !WASM
# CHECK-NEXT: FileHeader:
# CHECK-NEXT: Version: 0x1
# CHECK-NEXT: Sections:
# CHECK-NEXT: - Type: TYPE
# CHECK-NEXT: Signatures:
# CHECK-NEXT: - Index: 0
# CHECK-NEXT: ParamTypes: []
# CHECK-NEXT: ReturnTypes:
# CHECK-NEXT: - I32
# CHECK-NEXT: - Index: 1
# CHECK-NEXT: ParamTypes: []
# CHECK-NEXT: ReturnTypes:
# CHECK-NEXT: - I64
# CHECK-NEXT: - Type: IMPORT
# CHECK-NEXT: Imports:
# CHECK-NEXT: - Module: env
# CHECK-NEXT: Field: __linear_memory
# CHECK-NEXT: Kind: MEMORY
# CHECK-NEXT: Memory:
# CHECK-NEXT: Flags: [ IS_64 ]
# CHECK-NEXT: Minimum: 0x1
# CHECK-NEXT: - Module: env
# CHECK-NEXT: Field: default_func
# CHECK-NEXT: Kind: FUNCTION
# CHECK-NEXT: SigIndex: 0
# CHECK-NEXT: - Module: env
# CHECK-NEXT: Field: __indirect_function_table
# CHECK-NEXT: Kind: TABLE
# CHECK-NEXT: Table:
# CHECK-NEXT: Index: 0
# CHECK-NEXT: ElemType: FUNCREF
# CHECK-NEXT: Limits:
# CHECK-NEXT: Minimum: 0x1
# CHECK-NEXT: - Module: GOT.mem
# CHECK-NEXT: Field: default_data
# CHECK-NEXT: Kind: GLOBAL
# CHECK-NEXT: GlobalType: I32
# CHECK-NEXT: GlobalMutable: true
# CHECK-NEXT: - Module: GOT.func
# CHECK-NEXT: Field: default_func
# CHECK-NEXT: Kind: GLOBAL
# CHECK-NEXT: GlobalType: I32
# CHECK-NEXT: GlobalMutable: true
# CHECK-NEXT: - Type: FUNCTION
# CHECK-NEXT: FunctionTypes: [ 0, 0, 1, 1, 0 ]
# CHECK-NEXT: - Type: ELEM
# CHECK-NEXT: Segments:
# CHECK-NEXT: Offset:
# CHECK-NEXT: Opcode: I32_CONST
# CHECK-NEXT: Value: 1
# CHECK-NEXT: Functions: [ 5 ]
# CHECK-NEXT: - Type: DATACOUNT
# CHECK-NEXT: Count: 1
# CHECK-NEXT: - Type: CODE
# CHECK-NEXT: Relocations:
# CHECK-NEXT: - Type: R_WASM_GLOBAL_INDEX_LEB
# CHECK-NEXT: Index: 1
# CHECK-NEXT: Offset: 0x4
# CHECK-NEXT: - Type: R_WASM_GLOBAL_INDEX_LEB
# CHECK-NEXT: Index: 3
# CHECK-NEXT: Offset: 0x10
# CHECK-NEXT: - Type: R_WASM_MEMORY_ADDR_LEB
# CHECK-NEXT: Index: 5
# CHECK-NEXT: Offset: 0x1C
# CHECK-NEXT: - Type: R_WASM_MEMORY_ADDR_REL_SLEB
# CHECK-NEXT: Index: 6
# CHECK-NEXT: Offset: 0x22
# CHECK-NEXT: - Type: R_WASM_MEMORY_ADDR_LEB
# CHECK-NEXT: Index: 8
# CHECK-NEXT: Offset: 0x31
# CHECK-NEXT: - Type: R_WASM_TABLE_INDEX_REL_SLEB
# CHECK-NEXT: Index: 9
# CHECK-NEXT: Offset: 0x37
# CHECK-NEXT: Functions:
# CHECK-NEXT: - Index: 1
# CHECK-NEXT: Locals: []
# CHECK-NEXT: Body: 2380808080002802000B
# CHECK-NEXT: - Index: 2
# CHECK-NEXT: Locals: []
# CHECK-NEXT: Body: 2381808080002802000B
# CHECK-NEXT: - Index: 3
# CHECK-NEXT: Locals: []
# CHECK-NEXT: Body: 23808080800042808080808080808080007C0B
# CHECK-NEXT: - Index: 4
# CHECK-NEXT: Locals: []
# CHECK-NEXT: Body: 23808080800042808080808080808080007C0B
# CHECK-NEXT: - Index: 5
# CHECK-NEXT: Locals: []
# CHECK-NEXT: Body: 41000B
# CHECK-NEXT: - Type: DATA
# CHECK-NEXT: Segments:
# CHECK-NEXT: - SectionOffset: 6
# CHECK-NEXT: InitFlags: 0
# CHECK-NEXT: Offset:
# CHECK-NEXT: Opcode: I64_CONST
# CHECK-NEXT: Value: 0
# CHECK-NEXT: Content: '64'
# CHECK-NEXT: - Type: CUSTOM
# CHECK-NEXT: Name: linking
# CHECK-NEXT: Version: 2
# CHECK-NEXT: SymbolTable:
# CHECK-NEXT: - Index: 0
# CHECK-NEXT: Kind: FUNCTION
# CHECK-NEXT: Name: load_default_data
# CHECK-NEXT: Flags: [ BINDING_LOCAL ]
# CHECK-NEXT: Function: 1
# CHECK-NEXT: - Index: 1
# CHECK-NEXT: Kind: DATA
# CHECK-NEXT: Name: default_data
# CHECK-NEXT: Flags: [ UNDEFINED ]
# CHECK-NEXT: - Index: 2
# CHECK-NEXT: Kind: FUNCTION
# CHECK-NEXT: Name: load_default_func
# CHECK-NEXT: Flags: [ BINDING_LOCAL ]
# CHECK-NEXT: Function: 2
# CHECK-NEXT: - Index: 3
# CHECK-NEXT: Kind: FUNCTION
# CHECK-NEXT: Name: default_func
# CHECK-NEXT: Flags: [ UNDEFINED ]
# CHECK-NEXT: Function: 0
# CHECK-NEXT: - Index: 4
# CHECK-NEXT: Kind: FUNCTION
# CHECK-NEXT: Name: load_hidden_data
# CHECK-NEXT: Flags: [ BINDING_LOCAL ]
# CHECK-NEXT: Function: 3
# CHECK-NEXT: - Index: 5
# CHECK-NEXT: Kind: DATA
# CHECK-NEXT: Name: __memory_base
# CHECK-NEXT: Flags: [ UNDEFINED ]
# CHECK-NEXT: - Index: 6
# CHECK-NEXT: Kind: DATA
# CHECK-NEXT: Name: .L.hidden_data
# CHECK-NEXT: Flags: [ BINDING_LOCAL ]
# CHECK-NEXT: Segment: 0
# CHECK-NEXT: Size: 1
# CHECK-NEXT: - Index: 7
# CHECK-NEXT: Kind: FUNCTION
# CHECK-NEXT: Name: load_hidden_func
# CHECK-NEXT: Flags: [ BINDING_LOCAL ]
# CHECK-NEXT: Function: 4
# CHECK-NEXT: - Index: 8
# CHECK-NEXT: Kind: DATA
# CHECK-NEXT: Name: __table_base
# CHECK-NEXT: Flags: [ UNDEFINED ]
# CHECK-NEXT: - Index: 9
# CHECK-NEXT: Kind: FUNCTION
# CHECK-NEXT: Name: hidden_func
# CHECK-NEXT: Flags: [ BINDING_LOCAL ]
# CHECK-NEXT: Function: 5
# REF: - Index: 10
# REF-NEXT: Kind: TABLE
# REF-NEXT: Name: __indirect_function_table
# REF-NEXT: Flags: [ UNDEFINED, NO_STRIP ]
# REF-NEXT: Table: 0
# CHECK-NEXT: SegmentInfo:
# CHECK-NEXT: - Index: 0
# CHECK-NEXT: Name: .rodata.hidden_data
# CHECK-NEXT: Alignment: 0
# CHECK-NEXT: Flags: [ ]
# CHECK-NEXT: ...