10b57cec5SDimitry Andric //===- EhFrame.cpp -------------------------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // .eh_frame section contains information on how to unwind the stack when
100b57cec5SDimitry Andric // an exception is thrown. The section consists of sequence of CIE and FDE
110b57cec5SDimitry Andric // records. The linker needs to merge CIEs and associate FDEs to CIEs.
120b57cec5SDimitry Andric // That means the linker has to understand the format of the section.
130b57cec5SDimitry Andric //
140b57cec5SDimitry Andric // This file contains a few utility functions to read .eh_frame contents.
150b57cec5SDimitry Andric //
160b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
170b57cec5SDimitry Andric
180b57cec5SDimitry Andric #include "EhFrame.h"
190b57cec5SDimitry Andric #include "Config.h"
200b57cec5SDimitry Andric #include "InputSection.h"
210b57cec5SDimitry Andric #include "Relocations.h"
220b57cec5SDimitry Andric #include "Target.h"
230b57cec5SDimitry Andric #include "lld/Common/ErrorHandler.h"
240b57cec5SDimitry Andric #include "lld/Common/Strings.h"
250b57cec5SDimitry Andric #include "llvm/BinaryFormat/Dwarf.h"
260b57cec5SDimitry Andric #include "llvm/Object/ELF.h"
270b57cec5SDimitry Andric
280b57cec5SDimitry Andric using namespace llvm;
290b57cec5SDimitry Andric using namespace llvm::ELF;
300b57cec5SDimitry Andric using namespace llvm::dwarf;
310b57cec5SDimitry Andric using namespace llvm::object;
325ffd83dbSDimitry Andric using namespace lld;
335ffd83dbSDimitry Andric using namespace lld::elf;
340b57cec5SDimitry Andric
350b57cec5SDimitry Andric namespace {
360b57cec5SDimitry Andric class EhReader {
370b57cec5SDimitry Andric public:
EhReader(InputSectionBase * s,ArrayRef<uint8_t> d)380b57cec5SDimitry Andric EhReader(InputSectionBase *s, ArrayRef<uint8_t> d) : isec(s), d(d) {}
390b57cec5SDimitry Andric uint8_t getFdeEncoding();
40e8d8bef9SDimitry Andric bool hasLSDA();
410b57cec5SDimitry Andric
420b57cec5SDimitry Andric private:
failOn(const P * loc,const Twine & msg)430b57cec5SDimitry Andric template <class P> void failOn(const P *loc, const Twine &msg) {
440b57cec5SDimitry Andric fatal("corrupted .eh_frame: " + msg + "\n>>> defined in " +
45*bdd1243dSDimitry Andric isec->getObjMsg((const uint8_t *)loc - isec->content().data()));
460b57cec5SDimitry Andric }
470b57cec5SDimitry Andric
480b57cec5SDimitry Andric uint8_t readByte();
490b57cec5SDimitry Andric void skipBytes(size_t count);
500b57cec5SDimitry Andric StringRef readString();
510b57cec5SDimitry Andric void skipLeb128();
520b57cec5SDimitry Andric void skipAugP();
53e8d8bef9SDimitry Andric StringRef getAugmentation();
540b57cec5SDimitry Andric
550b57cec5SDimitry Andric InputSectionBase *isec;
560b57cec5SDimitry Andric ArrayRef<uint8_t> d;
570b57cec5SDimitry Andric };
580b57cec5SDimitry Andric }
590b57cec5SDimitry Andric
600b57cec5SDimitry Andric // Read a byte and advance D by one byte.
readByte()610b57cec5SDimitry Andric uint8_t EhReader::readByte() {
620b57cec5SDimitry Andric if (d.empty())
630b57cec5SDimitry Andric failOn(d.data(), "unexpected end of CIE");
640b57cec5SDimitry Andric uint8_t b = d.front();
650b57cec5SDimitry Andric d = d.slice(1);
660b57cec5SDimitry Andric return b;
670b57cec5SDimitry Andric }
680b57cec5SDimitry Andric
skipBytes(size_t count)690b57cec5SDimitry Andric void EhReader::skipBytes(size_t count) {
700b57cec5SDimitry Andric if (d.size() < count)
710b57cec5SDimitry Andric failOn(d.data(), "CIE is too small");
720b57cec5SDimitry Andric d = d.slice(count);
730b57cec5SDimitry Andric }
740b57cec5SDimitry Andric
750b57cec5SDimitry Andric // Read a null-terminated string.
readString()760b57cec5SDimitry Andric StringRef EhReader::readString() {
770b57cec5SDimitry Andric const uint8_t *end = llvm::find(d, '\0');
780b57cec5SDimitry Andric if (end == d.end())
790b57cec5SDimitry Andric failOn(d.data(), "corrupted CIE (failed to read string)");
800b57cec5SDimitry Andric StringRef s = toStringRef(d.slice(0, end - d.begin()));
810b57cec5SDimitry Andric d = d.slice(s.size() + 1);
820b57cec5SDimitry Andric return s;
830b57cec5SDimitry Andric }
840b57cec5SDimitry Andric
850b57cec5SDimitry Andric // Skip an integer encoded in the LEB128 format.
860b57cec5SDimitry Andric // Actual number is not of interest because only the runtime needs it.
870b57cec5SDimitry Andric // But we need to be at least able to skip it so that we can read
880b57cec5SDimitry Andric // the field that follows a LEB128 number.
skipLeb128()890b57cec5SDimitry Andric void EhReader::skipLeb128() {
900b57cec5SDimitry Andric const uint8_t *errPos = d.data();
910b57cec5SDimitry Andric while (!d.empty()) {
920b57cec5SDimitry Andric uint8_t val = d.front();
930b57cec5SDimitry Andric d = d.slice(1);
940b57cec5SDimitry Andric if ((val & 0x80) == 0)
950b57cec5SDimitry Andric return;
960b57cec5SDimitry Andric }
970b57cec5SDimitry Andric failOn(errPos, "corrupted CIE (failed to read LEB128)");
980b57cec5SDimitry Andric }
990b57cec5SDimitry Andric
getAugPSize(unsigned enc)1000b57cec5SDimitry Andric static size_t getAugPSize(unsigned enc) {
1010b57cec5SDimitry Andric switch (enc & 0x0f) {
1020b57cec5SDimitry Andric case DW_EH_PE_absptr:
1030b57cec5SDimitry Andric case DW_EH_PE_signed:
1040b57cec5SDimitry Andric return config->wordsize;
1050b57cec5SDimitry Andric case DW_EH_PE_udata2:
1060b57cec5SDimitry Andric case DW_EH_PE_sdata2:
1070b57cec5SDimitry Andric return 2;
1080b57cec5SDimitry Andric case DW_EH_PE_udata4:
1090b57cec5SDimitry Andric case DW_EH_PE_sdata4:
1100b57cec5SDimitry Andric return 4;
1110b57cec5SDimitry Andric case DW_EH_PE_udata8:
1120b57cec5SDimitry Andric case DW_EH_PE_sdata8:
1130b57cec5SDimitry Andric return 8;
1140b57cec5SDimitry Andric }
1150b57cec5SDimitry Andric return 0;
1160b57cec5SDimitry Andric }
1170b57cec5SDimitry Andric
skipAugP()1180b57cec5SDimitry Andric void EhReader::skipAugP() {
1190b57cec5SDimitry Andric uint8_t enc = readByte();
1200b57cec5SDimitry Andric if ((enc & 0xf0) == DW_EH_PE_aligned)
1210b57cec5SDimitry Andric failOn(d.data() - 1, "DW_EH_PE_aligned encoding is not supported");
1220b57cec5SDimitry Andric size_t size = getAugPSize(enc);
1230b57cec5SDimitry Andric if (size == 0)
1240b57cec5SDimitry Andric failOn(d.data() - 1, "unknown FDE encoding");
1250b57cec5SDimitry Andric if (size >= d.size())
1260b57cec5SDimitry Andric failOn(d.data() - 1, "corrupted CIE");
1270b57cec5SDimitry Andric d = d.slice(size);
1280b57cec5SDimitry Andric }
1290b57cec5SDimitry Andric
getFdeEncoding(EhSectionPiece * p)1305ffd83dbSDimitry Andric uint8_t elf::getFdeEncoding(EhSectionPiece *p) {
1310b57cec5SDimitry Andric return EhReader(p->sec, p->data()).getFdeEncoding();
1320b57cec5SDimitry Andric }
1330b57cec5SDimitry Andric
hasLSDA(const EhSectionPiece & p)134e8d8bef9SDimitry Andric bool elf::hasLSDA(const EhSectionPiece &p) {
135e8d8bef9SDimitry Andric return EhReader(p.sec, p.data()).hasLSDA();
136e8d8bef9SDimitry Andric }
137e8d8bef9SDimitry Andric
getAugmentation()138e8d8bef9SDimitry Andric StringRef EhReader::getAugmentation() {
1390b57cec5SDimitry Andric skipBytes(8);
1400b57cec5SDimitry Andric int version = readByte();
1410b57cec5SDimitry Andric if (version != 1 && version != 3)
1420b57cec5SDimitry Andric failOn(d.data() - 1,
1430b57cec5SDimitry Andric "FDE version 1 or 3 expected, but got " + Twine(version));
1440b57cec5SDimitry Andric
1450b57cec5SDimitry Andric StringRef aug = readString();
1460b57cec5SDimitry Andric
1470b57cec5SDimitry Andric // Skip code and data alignment factors.
1480b57cec5SDimitry Andric skipLeb128();
1490b57cec5SDimitry Andric skipLeb128();
1500b57cec5SDimitry Andric
1510b57cec5SDimitry Andric // Skip the return address register. In CIE version 1 this is a single
1520b57cec5SDimitry Andric // byte. In CIE version 3 this is an unsigned LEB128.
1530b57cec5SDimitry Andric if (version == 1)
1540b57cec5SDimitry Andric readByte();
1550b57cec5SDimitry Andric else
1560b57cec5SDimitry Andric skipLeb128();
157e8d8bef9SDimitry Andric return aug;
158e8d8bef9SDimitry Andric }
1590b57cec5SDimitry Andric
getFdeEncoding()160e8d8bef9SDimitry Andric uint8_t EhReader::getFdeEncoding() {
1610b57cec5SDimitry Andric // We only care about an 'R' value, but other records may precede an 'R'
1620b57cec5SDimitry Andric // record. Unfortunately records are not in TLV (type-length-value) format,
1630b57cec5SDimitry Andric // so we need to teach the linker how to skip records for each type.
164e8d8bef9SDimitry Andric StringRef aug = getAugmentation();
1650b57cec5SDimitry Andric for (char c : aug) {
1660b57cec5SDimitry Andric if (c == 'R')
1670b57cec5SDimitry Andric return readByte();
168e8d8bef9SDimitry Andric if (c == 'z')
1690b57cec5SDimitry Andric skipLeb128();
170e8d8bef9SDimitry Andric else if (c == 'L')
1710b57cec5SDimitry Andric readByte();
172e8d8bef9SDimitry Andric else if (c == 'P')
173e8d8bef9SDimitry Andric skipAugP();
17481ad6265SDimitry Andric else if (c != 'B' && c != 'S' && c != 'G')
1750b57cec5SDimitry Andric failOn(aug.data(), "unknown .eh_frame augmentation string: " + aug);
1760b57cec5SDimitry Andric }
1770b57cec5SDimitry Andric return DW_EH_PE_absptr;
1780b57cec5SDimitry Andric }
179e8d8bef9SDimitry Andric
hasLSDA()180e8d8bef9SDimitry Andric bool EhReader::hasLSDA() {
181e8d8bef9SDimitry Andric StringRef aug = getAugmentation();
182e8d8bef9SDimitry Andric for (char c : aug) {
183e8d8bef9SDimitry Andric if (c == 'L')
184e8d8bef9SDimitry Andric return true;
185e8d8bef9SDimitry Andric if (c == 'z')
186e8d8bef9SDimitry Andric skipLeb128();
187e8d8bef9SDimitry Andric else if (c == 'P')
188e8d8bef9SDimitry Andric skipAugP();
189e8d8bef9SDimitry Andric else if (c == 'R')
190e8d8bef9SDimitry Andric readByte();
19181ad6265SDimitry Andric else if (c != 'B' && c != 'S' && c != 'G')
192e8d8bef9SDimitry Andric failOn(aug.data(), "unknown .eh_frame augmentation string: " + aug);
193e8d8bef9SDimitry Andric }
194e8d8bef9SDimitry Andric return false;
195e8d8bef9SDimitry Andric }
196