diff --git a/lld/test/wasm/shared-export-dynamic.ll b/lld/test/wasm/shared-export-dynamic.ll deleted file mode 100644 --- a/lld/test/wasm/shared-export-dynamic.ll +++ /dev/null @@ -1,18 +0,0 @@ -; RUN: llc -relocation-model=pic -filetype=obj %s -o %t.o - -; By default all `default` symbols should be exported -; RUN: wasm-ld -shared -o %t.wasm %t.o -; RUN: obj2yaml %t.wasm | FileCheck %s -check-prefix=DEFAULT -; DEFAULT: foo - -; Verify that `--no-export-dynamic` works with `-shared` -; RUN: wasm-ld -shared --no-export-dynamic -o %t2.wasm %t.o -; RUN: obj2yaml %t2.wasm | FileCheck %s -check-prefix=NO-EXPORT -; NO-EXPORT-NOT: foo - -target triple = "wasm32-unknown-emscripten" - -define default i32 @foo() { -entry: - ret i32 0 -} diff --git a/lld/test/wasm/shared-export-dynamic.s b/lld/test/wasm/shared-export-dynamic.s new file mode 100644 --- /dev/null +++ b/lld/test/wasm/shared-export-dynamic.s @@ -0,0 +1,18 @@ +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s + +# By default all `default` symbols should be exported +# RUN: wasm-ld -shared --experimental-pic -o %t.wasm %t.o +# RUN: obj2yaml %t.wasm | FileCheck %s -check-prefix=DEFAULT +# DEFAULT: foo + +# Verify that `--no-export-dynamic` works with `-shared` +# RUN: wasm-ld -shared --experimental-pic --no-export-dynamic -o %t2.wasm %t.o +# RUN: obj2yaml %t2.wasm | FileCheck %s -check-prefix=NO-EXPORT +# NO-EXPORT-NOT: foo + +.globl foo + +foo: + .functype foo () -> (i32) + i32.const 0 + end_function diff --git a/lld/test/wasm/shared-needed.ll b/lld/test/wasm/shared-needed.ll deleted file mode 100644 --- a/lld/test/wasm/shared-needed.ll +++ /dev/null @@ -1,38 +0,0 @@ -; RUN: llc -filetype=obj %s -o %t.o -; RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %p/Inputs/ret32.s -o %t.ret32.o - -; RUN: wasm-ld -shared -o %t1.so %t.o -; RUN: obj2yaml %t1.so | FileCheck %s -check-prefix=SO1 - -; RUN: wasm-ld -shared -o %t2.so %t1.so %t.ret32.o -; RUN: obj2yaml %t2.so | FileCheck %s -check-prefix=SO2 - -target triple = "wasm32-unknown-unknown" - -@data = global i32 2, align 4 - -define default void @foo() { -entry: - ret void -} - -; SO1: Sections: -; SO1-NEXT: - Type: CUSTOM -; SO1-NEXT: Name: dylink -; SO1-NEXT: MemorySize: 4 -; SO1-NEXT: MemoryAlignment: 2 -; SO1-NEXT: TableSize: 0 -; SO1-NEXT: TableAlignment: 0 -; SO1-NEXT: Needed: [] -; SO1-NEXT: - Type: TYPE - -; SO2: Sections: -; SO2-NEXT: - Type: CUSTOM -; SO2-NEXT: Name: dylink -; SO2-NEXT: MemorySize: 0 -; SO2-NEXT: MemoryAlignment: 0 -; SO2-NEXT: TableSize: 0 -; SO2-NEXT: TableAlignment: 0 -; SO2-NEXT: Needed: -; SO2-NEXT: - shared-needed.ll.tmp1.so -; SO2-NEXT: - Type: TYPE diff --git a/lld/test/wasm/shared-needed.s b/lld/test/wasm/shared-needed.s new file mode 100644 --- /dev/null +++ b/lld/test/wasm/shared-needed.s @@ -0,0 +1,43 @@ +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %p/Inputs/ret32.s -o %t.ret32.o + +# RUN: wasm-ld -shared --experimental-pic -o %t1.so %t.o +# RUN: obj2yaml %t1.so | FileCheck %s -check-prefix=SO1 + +# RUN: wasm-ld -shared --experimental-pic -o %t2.so %t1.so %t.ret32.o +# RUN: obj2yaml %t2.so | FileCheck %s -check-prefix=SO2 + +.globl foo +.globl data + +foo: + .functype foo () -> () + end_function + +.section .data,"",@ +data: + .p2align 2 + .int32 0 + .size data,4 + + +# SO1: Sections: +# SO1-NEXT: - Type: CUSTOM +# SO1-NEXT: Name: dylink +# SO1-NEXT: MemorySize: 4 +# SO1-NEXT: MemoryAlignment: 2 +# SO1-NEXT: TableSize: 0 +# SO1-NEXT: TableAlignment: 0 +# SO1-NEXT: Needed: [] +# SO1-NEXT: - Type: TYPE + +# SO2: Sections: +# SO2-NEXT: - Type: CUSTOM +# SO2-NEXT: Name: dylink +# SO2-NEXT: MemorySize: 0 +# SO2-NEXT: MemoryAlignment: 0 +# SO2-NEXT: TableSize: 0 +# SO2-NEXT: TableAlignment: 0 +# SO2-NEXT: Needed: +# SO2-NEXT: - shared-needed.s.tmp1.so +# SO2-NEXT: - Type: TYPE diff --git a/lld/test/wasm/shared.ll b/lld/test/wasm/shared.ll deleted file mode 100644 --- a/lld/test/wasm/shared.ll +++ /dev/null @@ -1,156 +0,0 @@ -; RUN: llc -relocation-model=pic -mattr=+mutable-globals -filetype=obj %s -o %t.o -; RUN: wasm-ld --experimental-pic -shared -o %t.wasm %t.o -; RUN: obj2yaml %t.wasm | FileCheck %s - -target triple = "wasm32-unknown-emscripten" - -@data = hidden global i32 2, align 4 -@data_external = external global i32 -@indirect_func = local_unnamed_addr global i32 ()* @foo, align 4 -@indirect_func_external = local_unnamed_addr global void ()* @func_external, align 4 - -; Test data relocations -@data_addr = local_unnamed_addr global i32* @data, align 4 -; .. against external symbols -@data_addr_external = local_unnamed_addr global i32* @data_external, align 4 -; .. including addends -%struct.s = type { i32, i32 } -@extern_struct = external global %struct.s -@extern_struct_internal_ptr = local_unnamed_addr global i32* getelementptr inbounds (%struct.s, %struct.s* @extern_struct, i32 0, i32 1), align 4 - -define hidden i32 @foo() { -entry: - ; To ensure we use __stack_pointer - %ptr = alloca i32 - %0 = load i32, i32* @data, align 4 - %1 = load i32 ()*, i32 ()** @indirect_func, align 4 - call i32 %1() - ret i32 %0 -} - -define hidden i32* @get_data_address() { -entry: - ret i32* @data_external -} - -define hidden i8* @get_func_address() { -entry: - ret i8* bitcast (void ()* @func_external to i8*) -} - -define default i8* @get_local_func_address() { -entry: - ; Verify that a function which is otherwise not address taken *is* added to - ; the wasm table with referenced via R_WASM_TABLE_INDEX_REL_SLEB - ret i8* bitcast (i8* ()* @get_func_address to i8*) -} - -declare void @func_external() - -; 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: 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: I32 -; CHECK-NEXT: GlobalMutable: true -; CHECK-NEXT: - Module: env -; CHECK-NEXT: Field: __memory_base -; CHECK-NEXT: Kind: GLOBAL -; CHECK-NEXT: GlobalType: I32 -; CHECK-NEXT: GlobalMutable: false -; CHECK-NEXT: - Module: env -; CHECK-NEXT: Field: __table_base -; 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: 2 -; 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: 230141046A230241016A360200230141086A23043602002301410C6A230141006A360200230141106A2305360200230141146A230641046A3602000B - -; 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: '020000000100000000000000000000000000000000000000' diff --git a/lld/test/wasm/shared.s b/lld/test/wasm/shared.s new file mode 100644 --- /dev/null +++ b/lld/test/wasm/shared.s @@ -0,0 +1,232 @@ +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s +# RUN: wasm-ld --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 i32, i32 + global.get __stack_pointer + i32.const 16 + i32.sub + local.tee 0 + global.set __stack_pointer + global.get __memory_base + i32.const data@MBREL + i32.add + i32.load 0 + local.set 1 + global.get indirect_func@GOT + i32.load 0 + call_indirect () -> (i32) + drop + local.get 0 + i32.const 16 + i32.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_SLEB + .functype get_local_func_address () -> (i32) + global.get __table_base + i32.const get_func_address@TBREL + i32.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, i32 +.globaltype __table_base, i32, immutable +.globaltype __memory_base, i32, 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: 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: I32 +# CHECK-NEXT: GlobalMutable: true +# CHECK-NEXT: - Module: env +# CHECK-NEXT: Field: __memory_base +# CHECK-NEXT: Kind: GLOBAL +# CHECK-NEXT: GlobalType: I32 +# CHECK-NEXT: GlobalMutable: false +# CHECK-NEXT: - Module: env +# CHECK-NEXT: Field: __table_base +# 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: 2 +# 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: 230141046A2304360200230141086A230241016A3602002301410C6A230141006A360200230141106A2305360200230141146A230641046A3602000B + +# 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'