2016-05-24 10:55:45 +08:00
|
|
|
//===- EhFrame.cpp -------------------------------------------------------===//
|
|
|
|
//
|
2019-01-19 16:50:56 +08:00
|
|
|
// 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
|
2016-05-24 10:55:45 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// .eh_frame section contains information on how to unwind the stack when
|
|
|
|
// an exception is thrown. The section consists of sequence of CIE and FDE
|
|
|
|
// records. The linker needs to merge CIEs and associate FDEs to CIEs.
|
|
|
|
// That means the linker has to understand the format of the section.
|
|
|
|
//
|
|
|
|
// This file contains a few utility functions to read .eh_frame contents.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "EhFrame.h"
|
2017-10-27 11:14:09 +08:00
|
|
|
#include "Config.h"
|
2016-11-23 17:45:17 +08:00
|
|
|
#include "InputSection.h"
|
|
|
|
#include "Relocations.h"
|
2018-03-31 07:13:00 +08:00
|
|
|
#include "Target.h"
|
[lld] unified COFF and ELF error handling on new Common/ErrorHandler
Summary:
The COFF linker and the ELF linker have long had similar but separate
Error.h and Error.cpp files to implement error handling. This change
introduces new error handling code in Common/ErrorHandler.h, changes the
COFF and ELF linkers to use it, and removes the old, separate
implementations.
Reviewers: ruiu
Reviewed By: ruiu
Subscribers: smeenai, jyknight, emaste, sdardis, nemanjai, nhaehnle, mgorny, javed.absar, kbarton, fedor.sergeev, llvm-commits
Differential Revision: https://reviews.llvm.org/D39259
llvm-svn: 316624
2017-10-26 06:28:38 +08:00
|
|
|
#include "lld/Common/ErrorHandler.h"
|
2018-03-01 01:38:19 +08:00
|
|
|
#include "lld/Common/Strings.h"
|
2017-06-07 11:48:56 +08:00
|
|
|
#include "llvm/BinaryFormat/Dwarf.h"
|
2016-05-24 10:55:45 +08:00
|
|
|
#include "llvm/Object/ELF.h"
|
|
|
|
|
|
|
|
using namespace llvm;
|
|
|
|
using namespace llvm::ELF;
|
|
|
|
using namespace llvm::dwarf;
|
|
|
|
using namespace llvm::object;
|
2020-05-15 13:18:58 +08:00
|
|
|
using namespace lld;
|
|
|
|
using namespace lld::elf;
|
2016-05-24 10:55:45 +08:00
|
|
|
|
2016-11-23 17:45:17 +08:00
|
|
|
namespace {
|
2017-10-27 11:14:09 +08:00
|
|
|
class EhReader {
|
2016-11-23 17:45:17 +08:00
|
|
|
public:
|
2017-02-23 10:28:28 +08:00
|
|
|
EhReader(InputSectionBase *s, ArrayRef<uint8_t> d) : isec(s), d(d) {}
|
2016-11-23 17:45:17 +08:00
|
|
|
uint8_t getFdeEncoding();
|
2020-08-05 07:05:14 +08:00
|
|
|
bool hasLSDA();
|
2016-11-23 17:45:17 +08:00
|
|
|
|
|
|
|
private:
|
|
|
|
template <class P> void failOn(const P *loc, const Twine &msg) {
|
2017-04-01 09:42:20 +08:00
|
|
|
fatal("corrupted .eh_frame: " + msg + "\n>>> defined in " +
|
Avoid unnecessary buffer allocation and memcpy for compressed sections.
Previously, we uncompress all compressed sections before doing anything.
That works, and that is conceptually simple, but that could results in
a waste of CPU time and memory if uncompressed sections are then
discarded or just copied to the output buffer.
In particular, if .debug_gnu_pub{names,types} are compressed and if no
-gdb-index option is given, we wasted CPU and memory because we
uncompress them into newly allocated bufers and then memcpy the buffers
to the output buffer. That temporary buffer was redundant.
This patch changes how to uncompress sections. Now, compressed sections
are uncompressed lazily. To do that, `Data` member of `InputSectionBase`
is now hidden from outside, and `data()` accessor automatically expands
an compressed buffer if necessary.
If no one calls `data()`, then `writeTo()` directly uncompresses
compressed data into the output buffer. That eliminates the redundant
memory allocation and redundant memcpy.
This patch significantly reduces memory consumption (20 GiB max RSS to
15 Gib) for an executable whose .debug_gnu_pub{names,types} are in total
5 GiB in an uncompressed form.
Differential Revision: https://reviews.llvm.org/D52917
llvm-svn: 343979
2018-10-09 00:58:59 +08:00
|
|
|
isec->getObjMsg((const uint8_t *)loc - isec->data().data()));
|
2016-11-23 17:45:17 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t readByte();
|
|
|
|
void skipBytes(size_t count);
|
|
|
|
StringRef readString();
|
|
|
|
void skipLeb128();
|
|
|
|
void skipAugP();
|
2020-08-05 07:05:14 +08:00
|
|
|
StringRef getAugmentation();
|
2016-11-23 17:45:17 +08:00
|
|
|
|
2017-02-23 10:28:28 +08:00
|
|
|
InputSectionBase *isec;
|
2016-11-23 17:45:17 +08:00
|
|
|
ArrayRef<uint8_t> d;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2016-05-24 10:55:45 +08:00
|
|
|
// Read a byte and advance D by one byte.
|
2017-10-27 11:14:09 +08:00
|
|
|
uint8_t EhReader::readByte() {
|
2016-05-24 10:55:45 +08:00
|
|
|
if (d.empty())
|
2016-11-23 17:45:17 +08:00
|
|
|
failOn(d.data(), "unexpected end of CIE");
|
2016-05-24 10:55:45 +08:00
|
|
|
uint8_t b = d.front();
|
|
|
|
d = d.slice(1);
|
|
|
|
return b;
|
|
|
|
}
|
|
|
|
|
2017-10-27 11:14:09 +08:00
|
|
|
void EhReader::skipBytes(size_t count) {
|
2016-11-23 17:45:17 +08:00
|
|
|
if (d.size() < count)
|
|
|
|
failOn(d.data(), "CIE is too small");
|
|
|
|
d = d.slice(count);
|
|
|
|
}
|
|
|
|
|
2016-11-20 02:44:09 +08:00
|
|
|
// Read a null-terminated string.
|
2017-10-27 11:14:09 +08:00
|
|
|
StringRef EhReader::readString() {
|
2019-03-30 00:21:16 +08:00
|
|
|
const uint8_t *end = llvm::find(d, '\0');
|
2016-11-20 02:44:09 +08:00
|
|
|
if (end == d.end())
|
2016-11-23 17:45:17 +08:00
|
|
|
failOn(d.data(), "corrupted CIE (failed to read string)");
|
2016-11-20 02:44:09 +08:00
|
|
|
StringRef s = toStringRef(d.slice(0, end - d.begin()));
|
|
|
|
d = d.slice(s.size() + 1);
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2016-05-24 10:55:45 +08:00
|
|
|
// Skip an integer encoded in the LEB128 format.
|
|
|
|
// Actual number is not of interest because only the runtime needs it.
|
|
|
|
// But we need to be at least able to skip it so that we can read
|
|
|
|
// the field that follows a LEB128 number.
|
2017-10-27 11:14:09 +08:00
|
|
|
void EhReader::skipLeb128() {
|
2016-11-23 17:45:17 +08:00
|
|
|
const uint8_t *errPos = d.data();
|
2016-05-24 10:55:45 +08:00
|
|
|
while (!d.empty()) {
|
|
|
|
uint8_t val = d.front();
|
|
|
|
d = d.slice(1);
|
|
|
|
if ((val & 0x80) == 0)
|
|
|
|
return;
|
|
|
|
}
|
2016-11-23 17:45:17 +08:00
|
|
|
failOn(errPos, "corrupted CIE (failed to read LEB128)");
|
2016-05-24 10:55:45 +08:00
|
|
|
}
|
|
|
|
|
2017-03-18 07:29:01 +08:00
|
|
|
static size_t getAugPSize(unsigned enc) {
|
2016-05-24 10:55:45 +08:00
|
|
|
switch (enc & 0x0f) {
|
|
|
|
case DW_EH_PE_absptr:
|
|
|
|
case DW_EH_PE_signed:
|
2017-03-18 07:29:01 +08:00
|
|
|
return config->wordsize;
|
2016-05-24 10:55:45 +08:00
|
|
|
case DW_EH_PE_udata2:
|
|
|
|
case DW_EH_PE_sdata2:
|
|
|
|
return 2;
|
|
|
|
case DW_EH_PE_udata4:
|
|
|
|
case DW_EH_PE_sdata4:
|
|
|
|
return 4;
|
|
|
|
case DW_EH_PE_udata8:
|
|
|
|
case DW_EH_PE_sdata8:
|
|
|
|
return 8;
|
|
|
|
}
|
2016-11-23 17:45:17 +08:00
|
|
|
return 0;
|
2016-05-24 10:55:45 +08:00
|
|
|
}
|
|
|
|
|
2017-10-27 11:14:09 +08:00
|
|
|
void EhReader::skipAugP() {
|
2016-11-23 17:45:17 +08:00
|
|
|
uint8_t enc = readByte();
|
2016-05-24 10:55:45 +08:00
|
|
|
if ((enc & 0xf0) == DW_EH_PE_aligned)
|
2016-11-23 17:45:17 +08:00
|
|
|
failOn(d.data() - 1, "DW_EH_PE_aligned encoding is not supported");
|
2017-03-18 07:29:01 +08:00
|
|
|
size_t size = getAugPSize(enc);
|
2016-11-23 17:45:17 +08:00
|
|
|
if (size == 0)
|
|
|
|
failOn(d.data() - 1, "unknown FDE encoding");
|
2016-05-24 10:55:45 +08:00
|
|
|
if (size >= d.size())
|
2016-11-23 17:45:17 +08:00
|
|
|
failOn(d.data() - 1, "corrupted CIE");
|
2016-05-24 10:55:45 +08:00
|
|
|
d = d.slice(size);
|
|
|
|
}
|
|
|
|
|
2020-05-15 13:18:58 +08:00
|
|
|
uint8_t elf::getFdeEncoding(EhSectionPiece *p) {
|
2017-10-27 11:14:09 +08:00
|
|
|
return EhReader(p->sec, p->data()).getFdeEncoding();
|
2016-11-23 17:45:17 +08:00
|
|
|
}
|
2016-05-24 10:55:45 +08:00
|
|
|
|
2020-08-05 07:05:14 +08:00
|
|
|
bool elf::hasLSDA(const EhSectionPiece &p) {
|
|
|
|
return EhReader(p.sec, p.data()).hasLSDA();
|
|
|
|
}
|
|
|
|
|
|
|
|
StringRef EhReader::getAugmentation() {
|
2016-11-23 17:45:17 +08:00
|
|
|
skipBytes(8);
|
|
|
|
int version = readByte();
|
2016-05-24 10:55:45 +08:00
|
|
|
if (version != 1 && version != 3)
|
2016-11-23 17:45:17 +08:00
|
|
|
failOn(d.data() - 1,
|
|
|
|
"FDE version 1 or 3 expected, but got " + Twine(version));
|
2016-05-24 10:55:45 +08:00
|
|
|
|
2016-11-23 17:45:17 +08:00
|
|
|
StringRef aug = readString();
|
2016-05-24 10:55:45 +08:00
|
|
|
|
2016-11-20 02:44:09 +08:00
|
|
|
// Skip code and data alignment factors.
|
2016-11-23 17:45:17 +08:00
|
|
|
skipLeb128();
|
|
|
|
skipLeb128();
|
2016-05-24 10:55:45 +08:00
|
|
|
|
|
|
|
// Skip the return address register. In CIE version 1 this is a single
|
|
|
|
// byte. In CIE version 3 this is an unsigned LEB128.
|
|
|
|
if (version == 1)
|
2016-11-23 17:45:17 +08:00
|
|
|
readByte();
|
2016-05-24 10:55:45 +08:00
|
|
|
else
|
2016-11-23 17:45:17 +08:00
|
|
|
skipLeb128();
|
2020-08-05 07:05:14 +08:00
|
|
|
return aug;
|
|
|
|
}
|
2016-05-24 10:55:45 +08:00
|
|
|
|
2020-08-05 07:05:14 +08:00
|
|
|
uint8_t EhReader::getFdeEncoding() {
|
2016-05-24 10:55:45 +08:00
|
|
|
// We only care about an 'R' value, but other records may precede an 'R'
|
|
|
|
// record. Unfortunately records are not in TLV (type-length-value) format,
|
|
|
|
// so we need to teach the linker how to skip records for each type.
|
2020-08-05 07:05:14 +08:00
|
|
|
StringRef aug = getAugmentation();
|
2016-05-24 10:55:45 +08:00
|
|
|
for (char c : aug) {
|
|
|
|
if (c == 'R')
|
2016-11-23 17:45:17 +08:00
|
|
|
return readByte();
|
2020-08-08 13:08:00 +08:00
|
|
|
if (c == 'z')
|
2016-11-23 17:45:17 +08:00
|
|
|
skipLeb128();
|
2020-08-08 13:08:00 +08:00
|
|
|
else if (c == 'L')
|
2016-11-23 17:45:17 +08:00
|
|
|
readByte();
|
2020-08-08 13:08:00 +08:00
|
|
|
else if (c == 'P')
|
|
|
|
skipAugP();
|
2020-12-31 14:11:45 +08:00
|
|
|
else if (c != 'B' && c != 'S')
|
2020-08-08 13:08:00 +08:00
|
|
|
failOn(aug.data(), "unknown .eh_frame augmentation string: " + aug);
|
2016-05-24 10:55:45 +08:00
|
|
|
}
|
|
|
|
return DW_EH_PE_absptr;
|
|
|
|
}
|
2020-08-05 07:05:14 +08:00
|
|
|
|
|
|
|
bool EhReader::hasLSDA() {
|
|
|
|
StringRef aug = getAugmentation();
|
|
|
|
for (char c : aug) {
|
|
|
|
if (c == 'L')
|
|
|
|
return true;
|
|
|
|
if (c == 'z')
|
|
|
|
skipLeb128();
|
|
|
|
else if (c == 'P')
|
|
|
|
skipAugP();
|
|
|
|
else if (c == 'R')
|
|
|
|
readByte();
|
2020-12-31 14:11:45 +08:00
|
|
|
else if (c != 'B' && c != 'S')
|
2020-08-05 07:05:14 +08:00
|
|
|
failOn(aug.data(), "unknown .eh_frame augmentation string: " + aug);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|