mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-11-23 22:00:10 +00:00
[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:
parent
5b6cae5524
commit
3a293cbf13
@ -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
238
lld/test/wasm/shared64.s
Normal 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'
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
@ -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)
|
||||
|
@ -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 =
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -107,9 +107,8 @@ 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
|
||||
: wasm::WASM_TYPE_I32),
|
||||
uint8_t(Subtarget.hasAddr64() ? wasm::WASM_TYPE_I64
|
||||
: wasm::WASM_TYPE_I32),
|
||||
Mutable});
|
||||
return WasmSym;
|
||||
}
|
||||
|
209
llvm/test/MC/WebAssembly/reloc-pic64.s
Normal file
209
llvm/test/MC/WebAssembly/reloc-pic64.s
Normal 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: ...
|
Loading…
Reference in New Issue
Block a user