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
16using namespace llvm;
17using namespace lld;
18using namespace lld::macho;
19using namespace llvm::support::endian;
20
21uint64_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
39void 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.
47uint8_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
53uint32_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
61uint64_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.
76StringRef 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
88void 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
98void 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 */
108template <bool Invert = false>
109static 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
126void 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
132void 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
138void EhRelocator::commit() {
139  isec->relocs.insert(isec->relocs.end(), newRelocs.begin(), newRelocs.end());
140}
141