diff --git a/lld/test/wasm/reloc-addend.s b/lld/test/wasm/reloc-addend.s --- a/lld/test/wasm/reloc-addend.s +++ b/lld/test/wasm/reloc-addend.s @@ -1,6 +1,7 @@ # RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s # RUN: wasm-ld -r -o %t.wasm %t.o # RUN: obj2yaml %t.wasm | FileCheck %s +# RUN: llvm-objdump --disassemble-symbols=_start --no-show-raw-insn --no-leading-addr %t.wasm | FileCheck %s --check-prefixes DIS .hidden foo .hidden bar @@ -30,6 +31,39 @@ .int32 foo-16 .size negative_addend, 4 +.globl _start +.section .text,"",@ +_start: + .functype _start () -> () + i32.const 0 + i32.load foo + 10 + drop + i32.const 0 + i32.load foo - 10 + drop + i32.const 0 + # This will underflow because i32.load (and the + # corresponding relocation type) take an unsgiend (U32) + # immediate. + i32.load foo - 2048 + drop + end_function + +# CHECK: - Type: CODE +# CHECK-NEXT: Relocations: +# CHECK-NEXT: - Type: R_WASM_MEMORY_ADDR_LEB +# CHECK-NEXT: Index: 0 +# CHECK-NEXT: Offset: 0x7 +# CHECK-NEXT: Addend: 10 +# CHECK-NEXT: - Type: R_WASM_MEMORY_ADDR_LEB +# CHECK-NEXT: Index: 0 +# CHECK-NEXT: Offset: 0x11 +# CHECK-NEXT: Addend: -10 +# CHECK-NEXT: - Type: R_WASM_MEMORY_ADDR_LEB +# CHECK-NEXT: Index: 0 +# CHECK-NEXT: Offset: 0x1B +# CHECK-NEXT: Addend: -2048 + # CHECK: - Type: DATA # CHECK-NEXT: Relocations: # CHECK-NEXT: - Type: R_WASM_MEMORY_ADDR_I32 @@ -40,3 +74,17 @@ # CHECK-NEXT: Index: 0 # CHECK-NEXT: Offset: 0xF # CHECK-NEXT: Addend: -16 + +# DIS: <_start>: +# DIS-EMPTY: +# DIS-NEXT: i32.const 0 +# DIS-NEXT: i32.load 26 +# DIS-NEXT: drop +# DIS-NEXT: i32.const 0 +# DIS-NEXT: i32.load 6 +# DIS-NEXT: drop +# DIS-NEXT: i32.const 0 +# TODO(sbc): We should probably error here rather than allowing u32 to wrap +# DIS-NEXT: i32.load 4294965264 +# DIS-NEXT: drop +# DIS-NEXT: end diff --git a/lld/wasm/InputChunks.cpp b/lld/wasm/InputChunks.cpp --- a/lld/wasm/InputChunks.cpp +++ b/lld/wasm/InputChunks.cpp @@ -116,7 +116,7 @@ LLVM_DEBUG(dbgs() << " sym=" << file->getSymbols()[rel.Index]->getName()); LLVM_DEBUG(dbgs() << " addend=" << rel.Addend << " index=" << rel.Index << " offset=" << rel.Offset << "\n"); - auto value = file->calcNewValue(rel, tombstone, this); + uint64_t value = file->calcNewValue(rel, tombstone, this); switch (rel.Type) { case R_WASM_TYPE_INDEX_LEB: @@ -125,7 +125,7 @@ case R_WASM_TAG_INDEX_LEB: case R_WASM_MEMORY_ADDR_LEB: case R_WASM_TABLE_NUMBER_LEB: - encodeULEB128(value, loc, 5); + encodeULEB128(static_cast(value), loc, 5); break; case R_WASM_MEMORY_ADDR_LEB64: encodeULEB128(value, loc, 10); 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,7 @@ uint32_t calcNewIndex(const WasmRelocation &reloc) const; uint64_t calcNewValue(const WasmRelocation &reloc, uint64_t tombstone, const InputChunk *chunk) const; - uint64_t calcNewAddend(const WasmRelocation &reloc) const; + int64_t calcNewAddend(const WasmRelocation &reloc) const; Symbol *getSymbol(const WasmRelocation &reloc) const { return symbols[reloc.Index]; }; diff --git a/lld/wasm/InputFiles.cpp b/lld/wasm/InputFiles.cpp --- a/lld/wasm/InputFiles.cpp +++ b/lld/wasm/InputFiles.cpp @@ -106,7 +106,7 @@ // Relocations can contain addend for combined sections. This function takes a // relocation and returns updated addend by offset in the output section. -uint64_t ObjFile::calcNewAddend(const WasmRelocation &reloc) const { +int64_t ObjFile::calcNewAddend(const WasmRelocation &reloc) const { switch (reloc.Type) { case R_WASM_MEMORY_ADDR_LEB: case R_WASM_MEMORY_ADDR_LEB64: 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 @@ -137,36 +137,58 @@ } #endif -// Write X as an (unsigned) LEB value at offset Offset in Stream, padded +// Write Value as an (unsigned) LEB value at offset Offset in Stream, padded // to allow patching. -template -void writePatchableLEB(raw_pwrite_stream &Stream, uint64_t X, uint64_t Offset) { +template +void writePatchableULEB(raw_pwrite_stream &Stream, T Value, uint64_t Offset) { uint8_t Buffer[W]; - unsigned SizeLen = encodeULEB128(X, Buffer, W); + unsigned SizeLen = encodeULEB128(Value, Buffer, W); assert(SizeLen == W); Stream.pwrite((char *)Buffer, SizeLen, Offset); } -// Write X as an signed LEB value at offset Offset in Stream, padded +// Write Value as an signed LEB value at offset Offset in Stream, padded // to allow patching. -template -void writePatchableSLEB(raw_pwrite_stream &Stream, int64_t X, uint64_t Offset) { +template +void writePatchableSLEB(raw_pwrite_stream &Stream, T Value, uint64_t Offset) { uint8_t Buffer[W]; - unsigned SizeLen = encodeSLEB128(X, Buffer, W); + unsigned SizeLen = encodeSLEB128(Value, Buffer, W); assert(SizeLen == W); Stream.pwrite((char *)Buffer, SizeLen, Offset); } -// Write X as a plain integer value at offset Offset in Stream. -static void patchI32(raw_pwrite_stream &Stream, uint32_t X, uint64_t Offset) { +static void writePatchableU32(raw_pwrite_stream &Stream, uint32_t Value, + uint64_t Offset) { + writePatchableULEB(Stream, Value, Offset); +} + +static void writePatchableS32(raw_pwrite_stream &Stream, int32_t Value, + uint64_t Offset) { + writePatchableSLEB(Stream, Value, Offset); +} + +static void writePatchableU64(raw_pwrite_stream &Stream, uint64_t Value, + uint64_t Offset) { + writePatchableSLEB(Stream, Value, Offset); +} + +static void writePatchableS64(raw_pwrite_stream &Stream, int64_t Value, + uint64_t Offset) { + writePatchableSLEB(Stream, Value, Offset); +} + +// Write Value as a plain integer value at offset Offset in Stream. +static void patchI32(raw_pwrite_stream &Stream, uint32_t Value, + uint64_t Offset) { uint8_t Buffer[4]; - support::endian::write32le(Buffer, X); + support::endian::write32le(Buffer, Value); Stream.pwrite((char *)Buffer, sizeof(Buffer), Offset); } -static void patchI64(raw_pwrite_stream &Stream, uint64_t X, uint64_t Offset) { +static void patchI64(raw_pwrite_stream &Stream, uint64_t Value, + uint64_t Offset) { uint8_t Buffer[8]; - support::endian::write64le(Buffer, X); + support::endian::write64le(Buffer, Value); Stream.pwrite((char *)Buffer, sizeof(Buffer), Offset); } @@ -329,8 +351,8 @@ const MCAssembler &Asm, const MCAsmLayout &Layout); void writeCustomRelocSections(); - uint64_t getProvisionalValue(const WasmRelocationEntry &RelEntry, - const MCAsmLayout &Layout); + int64_t getProvisionalValue(const WasmRelocationEntry &RelEntry, + const MCAsmLayout &Layout); void applyRelocations(ArrayRef Relocations, uint64_t ContentsOffset, const MCAsmLayout &Layout); @@ -420,8 +442,8 @@ // Write the final section size to the payload_len field, which follows // the section id byte. - writePatchableLEB<5>(static_cast(W->OS), Size, - Section.SizeOffset); + writePatchableU32(static_cast(W->OS), Size, + Section.SizeOffset); } // Emit the Wasm header. @@ -618,7 +640,7 @@ // by RelEntry. This value isn't used by the static linker; it just serves // to make the object format more readable and more likely to be directly // useable. -uint64_t +int64_t WasmObjectWriter::getProvisionalValue(const WasmRelocationEntry &RelEntry, const MCAsmLayout &Layout) { if ((RelEntry.Type == wasm::R_WASM_GLOBAL_INDEX_LEB || @@ -752,7 +774,7 @@ RelEntry.Offset; LLVM_DEBUG(dbgs() << "applyRelocation: " << RelEntry << "\n"); - auto Value = getProvisionalValue(RelEntry, Layout); + int64_t Value = getProvisionalValue(RelEntry, Layout); switch (RelEntry.Type) { case wasm::R_WASM_FUNCTION_INDEX_LEB: @@ -761,10 +783,10 @@ case wasm::R_WASM_MEMORY_ADDR_LEB: case wasm::R_WASM_TAG_INDEX_LEB: case wasm::R_WASM_TABLE_NUMBER_LEB: - writePatchableLEB<5>(Stream, Value, Offset); + writePatchableU32(Stream, Value, Offset); break; case wasm::R_WASM_MEMORY_ADDR_LEB64: - writePatchableLEB<10>(Stream, Value, Offset); + writePatchableU64(Stream, Value, Offset); break; case wasm::R_WASM_TABLE_INDEX_I32: case wasm::R_WASM_MEMORY_ADDR_I32: @@ -784,14 +806,14 @@ case wasm::R_WASM_MEMORY_ADDR_SLEB: case wasm::R_WASM_MEMORY_ADDR_REL_SLEB: case wasm::R_WASM_MEMORY_ADDR_TLS_SLEB: - writePatchableSLEB<5>(Stream, Value, Offset); + writePatchableS32(Stream, Value, Offset); break; case wasm::R_WASM_TABLE_INDEX_SLEB64: case wasm::R_WASM_TABLE_INDEX_REL_SLEB64: case wasm::R_WASM_MEMORY_ADDR_SLEB64: case wasm::R_WASM_MEMORY_ADDR_REL_SLEB64: case wasm::R_WASM_MEMORY_ADDR_TLS_SLEB64: - writePatchableSLEB<10>(Stream, Value, Offset); + writePatchableS64(Stream, Value, Offset); break; default: llvm_unreachable("invalid relocation type"); diff --git a/llvm/test/MC/WebAssembly/reloc-code.s b/llvm/test/MC/WebAssembly/reloc-code.s --- a/llvm/test/MC/WebAssembly/reloc-code.s +++ b/llvm/test/MC/WebAssembly/reloc-code.s @@ -14,12 +14,12 @@ # Call functions at `a` and `b` indirectly. i32.const 0 - i32.load a + i32.load a - 10 call_indirect () -> (i64) drop i32.const 0 - i32.load b + i32.load b + 20 call_indirect () -> (i32) drop @@ -49,7 +49,7 @@ # CHECK-NEXT: Type: R_WASM_MEMORY_ADDR_LEB (3) # CHECK-NEXT: Offset: 0x7 # CHECK-NEXT: Symbol: a -# CHECK-NEXT: Addend: 0 +# CHECK-NEXT: Addend: -10 # CHECK-NEXT: } # CHECK-NEXT: Relocation { # CHECK-NEXT: Type: R_WASM_TYPE_INDEX_LEB (6) @@ -60,7 +60,7 @@ # CHECK-NEXT: Type: R_WASM_MEMORY_ADDR_LEB (3) # CHECK-NEXT: Offset: 0x18 # CHECK-NEXT: Symbol: b -# CHECK-NEXT: Addend: 0 +# CHECK-NEXT: Addend: 20 # CHECK-NEXT: } # CHECK-NEXT: Relocation { # CHECK-NEXT: Type: R_WASM_TYPE_INDEX_LEB (6) @@ -87,7 +87,7 @@ # REF-NEXT: Type: R_WASM_MEMORY_ADDR_LEB (3) # REF-NEXT: Offset: 0x7 # REF-NEXT: Symbol: a -# REF-NEXT: Addend: 0 +# REF-NEXT: Addend: -10 # REF-NEXT: } # REF-NEXT: Relocation { # REF-NEXT: Type: R_WASM_TYPE_INDEX_LEB (6) @@ -103,7 +103,7 @@ # REF-NEXT: Type: R_WASM_MEMORY_ADDR_LEB (3) # REF-NEXT: Offset: 0x1C # REF-NEXT: Symbol: b -# REF-NEXT: Addend: 0 +# REF-NEXT: Addend: 20 # REF-NEXT: } # REF-NEXT: Relocation { # REF-NEXT: Type: R_WASM_TYPE_INDEX_LEB (6)