181ad6265SDimitry Andric //===- EhFrame.cpp --------------------------------------------------------===//
281ad6265SDimitry Andric //
381ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
481ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
581ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
681ad6265SDimitry Andric //
781ad6265SDimitry Andric //===----------------------------------------------------------------------===//
881ad6265SDimitry Andric
981ad6265SDimitry Andric #include "EhFrame.h"
1081ad6265SDimitry Andric #include "InputFiles.h"
1181ad6265SDimitry Andric
1281ad6265SDimitry Andric #include "lld/Common/ErrorHandler.h"
1381ad6265SDimitry Andric #include "llvm/BinaryFormat/Dwarf.h"
1481ad6265SDimitry Andric #include "llvm/Support/Endian.h"
1581ad6265SDimitry Andric
1681ad6265SDimitry Andric using namespace llvm;
1781ad6265SDimitry Andric using namespace lld;
1881ad6265SDimitry Andric using namespace lld::macho;
1981ad6265SDimitry Andric using namespace llvm::support::endian;
2081ad6265SDimitry Andric
readLength(size_t * off) const2181ad6265SDimitry Andric uint64_t EhReader::readLength(size_t *off) const {
2281ad6265SDimitry Andric const size_t errOff = *off;
2381ad6265SDimitry Andric if (*off + 4 > data.size())
2481ad6265SDimitry Andric failOn(errOff, "CIE/FDE too small");
2581ad6265SDimitry Andric uint64_t len = read32le(data.data() + *off);
2681ad6265SDimitry Andric *off += 4;
2781ad6265SDimitry Andric if (len == dwarf::DW_LENGTH_DWARF64) {
2881ad6265SDimitry Andric // FIXME: test this DWARF64 code path
2981ad6265SDimitry Andric if (*off + 8 > data.size())
3081ad6265SDimitry Andric failOn(errOff, "CIE/FDE too small");
3181ad6265SDimitry Andric len = read64le(data.data() + *off);
3281ad6265SDimitry Andric *off += 8;
3381ad6265SDimitry Andric }
3481ad6265SDimitry Andric if (*off + len > data.size())
3581ad6265SDimitry Andric failOn(errOff, "CIE/FDE extends past the end of the section");
3681ad6265SDimitry Andric return len;
3781ad6265SDimitry Andric }
3881ad6265SDimitry Andric
skipValidLength(size_t * off) const3981ad6265SDimitry Andric void EhReader::skipValidLength(size_t *off) const {
4081ad6265SDimitry Andric uint32_t len = read32le(data.data() + *off);
4181ad6265SDimitry Andric *off += 4;
4281ad6265SDimitry Andric if (len == dwarf::DW_LENGTH_DWARF64)
4381ad6265SDimitry Andric *off += 8;
4481ad6265SDimitry Andric }
4581ad6265SDimitry Andric
4681ad6265SDimitry Andric // Read a byte and advance off by one byte.
readByte(size_t * off) const4781ad6265SDimitry Andric uint8_t EhReader::readByte(size_t *off) const {
4881ad6265SDimitry Andric if (*off + 1 > data.size())
4981ad6265SDimitry Andric failOn(*off, "unexpected end of CIE/FDE");
5081ad6265SDimitry Andric return data[(*off)++];
5181ad6265SDimitry Andric }
5281ad6265SDimitry Andric
readU32(size_t * off) const5381ad6265SDimitry Andric uint32_t EhReader::readU32(size_t *off) const {
5481ad6265SDimitry Andric if (*off + 4 > data.size())
5581ad6265SDimitry Andric failOn(*off, "unexpected end of CIE/FDE");
5681ad6265SDimitry Andric uint32_t v = read32le(data.data() + *off);
5781ad6265SDimitry Andric *off += 4;
5881ad6265SDimitry Andric return v;
5981ad6265SDimitry Andric }
6081ad6265SDimitry Andric
readPointer(size_t * off,uint8_t size) const61*61cfbce3SDimitry Andric uint64_t EhReader::readPointer(size_t *off, uint8_t size) const {
62*61cfbce3SDimitry Andric if (*off + size > data.size())
6381ad6265SDimitry Andric failOn(*off, "unexpected end of CIE/FDE");
6481ad6265SDimitry Andric uint64_t v;
65*61cfbce3SDimitry Andric if (size == 8)
6681ad6265SDimitry Andric v = read64le(data.data() + *off);
6781ad6265SDimitry Andric else {
68*61cfbce3SDimitry Andric assert(size == 4);
6981ad6265SDimitry Andric v = read32le(data.data() + *off);
7081ad6265SDimitry Andric }
71*61cfbce3SDimitry Andric *off += size;
7281ad6265SDimitry Andric return v;
7381ad6265SDimitry Andric }
7481ad6265SDimitry Andric
7581ad6265SDimitry Andric // Read a null-terminated string.
readString(size_t * off) const7681ad6265SDimitry Andric StringRef EhReader::readString(size_t *off) const {
7781ad6265SDimitry Andric if (*off > data.size())
7881ad6265SDimitry Andric failOn(*off, "corrupted CIE (failed to read string)");
7981ad6265SDimitry Andric const size_t maxlen = data.size() - *off;
8081ad6265SDimitry Andric auto *c = reinterpret_cast<const char *>(data.data() + *off);
8181ad6265SDimitry Andric size_t len = strnlen(c, maxlen);
8281ad6265SDimitry Andric if (len == maxlen) // we failed to find the null terminator
8381ad6265SDimitry Andric failOn(*off, "corrupted CIE (failed to read string)");
8481ad6265SDimitry Andric *off += len + 1; // skip the null byte too
8581ad6265SDimitry Andric return StringRef(c, len);
8681ad6265SDimitry Andric }
8781ad6265SDimitry Andric
skipLeb128(size_t * off) const8881ad6265SDimitry Andric void EhReader::skipLeb128(size_t *off) const {
8981ad6265SDimitry Andric const size_t errOff = *off;
9081ad6265SDimitry Andric while (*off < data.size()) {
9181ad6265SDimitry Andric uint8_t val = data[(*off)++];
9281ad6265SDimitry Andric if ((val & 0x80) == 0)
9381ad6265SDimitry Andric return;
9481ad6265SDimitry Andric }
9581ad6265SDimitry Andric failOn(errOff, "corrupted CIE (failed to read LEB128)");
9681ad6265SDimitry Andric }
9781ad6265SDimitry Andric
failOn(size_t errOff,const Twine & msg) const9881ad6265SDimitry Andric void EhReader::failOn(size_t errOff, const Twine &msg) const {
9981ad6265SDimitry Andric fatal(toString(file) + ":(__eh_frame+0x" +
10081ad6265SDimitry Andric Twine::utohexstr(dataOff + errOff) + "): " + msg);
10181ad6265SDimitry Andric }
10281ad6265SDimitry Andric
10381ad6265SDimitry Andric /*
10481ad6265SDimitry Andric * Create a pair of relocs to write the value of:
10581ad6265SDimitry Andric * `b - (offset + a)` if Invert == false
10681ad6265SDimitry Andric * `(a + offset) - b` if Invert == true
10781ad6265SDimitry Andric */
10881ad6265SDimitry Andric template <bool Invert = false>
createSubtraction(PointerUnion<Symbol *,InputSection * > a,PointerUnion<Symbol *,InputSection * > b,uint64_t off,uint8_t length,SmallVectorImpl<Reloc> * newRelocs)10981ad6265SDimitry Andric static void createSubtraction(PointerUnion<Symbol *, InputSection *> a,
11081ad6265SDimitry Andric PointerUnion<Symbol *, InputSection *> b,
11181ad6265SDimitry Andric uint64_t off, uint8_t length,
11281ad6265SDimitry Andric SmallVectorImpl<Reloc> *newRelocs) {
11381ad6265SDimitry Andric auto subtrahend = a;
11481ad6265SDimitry Andric auto minuend = b;
11581ad6265SDimitry Andric if (Invert)
11681ad6265SDimitry Andric std::swap(subtrahend, minuend);
11781ad6265SDimitry Andric assert(subtrahend.is<Symbol *>());
11881ad6265SDimitry Andric Reloc subtrahendReloc(target->subtractorRelocType, /*pcrel=*/false, length,
11981ad6265SDimitry Andric off, /*addend=*/0, subtrahend);
12081ad6265SDimitry Andric Reloc minuendReloc(target->unsignedRelocType, /*pcrel=*/false, length, off,
12181ad6265SDimitry Andric (Invert ? 1 : -1) * off, minuend);
12281ad6265SDimitry Andric newRelocs->push_back(subtrahendReloc);
12381ad6265SDimitry Andric newRelocs->push_back(minuendReloc);
12481ad6265SDimitry Andric }
12581ad6265SDimitry Andric
makePcRel(uint64_t off,PointerUnion<Symbol *,InputSection * > target,uint8_t length)12681ad6265SDimitry Andric void EhRelocator::makePcRel(uint64_t off,
12781ad6265SDimitry Andric PointerUnion<Symbol *, InputSection *> target,
12881ad6265SDimitry Andric uint8_t length) {
12981ad6265SDimitry Andric createSubtraction(isec->symbols[0], target, off, length, &newRelocs);
13081ad6265SDimitry Andric }
13181ad6265SDimitry Andric
makeNegativePcRel(uint64_t off,PointerUnion<Symbol *,InputSection * > target,uint8_t length)13281ad6265SDimitry Andric void EhRelocator::makeNegativePcRel(
13381ad6265SDimitry Andric uint64_t off, PointerUnion<Symbol *, InputSection *> target,
13481ad6265SDimitry Andric uint8_t length) {
13581ad6265SDimitry Andric createSubtraction</*Invert=*/true>(isec, target, off, length, &newRelocs);
13681ad6265SDimitry Andric }
13781ad6265SDimitry Andric
commit()13881ad6265SDimitry Andric void EhRelocator::commit() {
13981ad6265SDimitry Andric isec->relocs.insert(isec->relocs.end(), newRelocs.begin(), newRelocs.end());
14081ad6265SDimitry Andric }
141