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, msg: "CIE/FDE too small" ); |
25 | uint64_t len = read32le(P: 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, msg: "CIE/FDE too small" ); |
31 | len = read64le(P: data.data() + *off); |
32 | *off += 8; |
33 | } |
34 | if (*off + len > data.size()) |
35 | failOn(errOff, msg: "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(P: 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(errOff: *off, msg: "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(errOff: *off, msg: "unexpected end of CIE/FDE" ); |
56 | uint32_t v = read32le(P: 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(errOff: *off, msg: "unexpected end of CIE/FDE" ); |
64 | uint64_t v; |
65 | if (size == 8) |
66 | v = read64le(P: data.data() + *off); |
67 | else { |
68 | assert(size == 4); |
69 | v = read32le(P: 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(errOff: *off, msg: "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(string: c, maxlen: maxlen); |
82 | if (len == maxlen) // we failed to find the null terminator |
83 | failOn(errOff: *off, msg: "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, msg: "corrupted CIE (failed to read LEB128)" ); |
96 | } |
97 | |
98 | void EhReader::failOn(size_t errOff, const Twine &msg) const { |
99 | fatal(msg: toString(file) + ":(__eh_frame+0x" + |
100 | Twine::utohexstr(Val: 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(a&: subtrahend, b&: 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(Elt: subtrahendReloc); |
123 | newRelocs->push_back(Elt: minuendReloc); |
124 | } |
125 | |
126 | void EhRelocator::makePcRel(uint64_t off, |
127 | PointerUnion<Symbol *, InputSection *> target, |
128 | uint8_t length) { |
129 | createSubtraction(a: isec->symbols[0], b: target, off, length, newRelocs: &newRelocs); |
130 | } |
131 | |
132 | void EhRelocator::makeNegativePcRel( |
133 | uint64_t off, PointerUnion<Symbol *, InputSection *> target, |
134 | uint8_t length) { |
135 | createSubtraction</*Invert=*/true>(a: isec, b: target, off, length, newRelocs: &newRelocs); |
136 | } |
137 | |
138 | void EhRelocator::commit() { |
139 | isec->relocs.insert(position: isec->relocs.end(), first: newRelocs.begin(), last: newRelocs.end()); |
140 | } |
141 | |