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