diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2018-07-28 11:08:33 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2018-07-28 11:08:33 +0000 |
commit | 20d35e67e67f106f617c939725101223211659f0 (patch) | |
tree | 64eb963cbf5ba58765e0a6b64a440965d66a7a4d /wasm/InputChunks.cpp | |
parent | ae1a339de31cf4065777531959a11e55a2e5fa00 (diff) |
Notes
Diffstat (limited to 'wasm/InputChunks.cpp')
-rw-r--r-- | wasm/InputChunks.cpp | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/wasm/InputChunks.cpp b/wasm/InputChunks.cpp new file mode 100644 index 000000000000..fcefac7d99b8 --- /dev/null +++ b/wasm/InputChunks.cpp @@ -0,0 +1,295 @@ +//===- InputChunks.cpp ----------------------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "InputChunks.h" +#include "Config.h" +#include "OutputSegment.h" +#include "WriterUtils.h" +#include "lld/Common/ErrorHandler.h" +#include "lld/Common/LLVM.h" +#include "llvm/Support/LEB128.h" + +#define DEBUG_TYPE "lld" + +using namespace llvm; +using namespace llvm::wasm; +using namespace llvm::support::endian; +using namespace lld; +using namespace lld::wasm; + +static StringRef ReloctTypeToString(uint8_t RelocType) { + switch (RelocType) { +#define WASM_RELOC(NAME, REL) case REL: return #NAME; +#include "llvm/BinaryFormat/WasmRelocs.def" +#undef WASM_RELOC + } + llvm_unreachable("unknown reloc type"); +} + +std::string lld::toString(const InputChunk *C) { + return (toString(C->File) + ":(" + C->getName() + ")").str(); +} + +StringRef InputChunk::getComdatName() const { + uint32_t Index = getComdat(); + if (Index == UINT32_MAX) + return StringRef(); + return File->getWasmObj()->linkingData().Comdats[Index]; +} + +void InputChunk::copyRelocations(const WasmSection &Section) { + if (Section.Relocations.empty()) + return; + size_t Start = getInputSectionOffset(); + size_t Size = getInputSize(); + for (const WasmRelocation &R : Section.Relocations) + if (R.Offset >= Start && R.Offset < Start + Size) + Relocations.push_back(R); +} + +void InputChunk::verifyRelocTargets() const { + for (const WasmRelocation &Rel : Relocations) { + uint32_t ExistingValue; + unsigned BytesRead = 0; + uint32_t Offset = Rel.Offset - getInputSectionOffset(); + const uint8_t *Loc = data().data() + Offset; + switch (Rel.Type) { + case R_WEBASSEMBLY_TYPE_INDEX_LEB: + case R_WEBASSEMBLY_FUNCTION_INDEX_LEB: + case R_WEBASSEMBLY_GLOBAL_INDEX_LEB: + case R_WEBASSEMBLY_MEMORY_ADDR_LEB: + ExistingValue = decodeULEB128(Loc, &BytesRead); + break; + case R_WEBASSEMBLY_TABLE_INDEX_SLEB: + case R_WEBASSEMBLY_MEMORY_ADDR_SLEB: + ExistingValue = static_cast<uint32_t>(decodeSLEB128(Loc, &BytesRead)); + break; + case R_WEBASSEMBLY_TABLE_INDEX_I32: + case R_WEBASSEMBLY_MEMORY_ADDR_I32: + case R_WEBASSEMBLY_FUNCTION_OFFSET_I32: + case R_WEBASSEMBLY_SECTION_OFFSET_I32: + ExistingValue = static_cast<uint32_t>(read32le(Loc)); + break; + default: + llvm_unreachable("unknown relocation type"); + } + + if (BytesRead && BytesRead != 5) + warn("expected LEB at relocation site be 5-byte padded"); + uint32_t ExpectedValue = File->calcExpectedValue(Rel); + if (ExpectedValue != ExistingValue) + warn("unexpected existing value for " + ReloctTypeToString(Rel.Type) + + ": existing=" + Twine(ExistingValue) + + " expected=" + Twine(ExpectedValue)); + } +} + +// Copy this input chunk to an mmap'ed output file and apply relocations. +void InputChunk::writeTo(uint8_t *Buf) const { + // Copy contents + memcpy(Buf + OutputOffset, data().data(), data().size()); + + // Apply relocations + if (Relocations.empty()) + return; + +#ifndef NDEBUG + verifyRelocTargets(); +#endif + + LLVM_DEBUG(dbgs() << "applying relocations: " << getName() + << " count=" << Relocations.size() << "\n"); + int32_t Off = OutputOffset - getInputSectionOffset(); + + for (const WasmRelocation &Rel : Relocations) { + uint8_t *Loc = Buf + Rel.Offset + Off; + uint32_t Value = File->calcNewValue(Rel); + LLVM_DEBUG(dbgs() << "apply reloc: type=" << ReloctTypeToString(Rel.Type) + << " addend=" << Rel.Addend << " index=" << Rel.Index + << " value=" << Value << " offset=" << Rel.Offset + << "\n"); + + switch (Rel.Type) { + case R_WEBASSEMBLY_TYPE_INDEX_LEB: + case R_WEBASSEMBLY_FUNCTION_INDEX_LEB: + case R_WEBASSEMBLY_GLOBAL_INDEX_LEB: + case R_WEBASSEMBLY_MEMORY_ADDR_LEB: + encodeULEB128(Value, Loc, 5); + break; + case R_WEBASSEMBLY_TABLE_INDEX_SLEB: + case R_WEBASSEMBLY_MEMORY_ADDR_SLEB: + encodeSLEB128(static_cast<int32_t>(Value), Loc, 5); + break; + case R_WEBASSEMBLY_TABLE_INDEX_I32: + case R_WEBASSEMBLY_MEMORY_ADDR_I32: + case R_WEBASSEMBLY_FUNCTION_OFFSET_I32: + case R_WEBASSEMBLY_SECTION_OFFSET_I32: + write32le(Loc, Value); + break; + default: + llvm_unreachable("unknown relocation type"); + } + } +} + +// Copy relocation entries to a given output stream. +// This function is used only when a user passes "-r". For a regular link, +// we consume relocations instead of copying them to an output file. +void InputChunk::writeRelocations(raw_ostream &OS) const { + if (Relocations.empty()) + return; + + int32_t Off = OutputOffset - getInputSectionOffset(); + LLVM_DEBUG(dbgs() << "writeRelocations: " << File->getName() + << " offset=" << Twine(Off) << "\n"); + + for (const WasmRelocation &Rel : Relocations) { + writeUleb128(OS, Rel.Type, "reloc type"); + writeUleb128(OS, Rel.Offset + Off, "reloc offset"); + writeUleb128(OS, File->calcNewIndex(Rel), "reloc index"); + + switch (Rel.Type) { + case R_WEBASSEMBLY_MEMORY_ADDR_LEB: + case R_WEBASSEMBLY_MEMORY_ADDR_SLEB: + case R_WEBASSEMBLY_MEMORY_ADDR_I32: + case R_WEBASSEMBLY_FUNCTION_OFFSET_I32: + case R_WEBASSEMBLY_SECTION_OFFSET_I32: + writeSleb128(OS, File->calcNewAddend(Rel), "reloc addend"); + break; + } + } +} + +void InputFunction::setFunctionIndex(uint32_t Index) { + LLVM_DEBUG(dbgs() << "InputFunction::setFunctionIndex: " << getName() + << " -> " << Index << "\n"); + assert(!hasFunctionIndex()); + FunctionIndex = Index; +} + +void InputFunction::setTableIndex(uint32_t Index) { + LLVM_DEBUG(dbgs() << "InputFunction::setTableIndex: " << getName() << " -> " + << Index << "\n"); + assert(!hasTableIndex()); + TableIndex = Index; +} + +// Write a relocation value without padding and return the number of bytes +// witten. +static unsigned writeCompressedReloc(uint8_t *Buf, const WasmRelocation &Rel, + uint32_t Value) { + switch (Rel.Type) { + case R_WEBASSEMBLY_TYPE_INDEX_LEB: + case R_WEBASSEMBLY_FUNCTION_INDEX_LEB: + case R_WEBASSEMBLY_GLOBAL_INDEX_LEB: + case R_WEBASSEMBLY_MEMORY_ADDR_LEB: + return encodeULEB128(Value, Buf); + case R_WEBASSEMBLY_TABLE_INDEX_SLEB: + case R_WEBASSEMBLY_MEMORY_ADDR_SLEB: + return encodeSLEB128(static_cast<int32_t>(Value), Buf); + default: + llvm_unreachable("unexpected relocation type"); + } +} + +static unsigned getRelocWidthPadded(const WasmRelocation &Rel) { + switch (Rel.Type) { + case R_WEBASSEMBLY_TYPE_INDEX_LEB: + case R_WEBASSEMBLY_FUNCTION_INDEX_LEB: + case R_WEBASSEMBLY_GLOBAL_INDEX_LEB: + case R_WEBASSEMBLY_MEMORY_ADDR_LEB: + case R_WEBASSEMBLY_TABLE_INDEX_SLEB: + case R_WEBASSEMBLY_MEMORY_ADDR_SLEB: + return 5; + default: + llvm_unreachable("unexpected relocation type"); + } +} + +static unsigned getRelocWidth(const WasmRelocation &Rel, uint32_t Value) { + uint8_t Buf[5]; + return writeCompressedReloc(Buf, Rel, Value); +} + +// Relocations of type LEB and SLEB in the code section are padded to 5 bytes +// so that a fast linker can blindly overwrite them without needing to worry +// about the number of bytes needed to encode the values. +// However, for optimal output the code section can be compressed to remove +// the padding then outputting non-relocatable files. +// In this case we need to perform a size calculation based on the value at each +// relocation. At best we end up saving 4 bytes for each relocation entry. +// +// This function only computes the final output size. It must be called +// before getSize() is used to calculate of layout of the code section. +void InputFunction::calculateSize() { + if (!File || !Config->CompressRelocTargets) + return; + + LLVM_DEBUG(dbgs() << "calculateSize: " << getName() << "\n"); + + const uint8_t *SecStart = File->CodeSection->Content.data(); + const uint8_t *FuncStart = SecStart + getInputSectionOffset(); + uint32_t FunctionSizeLength; + decodeULEB128(FuncStart, &FunctionSizeLength); + + uint32_t Start = getInputSectionOffset(); + uint32_t End = Start + Function->Size; + + uint32_t LastRelocEnd = Start + FunctionSizeLength; + for (WasmRelocation &Rel : Relocations) { + LLVM_DEBUG(dbgs() << " region: " << (Rel.Offset - LastRelocEnd) << "\n"); + CompressedFuncSize += Rel.Offset - LastRelocEnd; + CompressedFuncSize += getRelocWidth(Rel, File->calcNewValue(Rel)); + LastRelocEnd = Rel.Offset + getRelocWidthPadded(Rel); + } + LLVM_DEBUG(dbgs() << " final region: " << (End - LastRelocEnd) << "\n"); + CompressedFuncSize += End - LastRelocEnd; + + // Now we know how long the resulting function is we can add the encoding + // of its length + uint8_t Buf[5]; + CompressedSize = CompressedFuncSize + encodeULEB128(CompressedFuncSize, Buf); + + LLVM_DEBUG(dbgs() << " calculateSize orig: " << Function->Size << "\n"); + LLVM_DEBUG(dbgs() << " calculateSize new: " << CompressedSize << "\n"); +} + +// Override the default writeTo method so that we can (optionally) write the +// compressed version of the function. +void InputFunction::writeTo(uint8_t *Buf) const { + if (!File || !Config->CompressRelocTargets) + return InputChunk::writeTo(Buf); + + Buf += OutputOffset; + uint8_t *Orig = Buf; (void)Orig; + + const uint8_t *SecStart = File->CodeSection->Content.data(); + const uint8_t *FuncStart = SecStart + getInputSectionOffset(); + const uint8_t *End = FuncStart + Function->Size; + uint32_t Count; + decodeULEB128(FuncStart, &Count); + FuncStart += Count; + + LLVM_DEBUG(dbgs() << "write func: " << getName() << "\n"); + Buf += encodeULEB128(CompressedFuncSize, Buf); + const uint8_t *LastRelocEnd = FuncStart; + for (const WasmRelocation &Rel : Relocations) { + unsigned ChunkSize = (SecStart + Rel.Offset) - LastRelocEnd; + LLVM_DEBUG(dbgs() << " write chunk: " << ChunkSize << "\n"); + memcpy(Buf, LastRelocEnd, ChunkSize); + Buf += ChunkSize; + Buf += writeCompressedReloc(Buf, Rel, File->calcNewValue(Rel)); + LastRelocEnd = SecStart + Rel.Offset + getRelocWidthPadded(Rel); + } + + unsigned ChunkSize = End - LastRelocEnd; + LLVM_DEBUG(dbgs() << " write final chunk: " << ChunkSize << "\n"); + memcpy(Buf, LastRelocEnd, ChunkSize); + LLVM_DEBUG(dbgs() << " total: " << (Buf + ChunkSize - Orig) << "\n"); +} |