Skip to content

Commit

Permalink
[WebAssembly] Add new relocation for location relative data
Browse files Browse the repository at this point in the history
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: WebAssembly/tool-conventions#162

Differential Revision: https://reviews.llvm.org/D96659
  • Loading branch information
kateinoigakukun authored and arichardson committed Mar 30, 2021
2 parents 3e8a899 + aa0c571 commit 31cbb1f
Show file tree
Hide file tree
Showing 13 changed files with 226 additions and 28 deletions.
89 changes: 89 additions & 0 deletions lld/test/wasm/reloc-relative.s
Original file line number Diff line number Diff line change
@@ -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'

12 changes: 8 additions & 4 deletions lld/wasm/InputChunks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ void InputChunk::verifyRelocTargets() const {
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:
Expand Down Expand Up @@ -139,7 +140,7 @@ void InputChunk::writeTo(uint8_t *buf) const {

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());
Expand Down Expand Up @@ -176,6 +177,7 @@ void InputChunk::writeTo(uint8_t *buf) const {
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:
Expand Down Expand Up @@ -302,7 +304,8 @@ void InputFunction::calculateSize() {
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");
Expand Down Expand Up @@ -343,7 +346,8 @@ void InputFunction::writeTo(uint8_t *buf) const {
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);
}

Expand Down Expand Up @@ -416,7 +420,7 @@ void InputSegment::generateRelocationCode(raw_ostream &os) const {
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");
}

Expand Down
20 changes: 16 additions & 4 deletions lld/wasm/InputFiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ uint64_t ObjFile::calcNewAddend(const WasmRelocation &reloc) const {
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);
Expand Down Expand Up @@ -158,7 +159,8 @@ uint64_t ObjFile::calcExpectedValue(const WasmRelocation &reloc) const {
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;
Expand Down Expand Up @@ -199,7 +201,8 @@ uint64_t ObjFile::calcExpectedValue(const WasmRelocation &reloc) const {
}

// 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];
Expand Down Expand Up @@ -234,7 +237,8 @@ uint64_t ObjFile::calcNewValue(const WasmRelocation &reloc, uint64_t tombstone)
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);
Expand All @@ -245,7 +249,15 @@ uint64_t ObjFile::calcNewValue(const WasmRelocation &reloc, uint64_t tombstone)
// 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())
Expand Down
3 changes: 2 additions & 1 deletion lld/wasm/InputFiles.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@ class ObjFile : public InputFile {
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 {
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/BinaryFormat/WasmRelocs.def
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ 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_LOCREL_I32, 23)
4 changes: 2 additions & 2 deletions llvm/include/llvm/MC/MCWasmObjectWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ class MCWasmObjectTargetWriter : public MCObjectTargetWriter {
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
/// @{
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/BinaryFormat/Wasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ bool llvm::wasm::relocTypeHasAddend(uint32_t Type) {
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;
Expand Down
40 changes: 30 additions & 10 deletions llvm/lib/MC/WasmObjectWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -445,17 +445,35 @@ void WasmObjectWriter::recordRelocation(MCAssembler &Asm,
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<MCSymbolWasm>(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.
Expand All @@ -480,7 +498,7 @@ void WasmObjectWriter::recordRelocation(MCAssembler &Asm,
// 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.
Expand Down Expand Up @@ -611,7 +629,8 @@ WasmObjectWriter::getProvisionalValue(const WasmRelocationEntry &RelEntry,
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())
Expand Down Expand Up @@ -707,6 +726,7 @@ void WasmObjectWriter::applyRelocations(
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:
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Object/RelocationResolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,7 @@ static bool supportsWasm32(uint64_t Type) {
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;
Expand Down Expand Up @@ -611,6 +612,7 @@ static uint64_t resolveWasm32(uint64_t Type, uint64_t Offset, uint64_t S,
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:
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Object/WasmObjectFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -905,6 +905,7 @@ Error WasmObjectFile::parseRelocSection(StringRef Name, ReadContext &Ctx) {
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<GenericBinaryError>("invalid relocation data index",
object_error::parse_failed);
Expand Down Expand Up @@ -953,6 +954,7 @@ Error WasmObjectFile::parseRelocSection(StringRef Name, ReadContext &Ctx) {
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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ class WebAssemblyWasmObjectWriter final : public MCWasmObjectTargetWriter {
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

Expand Down Expand Up @@ -63,7 +63,8 @@ static const MCSection *getFixupSection(const MCExpr *Expr) {
}

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<MCSymbolWasm>(RefA->getSymbol());
Expand Down Expand Up @@ -122,7 +123,8 @@ unsigned WebAssemblyWasmObjectWriter::getRelocType(const MCValue &Target,
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;
Expand Down
Loading

0 comments on commit 31cbb1f

Please sign in to comment.