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