diff options
Diffstat (limited to 'lld/MachO/EhFrame.cpp')
| -rw-r--r-- | lld/MachO/EhFrame.cpp | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/lld/MachO/EhFrame.cpp b/lld/MachO/EhFrame.cpp new file mode 100644 index 000000000000..50d8accc0596 --- /dev/null +++ b/lld/MachO/EhFrame.cpp @@ -0,0 +1,140 @@ +//===- EhFrame.cpp --------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "EhFrame.h" +#include "InputFiles.h" + +#include "lld/Common/ErrorHandler.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace lld; +using namespace lld::macho; +using namespace llvm::support::endian; + +uint64_t EhReader::readLength(size_t *off) const { + const size_t errOff = *off; + if (*off + 4 > data.size()) + failOn(errOff, "CIE/FDE too small"); + uint64_t len = read32le(data.data() + *off); + *off += 4; + if (len == dwarf::DW_LENGTH_DWARF64) { + // FIXME: test this DWARF64 code path + if (*off + 8 > data.size()) + failOn(errOff, "CIE/FDE too small"); + len = read64le(data.data() + *off); + *off += 8; + } + if (*off + len > data.size()) + failOn(errOff, "CIE/FDE extends past the end of the section"); + return len; +} + +void EhReader::skipValidLength(size_t *off) const { + uint32_t len = read32le(data.data() + *off); + *off += 4; + if (len == dwarf::DW_LENGTH_DWARF64) + *off += 8; +} + +// Read a byte and advance off by one byte. +uint8_t EhReader::readByte(size_t *off) const { + if (*off + 1 > data.size()) + failOn(*off, "unexpected end of CIE/FDE"); + return data[(*off)++]; +} + +uint32_t EhReader::readU32(size_t *off) const { + if (*off + 4 > data.size()) + failOn(*off, "unexpected end of CIE/FDE"); + uint32_t v = read32le(data.data() + *off); + *off += 4; + return v; +} + +uint64_t EhReader::readPointer(size_t *off) const { + if (*off + wordSize > data.size()) + failOn(*off, "unexpected end of CIE/FDE"); + uint64_t v; + if (wordSize == 8) + v = read64le(data.data() + *off); + else { + assert(wordSize == 4); + v = read32le(data.data() + *off); + } + *off += wordSize; + return v; +} + +// Read a null-terminated string. +StringRef EhReader::readString(size_t *off) const { + if (*off > data.size()) + failOn(*off, "corrupted CIE (failed to read string)"); + const size_t maxlen = data.size() - *off; + auto *c = reinterpret_cast<const char *>(data.data() + *off); + size_t len = strnlen(c, maxlen); + if (len == maxlen) // we failed to find the null terminator + failOn(*off, "corrupted CIE (failed to read string)"); + *off += len + 1; // skip the null byte too + return StringRef(c, len); +} + +void EhReader::skipLeb128(size_t *off) const { + const size_t errOff = *off; + while (*off < data.size()) { + uint8_t val = data[(*off)++]; + if ((val & 0x80) == 0) + return; + } + failOn(errOff, "corrupted CIE (failed to read LEB128)"); +} + +void EhReader::failOn(size_t errOff, const Twine &msg) const { + fatal(toString(file) + ":(__eh_frame+0x" + + Twine::utohexstr(dataOff + errOff) + "): " + msg); +} + +/* + * Create a pair of relocs to write the value of: + * `b - (offset + a)` if Invert == false + * `(a + offset) - b` if Invert == true + */ +template <bool Invert = false> +static void createSubtraction(PointerUnion<Symbol *, InputSection *> a, + PointerUnion<Symbol *, InputSection *> b, + uint64_t off, uint8_t length, + SmallVectorImpl<Reloc> *newRelocs) { + auto subtrahend = a; + auto minuend = b; + if (Invert) + std::swap(subtrahend, minuend); + assert(subtrahend.is<Symbol *>()); + Reloc subtrahendReloc(target->subtractorRelocType, /*pcrel=*/false, length, + off, /*addend=*/0, subtrahend); + Reloc minuendReloc(target->unsignedRelocType, /*pcrel=*/false, length, off, + (Invert ? 1 : -1) * off, minuend); + newRelocs->push_back(subtrahendReloc); + newRelocs->push_back(minuendReloc); +} + +void EhRelocator::makePcRel(uint64_t off, + PointerUnion<Symbol *, InputSection *> target, + uint8_t length) { + createSubtraction(isec->symbols[0], target, off, length, &newRelocs); +} + +void EhRelocator::makeNegativePcRel( + uint64_t off, PointerUnion<Symbol *, InputSection *> target, + uint8_t length) { + createSubtraction</*Invert=*/true>(isec, target, off, length, &newRelocs); +} + +void EhRelocator::commit() { + isec->relocs.insert(isec->relocs.end(), newRelocs.begin(), newRelocs.end()); +} |
