[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 {