[WebAssembly] Add new relocation for location relative data
This `R_WASM_MEMORY_ADDR_SELFREL_I32` relocation represents an offset
between its relocating address and the symbol address. It's very similar
to `R_X86_64_PC32` but restricted to be used for only data segments.
```
S + A - P
```
A: Represents the addend used to compute the value of the relocatable
field.
P: Represents the place of the storage unit being relocated.
S: Represents the value of the symbol whose index resides in the
relocation entry.
Proposal: https://github.com/WebAssembly/tool-conventions/issues/162
Differential Revision: https://reviews.llvm.org/D96659
GitOrigin-RevId: aa0c571a5fa9130ccd932f9b6970440a1bae177f
diff --git a/test/wasm/reloc-relative.s b/test/wasm/reloc-relative.s
new file mode 100644
index 0000000..fde1d1d
--- /dev/null
+++ b/test/wasm/reloc-relative.s
@@ -0,0 +1,89 @@
+# 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 %t.o
+# RUN: wasm-ld --no-entry --no-gc-sections --allow-undefined -fatal-warnings -o %t.wasm %t.o %t.hello32.o
+# RUN: obj2yaml %t.wasm | FileCheck %s
+
+.section .x_sec,"",@
+internal_x_seg_pad:
+ # padding for provisioning value assertion
+ .int32 0
+ .size internal_x_seg_pad, 4
+internal_x_seg:
+ .int32 42
+ .size internal_x_seg, 4
+
+# internal cross segment subtraction
+.section .foo,"",@
+.globl foo
+foo:
+ .int32 internal_x_seg - foo
+ .size foo, 4
+foo_addend:
+ .int32 internal_x_seg - foo
+ .size foo_addend, 4
+
+# external cross segment subtraction
+.section .bar,"",@
+.globl bar
+bar:
+ .int32 hello_str - bar
+ .size bar, 4
+bar_addend:
+ .int32 hello_str - bar
+ .size bar_addend, 4
+
+# positive calc result
+.section .fizz,"",@
+.globl fizz
+fizz:
+ .int32 far - fizz
+ .size fizz, 4
+fizz_addend:
+ .int32 far - fizz
+ .size fizz_addend, 4
+
+.section .far,"",@
+.globl far
+far:
+ .int32 21
+ .size far, 4
+
+# CHECK: - Type: DATA
+# CHECK-NEXT: Segments:
+# CHECK-NEXT: - SectionOffset: 7
+# CHECK-NEXT: InitFlags: 0
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: Opcode: I32_CONST
+# CHECK-NEXT: Value: 1024
+# CHECK-NEXT: Content: 68656C6C6F0A00
+# CHECK-NEXT: - SectionOffset: 20
+# CHECK-NEXT: InitFlags: 0
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: Opcode: I32_CONST
+# CHECK-NEXT: Value: 1031
+# CHECK-NEXT: Content: 000000002A000000
+# CHECK-NEXT: - SectionOffset: 34
+# CHECK-NEXT: InitFlags: 0
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: Opcode: I32_CONST
+# CHECK-NEXT: Value: 1039
+# CHECK-NEXT: Content: FCFFFFFFFCFFFFFF
+# CHECK-NEXT: - SectionOffset: 48
+# CHECK-NEXT: InitFlags: 0
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: Opcode: I32_CONST
+# CHECK-NEXT: Value: 1047
+# CHECK-NEXT: Content: E9FFFFFFE9FFFFFF
+# CHECK-NEXT: - SectionOffset: 62
+# CHECK-NEXT: InitFlags: 0
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: Opcode: I32_CONST
+# CHECK-NEXT: Value: 1055
+# CHECK-NEXT: Content: '0800000008000000'
+# CHECK-NEXT: - SectionOffset: 76
+# CHECK-NEXT: InitFlags: 0
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: Opcode: I32_CONST
+# CHECK-NEXT: Value: 1063
+# CHECK-NEXT: Content: '15000000'
+
diff --git a/wasm/InputChunks.cpp b/wasm/InputChunks.cpp
index 5e96512..75343cf 100644
--- a/wasm/InputChunks.cpp
+++ b/wasm/InputChunks.cpp
@@ -94,6 +94,7 @@
case R_WASM_FUNCTION_OFFSET_I32:
case R_WASM_SECTION_OFFSET_I32:
case R_WASM_GLOBAL_INDEX_I32:
+ case R_WASM_MEMORY_ADDR_LOCREL_I32:
existingValue = read32le(loc);
break;
case R_WASM_TABLE_INDEX_I64:
@@ -139,7 +140,7 @@
for (const WasmRelocation &rel : relocations) {
uint8_t *loc = buf + rel.Offset + off;
- auto value = file->calcNewValue(rel, tombstone);
+ auto value = file->calcNewValue(rel, tombstone, this);
LLVM_DEBUG(dbgs() << "apply reloc: type=" << relocTypeToString(rel.Type));
if (rel.Type != R_WASM_TYPE_INDEX_LEB)
LLVM_DEBUG(dbgs() << " sym=" << file->getSymbols()[rel.Index]->getName());
@@ -176,6 +177,7 @@
case R_WASM_FUNCTION_OFFSET_I32:
case R_WASM_SECTION_OFFSET_I32:
case R_WASM_GLOBAL_INDEX_I32:
+ case R_WASM_MEMORY_ADDR_LOCREL_I32:
write32le(loc, value);
break;
case R_WASM_TABLE_INDEX_I64:
@@ -302,7 +304,8 @@
for (const WasmRelocation &rel : relocations) {
LLVM_DEBUG(dbgs() << " region: " << (rel.Offset - lastRelocEnd) << "\n");
compressedFuncSize += rel.Offset - lastRelocEnd;
- compressedFuncSize += getRelocWidth(rel, file->calcNewValue(rel, tombstone));
+ compressedFuncSize +=
+ getRelocWidth(rel, file->calcNewValue(rel, tombstone, this));
lastRelocEnd = rel.Offset + getRelocWidthPadded(rel);
}
LLVM_DEBUG(dbgs() << " final region: " << (end - lastRelocEnd) << "\n");
@@ -343,7 +346,8 @@
LLVM_DEBUG(dbgs() << " write chunk: " << chunkSize << "\n");
memcpy(buf, lastRelocEnd, chunkSize);
buf += chunkSize;
- buf += writeCompressedReloc(buf, rel, file->calcNewValue(rel, tombstone));
+ buf += writeCompressedReloc(buf, rel,
+ file->calcNewValue(rel, tombstone, this));
lastRelocEnd = secStart + rel.Offset + getRelocWidthPadded(rel);
}
@@ -416,7 +420,7 @@
writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
writeUleb128(os, baseSymbol->getGlobalIndex(), "base");
writeU8(os, opcode_reloc_const, "CONST");
- writeSleb128(os, file->calcNewValue(rel, tombstone), "offset");
+ writeSleb128(os, file->calcNewValue(rel, tombstone, this), "offset");
writeU8(os, opcode_reloc_add, "ADD");
}
diff --git a/wasm/InputFiles.cpp b/wasm/InputFiles.cpp
index d650c2a..6b3b0d2 100644
--- a/wasm/InputFiles.cpp
+++ b/wasm/InputFiles.cpp
@@ -126,6 +126,7 @@
case R_WASM_MEMORY_ADDR_TLS_SLEB:
case R_WASM_FUNCTION_OFFSET_I32:
case R_WASM_FUNCTION_OFFSET_I64:
+ case R_WASM_MEMORY_ADDR_LOCREL_I32:
return reloc.Addend;
case R_WASM_SECTION_OFFSET_I32:
return getSectionSymbol(reloc.Index)->section->getOffset(reloc.Addend);
@@ -158,7 +159,8 @@
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_MEMORY_ADDR_TLS_SLEB:
+ case R_WASM_MEMORY_ADDR_LOCREL_I32: {
const WasmSymbol &sym = wasmObj->syms()[reloc.Index];
if (sym.isUndefined())
return 0;
@@ -199,7 +201,8 @@
}
// Translate from the relocation's index into the final linked output value.
-uint64_t ObjFile::calcNewValue(const WasmRelocation &reloc, uint64_t tombstone) const {
+uint64_t ObjFile::calcNewValue(const WasmRelocation &reloc, uint64_t tombstone,
+ const InputChunk *chunk) const {
const Symbol* sym = nullptr;
if (reloc.Type != R_WASM_TYPE_INDEX_LEB) {
sym = symbols[reloc.Index];
@@ -234,7 +237,8 @@
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_LOCREL_I32: {
if (isa<UndefinedData>(sym) || sym->isUndefWeak())
return 0;
auto D = cast<DefinedData>(sym);
@@ -245,7 +249,15 @@
// backward compat with old object files built with `-fPIC`.
if (D->segment && D->segment->outputSeg->name == ".tdata")
return D->getOutputSegmentOffset() + reloc.Addend;
- return D->getVA(reloc.Addend);
+
+ uint64_t value = D->getVA(reloc.Addend);
+ if (reloc.Type == R_WASM_MEMORY_ADDR_LOCREL_I32) {
+ const auto *segment = cast<InputSegment>(chunk);
+ uint64_t p = segment->outputSeg->startVA + segment->outputSegmentOffset +
+ reloc.Offset - segment->getInputSectionOffset();
+ value -= p;
+ }
+ return value;
}
case R_WASM_MEMORY_ADDR_TLS_SLEB:
if (isa<UndefinedData>(sym) || sym->isUndefWeak())
diff --git a/wasm/InputFiles.h b/wasm/InputFiles.h
index 8152420..78b3cee 100644
--- a/wasm/InputFiles.h
+++ b/wasm/InputFiles.h
@@ -119,7 +119,8 @@
void dumpInfo() const;
uint32_t calcNewIndex(const WasmRelocation &reloc) const;
- uint64_t calcNewValue(const WasmRelocation &reloc, uint64_t tombstone) const;
+ uint64_t calcNewValue(const WasmRelocation &reloc, uint64_t tombstone,
+ const InputChunk *chunk) const;
uint64_t calcNewAddend(const WasmRelocation &reloc) const;
uint64_t calcExpectedValue(const WasmRelocation &reloc) const;
Symbol *getSymbol(const WasmRelocation &reloc) const {