1 //===- EhFrame.cpp --------------------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "EhFrame.h" 10 #include "InputFiles.h" 11 12 #include "lld/Common/ErrorHandler.h" 13 #include "llvm/BinaryFormat/Dwarf.h" 14 #include "llvm/Support/Endian.h" 15 16 using namespace llvm; 17 using namespace lld; 18 using namespace lld::macho; 19 using namespace llvm::support::endian; 20 21 uint64_t EhReader::readLength(size_t *off) const { 22 const size_t errOff = *off; 23 if (*off + 4 > data.size()) 24 failOn(errOff, "CIE/FDE too small"); 25 uint64_t len = read32le(data.data() + *off); 26 *off += 4; 27 if (len == dwarf::DW_LENGTH_DWARF64) { 28 // FIXME: test this DWARF64 code path 29 if (*off + 8 > data.size()) 30 failOn(errOff, "CIE/FDE too small"); 31 len = read64le(data.data() + *off); 32 *off += 8; 33 } 34 if (*off + len > data.size()) 35 failOn(errOff, "CIE/FDE extends past the end of the section"); 36 return len; 37 } 38 39 void EhReader::skipValidLength(size_t *off) const { 40 uint32_t len = read32le(data.data() + *off); 41 *off += 4; 42 if (len == dwarf::DW_LENGTH_DWARF64) 43 *off += 8; 44 } 45 46 // Read a byte and advance off by one byte. 47 uint8_t EhReader::readByte(size_t *off) const { 48 if (*off + 1 > data.size()) 49 failOn(*off, "unexpected end of CIE/FDE"); 50 return data[(*off)++]; 51 } 52 53 uint32_t EhReader::readU32(size_t *off) const { 54 if (*off + 4 > data.size()) 55 failOn(*off, "unexpected end of CIE/FDE"); 56 uint32_t v = read32le(data.data() + *off); 57 *off += 4; 58 return v; 59 } 60 61 uint64_t EhReader::readPointer(size_t *off, uint8_t size) const { 62 if (*off + size > data.size()) 63 failOn(*off, "unexpected end of CIE/FDE"); 64 uint64_t v; 65 if (size == 8) 66 v = read64le(data.data() + *off); 67 else { 68 assert(size == 4); 69 v = read32le(data.data() + *off); 70 } 71 *off += size; 72 return v; 73 } 74 75 // Read a null-terminated string. 76 StringRef EhReader::readString(size_t *off) const { 77 if (*off > data.size()) 78 failOn(*off, "corrupted CIE (failed to read string)"); 79 const size_t maxlen = data.size() - *off; 80 auto *c = reinterpret_cast<const char *>(data.data() + *off); 81 size_t len = strnlen(c, maxlen); 82 if (len == maxlen) // we failed to find the null terminator 83 failOn(*off, "corrupted CIE (failed to read string)"); 84 *off += len + 1; // skip the null byte too 85 return StringRef(c, len); 86 } 87 88 void EhReader::skipLeb128(size_t *off) const { 89 const size_t errOff = *off; 90 while (*off < data.size()) { 91 uint8_t val = data[(*off)++]; 92 if ((val & 0x80) == 0) 93 return; 94 } 95 failOn(errOff, "corrupted CIE (failed to read LEB128)"); 96 } 97 98 void EhReader::failOn(size_t errOff, const Twine &msg) const { 99 fatal(toString(file) + ":(__eh_frame+0x" + 100 Twine::utohexstr(dataOff + errOff) + "): " + msg); 101 } 102 103 /* 104 * Create a pair of relocs to write the value of: 105 * `b - (offset + a)` if Invert == false 106 * `(a + offset) - b` if Invert == true 107 */ 108 template <bool Invert = false> 109 static void createSubtraction(PointerUnion<Symbol *, InputSection *> a, 110 PointerUnion<Symbol *, InputSection *> b, 111 uint64_t off, uint8_t length, 112 SmallVectorImpl<Reloc> *newRelocs) { 113 auto subtrahend = a; 114 auto minuend = b; 115 if (Invert) 116 std::swap(subtrahend, minuend); 117 assert(subtrahend.is<Symbol *>()); 118 Reloc subtrahendReloc(target->subtractorRelocType, /*pcrel=*/false, length, 119 off, /*addend=*/0, subtrahend); 120 Reloc minuendReloc(target->unsignedRelocType, /*pcrel=*/false, length, off, 121 (Invert ? 1 : -1) * off, minuend); 122 newRelocs->push_back(subtrahendReloc); 123 newRelocs->push_back(minuendReloc); 124 } 125 126 void EhRelocator::makePcRel(uint64_t off, 127 PointerUnion<Symbol *, InputSection *> target, 128 uint8_t length) { 129 createSubtraction(isec->symbols[0], target, off, length, &newRelocs); 130 } 131 132 void EhRelocator::makeNegativePcRel( 133 uint64_t off, PointerUnion<Symbol *, InputSection *> target, 134 uint8_t length) { 135 createSubtraction</*Invert=*/true>(isec, target, off, length, &newRelocs); 136 } 137 138 void EhRelocator::commit() { 139 isec->relocs.insert(isec->relocs.end(), newRelocs.begin(), newRelocs.end()); 140 } 141