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