diff --git a/lld/test/wasm/reloc-relative.s b/lld/test/wasm/reloc-relative.s new file mode 100644 --- /dev/null +++ b/lld/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/lld/wasm/InputChunks.cpp b/lld/wasm/InputChunks.cpp --- a/lld/wasm/InputChunks.cpp +++ b/lld/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/lld/wasm/InputFiles.h b/lld/wasm/InputFiles.h --- a/lld/wasm/InputFiles.h +++ b/lld/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 { diff --git a/lld/wasm/InputFiles.cpp b/lld/wasm/InputFiles.cpp --- a/lld/wasm/InputFiles.cpp +++ b/lld/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(sym) || sym->isUndefWeak()) return 0; auto D = cast(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(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(sym) || sym->isUndefWeak()) diff --git a/llvm/include/llvm/BinaryFormat/WasmRelocs.def b/llvm/include/llvm/BinaryFormat/WasmRelocs.def --- a/llvm/include/llvm/BinaryFormat/WasmRelocs.def +++ b/llvm/include/llvm/BinaryFormat/WasmRelocs.def @@ -25,3 +25,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) diff --git a/llvm/include/llvm/MC/MCWasmObjectWriter.h b/llvm/include/llvm/MC/MCWasmObjectWriter.h --- a/llvm/include/llvm/MC/MCWasmObjectWriter.h +++ b/llvm/include/llvm/MC/MCWasmObjectWriter.h @@ -33,8 +33,8 @@ return W->getFormat() == Triple::Wasm; } - virtual unsigned getRelocType(const MCValue &Target, - const MCFixup &Fixup) const = 0; + virtual unsigned getRelocType(const MCValue &Target, const MCFixup &Fixup, + bool IsLocRel) const = 0; /// \name Accessors /// @{ diff --git a/llvm/lib/BinaryFormat/Wasm.cpp b/llvm/lib/BinaryFormat/Wasm.cpp --- a/llvm/lib/BinaryFormat/Wasm.cpp +++ b/llvm/lib/BinaryFormat/Wasm.cpp @@ -52,6 +52,7 @@ case R_WASM_FUNCTION_OFFSET_I32: case R_WASM_FUNCTION_OFFSET_I64: case R_WASM_SECTION_OFFSET_I32: + case R_WASM_MEMORY_ADDR_LOCREL_I32: return true; default: return false; diff --git a/llvm/lib/MC/WasmObjectWriter.cpp b/llvm/lib/MC/WasmObjectWriter.cpp --- a/llvm/lib/MC/WasmObjectWriter.cpp +++ b/llvm/lib/MC/WasmObjectWriter.cpp @@ -445,17 +445,35 @@ uint64_t C = Target.getConstant(); uint64_t FixupOffset = Layout.getFragmentOffset(Fragment) + Fixup.getOffset(); MCContext &Ctx = Asm.getContext(); + bool IsLocRel = false; if (const MCSymbolRefExpr *RefB = Target.getSymB()) { - // To get here the A - B expression must have failed evaluateAsRelocatable. - // This means either A or B must be undefined and in WebAssembly we can't - // support either of those cases. + const auto &SymB = cast(RefB->getSymbol()); - Ctx.reportError( - Fixup.getLoc(), - Twine("symbol '") + SymB.getName() + - "': unsupported subtraction expression used in relocation."); - return; + + if (FixupSection.getKind().isText()) { + Ctx.reportError(Fixup.getLoc(), + Twine("symbol '") + SymB.getName() + + "' unsupported subtraction expression used in " + "relocation in code section."); + return; + } + + if (SymB.isUndefined()) { + Ctx.reportError(Fixup.getLoc(), + Twine("symbol '") + SymB.getName() + + "' can not be undefined in a subtraction expression"); + return; + } + const MCSection &SecB = SymB.getSection(); + if (&SecB != &FixupSection) { + Ctx.reportError(Fixup.getLoc(), + Twine("symbol '") + SymB.getName() + + "' can not be placed in a different section"); + return; + } + IsLocRel = true; + C += FixupOffset - Layout.getSymbolOffset(SymB); } // We either rejected the fixup or folded B into C at this point. @@ -480,7 +498,7 @@ // be negative and don't wrap. FixedValue = 0; - unsigned Type = TargetObjectWriter->getRelocType(Target, Fixup); + unsigned Type = TargetObjectWriter->getRelocType(Target, Fixup, IsLocRel); // Absolute offset within a section or a function. // Currently only supported for for metadata sections. @@ -611,7 +629,8 @@ case wasm::R_WASM_MEMORY_ADDR_REL_SLEB64: case wasm::R_WASM_MEMORY_ADDR_I32: case wasm::R_WASM_MEMORY_ADDR_I64: - case wasm::R_WASM_MEMORY_ADDR_TLS_SLEB: { + case wasm::R_WASM_MEMORY_ADDR_TLS_SLEB: + case wasm::R_WASM_MEMORY_ADDR_LOCREL_I32: { // Provisional value is address of the global plus the offset // For undefined symbols, use zero if (!RelEntry.Symbol->isDefined()) @@ -707,6 +726,7 @@ case wasm::R_WASM_FUNCTION_OFFSET_I32: case wasm::R_WASM_SECTION_OFFSET_I32: case wasm::R_WASM_GLOBAL_INDEX_I32: + case wasm::R_WASM_MEMORY_ADDR_LOCREL_I32: patchI32(Stream, Value, Offset); break; case wasm::R_WASM_TABLE_INDEX_I64: diff --git a/llvm/lib/Object/RelocationResolver.cpp b/llvm/lib/Object/RelocationResolver.cpp --- a/llvm/lib/Object/RelocationResolver.cpp +++ b/llvm/lib/Object/RelocationResolver.cpp @@ -575,6 +575,7 @@ case wasm::R_WASM_EVENT_INDEX_LEB: case wasm::R_WASM_GLOBAL_INDEX_I32: case wasm::R_WASM_TABLE_NUMBER_LEB: + case wasm::R_WASM_MEMORY_ADDR_LOCREL_I32: return true; default: return false; @@ -611,6 +612,7 @@ case wasm::R_WASM_EVENT_INDEX_LEB: case wasm::R_WASM_GLOBAL_INDEX_I32: case wasm::R_WASM_TABLE_NUMBER_LEB: + case wasm::R_WASM_MEMORY_ADDR_LOCREL_I32: // For wasm section, its offset at 0 -- ignoring Value return LocData; default: diff --git a/llvm/lib/Object/WasmObjectFile.cpp b/llvm/lib/Object/WasmObjectFile.cpp --- a/llvm/lib/Object/WasmObjectFile.cpp +++ b/llvm/lib/Object/WasmObjectFile.cpp @@ -905,6 +905,7 @@ case wasm::R_WASM_MEMORY_ADDR_I32: case wasm::R_WASM_MEMORY_ADDR_REL_SLEB: case wasm::R_WASM_MEMORY_ADDR_TLS_SLEB: + case wasm::R_WASM_MEMORY_ADDR_LOCREL_I32: if (!isValidDataSymbol(Reloc.Index)) return make_error("invalid relocation data index", object_error::parse_failed); @@ -953,6 +954,7 @@ Size = 10; if (Reloc.Type == wasm::R_WASM_TABLE_INDEX_I32 || Reloc.Type == wasm::R_WASM_MEMORY_ADDR_I32 || + Reloc.Type == wasm::R_WASM_MEMORY_ADDR_LOCREL_I32 || Reloc.Type == wasm::R_WASM_SECTION_OFFSET_I32 || Reloc.Type == wasm::R_WASM_FUNCTION_OFFSET_I32 || Reloc.Type == wasm::R_WASM_GLOBAL_INDEX_I32) diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp @@ -34,8 +34,8 @@ explicit WebAssemblyWasmObjectWriter(bool Is64Bit, bool IsEmscripten); private: - unsigned getRelocType(const MCValue &Target, - const MCFixup &Fixup) const override; + unsigned getRelocType(const MCValue &Target, const MCFixup &Fixup, + bool IsLocRel) const override; }; } // end anonymous namespace @@ -63,7 +63,8 @@ } unsigned WebAssemblyWasmObjectWriter::getRelocType(const MCValue &Target, - const MCFixup &Fixup) const { + const MCFixup &Fixup, + bool IsLocRel) const { const MCSymbolRefExpr *RefA = Target.getSymA(); assert(RefA); auto& SymA = cast(RefA->getSymbol()); @@ -122,7 +123,8 @@ else if (!Section->isWasmData()) return wasm::R_WASM_SECTION_OFFSET_I32; } - return wasm::R_WASM_MEMORY_ADDR_I32; + return IsLocRel ? wasm::R_WASM_MEMORY_ADDR_LOCREL_I32 + : wasm::R_WASM_MEMORY_ADDR_I32; case FK_Data_8: if (SymA.isFunction()) return wasm::R_WASM_TABLE_INDEX_I64; diff --git a/llvm/test/MC/WebAssembly/bad-fixup-expr.s b/llvm/test/MC/WebAssembly/bad-fixup-expr.s --- a/llvm/test/MC/WebAssembly/bad-fixup-expr.s +++ b/llvm/test/MC/WebAssembly/bad-fixup-expr.s @@ -13,6 +13,22 @@ .int8 1 .size bar, 1 + .section .data.fizz,"",@ +fizz: + .int8 1 + .size fizz, 1 + + .section .data.segment1,"",@ +segment1: +// CHECK: 'bar' can not be placed in a different section + .int32 fizz-bar +// CHECK: 'undef_baz' can not be undefined in a subtraction expression + .int32 fizz-undef_baz +// CHECK: 'fizz' can not be placed in a different section + .int32 undef_baz-fizz + .size segment1, 12 + + .text .section .text.main,"",@ main: @@ -23,10 +39,10 @@ i32.const foo-foo_other+2 i32.const foo_other-foo-10 -// CHECK: 'bar': unsupported subtraction expression used in relocation +// CHECK: 'bar' unsupported subtraction expression used in relocation in code section. i32.const foo-bar -// CHECK: 'undef_baz': unsupported subtraction expression used in relocation +// CHECK: 'undef_baz' unsupported subtraction expression used in relocation in code section. i32.const foo-undef_baz -// CHECK: 'foo': unsupported subtraction expression used in relocation +// CHECK: 'foo' unsupported subtraction expression used in relocation in code section. i32.const undef_baz-foo end_function diff --git a/llvm/test/MC/WebAssembly/reloc-relative.ll b/llvm/test/MC/WebAssembly/reloc-relative.ll new file mode 100644 --- /dev/null +++ b/llvm/test/MC/WebAssembly/reloc-relative.ll @@ -0,0 +1,48 @@ +; RUN: llc -O0 -filetype=obj %s -o - | llvm-readobj -r --expand-relocs - | FileCheck %s + +; CHECK: Format: WASM +; CHECK: Relocations [ +; CHECK-NEXT: Section (3) DATA { +; CHECK-NEXT: Relocation { +; CHECK-NEXT: Type: R_WASM_MEMORY_ADDR_LOCREL_I32 (23) +; CHECK-NEXT: Offset: 0x6 +; CHECK-NEXT: Symbol: foo +; CHECK-NEXT: Addend: 0 +; CHECK-NEXT: } +; CHECK-NEXT: Relocation { +; CHECK-NEXT: Type: R_WASM_MEMORY_ADDR_LOCREL_I32 (23) +; CHECK-NEXT: Offset: 0xA +; CHECK-NEXT: Symbol: fizz +; CHECK-NEXT: Addend: 0 +; CHECK-NEXT: } +; CHECK-NEXT: Relocation { +; CHECK-NEXT: Type: R_WASM_MEMORY_ADDR_LOCREL_I32 (23) +; CHECK-NEXT: Offset: 0x17 +; CHECK-NEXT: Symbol: foo +; CHECK-NEXT: Addend: 4 +; CHECK-NEXT: } +; CHECK-NEXT: } +; CHECK-NEXT: ] + +target triple = "wasm32-unknown-unknown" + + +; @foo - @bar +@foo = external global i32, align 4 +@bar = constant i32 sub ( + i32 ptrtoint (i32* @foo to i32), + i32 ptrtoint (i32* @bar to i32) +), section ".sec1" + + +; @foo - @addend + 4 +@fizz = constant i32 42, align 4, section ".sec2" +@addend = constant i32 sub ( + i32 ptrtoint (i32* @foo to i32), + i32 ptrtoint (i32* @fizz to i32) +), section ".sec2" + +@x_sec = constant i32 sub ( + i32 ptrtoint (i32* @fizz to i32), + i32 ptrtoint (i32* @x_sec to i32) +), section ".sec1"