[WebAssembly] Verify contents of relocation target before writing it
authorSam Clegg <sbc@chromium.org>
Mon, 12 Mar 2018 19:54:26 +0000 (19:54 +0000)
committerSam Clegg <sbc@chromium.org>
Mon, 12 Mar 2018 19:54:26 +0000 (19:54 +0000)
Verify that the location where a relocation is about the be
applied contains the expected existing value.

This is essentially a sanity check to catch bugs in the compiler
and the linker.

Differential Revision: https://reviews.llvm.org/D44349

llvm-svn: 327325

lld/wasm/InputChunks.cpp
lld/wasm/InputFiles.cpp
lld/wasm/InputFiles.h

index 585d116..904ccef 100644 (file)
@@ -61,6 +61,7 @@ void InputChunk::writeTo(uint8_t *Buf) const {
   for (const WasmRelocation &Rel : Relocations) {
     uint8_t *Loc = Buf + Rel.Offset + Off;
     uint32_t Value = File->calcNewValue(Rel);
+    uint32_t ExistingValue;
     DEBUG(dbgs() << "apply reloc: type=" << ReloctTypeToString(Rel.Type)
                  << " addend=" << Rel.Addend << " index=" << Rel.Index
                  << " value=" << Value << " offset=" << Rel.Offset << "\n");
@@ -70,19 +71,28 @@ void InputChunk::writeTo(uint8_t *Buf) const {
     case R_WEBASSEMBLY_FUNCTION_INDEX_LEB:
     case R_WEBASSEMBLY_GLOBAL_INDEX_LEB:
     case R_WEBASSEMBLY_MEMORY_ADDR_LEB:
+      ExistingValue = decodeULEB128(Loc);
       encodeULEB128(Value, Loc, 5);
       break;
     case R_WEBASSEMBLY_TABLE_INDEX_SLEB:
     case R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
+      ExistingValue = static_cast<uint32_t>(decodeSLEB128(Loc));
       encodeSLEB128(static_cast<int32_t>(Value), Loc, 5);
       break;
     case R_WEBASSEMBLY_TABLE_INDEX_I32:
     case R_WEBASSEMBLY_MEMORY_ADDR_I32:
+      ExistingValue = static_cast<uint32_t>(read32le(Loc));
       write32le(Loc, Value);
       break;
     default:
       llvm_unreachable("unknown relocation type");
     }
+
+    uint32_t ExpectedValue = File->calcExpectedValue(Rel);
+    if (ExpectedValue != ExistingValue)
+      error("unexpected existing value for " + ReloctTypeToString(Rel.Type) +
+            ": existing=" + Twine(ExistingValue) +
+            " expected=" + Twine(ExpectedValue));
   }
 }
 
index 5b69070..c829d57 100644 (file)
@@ -60,6 +60,37 @@ uint32_t ObjFile::calcNewIndex(const WasmRelocation &Reloc) const {
   return Symbols[Reloc.Index]->getOutputSymbolIndex();
 }
 
+// Calculate the value we expect to find at the relocation location.
+// This is used as a sanity check before applying a relocation to a given
+// location.  It is useful for catching bugs in the compiler and linker.
+uint32_t ObjFile::calcExpectedValue(const WasmRelocation &Reloc) const {
+  switch (Reloc.Type) {
+  case R_WEBASSEMBLY_TABLE_INDEX_I32:
+  case R_WEBASSEMBLY_TABLE_INDEX_SLEB: {
+    const WasmSymbol& Sym = WasmObj->syms()[Reloc.Index];
+    return TableEntries[Sym.Info.ElementIndex];
+  }
+  case R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
+  case R_WEBASSEMBLY_MEMORY_ADDR_I32:
+  case R_WEBASSEMBLY_MEMORY_ADDR_LEB: {
+    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 + Sym.Info.DataRef.Offset;
+  }
+  case R_WEBASSEMBLY_TYPE_INDEX_LEB:
+    return Reloc.Index;
+  case R_WEBASSEMBLY_FUNCTION_INDEX_LEB:
+  case R_WEBASSEMBLY_GLOBAL_INDEX_LEB: {
+    const WasmSymbol& Sym = WasmObj->syms()[Reloc.Index];
+    return Sym.Info.ElementIndex;
+  }
+  default:
+    llvm_unreachable("unknown relocation type");
+  }
+}
+
 // Translate from the relocation's index into the final linked output value.
 uint32_t ObjFile::calcNewValue(const WasmRelocation &Reloc) const {
   switch (Reloc.Type) {
@@ -97,6 +128,22 @@ void ObjFile::parse() {
   Bin.release();
   WasmObj.reset(Obj);
 
+  // Build up a map of function indices to table indices for use when
+  // verifying the existing table index relocations
+  uint32_t TotalFunctions =
+      WasmObj->getNumImportedFunctions() + WasmObj->functions().size();
+  TableEntries.resize(TotalFunctions);
+  for (const WasmElemSegment &Seg : WasmObj->elements()) {
+    if (Seg.Offset.Opcode != WASM_OPCODE_I32_CONST)
+      fatal(toString(this) + ": invalid table elements");
+    uint32_t Offset = Seg.Offset.Value.Int32;
+    for (uint32_t Index = 0; Index < Seg.Functions.size(); Index++) {
+
+      uint32_t FunctionIndex = Seg.Functions[Index];
+      TableEntries[FunctionIndex] = Offset + Index;
+    }
+  }
+
   // Find the code and data sections.  Wasm objects can have at most one code
   // and one data section.
   for (const SectionRef &Sec : WasmObj->sections()) {
index 4788201..1239e5b 100644 (file)
@@ -94,12 +94,16 @@ public:
 
   uint32_t calcNewIndex(const WasmRelocation &Reloc) const;
   uint32_t calcNewValue(const WasmRelocation &Reloc) const;
+  uint32_t calcExpectedValue(const WasmRelocation &Reloc) const;
 
   const WasmSection *CodeSection = nullptr;
   const WasmSection *DataSection = nullptr;
 
+  // Maps input type indices to output type indices
   std::vector<uint32_t> TypeMap;
   std::vector<bool> TypeIsUsed;
+  // Maps function indices to table indices
+  std::vector<uint32_t> TableEntries;
   std::vector<InputSegment *> Segments;
   std::vector<InputFunction *> Functions;
   std::vector<InputGlobal *> Globals;