[lld] [lld][WebAssembly] Fix relocation of Wasm table index with GOT access (PR #104043)

Luc Blaeser via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 14 08:37:37 PDT 2024


https://github.com/luc-blaeser created https://github.com/llvm/llvm-project/pull/104043

Fix `wasm-ld` to handle the relocation of table indices for __shared functions__ in the __code segment__. The table element was not created for the corresponding table index. Moreover, `GOT.func` was missing for the corresponding function.

>From d40eb7c4e4b5b123abc6a0789f2b9d465c0ca1a9 Mon Sep 17 00:00:00 2001
From: luc-blaeser <luc.blaeser at dfinity.org>
Date: Mon, 12 Aug 2024 11:18:41 +0200
Subject: [PATCH] Fix relocation of Wasm table index with GOT access

---
 lld/test/wasm/call-indirect-external.s |  27 +++++
 lld/test/wasm/shared-call-indirect.s   | 126 +++++++++++++++++++++++
 lld/test/wasm/shared-call-indirect64.s | 135 +++++++++++++++++++++++++
 lld/wasm/Relocations.cpp               |  29 +++++-
 4 files changed, 314 insertions(+), 3 deletions(-)
 create mode 100644 lld/test/wasm/call-indirect-external.s
 create mode 100644 lld/test/wasm/shared-call-indirect.s
 create mode 100644 lld/test/wasm/shared-call-indirect64.s

diff --git a/lld/test/wasm/call-indirect-external.s b/lld/test/wasm/call-indirect-external.s
new file mode 100644
index 00000000000000..e226b891442485
--- /dev/null
+++ b/lld/test/wasm/call-indirect-external.s
@@ -0,0 +1,27 @@
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
+
+# RUN: not wasm-ld --experimental-pic -shared %t.o -o /dev/null 2>&1 | \
+# RUN:   FileCheck -check-prefix=ERRUND %s
+# ERRUND: error: {{.*}}.o: relocation R_WASM_TABLE_INDEX_REL_SLEB cannot be used against an undefined symbol `external_func`
+
+# RUN: not wasm-ld --experimental-pic -shared %t.o -o /dev/null  --unresolved-symbols=report-all 2>&1 | \
+# RUN:   FileCheck -check-prefix=ERRUND %s
+
+# RUN: not wasm-ld --experimental-pic -shared %t.o -o /dev/null  --warn-unresolved-symbols 2>&1 | \
+# RUN:   FileCheck -check-prefix=ERRUND %s
+
+# RUN: not wasm-ld --experimental-pic -shared %t.o -o /dev/null  --unresolved-symbols=ignore-all 2>&1 | \
+# RUN:   FileCheck -check-prefix=ERRUND %s
+
+.globaltype	__table_base, i32, immutable
+
+.functype external_func () -> ()
+
+.globl _start
+_start:
+    .functype _start () -> ()
+    global.get  __table_base
+    i32.const external_func at TBREL
+    i32.add
+    call_indirect () -> ()
+    end_function
diff --git a/lld/test/wasm/shared-call-indirect.s b/lld/test/wasm/shared-call-indirect.s
new file mode 100644
index 00000000000000..d805670b8513c7
--- /dev/null
+++ b/lld/test/wasm/shared-call-indirect.s
@@ -0,0 +1,126 @@
+# 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
+# RUN: llvm-objdump --disassemble-symbols=test_indirect_local_func_call,test_indirect_shared_func_call --no-show-raw-insn --no-leading-addr %t.wasm | FileCheck %s --check-prefixes DIS
+
+.globaltype	__table_base, i32, immutable
+
+test_indirect_local_func_call:
+    .functype test_indirect_local_func_call () -> ()
+    global.get  __table_base
+    i32.const local_func at TBREL
+    i32.add
+    call_indirect () -> ()
+    end_function
+
+# Test table index relocation in code section for shared functions
+
+test_indirect_shared_func_call:
+    .functype test_indirect_shared_func_call () -> ()
+    i64.const 12345
+    global.get  __table_base
+    i32.const shared_func at TBREL
+    i32.add
+    call_indirect (i64) -> (i32)
+    drop
+    end_function
+
+local_func:
+    .functype local_func () -> ()
+    end_function
+
+.globl shared_func
+shared_func:
+    .functype shared_func (i64) -> (i32)
+    local.get 0
+    i32.wrap_i64
+    end_function
+
+.globl _start
+_start:
+    .functype _start () -> ()
+    call test_indirect_local_func_call
+    call test_indirect_shared_func_call
+    end_function
+
+# CHECK:      Sections:
+# CHECK-NEXT:   - Type:            CUSTOM
+# CHECK-NEXT:     Name:            dylink.0
+# CHECK-NEXT:     MemorySize:      0
+# CHECK-NEXT:     MemoryAlignment: 0
+# CHECK-NEXT:     TableSize:       2
+# CHECK-NEXT:     TableAlignment:  0
+# CHECK-NEXT:     Needed:          []
+# CHECK-NEXT:   - Type:            TYPE
+
+# CHECK:        - Type:            IMPORT
+# CHECK-NEXT:     Imports:
+# CHECK-NEXT:      - Module:          env
+# CHECK-NEXT:        Field:           memory
+# CHECK-NEXT:        Kind:            MEMORY
+# CHECK-NEXT:        Memory:
+# CHECK-NEXT:          Minimum:         0x0
+# 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:           __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:          GOT.func
+# CHECK-NEXT:        Field:           shared_func
+# CHECK-NEXT:        Kind:            GLOBAL
+# CHECK-NEXT:        GlobalType:      I32
+# CHECK-NEXT:        GlobalMutable:   true
+# CHECK-NEXT:  - Type:            FUNCTION
+# CHECK-NEXT:    FunctionTypes:   [ 0, 0, 0, 0, 0, 1, 0 ]
+# CHECK-NEXT:  - Type:            EXPORT
+# CHECK-NEXT:    Exports:
+# CHECK-NEXT:      - Name:            __wasm_call_ctors
+# CHECK-NEXT:        Kind:            FUNCTION
+# CHECK-NEXT:        Index:           0
+# CHECK-NEXT:      - Name:            __wasm_apply_data_relocs
+# CHECK-NEXT:        Kind:            FUNCTION
+# CHECK-NEXT:        Index:           1
+# CHECK-NEXT:      - Name:            shared_func
+# CHECK-NEXT:        Kind:            FUNCTION
+# CHECK-NEXT:        Index:           5
+# CHECK-NEXT:      - Name:            _start
+# CHECK-NEXT:        Kind:            FUNCTION
+# CHECK-NEXT:        Index:           6
+# CHECK-NEXT:  - Type:            ELEM
+# CHECK-NEXT:    Segments:
+# CHECK-NEXT:      - Offset:
+# CHECK-NEXT:          Opcode:          GLOBAL_GET
+# CHECK-NEXT:          Index:           1
+# CHECK-NEXT:        Functions:       [ 3, 5 ]
+
+
+# DIS: <test_indirect_local_func_call>:
+# DIS-EMPTY:
+# DIS-NEXT:               	global.get	1
+# DIS-NEXT:               	i32.const	0
+# DIS-NEXT:               	i32.add 
+# DIS-NEXT:               	call_indirect	 0
+# DIS-NEXT:               	end
+
+# DIS: <test_indirect_shared_func_call>:
+# DIS-EMPTY:
+# DIS-NEXT:               	i64.const	12345
+# DIS-NEXT:               	global.get	1
+# DIS-NEXT:               	i32.const	1
+# DIS-NEXT:               	i32.add 
+# DIS-NEXT:               	call_indirect	 1
+# DIS-NEXT:               	drop
+# DIS-NEXT:               	end
diff --git a/lld/test/wasm/shared-call-indirect64.s b/lld/test/wasm/shared-call-indirect64.s
new file mode 100644
index 00000000000000..b5e8a8de740802
--- /dev/null
+++ b/lld/test/wasm/shared-call-indirect64.s
@@ -0,0 +1,135 @@
+# 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
+# RUN: llvm-objdump --disassemble-symbols=test_indirect_local_func_call,test_indirect_shared_func_call --no-show-raw-insn --no-leading-addr %t.wasm | FileCheck %s --check-prefixes DIS
+
+.globaltype	__table_base, i64, immutable
+
+test_indirect_local_func_call:
+    .functype test_indirect_local_func_call () -> ()
+    global.get  __table_base
+    i64.const local_func at TBREL
+    i64.add
+    i32.wrap_i64 # Remove when table64 is supported
+    call_indirect () -> ()
+    end_function
+
+# Test table index relocation in code section for shared functions
+
+test_indirect_shared_func_call:
+    .functype test_indirect_shared_func_call () -> ()
+    i32.const 12345
+    global.get  __table_base
+    i64.const shared_func at TBREL
+    i64.add
+    i32.wrap_i64 # Remove when table64 is supported
+    call_indirect (i32) -> (i32)
+    drop
+    end_function
+
+local_func:
+    .functype local_func () -> ()
+    end_function
+
+.globl shared_func
+shared_func:
+    .functype shared_func (i32) -> (i32)
+    local.get 0
+    end_function
+
+.globl _start
+_start:
+    .functype _start () -> ()
+    call test_indirect_local_func_call
+    call test_indirect_shared_func_call
+    end_function
+
+# CHECK:      Sections:
+# CHECK-NEXT:  - Type:            CUSTOM
+# CHECK-NEXT:    Name:            dylink.0
+# CHECK-NEXT:    MemorySize:      0
+# CHECK-NEXT:    MemoryAlignment: 0
+# CHECK-NEXT:    TableSize:       2
+# CHECK-NEXT:    TableAlignment:  0
+# CHECK-NEXT:    Needed:          []
+# CHECK-NEXT:  - Type:            TYPE
+
+# 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:         0x0
+# 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:           __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:          GOT.func
+# CHECK-NEXT:        Field:           shared_func
+# CHECK-NEXT:        Kind:            GLOBAL
+# CHECK-NEXT:        GlobalType:      I64
+# CHECK-NEXT:        GlobalMutable:   true
+# CHECK-NEXT:  - Type:            FUNCTION
+# CHECK-NEXT:    FunctionTypes:   [ 0, 0, 0, 0, 0, 1, 0 ]
+# CHECK-NEXT:  - Type:            EXPORT
+# CHECK-NEXT:    Exports:
+# CHECK-NEXT:      - Name:            __wasm_call_ctors
+# CHECK-NEXT:        Kind:            FUNCTION
+# CHECK-NEXT:        Index:           0
+# CHECK-NEXT:      - Name:            __wasm_apply_data_relocs
+# CHECK-NEXT:        Kind:            FUNCTION
+# CHECK-NEXT:        Index:           1
+# CHECK-NEXT:      - Name:            shared_func
+# CHECK-NEXT:        Kind:            FUNCTION
+# CHECK-NEXT:        Index:           5
+# CHECK-NEXT:      - Name:            _start
+# CHECK-NEXT:        Kind:            FUNCTION
+# CHECK-NEXT:        Index:           6
+# CHECK-NEXT:  - Type:            ELEM
+# CHECK-NEXT:    Segments:
+# CHECK-NEXT:      - Offset:
+# CHECK-NEXT:          Opcode:          GLOBAL_GET
+# CHECK-NEXT:          Index:           2
+# CHECK-NEXT:        Functions:       [ 3, 5 ]
+
+
+# DIS: <test_indirect_local_func_call>:
+# DIS-EMPTY:
+# DIS-NEXT:               	global.get	1
+# DIS-NEXT:               	i64.const	0
+# DIS-NEXT:               	i64.add 
+# DIS-NEXT:               	i32.wrap_i64 
+# DIS-NEXT:               	call_indirect	 0
+# DIS-NEXT:               	end
+
+# DIS: <test_indirect_shared_func_call>:
+# DIS-EMPTY:
+# DIS-NEXT:               	i32.const	12345
+# DIS-NEXT:               	global.get	1
+# DIS-NEXT:               	i64.const	1
+# DIS-NEXT:               	i64.add 
+# DIS-NEXT:               	i32.wrap_i64 
+# DIS-NEXT:               	call_indirect	 1
+# DIS-NEXT:               	drop
+# DIS-NEXT:               	end
diff --git a/lld/wasm/Relocations.cpp b/lld/wasm/Relocations.cpp
index 6f33a4f28a9d09..bb9c53954d589d 100644
--- a/lld/wasm/Relocations.cpp
+++ b/lld/wasm/Relocations.cpp
@@ -102,13 +102,27 @@ void scanRelocations(InputChunk *chunk) {
     switch (reloc.Type) {
     case R_WASM_TABLE_INDEX_I32:
     case R_WASM_TABLE_INDEX_I64:
+      // These relocations target the data section and are handled
+      // by `generateRelocationCode`. GOT accesses are handled below.
+      if (requiresGOTAccess(sym))
+        break;
+      out.elemSec->addEntry(cast<FunctionSymbol>(sym));
+      break;
     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));
+      // These relocations target the code section and can only be resolved
+      // at linking time.
+      if (requiresGOTAccess(sym)) {
+        addGOTEntry(sym);
+      }
+      // The indirect call needs a table element that can only be added
+      // for defined functions.
+      if (sym->isDefined()) {
+        out.elemSec->addEntry(cast<FunctionSymbol>(sym));
+      }
+      // Unresolved symbols are handled below.
       break;
     case R_WASM_GLOBAL_INDEX_LEB:
     case R_WASM_GLOBAL_INDEX_I32:
@@ -160,6 +174,15 @@ void scanRelocations(InputChunk *chunk) {
               " cannot be used against symbol `" + toString(*sym) +
               "`; recompile with -fPIC");
         break;
+      case R_WASM_TABLE_INDEX_REL_SLEB:
+      case R_WASM_TABLE_INDEX_REL_SLEB64:
+        if (sym->isUndefined()) {
+          error(toString(file) + ": relocation " +
+                relocTypeToString(reloc.Type) +
+                " cannot be used against an undefined symbol `" +
+                toString(*sym) + "`");
+        }
+        break;
       case R_WASM_TABLE_INDEX_I32:
       case R_WASM_TABLE_INDEX_I64:
       case R_WASM_MEMORY_ADDR_I32:



More information about the llvm-commits mailing list