1 //===- EhFrameReader.cpp --------------------------------------------------===//
2 //
3 // The MCLinker Project
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 #include "mcld/LD/EhFrameReader.h"
10
11 #include "mcld/Fragment/NullFragment.h"
12 #include "mcld/MC/Input.h"
13 #include "mcld/LD/LDSection.h"
14 #include "mcld/Support/MsgHandling.h"
15 #include "mcld/Support/MemoryArea.h"
16
17 #include <llvm/ADT/StringRef.h>
18 #include <llvm/Support/Dwarf.h>
19 #include <llvm/Support/LEB128.h>
20
21 namespace mcld {
22
23 //===----------------------------------------------------------------------===//
24 // Helper Functions
25 //===----------------------------------------------------------------------===//
26 /// skip_LEB128 - skip the first LEB128 encoded value from *pp, update *pp
27 /// to the next character.
28 /// @return - false if we ran off the end of the string.
skip_LEB128(EhFrameReader::ConstAddress * pp,EhFrameReader::ConstAddress pend)29 static bool skip_LEB128(EhFrameReader::ConstAddress* pp,
30 EhFrameReader::ConstAddress pend) {
31 for (EhFrameReader::ConstAddress p = *pp; p < pend; ++p) {
32 if ((*p & 0x80) == 0x0) {
33 *pp = p + 1;
34 return true;
35 }
36 }
37 return false;
38 }
39
40 //===----------------------------------------------------------------------===//
41 // EhFrameReader
42 //===----------------------------------------------------------------------===//
43 template <>
scan(ConstAddress pHandler,uint64_t pOffset,llvm::StringRef pData) const44 EhFrameReader::Token EhFrameReader::scan<true>(ConstAddress pHandler,
45 uint64_t pOffset,
46 llvm::StringRef pData) const {
47 Token result;
48 result.file_off = pOffset;
49
50 const uint32_t* data = (const uint32_t*)pHandler;
51 size_t cur_idx = 0;
52
53 // Length Field
54 uint32_t length = data[cur_idx++];
55 if (length == 0x0) {
56 // terminator
57 result.kind = Terminator;
58 result.data_off = 4;
59 result.size = 4;
60 return result;
61 }
62
63 // Extended Field
64 uint64_t extended = 0x0;
65 if (length == 0xFFFFFFFF) {
66 extended = data[cur_idx++];
67 extended <<= 32;
68 extended |= data[cur_idx++];
69 result.size = extended + 12;
70 result.data_off = 16;
71 // 64-bit obj file still uses 32-bit eh_frame.
72 assert(false && "We don't support 64-bit eh_frame.");
73 } else {
74 result.size = length + 4;
75 result.data_off = 8;
76 }
77
78 // ID Field
79 uint32_t ID = data[cur_idx++];
80 if (ID == 0x0)
81 result.kind = CIE;
82 else
83 result.kind = FDE;
84
85 return result;
86 }
87
88 template <>
read(Input & pInput,EhFrame & pEhFrame)89 bool EhFrameReader::read<32, true>(Input& pInput, EhFrame& pEhFrame) {
90 // Alphabet:
91 // {CIE, FDE, CIEt}
92 //
93 // Regular Expression:
94 // (CIE FDE*)+ CIEt
95 //
96 // Autometa:
97 // S = {Q0, Q1, Q2}, Start = Q0, Accept = Q2
98 //
99 // FDE
100 // +---+
101 // CIE \ / CIEt
102 // Q0 -------> Q1 -------> Q2
103 // | / \ ^
104 // | +---+ |
105 // | CIE |
106 // +-----------------------+
107 // CIEt
108 const State autometa[NumOfStates][NumOfTokenKinds] = {
109 // CIE FDE Term Unknown
110 {Q1, Reject, Accept, Reject}, // Q0
111 {Q1, Q1, Accept, Reject}, // Q1
112 };
113
114 const Action transition[NumOfStates][NumOfTokenKinds] = {
115 /* CIE FDE Term Unknown */
116 {addCIE, reject, addTerm, reject}, // Q0
117 {addCIE, addFDE, addTerm, reject}, // Q1
118 };
119
120 LDSection& section = pEhFrame.getSection();
121 if (section.size() == 0x0) {
122 NullFragment* frag = new NullFragment();
123 pEhFrame.addFragment(*frag);
124 return true;
125 }
126
127 // get file offset and address
128 uint64_t file_off = pInput.fileOffset() + section.offset();
129 llvm::StringRef sect_reg =
130 pInput.memArea()->request(file_off, section.size());
131 ConstAddress handler = (ConstAddress)sect_reg.begin();
132
133 State cur_state = Q0;
134 while (Reject != cur_state && Accept != cur_state) {
135 Token token = scan<true>(handler, file_off, sect_reg);
136 llvm::StringRef entry =
137 pInput.memArea()->request(token.file_off, token.size);
138
139 if (!transition[cur_state][token.kind](pEhFrame, entry, token)) {
140 // fail to scan
141 debug(diag::debug_cannot_scan_eh) << pInput.name();
142 return false;
143 }
144
145 file_off += token.size;
146 handler += token.size;
147
148 if (handler == sect_reg.end()) {
149 cur_state = Accept;
150 } else if (handler > sect_reg.end()) {
151 cur_state = Reject;
152 } else {
153 cur_state = autometa[cur_state][token.kind];
154 }
155 } // end of while
156
157 if (Reject == cur_state) {
158 // fail to parse
159 debug(diag::debug_cannot_parse_eh) << pInput.name();
160 return false;
161 }
162 return true;
163 }
164
addCIE(EhFrame & pEhFrame,llvm::StringRef pRegion,const EhFrameReader::Token & pToken)165 bool EhFrameReader::addCIE(EhFrame& pEhFrame,
166 llvm::StringRef pRegion,
167 const EhFrameReader::Token& pToken) {
168 // skip Length, Extended Length and CIE ID.
169 ConstAddress handler = pRegion.begin() + pToken.data_off;
170 ConstAddress cie_end = pRegion.end();
171 ConstAddress handler_start = handler;
172 uint64_t pr_ptr_data_offset = pToken.data_off;
173
174 // the version should be 1 or 3
175 uint8_t version = *handler++;
176 if (version != 1 && version != 3) {
177 return false;
178 }
179
180 // Set up the Augumentation String
181 ConstAddress aug_str_front = handler;
182 ConstAddress aug_str_back = static_cast<ConstAddress>(
183 memchr(aug_str_front, '\0', cie_end - aug_str_front));
184 if (aug_str_back == NULL) {
185 return false;
186 }
187
188 // skip the Augumentation String field
189 handler = aug_str_back + 1;
190
191 // skip the Code Alignment Factor
192 if (!skip_LEB128(&handler, cie_end)) {
193 return false;
194 }
195 // skip the Data Alignment Factor
196 if (!skip_LEB128(&handler, cie_end)) {
197 return false;
198 }
199 // skip the Return Address Register
200 if (version == 1) {
201 if (cie_end - handler < 1)
202 return false;
203 ++handler;
204 } else {
205 if (!skip_LEB128(&handler, cie_end))
206 return false;
207 }
208
209 llvm::StringRef augment((const char*)aug_str_front);
210
211 // we discard this CIE if the augumentation string is '\0'
212 if (augment.size() == 0) {
213 EhFrame::CIE* cie = new EhFrame::CIE(pRegion);
214 cie->setFDEEncode(llvm::dwarf::DW_EH_PE_absptr);
215 pEhFrame.addCIE(*cie);
216 pEhFrame.getCIEMap().insert(std::make_pair(pToken.file_off, cie));
217 return true;
218 }
219
220 // the Augmentation String start with 'eh' is a CIE from gcc before 3.0,
221 // in LSB Core Spec 3.0RC1. We do not support it.
222 if (augment.size() > 1 && augment[0] == 'e' && augment[1] == 'h') {
223 return false;
224 }
225
226 // parse the Augmentation String to get the FDE encodeing if 'z' existed
227 uint8_t fde_encoding = llvm::dwarf::DW_EH_PE_absptr;
228 std::string augdata;
229 std::string pr_ptr_data;
230 if (augment[0] == 'z') {
231 unsigned offset;
232 size_t augdata_size = llvm::decodeULEB128((const uint8_t*)handler, &offset);
233 handler += offset;
234 augdata = std::string((const char*)handler, augdata_size);
235
236 // parse the Augmentation String
237 for (size_t i = 1; i < augment.size(); ++i) {
238 switch (augment[i]) {
239 // LDSA encoding (1 byte)
240 case 'L': {
241 if (cie_end - handler < 1) {
242 return false;
243 }
244 ++handler;
245 break;
246 }
247 // Two arguments, the first one represents the encoding of the second
248 // argument (1 byte). The second one is the address of personality
249 // routine.
250 case 'P': {
251 // the first argument
252 if (cie_end - handler < 1) {
253 return false;
254 }
255 uint8_t per_encode = *handler;
256 ++handler;
257 // get the length of the second argument
258 uint32_t per_length = 0;
259 if ((per_encode & 0x60) == 0x60) {
260 return false;
261 }
262 switch (per_encode & 7) {
263 default:
264 return false;
265 case llvm::dwarf::DW_EH_PE_udata2:
266 per_length = 2;
267 break;
268 case llvm::dwarf::DW_EH_PE_udata4:
269 per_length = 4;
270 break;
271 case llvm::dwarf::DW_EH_PE_udata8:
272 per_length = 8;
273 break;
274 case llvm::dwarf::DW_EH_PE_absptr:
275 per_length = 4; // pPkg.bitclass / 8;
276 break;
277 }
278 // skip the alignment
279 if (llvm::dwarf::DW_EH_PE_aligned == (per_encode & 0xf0)) {
280 uint32_t per_align = handler - cie_end;
281 per_align += per_length - 1;
282 per_align &= ~(per_length - 1);
283 if (static_cast<uint32_t>(cie_end - handler) < per_align) {
284 return false;
285 }
286 handler += per_align;
287 }
288 // skip the second argument
289 if (static_cast<uint32_t>(cie_end - handler) < per_length) {
290 return false;
291 }
292 pr_ptr_data_offset += handler - handler_start;
293 pr_ptr_data = std::string((const char*)handler, per_length);
294 handler += per_length;
295 break;
296 } // end of case 'P'
297
298 // FDE encoding (1 byte)
299 case 'R': {
300 if (cie_end - handler < 1) {
301 return false;
302 }
303 fde_encoding = *handler;
304 switch (fde_encoding & 7) {
305 case llvm::dwarf::DW_EH_PE_udata2:
306 case llvm::dwarf::DW_EH_PE_udata4:
307 case llvm::dwarf::DW_EH_PE_udata8:
308 case llvm::dwarf::DW_EH_PE_absptr:
309 break;
310 default:
311 return false;
312 }
313 ++handler;
314 break;
315 }
316 default:
317 return false;
318 } // end switch
319 } // the rest chars.
320 } // first char is 'z'
321
322 // create and push back the CIE entry
323 EhFrame::CIE* cie = new EhFrame::CIE(pRegion);
324 cie->setFDEEncode(fde_encoding);
325 cie->setPersonalityOffset(pr_ptr_data_offset);
326 cie->setPersonalityName(pr_ptr_data);
327 cie->setAugmentationData(augdata);
328 pEhFrame.addCIE(*cie);
329 pEhFrame.getCIEMap().insert(std::make_pair(pToken.file_off, cie));
330 return true;
331 }
332
addFDE(EhFrame & pEhFrame,llvm::StringRef pRegion,const EhFrameReader::Token & pToken)333 bool EhFrameReader::addFDE(EhFrame& pEhFrame,
334 llvm::StringRef pRegion,
335 const EhFrameReader::Token& pToken) {
336 if (pToken.data_off == pRegion.size())
337 return false;
338
339 const int32_t offset =
340 *(const int32_t*)(pRegion.begin() + pToken.data_off - 4);
341 size_t cie_offset =
342 (size_t)((int64_t)(pToken.file_off + 4) - (int32_t)offset);
343
344 EhFrame::CIEMap::iterator iter = pEhFrame.getCIEMap().find(cie_offset);
345 if (iter == pEhFrame.getCIEMap().end())
346 return false;
347
348 // create and push back the FDE entry
349 EhFrame::FDE* fde = new EhFrame::FDE(pRegion, *iter->second);
350 pEhFrame.addFDE(*fde);
351 return true;
352 }
353
addTerm(EhFrame & pEhFrame,llvm::StringRef pRegion,const EhFrameReader::Token & pToken)354 bool EhFrameReader::addTerm(EhFrame& pEhFrame,
355 llvm::StringRef pRegion,
356 const EhFrameReader::Token& pToken) {
357 return true;
358 }
359
reject(EhFrame & pEhFrame,llvm::StringRef pRegion,const EhFrameReader::Token & pToken)360 bool EhFrameReader::reject(EhFrame& pEhFrame,
361 llvm::StringRef pRegion,
362 const EhFrameReader::Token& pToken) {
363 return true;
364 }
365
366 } // namespace mcld
367