diff --git a/lld/test/wasm/selfrel-reloc.ll b/lld/test/wasm/selfrel-reloc.ll new file mode 100644 --- /dev/null +++ b/lld/test/wasm/selfrel-reloc.ll @@ -0,0 +1,50 @@ +; RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %p/Inputs/hello.s -o %t.hello32.o +; RUN: llc -mtriple=wasm32-unknown-unknown -filetype=obj %s -o %t.o +; RUN: wasm-ld --no-entry --no-gc-sections --allow-undefined -o %t.wasm %t.o %t.hello32.o +; RUN: obj2yaml %t.wasm | FileCheck %s + +target triple = "wasm32-unknown-unknown" + + +; @hello_str - @bar_neg +@hello_str = external global i8* +@bar_neg = constant i32 sub ( + i32 ptrtoint (i8** @hello_str to i32), + i32 ptrtoint (i32* @bar_neg to i32) +), section ".sec1" + +@bar_pos = constant i32 sub ( + i32 ptrtoint (i32* @fizz to i32), + i32 ptrtoint (i32* @bar_pos to i32) +), section ".sec1" + + +; @hello_str - @addend + 4 +@fizz = constant i32 42, align 4, section ".sec2" +@addend = constant i32 sub ( + i32 ptrtoint (i8** @hello_str to i32), + i32 ptrtoint (i32* @fizz to i32) +), section ".sec2" + + +; 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: 1032 +; CHECK-NEXT: Content: F8FFFFFF04000000 +; CHECK-NEXT: - SectionOffset: 34 +; CHECK-NEXT: InitFlags: 0 +; CHECK-NEXT: Offset: +; CHECK-NEXT: Opcode: I32_CONST +; CHECK-NEXT: Value: 1040 +; CHECK-NEXT: Content: 2A000000F0FFFFFF + 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_SELFREL_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_SELFREL_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); } @@ -417,7 +421,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_SELFREL_I32: return reloc.Addend; case R_WASM_SECTION_OFFSET_I32: return getSectionSymbol(reloc.Index)->section->outputOffset + reloc.Addend; @@ -173,6 +174,14 @@ else llvm_unreachable("unknown init expr opcode"); } + case R_WASM_MEMORY_ADDR_SELFREL_I32: { + const WasmSymbol &sym = wasmObj->syms()[reloc.Index]; + if (sym.isUndefined()) + return 0; + const WasmSegment &segment = + wasmObj->dataSegments()[sym.Info.DataRef.Segment]; + return segment.Data.Offset.Value.Int32; + } case R_WASM_FUNCTION_OFFSET_I32: case R_WASM_FUNCTION_OFFSET_I64: { const WasmSymbol &sym = wasmObj->syms()[reloc.Index]; @@ -199,7 +208,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 +244,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_SELFREL_I32: { if (isa(sym) || sym->isUndefWeak()) return 0; auto D = cast(sym); @@ -245,7 +256,14 @@ // backward compat with old object files built with `-fPIC`. if (D->segment && D->segment->outputSeg->name == ".tdata") return D->getOutputSegmentOffset() + reloc.Addend; - return D->getVirtualAddress() + reloc.Addend; + uint64_t value = D->getVirtualAddress() + reloc.Addend; + if (reloc.Type == R_WASM_MEMORY_ADDR_SELFREL_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 @@ -2,26 +2,27 @@ #error "WASM_RELOC must be defined" #endif -WASM_RELOC(R_WASM_FUNCTION_INDEX_LEB, 0) -WASM_RELOC(R_WASM_TABLE_INDEX_SLEB, 1) -WASM_RELOC(R_WASM_TABLE_INDEX_I32, 2) -WASM_RELOC(R_WASM_MEMORY_ADDR_LEB, 3) -WASM_RELOC(R_WASM_MEMORY_ADDR_SLEB, 4) -WASM_RELOC(R_WASM_MEMORY_ADDR_I32, 5) -WASM_RELOC(R_WASM_TYPE_INDEX_LEB, 6) -WASM_RELOC(R_WASM_GLOBAL_INDEX_LEB, 7) -WASM_RELOC(R_WASM_FUNCTION_OFFSET_I32, 8) -WASM_RELOC(R_WASM_SECTION_OFFSET_I32, 9) -WASM_RELOC(R_WASM_EVENT_INDEX_LEB, 10) -WASM_RELOC(R_WASM_MEMORY_ADDR_REL_SLEB, 11) -WASM_RELOC(R_WASM_TABLE_INDEX_REL_SLEB, 12) -WASM_RELOC(R_WASM_GLOBAL_INDEX_I32, 13) -WASM_RELOC(R_WASM_MEMORY_ADDR_LEB64, 14) -WASM_RELOC(R_WASM_MEMORY_ADDR_SLEB64, 15) -WASM_RELOC(R_WASM_MEMORY_ADDR_I64, 16) -WASM_RELOC(R_WASM_MEMORY_ADDR_REL_SLEB64, 17) -WASM_RELOC(R_WASM_TABLE_INDEX_SLEB64, 18) -WASM_RELOC(R_WASM_TABLE_INDEX_I64, 19) -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_FUNCTION_INDEX_LEB, 0) +WASM_RELOC(R_WASM_TABLE_INDEX_SLEB, 1) +WASM_RELOC(R_WASM_TABLE_INDEX_I32, 2) +WASM_RELOC(R_WASM_MEMORY_ADDR_LEB, 3) +WASM_RELOC(R_WASM_MEMORY_ADDR_SLEB, 4) +WASM_RELOC(R_WASM_MEMORY_ADDR_I32, 5) +WASM_RELOC(R_WASM_TYPE_INDEX_LEB, 6) +WASM_RELOC(R_WASM_GLOBAL_INDEX_LEB, 7) +WASM_RELOC(R_WASM_FUNCTION_OFFSET_I32, 8) +WASM_RELOC(R_WASM_SECTION_OFFSET_I32, 9) +WASM_RELOC(R_WASM_EVENT_INDEX_LEB, 10) +WASM_RELOC(R_WASM_MEMORY_ADDR_REL_SLEB, 11) +WASM_RELOC(R_WASM_TABLE_INDEX_REL_SLEB, 12) +WASM_RELOC(R_WASM_GLOBAL_INDEX_I32, 13) +WASM_RELOC(R_WASM_MEMORY_ADDR_LEB64, 14) +WASM_RELOC(R_WASM_MEMORY_ADDR_SLEB64, 15) +WASM_RELOC(R_WASM_MEMORY_ADDR_I64, 16) +WASM_RELOC(R_WASM_MEMORY_ADDR_REL_SLEB64, 17) +WASM_RELOC(R_WASM_TABLE_INDEX_SLEB64, 18) +WASM_RELOC(R_WASM_TABLE_INDEX_I64, 19) +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_SELFREL_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_SELFREL_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 @@ -439,17 +439,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. @@ -474,7 +492,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. @@ -608,7 +626,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_SELFREL_I32: { // Provisional value is address of the global plus the offset // For undefined symbols, use zero if (!RelEntry.Symbol->isDefined()) @@ -704,6 +723,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_SELFREL_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_SELFREL_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_SELFREL_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_SELFREL_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_SELFREL_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_SELFREL_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/selfrel.ll b/llvm/test/MC/WebAssembly/selfrel.ll new file mode 100644 --- /dev/null +++ b/llvm/test/MC/WebAssembly/selfrel.ll @@ -0,0 +1,49 @@ +; 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_SELFREL_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_SELFREL_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_SELFREL_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" +