forked from llvm/llvm-project
- Notifications
You must be signed in to change notification settings - Fork 339
/
Copy pathEhFrame.cpp
208 lines (188 loc) · 5.46 KB
/
EhFrame.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
//===- 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
//
//===----------------------------------------------------------------------===//
//
// .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"
#include"Config.h"
#include"InputFiles.h"
#include"InputSection.h"
#include"Relocations.h"
#include"Target.h"
#include"lld/Common/ErrorHandler.h"
#include"lld/Common/Strings.h"
#include"llvm/BinaryFormat/Dwarf.h"
#include"llvm/Object/ELF.h"
usingnamespacellvm;
usingnamespacellvm::ELF;
usingnamespacellvm::dwarf;
usingnamespacellvm::object;
usingnamespacelld;
usingnamespacelld::elf;
namespace {
classEhReader {
public:
EhReader(InputSectionBase *s, ArrayRef<uint8_t> d) : isec(s), d(d) {}
uint8_tgetFdeEncoding();
boolhasLSDA();
private:
template <classP> voiderrOn(const P *loc, const Twine &msg) {
Ctx &ctx = isec->file->ctx;
Err(ctx) << "corrupted .eh_frame: " << msg << "\n>>> defined in "
<< isec->getObjMsg((constuint8_t *)loc - isec->content().data());
}
uint8_treadByte();
voidskipBytes(size_t count);
StringRef readString();
voidskipLeb128();
voidskipAugP();
StringRef getAugmentation();
InputSectionBase *isec;
ArrayRef<uint8_t> d;
};
}
// Read a byte and advance D by one byte.
uint8_tEhReader::readByte() {
if (d.empty()) {
errOn(d.data(), "unexpected end of CIE");
return0;
}
uint8_t b = d.front();
d = d.slice(1);
return b;
}
voidEhReader::skipBytes(size_t count) {
if (d.size() < count)
errOn(d.data(), "CIE is too small");
else
d = d.slice(count);
}
// Read a null-terminated string.
StringRef EhReader::readString() {
constuint8_t *end = llvm::find(d, '\0');
if (end == d.end()) {
errOn(d.data(), "corrupted CIE (failed to read string)");
return {};
}
StringRef s = toStringRef(d.slice(0, end - d.begin()));
d = d.slice(s.size() + 1);
return s;
}
// 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.
voidEhReader::skipLeb128() {
constuint8_t *errPos = d.data();
while (!d.empty()) {
uint8_t val = d.front();
d = d.slice(1);
if ((val & 0x80) == 0)
return;
}
errOn(errPos, "corrupted CIE (failed to read LEB128)");
}
staticsize_tgetAugPSize(Ctx &ctx, unsigned enc) {
switch (enc & 0x0f) {
case DW_EH_PE_absptr:
case DW_EH_PE_signed:
return ctx.arg.wordsize;
case DW_EH_PE_udata2:
case DW_EH_PE_sdata2:
return2;
case DW_EH_PE_udata4:
case DW_EH_PE_sdata4:
return4;
case DW_EH_PE_udata8:
case DW_EH_PE_sdata8:
return8;
}
return0;
}
voidEhReader::skipAugP() {
uint8_t enc = readByte();
if ((enc & 0xf0) == DW_EH_PE_aligned)
returnerrOn(d.data() - 1, "DW_EH_PE_aligned encoding is not supported");
size_t size = getAugPSize(isec->getCtx(), enc);
if (size == 0)
returnerrOn(d.data() - 1, "unknown FDE encoding");
if (size >= d.size())
returnerrOn(d.data() - 1, "corrupted CIE");
d = d.slice(size);
}
uint8_telf::getFdeEncoding(EhSectionPiece *p) {
returnEhReader(p->sec, p->data()).getFdeEncoding();
}
boolelf::hasLSDA(const EhSectionPiece &p) {
returnEhReader(p.sec, p.data()).hasLSDA();
}
StringRef EhReader::getAugmentation() {
skipBytes(8);
int version = readByte();
if (version != 1 && version != 3) {
errOn(d.data() - 1,
"FDE version 1 or 3 expected, but got " + Twine(version));
return {};
}
StringRef aug = readString();
// Skip code and data alignment factors.
skipLeb128();
skipLeb128();
// 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)
readByte();
else
skipLeb128();
return aug;
}
uint8_tEhReader::getFdeEncoding() {
// 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.
StringRef aug = getAugmentation();
for (char c : aug) {
if (c == 'R')
returnreadByte();
if (c == 'z')
skipLeb128();
elseif (c == 'L')
readByte();
elseif (c == 'P')
skipAugP();
elseif (c != 'B' && c != 'S' && c != 'G') {
errOn(aug.data(), "unknown .eh_frame augmentation string: " + aug);
break;
}
}
return DW_EH_PE_absptr;
}
boolEhReader::hasLSDA() {
StringRef aug = getAugmentation();
for (char c : aug) {
if (c == 'L')
returntrue;
if (c == 'z')
skipLeb128();
elseif (c == 'P')
skipAugP();
elseif (c == 'R')
readByte();
elseif (c != 'B' && c != 'S' && c != 'G') {
errOn(aug.data(), "unknown .eh_frame augmentation string: " + aug);
break;
}
}
returnfalse;
}