aboutsummaryrefslogtreecommitdiff
path: root/lld/MachO/EhFrame.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lld/MachO/EhFrame.cpp')
-rw-r--r--lld/MachO/EhFrame.cpp140
1 files changed, 140 insertions, 0 deletions
diff --git a/lld/MachO/EhFrame.cpp b/lld/MachO/EhFrame.cpp
new file mode 100644
index 000000000000..50d8accc0596
--- /dev/null
+++ b/lld/MachO/EhFrame.cpp
@@ -0,0 +1,140 @@
+//===- EhFrame.cpp --------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "EhFrame.h"
+#include "InputFiles.h"
+
+#include "lld/Common/ErrorHandler.h"
+#include "llvm/BinaryFormat/Dwarf.h"
+#include "llvm/Support/Endian.h"
+
+using namespace llvm;
+using namespace lld;
+using namespace lld::macho;
+using namespace llvm::support::endian;
+
+uint64_t EhReader::readLength(size_t *off) const {
+ const size_t errOff = *off;
+ if (*off + 4 > data.size())
+ failOn(errOff, "CIE/FDE too small");
+ uint64_t len = read32le(data.data() + *off);
+ *off += 4;
+ if (len == dwarf::DW_LENGTH_DWARF64) {
+ // FIXME: test this DWARF64 code path
+ if (*off + 8 > data.size())
+ failOn(errOff, "CIE/FDE too small");
+ len = read64le(data.data() + *off);
+ *off += 8;
+ }
+ if (*off + len > data.size())
+ failOn(errOff, "CIE/FDE extends past the end of the section");
+ return len;
+}
+
+void EhReader::skipValidLength(size_t *off) const {
+ uint32_t len = read32le(data.data() + *off);
+ *off += 4;
+ if (len == dwarf::DW_LENGTH_DWARF64)
+ *off += 8;
+}
+
+// Read a byte and advance off by one byte.
+uint8_t EhReader::readByte(size_t *off) const {
+ if (*off + 1 > data.size())
+ failOn(*off, "unexpected end of CIE/FDE");
+ return data[(*off)++];
+}
+
+uint32_t EhReader::readU32(size_t *off) const {
+ if (*off + 4 > data.size())
+ failOn(*off, "unexpected end of CIE/FDE");
+ uint32_t v = read32le(data.data() + *off);
+ *off += 4;
+ return v;
+}
+
+uint64_t EhReader::readPointer(size_t *off) const {
+ if (*off + wordSize > data.size())
+ failOn(*off, "unexpected end of CIE/FDE");
+ uint64_t v;
+ if (wordSize == 8)
+ v = read64le(data.data() + *off);
+ else {
+ assert(wordSize == 4);
+ v = read32le(data.data() + *off);
+ }
+ *off += wordSize;
+ return v;
+}
+
+// Read a null-terminated string.
+StringRef EhReader::readString(size_t *off) const {
+ if (*off > data.size())
+ failOn(*off, "corrupted CIE (failed to read string)");
+ const size_t maxlen = data.size() - *off;
+ auto *c = reinterpret_cast<const char *>(data.data() + *off);
+ size_t len = strnlen(c, maxlen);
+ if (len == maxlen) // we failed to find the null terminator
+ failOn(*off, "corrupted CIE (failed to read string)");
+ *off += len + 1; // skip the null byte too
+ return StringRef(c, len);
+}
+
+void EhReader::skipLeb128(size_t *off) const {
+ const size_t errOff = *off;
+ while (*off < data.size()) {
+ uint8_t val = data[(*off)++];
+ if ((val & 0x80) == 0)
+ return;
+ }
+ failOn(errOff, "corrupted CIE (failed to read LEB128)");
+}
+
+void EhReader::failOn(size_t errOff, const Twine &msg) const {
+ fatal(toString(file) + ":(__eh_frame+0x" +
+ Twine::utohexstr(dataOff + errOff) + "): " + msg);
+}
+
+/*
+ * Create a pair of relocs to write the value of:
+ * `b - (offset + a)` if Invert == false
+ * `(a + offset) - b` if Invert == true
+ */
+template <bool Invert = false>
+static void createSubtraction(PointerUnion<Symbol *, InputSection *> a,
+ PointerUnion<Symbol *, InputSection *> b,
+ uint64_t off, uint8_t length,
+ SmallVectorImpl<Reloc> *newRelocs) {
+ auto subtrahend = a;
+ auto minuend = b;
+ if (Invert)
+ std::swap(subtrahend, minuend);
+ assert(subtrahend.is<Symbol *>());
+ Reloc subtrahendReloc(target->subtractorRelocType, /*pcrel=*/false, length,
+ off, /*addend=*/0, subtrahend);
+ Reloc minuendReloc(target->unsignedRelocType, /*pcrel=*/false, length, off,
+ (Invert ? 1 : -1) * off, minuend);
+ newRelocs->push_back(subtrahendReloc);
+ newRelocs->push_back(minuendReloc);
+}
+
+void EhRelocator::makePcRel(uint64_t off,
+ PointerUnion<Symbol *, InputSection *> target,
+ uint8_t length) {
+ createSubtraction(isec->symbols[0], target, off, length, &newRelocs);
+}
+
+void EhRelocator::makeNegativePcRel(
+ uint64_t off, PointerUnion<Symbol *, InputSection *> target,
+ uint8_t length) {
+ createSubtraction</*Invert=*/true>(isec, target, off, length, &newRelocs);
+}
+
+void EhRelocator::commit() {
+ isec->relocs.insert(isec->relocs.end(), newRelocs.begin(), newRelocs.end());
+}