//===- 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, uint8_t size) const { if (*off + size > data.size()) failOn(*off, "unexpected end of CIE/FDE"); uint64_t v; if (size == 8) v = read64le(data.data() + *off); else { assert(size == 4); v = read32le(data.data() + *off); } *off += size; 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(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 static void createSubtraction(PointerUnion a, PointerUnion b, uint64_t off, uint8_t length, SmallVectorImpl *newRelocs) { auto subtrahend = a; auto minuend = b; if (Invert) std::swap(subtrahend, minuend); assert(subtrahend.is()); 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 target, uint8_t length) { createSubtraction(isec->symbols[0], target, off, length, &newRelocs); } void EhRelocator::makeNegativePcRel( uint64_t off, PointerUnion target, uint8_t length) { createSubtraction(isec, target, off, length, &newRelocs); } void EhRelocator::commit() { isec->relocs.insert(isec->relocs.end(), newRelocs.begin(), newRelocs.end()); }