]>
Commit | Line | Data |
---|---|---|
afe874b1 A |
1 | /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- |
2 | * | |
f80fe69f | 3 | * Copyright (c) 2008-2010 Apple Inc. All rights reserved. |
afe874b1 A |
4 | * |
5 | * @APPLE_LICENSE_HEADER_START@ | |
6 | * | |
7 | * This file contains Original Code and/or Modifications of Original Code | |
8 | * as defined in and that are subject to the Apple Public Source License | |
9 | * Version 2.0 (the 'License'). You may not use this file except in | |
10 | * compliance with the License. Please obtain a copy of the License at | |
11 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
12 | * file. | |
13 | * | |
14 | * The Original Code and all software distributed under the License are | |
15 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
19 | * Please see the License for the specific language governing rights and | |
20 | * limitations under the License. | |
21 | * | |
22 | * @APPLE_LICENSE_HEADER_END@ | |
23 | */ | |
24 | ||
25 | // | |
26 | // processor specific parsing of dwarf unwind instructions | |
27 | // | |
28 | ||
29 | #ifndef __DWARF_PARSER_HPP__ | |
30 | #define __DWARF_PARSER_HPP__ | |
31 | ||
32 | #include <stdint.h> | |
33 | #include <stdio.h> | |
34 | #include <stdlib.h> | |
35 | ||
36 | #include <vector> | |
37 | ||
38 | #include "libunwind.h" | |
39 | #include "dwarf2.h" | |
40 | ||
41 | #include "AddressSpace.hpp" | |
42 | ||
43 | ||
44 | namespace libunwind { | |
45 | ||
46 | ||
47 | /// | |
48 | /// CFI_Parser does basic parsing of a CFI (Call Frame Information) records. | |
49 | /// See Dwarf Spec for details: | |
50 | /// http://www.linux-foundation.org/spec/booksets/LSB-Core-generic/LSB-Core-generic/ehframechpt.html | |
51 | /// | |
52 | template <typename A> | |
53 | class CFI_Parser | |
54 | { | |
55 | public: | |
56 | typedef typename A::pint_t pint_t; | |
57 | ||
58 | /// | |
59 | /// Information encoded in a CIE (Common Information Entry) | |
60 | /// | |
61 | struct CIE_Info { | |
62 | pint_t cieStart; | |
63 | pint_t cieLength; | |
64 | pint_t cieInstructions; | |
65 | uint8_t pointerEncoding; | |
66 | uint8_t lsdaEncoding; | |
67 | uint8_t personalityEncoding; | |
68 | uint8_t personalityOffsetInCIE; | |
69 | pint_t personality; | |
70 | int codeAlignFactor; | |
71 | int dataAlignFactor; | |
72 | bool isSignalFrame; | |
73 | bool fdesHaveAugmentationData; | |
74 | }; | |
75 | ||
76 | /// | |
77 | /// Information about an FDE (Frame Description Entry) | |
78 | /// | |
79 | struct FDE_Info { | |
80 | pint_t fdeStart; | |
81 | pint_t fdeLength; | |
82 | pint_t fdeInstructions; | |
83 | pint_t pcStart; | |
84 | pint_t pcEnd; | |
85 | pint_t lsda; | |
86 | }; | |
87 | ||
88 | /// | |
89 | /// Used by linker when parsing __eh_frame section | |
90 | /// | |
91 | struct FDE_Reference { | |
92 | pint_t address; | |
93 | uint32_t offsetInFDE; | |
94 | uint8_t encodingOfAddress; | |
95 | }; | |
96 | struct FDE_Atom_Info { | |
97 | pint_t fdeAddress; | |
98 | FDE_Reference function; | |
99 | FDE_Reference cie; | |
100 | FDE_Reference lsda; | |
101 | }; | |
102 | struct CIE_Atom_Info { | |
103 | pint_t cieAddress; | |
104 | FDE_Reference personality; | |
105 | }; | |
106 | ||
107 | ||
108 | /// | |
109 | /// Information about a frame layout and registers saved determined | |
110 | /// by "running" the dwarf FDE "instructions" | |
111 | /// | |
599556ff | 112 | enum { kMaxRegisterNumber = 300 }; |
afe874b1 A |
113 | enum RegisterSavedWhere { kRegisterUnused, kRegisterInCFA, kRegisterOffsetFromCFA, |
114 | kRegisterInRegister, kRegisterAtExpression, kRegisterIsExpression } ; | |
115 | struct RegisterLocation { | |
116 | RegisterSavedWhere location; | |
117 | int64_t value; | |
118 | }; | |
119 | struct PrologInfo { | |
120 | uint32_t cfaRegister; | |
121 | int32_t cfaRegisterOffset; // CFA = (cfaRegister)+cfaRegisterOffset | |
122 | int64_t cfaExpression; // CFA = expression | |
123 | uint32_t spExtraArgSize; | |
124 | uint32_t codeOffsetAtStackDecrement; | |
125 | uint8_t registerSavedTwiceInCIE; | |
126 | bool registersInOtherRegisters; | |
127 | bool registerSavedMoreThanOnce; | |
128 | bool cfaOffsetWasNegative; | |
129 | bool sameValueUsed; | |
130 | RegisterLocation savedRegisters[kMaxRegisterNumber]; // from where to restore registers | |
131 | }; | |
132 | ||
133 | struct PrologInfoStackEntry { | |
134 | PrologInfoStackEntry(PrologInfoStackEntry* n, const PrologInfo& i) | |
135 | : next(n), info(i) {} | |
136 | PrologInfoStackEntry* next; | |
137 | PrologInfo info; | |
138 | }; | |
139 | ||
140 | static bool findFDE(A& addressSpace, pint_t pc, pint_t ehSectionStart, uint32_t sectionLength, pint_t fdeHint, FDE_Info* fdeInfo, CIE_Info* cieInfo); | |
141 | static const char* decodeFDE(A& addressSpace, pint_t fdeStart, FDE_Info* fdeInfo, CIE_Info* cieInfo); | |
142 | static bool parseFDEInstructions(A& addressSpace, const FDE_Info& fdeInfo, const CIE_Info& cieInfo, pint_t upToPC, PrologInfo* results); | |
143 | static const char* getCFIs(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength, | |
144 | std::vector<FDE_Atom_Info>& fdes, std::vector<CIE_Atom_Info>& cies); | |
145 | static uint32_t getCFICount(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength); | |
146 | ||
147 | static const char* parseCIE(A& addressSpace, pint_t cie, CIE_Info* cieInfo); | |
148 | ||
149 | private: | |
150 | static bool parseInstructions(A& addressSpace, pint_t instructions, pint_t instructionsEnd, const CIE_Info& cieInfo, | |
151 | pint_t pcoffset, PrologInfoStackEntry*& rememberStack, PrologInfo* results); | |
152 | }; | |
153 | ||
154 | ||
155 | /// | |
156 | /// Parse a FDE into a CIE_Info and an FDE_Info | |
157 | /// | |
158 | template <typename A> | |
159 | const char* CFI_Parser<A>::decodeFDE(A& addressSpace, pint_t fdeStart, FDE_Info* fdeInfo, CIE_Info* cieInfo) | |
160 | { | |
161 | pint_t p = fdeStart; | |
162 | uint64_t cfiLength = addressSpace.get32(p); | |
163 | p += 4; | |
164 | if ( cfiLength == 0xffffffff ) { | |
165 | // 0xffffffff means length is really next 8 bytes | |
166 | cfiLength = addressSpace.get64(p); | |
167 | p += 8; | |
168 | } | |
169 | if ( cfiLength == 0 ) | |
170 | return "FDE has zero length"; // end marker | |
171 | uint32_t ciePointer = addressSpace.get32(p); | |
172 | if ( ciePointer == 0 ) | |
173 | return "FDE is really a CIE"; // this is a CIE not an FDE | |
174 | pint_t nextCFI = p + cfiLength; | |
175 | pint_t cieStart = p-ciePointer; | |
176 | const char* err = parseCIE(addressSpace, cieStart, cieInfo); | |
177 | if (err != NULL) | |
178 | return err; | |
179 | p += 4; | |
180 | // parse pc begin and range | |
181 | pint_t pcStart = addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding); | |
182 | pint_t pcRange = addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding & 0x0F); | |
183 | // parse rest of info | |
184 | fdeInfo->lsda = 0; | |
185 | // check for augmentation length | |
186 | if ( cieInfo->fdesHaveAugmentationData ) { | |
187 | uintptr_t augLen = addressSpace.getULEB128(p, nextCFI); | |
188 | pint_t endOfAug = p + augLen; | |
189 | if ( cieInfo->lsdaEncoding != 0 ) { | |
190 | // peek at value (without indirection). Zero means no lsda | |
191 | pint_t lsdaStart = p; | |
192 | if ( addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding & 0x0F) != 0 ) { | |
193 | // reset pointer and re-parse lsda address | |
194 | p = lsdaStart; | |
195 | fdeInfo->lsda = addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding); | |
196 | } | |
197 | } | |
198 | p = endOfAug; | |
199 | } | |
200 | fdeInfo->fdeStart = fdeStart; | |
201 | fdeInfo->fdeLength = nextCFI - fdeStart; | |
202 | fdeInfo->fdeInstructions = p; | |
203 | fdeInfo->pcStart = pcStart; | |
204 | fdeInfo->pcEnd = pcStart+pcRange; | |
205 | return NULL; // success | |
206 | } | |
207 | ||
208 | ||
209 | /// | |
210 | /// Scan an eh_frame section to find an FDE for a pc | |
211 | /// | |
212 | template <typename A> | |
213 | bool CFI_Parser<A>::findFDE(A& addressSpace, pint_t pc, pint_t ehSectionStart, uint32_t sectionLength, pint_t fdeHint, FDE_Info* fdeInfo, CIE_Info* cieInfo) | |
214 | { | |
215 | //fprintf(stderr, "findFDE(0x%llX)\n", (long long)pc); | |
216 | pint_t p = (fdeHint != 0) ? fdeHint : ehSectionStart; | |
217 | const pint_t ehSectionEnd = p + sectionLength; | |
218 | while ( p < ehSectionEnd ) { | |
219 | pint_t currentCFI = p; | |
220 | //fprintf(stderr, "findFDE() CFI at 0x%llX\n", (long long)p); | |
221 | uint64_t cfiLength = addressSpace.get32(p); | |
222 | p += 4; | |
223 | if ( cfiLength == 0xffffffff ) { | |
224 | // 0xffffffff means length is really next 8 bytes | |
225 | cfiLength = addressSpace.get64(p); | |
226 | p += 8; | |
227 | } | |
228 | if ( cfiLength == 0 ) | |
229 | return false; // end marker | |
230 | uint32_t id = addressSpace.get32(p); | |
231 | if ( id == 0 ) { | |
232 | // skip over CIEs | |
233 | p += cfiLength; | |
234 | } | |
235 | else { | |
236 | // process FDE to see if it covers pc | |
237 | pint_t nextCFI = p + cfiLength; | |
238 | uint32_t ciePointer = addressSpace.get32(p); | |
239 | pint_t cieStart = p-ciePointer; | |
240 | // validate pointer to CIE is within section | |
241 | if ( (ehSectionStart <= cieStart) && (cieStart < ehSectionEnd) ) { | |
242 | if ( parseCIE(addressSpace, cieStart, cieInfo) == NULL ) { | |
243 | p += 4; | |
244 | // parse pc begin and range | |
245 | pint_t pcStart = addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding); | |
246 | pint_t pcRange = addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding & 0x0F); | |
247 | //fprintf(stderr, "FDE with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pcStart, (uint64_t)(pcStart+pcRange)); | |
248 | // test if pc is within the function this FDE covers | |
249 | if ( (pcStart < pc) && (pc <= pcStart+pcRange) ) { | |
250 | // parse rest of info | |
251 | fdeInfo->lsda = 0; | |
252 | // check for augmentation length | |
253 | if ( cieInfo->fdesHaveAugmentationData ) { | |
254 | uintptr_t augLen = addressSpace.getULEB128(p, nextCFI); | |
255 | pint_t endOfAug = p + augLen; | |
256 | if ( cieInfo->lsdaEncoding != 0 ) { | |
257 | // peek at value (without indirection). Zero means no lsda | |
258 | pint_t lsdaStart = p; | |
259 | if ( addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding & 0x0F) != 0 ) { | |
260 | // reset pointer and re-parse lsda address | |
261 | p = lsdaStart; | |
262 | fdeInfo->lsda = addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding); | |
263 | } | |
264 | } | |
265 | p = endOfAug; | |
266 | } | |
267 | fdeInfo->fdeStart = currentCFI; | |
268 | fdeInfo->fdeLength = nextCFI - currentCFI; | |
269 | fdeInfo->fdeInstructions = p; | |
270 | fdeInfo->pcStart = pcStart; | |
271 | fdeInfo->pcEnd = pcStart+pcRange; | |
272 | //fprintf(stderr, "findFDE(pc=0x%llX) found with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pc, (uint64_t)pcStart, (uint64_t)(pcStart+pcRange)); | |
273 | return true; | |
274 | } | |
275 | else { | |
276 | //fprintf(stderr, "findFDE(pc=0x%llX) not found with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pc, (uint64_t)pcStart, (uint64_t)(pcStart+pcRange)); | |
277 | // pc is not in begin/range, skip this FDE | |
278 | } | |
279 | } | |
280 | else { | |
281 | // malformed CIE, now augmentation describing pc range encoding | |
282 | //fprintf(stderr, "malformed CIE\n"); | |
283 | } | |
284 | } | |
285 | else { | |
286 | // malformed FDE. CIE is bad | |
287 | //fprintf(stderr, "malformed FDE, cieStart=0x%llX, ehSectionStart=0x%llX, ehSectionEnd=0x%llX\n", | |
288 | // (uint64_t)cieStart, (uint64_t)ehSectionStart, (uint64_t)ehSectionEnd); | |
289 | } | |
290 | p = nextCFI; | |
291 | } | |
292 | } | |
293 | //fprintf(stderr, "findFDE(pc=0x%llX) not found\n",(uint64_t)pc); | |
294 | return false; | |
295 | } | |
296 | ||
297 | ||
298 | ||
299 | /// | |
300 | /// Extract info from a CIE | |
301 | /// | |
302 | template <typename A> | |
303 | const char* CFI_Parser<A>::parseCIE(A& addressSpace, pint_t cie, CIE_Info* cieInfo) | |
304 | { | |
305 | //fprintf(stderr, "parseCIE(0x%llX)\n", (long long)cie); | |
306 | cieInfo->pointerEncoding = 0; | |
307 | cieInfo->lsdaEncoding = 0; | |
308 | cieInfo->personalityEncoding = 0; | |
309 | cieInfo->personalityOffsetInCIE = 0; | |
310 | cieInfo->personality = 0; | |
311 | cieInfo->codeAlignFactor = 0; | |
312 | cieInfo->dataAlignFactor = 0; | |
313 | cieInfo->isSignalFrame = false; | |
314 | cieInfo->fdesHaveAugmentationData = false; | |
315 | cieInfo->cieStart = cie; | |
316 | pint_t p = cie; | |
317 | uint64_t cieLength = addressSpace.get32(p); | |
318 | p += 4; | |
319 | pint_t cieContentEnd = p + cieLength; | |
320 | if ( cieLength == 0xffffffff ) { | |
321 | // 0xffffffff means length is really next 8 bytes | |
322 | cieLength = addressSpace.get64(p); | |
323 | p += 8; | |
324 | cieContentEnd = p + cieLength; | |
325 | } | |
326 | if ( cieLength == 0 ) | |
327 | return NULL; | |
328 | // CIE ID is always 0 | |
329 | if ( addressSpace.get32(p) != 0 ) | |
330 | return "CIE ID is not zero"; | |
331 | p += 4; | |
332 | // Version is always 1 or 3 | |
333 | uint8_t version = addressSpace.get8(p); | |
334 | if ( (version != 1) && (version != 3) ) | |
335 | return "CIE version is not 1 or 3"; | |
336 | ++p; | |
337 | // save start of augmentation string and find end | |
338 | pint_t strStart = p; | |
339 | while ( addressSpace.get8(p) != 0 ) | |
340 | ++p; | |
341 | ++p; | |
342 | // parse code aligment factor | |
343 | cieInfo->codeAlignFactor = addressSpace.getULEB128(p, cieContentEnd); | |
344 | // parse data alignment factor | |
345 | cieInfo->dataAlignFactor = addressSpace.getSLEB128(p, cieContentEnd); | |
346 | // parse return address register | |
347 | addressSpace.getULEB128(p, cieContentEnd); | |
348 | // parse augmentation data based on augmentation string | |
349 | const char* result = NULL; | |
350 | if ( addressSpace.get8(strStart) == 'z' ) { | |
351 | // parse augmentation data length | |
352 | addressSpace.getULEB128(p, cieContentEnd); | |
353 | for (pint_t s=strStart; addressSpace.get8(s) != '\0'; ++s) { | |
354 | switch ( addressSpace.get8(s) ) { | |
355 | case 'z': | |
356 | cieInfo->fdesHaveAugmentationData = true; | |
357 | break; | |
358 | case 'P': | |
359 | cieInfo->personalityEncoding = addressSpace.get8(p); | |
360 | ++p; | |
361 | cieInfo->personalityOffsetInCIE = p-cie; | |
362 | cieInfo->personality = addressSpace.getEncodedP(p, cieContentEnd, cieInfo->personalityEncoding); | |
363 | break; | |
364 | case 'L': | |
365 | cieInfo->lsdaEncoding = addressSpace.get8(p); | |
366 | ++p; | |
367 | break; | |
368 | case 'R': | |
369 | cieInfo->pointerEncoding = addressSpace.get8(p); | |
370 | ++p; | |
371 | break; | |
372 | case 'S': | |
373 | cieInfo->isSignalFrame = true; | |
374 | break; | |
375 | default: | |
376 | // ignore unknown letters | |
377 | break; | |
378 | } | |
379 | } | |
380 | } | |
381 | cieInfo->cieLength = cieContentEnd - cieInfo->cieStart; | |
382 | cieInfo->cieInstructions = p; | |
383 | return result; | |
384 | } | |
385 | ||
386 | ||
387 | template <typename A> | |
388 | uint32_t CFI_Parser<A>::getCFICount(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength) | |
389 | { | |
390 | uint32_t count = 0; | |
391 | const pint_t ehSectionEnd = ehSectionStart + sectionLength; | |
392 | for (pint_t p=ehSectionStart; p < ehSectionEnd; ) { | |
393 | uint64_t cfiLength = addressSpace.get32(p); | |
394 | p += 4; | |
395 | if ( cfiLength == 0xffffffff ) { | |
396 | // 0xffffffff means length is really next 8 bytes | |
397 | cfiLength = addressSpace.get64(p); | |
398 | p += 8; | |
399 | } | |
400 | if ( cfiLength == 0 ) | |
401 | return count; // end marker | |
402 | ++count; | |
403 | p += cfiLength; | |
404 | } | |
405 | return count; | |
406 | } | |
407 | ||
408 | ||
409 | ||
410 | template <typename A> | |
411 | const char* CFI_Parser<A>::getCFIs(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength, | |
412 | std::vector<FDE_Atom_Info>& fdes, std::vector<CIE_Atom_Info>& cies) | |
413 | { | |
414 | const pint_t ehSectionEnd = ehSectionStart + sectionLength; | |
415 | for (pint_t p=ehSectionStart; p < ehSectionEnd; ) { | |
416 | pint_t currentCFI = p; | |
417 | uint64_t cfiLength = addressSpace.get32(p); | |
418 | p += 4; | |
419 | if ( cfiLength == 0xffffffff ) { | |
420 | // 0xffffffff means length is really next 8 bytes | |
421 | cfiLength = addressSpace.get64(p); | |
422 | p += 8; | |
423 | } | |
424 | if ( cfiLength == 0 ) | |
425 | return NULL; // end marker | |
426 | uint32_t id = addressSpace.get32(p); | |
427 | if ( id == 0 ) { | |
428 | // is CIE | |
429 | CIE_Info cieInfo; | |
430 | const char* err = parseCIE(addressSpace, currentCFI, &cieInfo); | |
431 | if ( err != NULL ) | |
432 | return err; | |
433 | CIE_Atom_Info entry; | |
434 | entry.cieAddress = currentCFI; | |
435 | entry.personality.address = cieInfo.personality; | |
436 | entry.personality.offsetInFDE = cieInfo.personalityOffsetInCIE; | |
437 | entry.personality.encodingOfAddress = cieInfo.personalityEncoding; | |
438 | cies.push_back(entry); | |
439 | p += cfiLength; | |
440 | } | |
441 | else { | |
442 | // is FDE | |
443 | FDE_Atom_Info entry; | |
444 | entry.fdeAddress = currentCFI; | |
445 | entry.function.address = 0; | |
446 | entry.cie.address = 0; | |
447 | entry.lsda.address = 0; | |
448 | pint_t nextCFI = p + cfiLength; | |
449 | uint32_t ciePointer = addressSpace.get32(p); | |
450 | pint_t cieStart = p-ciePointer; | |
451 | // validate pointer to CIE is within section | |
452 | if ( (cieStart < ehSectionStart) || (cieStart > ehSectionEnd) ) | |
453 | return "FDE points to CIE outside __eh_frame section"; | |
454 | CIE_Info cieInfo; | |
455 | const char* err = parseCIE(addressSpace, cieStart, &cieInfo); | |
456 | if ( err != NULL ) | |
457 | return err; | |
458 | entry.cie.address = cieStart; | |
459 | entry.cie.offsetInFDE = p-currentCFI; | |
460 | entry.cie.encodingOfAddress = DW_EH_PE_sdata4 | DW_EH_PE_pcrel; | |
461 | p += 4; | |
462 | // parse pc begin and range | |
463 | pint_t offsetOfFunctionAddress = p-currentCFI; | |
464 | pint_t pcStart = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding); | |
465 | pint_t pcRange = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding & 0x0F); | |
466 | //fprintf(stderr, "FDE with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pcStart, (uint64_t)(pcStart+pcRange)); | |
467 | // test if pc is within the function this FDE covers | |
468 | entry.function.address = pcStart; | |
469 | entry.function.offsetInFDE = offsetOfFunctionAddress; | |
470 | entry.function.encodingOfAddress = cieInfo.pointerEncoding; | |
471 | // skip over augmentation length | |
472 | if ( cieInfo.fdesHaveAugmentationData ) { | |
473 | uintptr_t augLen = addressSpace.getULEB128(p, nextCFI); | |
474 | pint_t endOfAug = p + augLen; | |
475 | if ( (cieInfo.lsdaEncoding != 0) && (addressSpace.getP(p) != 0) ) { | |
476 | pint_t offsetOfLSDAAddress = p-currentCFI; | |
477 | entry.lsda.address = addressSpace.getEncodedP(p, nextCFI, cieInfo.lsdaEncoding); | |
478 | entry.lsda.offsetInFDE = offsetOfLSDAAddress; | |
479 | entry.lsda.encodingOfAddress = cieInfo.lsdaEncoding; | |
480 | } | |
481 | p = endOfAug; | |
482 | } | |
483 | fdes.push_back(entry); | |
484 | p = nextCFI; | |
485 | } | |
486 | } | |
487 | return NULL; // success | |
488 | } | |
489 | ||
490 | ||
491 | ||
492 | /// | |
493 | /// "run" the dwarf instructions and create the abstact PrologInfo for an FDE | |
494 | /// | |
495 | template <typename A> | |
496 | bool CFI_Parser<A>::parseFDEInstructions(A& addressSpace, const FDE_Info& fdeInfo, const CIE_Info& cieInfo, pint_t upToPC, PrologInfo* results) | |
497 | { | |
498 | // clear results | |
499 | bzero(results, sizeof(PrologInfo)); | |
500 | PrologInfoStackEntry* rememberStack = NULL; | |
501 | ||
502 | // parse CIE then FDE instructions | |
503 | return parseInstructions(addressSpace, cieInfo.cieInstructions, cieInfo.cieStart+cieInfo.cieLength, | |
504 | cieInfo, (pint_t)(-1), rememberStack, results) | |
505 | && parseInstructions(addressSpace, fdeInfo.fdeInstructions, fdeInfo.fdeStart+fdeInfo.fdeLength, | |
506 | cieInfo, upToPC-fdeInfo.pcStart, rememberStack, results); | |
507 | } | |
508 | ||
509 | ||
510 | /// | |
511 | /// "run" the dwarf instructions | |
512 | /// | |
513 | template <typename A> | |
514 | bool CFI_Parser<A>::parseInstructions(A& addressSpace, pint_t instructions, pint_t instructionsEnd, const CIE_Info& cieInfo, | |
515 | pint_t pcoffset, PrologInfoStackEntry*& rememberStack, PrologInfo* results) | |
516 | { | |
517 | const bool logDwarf = false; | |
518 | pint_t p = instructions; | |
519 | uint32_t codeOffset = 0; | |
520 | PrologInfo initialState = *results; | |
521 | if ( logDwarf ) fprintf(stderr, "parseInstructions(instructions=0x%0llX)\n", (uint64_t)instructionsEnd); | |
522 | ||
523 | // see Dwarf Spec, section 6.4.2 for details on unwind opcodes | |
524 | while ( (p < instructionsEnd) && (codeOffset < pcoffset) ) { | |
525 | uint64_t reg; | |
526 | uint64_t reg2; | |
527 | int64_t offset; | |
528 | uint64_t length; | |
529 | uint8_t opcode = addressSpace.get8(p); | |
530 | uint8_t operand; | |
531 | PrologInfoStackEntry* entry; | |
532 | ++p; | |
533 | switch (opcode) { | |
534 | case DW_CFA_nop: | |
535 | if ( logDwarf ) fprintf(stderr, "DW_CFA_nop\n"); | |
536 | break; | |
537 | case DW_CFA_set_loc: | |
538 | codeOffset = addressSpace.getEncodedP(p, instructionsEnd, cieInfo.pointerEncoding); | |
539 | if ( logDwarf ) fprintf(stderr, "DW_CFA_set_loc\n"); | |
540 | break; | |
541 | case DW_CFA_advance_loc1: | |
542 | codeOffset += (addressSpace.get8(p) * cieInfo.codeAlignFactor); | |
543 | p += 1; | |
544 | if ( logDwarf ) fprintf(stderr, "DW_CFA_advance_loc1: new offset=%u\n", codeOffset); | |
545 | break; | |
546 | case DW_CFA_advance_loc2: | |
547 | codeOffset += (addressSpace.get16(p) * cieInfo.codeAlignFactor); | |
548 | p += 2; | |
549 | if ( logDwarf ) fprintf(stderr, "DW_CFA_advance_loc2: new offset=%u\n", codeOffset); | |
550 | break; | |
551 | case DW_CFA_advance_loc4: | |
552 | codeOffset += (addressSpace.get32(p) * cieInfo.codeAlignFactor); | |
553 | p += 4; | |
554 | if ( logDwarf ) fprintf(stderr, "DW_CFA_advance_loc4: new offset=%u\n", codeOffset); | |
555 | break; | |
556 | case DW_CFA_offset_extended: | |
557 | reg = addressSpace.getULEB128(p, instructionsEnd); | |
558 | offset = addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; | |
559 | if ( reg > kMaxRegisterNumber ) { | |
560 | fprintf(stderr, "malformed DW_CFA_offset_extended dwarf unwind, reg too big\n"); | |
561 | return false; | |
562 | } | |
563 | if ( results->savedRegisters[reg].location != kRegisterUnused ) | |
564 | results->registerSavedMoreThanOnce = true; | |
565 | results->savedRegisters[reg].location = kRegisterInCFA; | |
566 | results->savedRegisters[reg].value = offset; | |
567 | if ( logDwarf ) fprintf(stderr, "DW_CFA_offset_extended(reg=%lld, offset=%lld)\n", reg, offset); | |
568 | break; | |
569 | case DW_CFA_restore_extended: | |
570 | reg = addressSpace.getULEB128(p, instructionsEnd);; | |
571 | if ( reg > kMaxRegisterNumber ) { | |
572 | fprintf(stderr, "malformed DW_CFA_restore_extended dwarf unwind, reg too big\n"); | |
573 | return false; | |
574 | } | |
575 | results->savedRegisters[reg] = initialState.savedRegisters[reg]; | |
576 | if ( logDwarf ) fprintf(stderr, "DW_CFA_restore_extended(reg=%lld)\n", reg); | |
577 | break; | |
578 | case DW_CFA_undefined: | |
579 | reg = addressSpace.getULEB128(p, instructionsEnd); | |
580 | if ( reg > kMaxRegisterNumber ) { | |
581 | fprintf(stderr, "malformed DW_CFA_undefined dwarf unwind, reg too big\n"); | |
582 | return false; | |
583 | } | |
584 | results->savedRegisters[reg].location = kRegisterUnused; | |
585 | if ( logDwarf ) fprintf(stderr, "DW_CFA_undefined(reg=%lld)\n", reg); | |
586 | break; | |
587 | case DW_CFA_same_value: | |
588 | reg = addressSpace.getULEB128(p, instructionsEnd); | |
589 | if ( reg > kMaxRegisterNumber ) { | |
590 | fprintf(stderr, "malformed DW_CFA_same_value dwarf unwind, reg too big\n"); | |
591 | return false; | |
592 | } | |
593 | // <rdar://problem/8456377> DW_CFA_same_value unsupported | |
594 | // "same value" means register was stored in frame, but its current | |
595 | // value has not changed, so no need to restore from frame. | |
596 | // We model this as if the register was never saved. | |
597 | results->savedRegisters[reg].location = kRegisterUnused; | |
598 | // set flag to disable conversion to compact unwind | |
599 | results->sameValueUsed = true; | |
600 | if ( logDwarf ) fprintf(stderr, "DW_CFA_same_value(reg=%lld)\n", reg); | |
601 | break; | |
602 | case DW_CFA_register: | |
603 | reg = addressSpace.getULEB128(p, instructionsEnd); | |
604 | reg2 = addressSpace.getULEB128(p, instructionsEnd); | |
605 | if ( reg > kMaxRegisterNumber ) { | |
606 | fprintf(stderr, "malformed DW_CFA_register dwarf unwind, reg too big\n"); | |
607 | return false; | |
608 | } | |
609 | if ( reg2 > kMaxRegisterNumber ) { | |
610 | fprintf(stderr, "malformed DW_CFA_register dwarf unwind, reg2 too big\n"); | |
611 | return false; | |
612 | } | |
613 | results->savedRegisters[reg].location = kRegisterInRegister; | |
614 | results->savedRegisters[reg].value = reg2; | |
615 | // set flag to disable conversion to compact unwind | |
616 | results->registersInOtherRegisters = true; | |
617 | if ( logDwarf ) fprintf(stderr, "DW_CFA_register(reg=%lld, reg2=%lld)\n", reg, reg2); | |
618 | break; | |
619 | case DW_CFA_remember_state: | |
620 | // avoid operator new, because that would be an upward dependency | |
621 | entry = (PrologInfoStackEntry*)malloc(sizeof(PrologInfoStackEntry)); | |
622 | if ( entry != NULL ) { | |
623 | entry->next = rememberStack; | |
624 | entry->info = *results; | |
625 | rememberStack = entry; | |
626 | } | |
627 | else { | |
628 | return false; | |
629 | } | |
630 | if ( logDwarf ) fprintf(stderr, "DW_CFA_remember_state\n"); | |
631 | break; | |
632 | case DW_CFA_restore_state: | |
633 | if ( rememberStack != NULL ) { | |
634 | PrologInfoStackEntry* top = rememberStack; | |
635 | *results = top->info; | |
636 | rememberStack = top->next; | |
637 | free((char*)top); | |
638 | } | |
639 | else { | |
640 | return false; | |
641 | } | |
642 | if ( logDwarf ) fprintf(stderr, "DW_CFA_restore_state\n"); | |
643 | break; | |
644 | case DW_CFA_def_cfa: | |
645 | reg = addressSpace.getULEB128(p, instructionsEnd); | |
646 | offset = addressSpace.getULEB128(p, instructionsEnd); | |
647 | if ( reg > kMaxRegisterNumber ) { | |
648 | fprintf(stderr, "malformed DW_CFA_def_cfa dwarf unwind, reg too big\n"); | |
649 | return false; | |
650 | } | |
651 | results->cfaRegister = reg; | |
652 | results->cfaRegisterOffset = offset; | |
653 | if ( offset > 0x80000000 ) | |
654 | results->cfaOffsetWasNegative = true; | |
655 | if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa(reg=%lld, offset=%lld)\n", reg, offset); | |
656 | break; | |
657 | case DW_CFA_def_cfa_register: | |
658 | reg = addressSpace.getULEB128(p, instructionsEnd); | |
659 | if ( reg > kMaxRegisterNumber ) { | |
660 | fprintf(stderr, "malformed DW_CFA_def_cfa_register dwarf unwind, reg too big\n"); | |
661 | return false; | |
662 | } | |
663 | results->cfaRegister = reg; | |
664 | if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa_register(%lld)\n", reg); | |
665 | break; | |
666 | case DW_CFA_def_cfa_offset: | |
667 | results->cfaRegisterOffset = addressSpace.getULEB128(p, instructionsEnd); | |
668 | results->codeOffsetAtStackDecrement = codeOffset; | |
669 | if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa_offset(%d)\n", results->cfaRegisterOffset); | |
670 | break; | |
671 | case DW_CFA_def_cfa_expression: | |
672 | results->cfaRegister = 0; | |
673 | results->cfaExpression = p; | |
674 | length = addressSpace.getULEB128(p, instructionsEnd); | |
675 | p += length; | |
676 | if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa_expression(expression=0x%llX, length=%llu)\n", | |
677 | results->cfaExpression, length); | |
678 | break; | |
679 | case DW_CFA_expression: | |
680 | reg = addressSpace.getULEB128(p, instructionsEnd); | |
681 | if ( reg > kMaxRegisterNumber ) { | |
682 | fprintf(stderr, "malformed DW_CFA_expression dwarf unwind, reg too big\n"); | |
683 | return false; | |
684 | } | |
685 | results->savedRegisters[reg].location = kRegisterAtExpression; | |
686 | results->savedRegisters[reg].value = p; | |
687 | length = addressSpace.getULEB128(p, instructionsEnd); | |
688 | p += length; | |
689 | if ( logDwarf ) fprintf(stderr, "DW_CFA_expression(reg=%lld, expression=0x%llX, length=%llu)\n", | |
690 | reg, results->savedRegisters[reg].value, length); | |
691 | break; | |
692 | case DW_CFA_offset_extended_sf: | |
693 | reg = addressSpace.getULEB128(p, instructionsEnd); | |
694 | if ( reg > kMaxRegisterNumber ) { | |
695 | fprintf(stderr, "malformed DW_CFA_offset_extended_sf dwarf unwind, reg too big\n"); | |
696 | return false; | |
697 | } | |
698 | offset = addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; | |
699 | if ( results->savedRegisters[reg].location != kRegisterUnused ) | |
700 | results->registerSavedMoreThanOnce = true; | |
701 | results->savedRegisters[reg].location = kRegisterInCFA; | |
702 | results->savedRegisters[reg].value = offset; | |
703 | if ( logDwarf ) fprintf(stderr, "DW_CFA_offset_extended_sf(reg=%lld, offset=%lld)\n", reg, offset); | |
704 | break; | |
705 | case DW_CFA_def_cfa_sf: | |
706 | reg = addressSpace.getULEB128(p, instructionsEnd); | |
707 | offset = addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; | |
708 | if ( reg > kMaxRegisterNumber ) { | |
709 | fprintf(stderr, "malformed DW_CFA_def_cfa_sf dwarf unwind, reg too big\n"); | |
710 | return false; | |
711 | } | |
712 | results->cfaRegister = reg; | |
713 | results->cfaRegisterOffset = offset; | |
714 | if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa_sf(reg=%lld, offset=%lld)\n", reg, offset); | |
715 | break; | |
716 | case DW_CFA_def_cfa_offset_sf: | |
717 | results->cfaRegisterOffset = addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; | |
718 | results->codeOffsetAtStackDecrement = codeOffset; | |
719 | if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa_offset_sf(%d)\n", results->cfaRegisterOffset); | |
720 | break; | |
721 | case DW_CFA_val_offset: | |
722 | reg = addressSpace.getULEB128(p, instructionsEnd); | |
723 | offset = addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; | |
724 | results->savedRegisters[reg].location = kRegisterOffsetFromCFA; | |
725 | results->savedRegisters[reg].value = offset; | |
726 | if ( logDwarf ) fprintf(stderr, "DW_CFA_val_offset(reg=%lld, offset=%lld\n", reg, offset); | |
727 | break; | |
728 | case DW_CFA_val_offset_sf: | |
729 | reg = addressSpace.getULEB128(p, instructionsEnd); | |
730 | if ( reg > kMaxRegisterNumber ) { | |
731 | fprintf(stderr, "malformed DW_CFA_val_offset_sf dwarf unwind, reg too big\n"); | |
732 | return false; | |
733 | } | |
734 | offset = addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; | |
735 | results->savedRegisters[reg].location = kRegisterOffsetFromCFA; | |
736 | results->savedRegisters[reg].value = offset; | |
737 | if ( logDwarf ) fprintf(stderr, "DW_CFA_val_offset_sf(reg=%lld, offset=%lld\n", reg, offset); | |
738 | break; | |
739 | case DW_CFA_val_expression: | |
740 | reg = addressSpace.getULEB128(p, instructionsEnd); | |
741 | if ( reg > kMaxRegisterNumber ) { | |
742 | fprintf(stderr, "malformed DW_CFA_val_expression dwarf unwind, reg too big\n"); | |
743 | return false; | |
744 | } | |
745 | results->savedRegisters[reg].location = kRegisterIsExpression; | |
746 | results->savedRegisters[reg].value = p; | |
747 | length = addressSpace.getULEB128(p, instructionsEnd); | |
748 | p += length; | |
749 | if ( logDwarf ) fprintf(stderr, "DW_CFA_val_expression(reg=%lld, expression=0x%llX, length=%lld)\n", | |
750 | reg, results->savedRegisters[reg].value, length); | |
751 | break; | |
752 | case DW_CFA_GNU_args_size: | |
753 | offset = addressSpace.getULEB128(p, instructionsEnd); | |
754 | results->spExtraArgSize = offset; | |
755 | if ( logDwarf ) fprintf(stderr, "DW_CFA_GNU_args_size(%lld)\n", offset); | |
756 | break; | |
757 | case DW_CFA_GNU_negative_offset_extended: | |
758 | reg = addressSpace.getULEB128(p, instructionsEnd); | |
759 | if ( reg > kMaxRegisterNumber ) { | |
760 | fprintf(stderr, "malformed DW_CFA_GNU_negative_offset_extended dwarf unwind, reg too big\n"); | |
761 | return false; | |
762 | } | |
763 | offset = addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; | |
764 | if ( results->savedRegisters[reg].location != kRegisterUnused ) | |
765 | results->registerSavedMoreThanOnce = true; | |
766 | results->savedRegisters[reg].location = kRegisterInCFA; | |
767 | results->savedRegisters[reg].value = -offset; | |
768 | if ( logDwarf ) fprintf(stderr, "DW_CFA_GNU_negative_offset_extended(%lld)\n", offset); | |
769 | break; | |
770 | default: | |
771 | operand = opcode & 0x3F; | |
772 | switch ( opcode & 0xC0 ) { | |
773 | case DW_CFA_offset: | |
774 | reg = operand; | |
775 | offset = addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; | |
776 | if ( results->savedRegisters[reg].location != kRegisterUnused ) { | |
777 | // look for idiom of PC saved twice in CIE to mean disable compact unwind encoding | |
778 | if ( (pcoffset == (pint_t)(-1)) | |
779 | && (results->savedRegisters[reg].location == kRegisterInCFA) | |
780 | && (results->savedRegisters[reg].value == offset) ) | |
781 | results->registerSavedTwiceInCIE = reg; | |
782 | else | |
783 | results->registerSavedMoreThanOnce = true; | |
784 | } | |
785 | results->savedRegisters[reg].location = kRegisterInCFA; | |
786 | results->savedRegisters[reg].value = offset; | |
787 | if ( logDwarf ) fprintf(stderr, "DW_CFA_offset(reg=%d, offset=%lld)\n", operand, offset); | |
788 | break; | |
789 | case DW_CFA_advance_loc: | |
790 | codeOffset += operand * cieInfo.codeAlignFactor; | |
791 | if ( logDwarf ) fprintf(stderr, "DW_CFA_advance_loc: new offset=%u\n", codeOffset); | |
792 | break; | |
793 | case DW_CFA_restore: | |
794 | // <rdar://problem/7503075> Python crashes when handling an exception thrown by an obj-c object | |
795 | // libffi uses DW_CFA_restore in the middle of some custom dwarf, so it is not a good epilog flag | |
796 | //return true; // gcc-4.5 starts the epilog with this | |
797 | reg = operand; | |
798 | results->savedRegisters[reg] = initialState.savedRegisters[reg]; | |
799 | if ( logDwarf ) fprintf(stderr, "DW_CFA_restore(reg=%lld)\n", reg); | |
800 | break; | |
801 | default: | |
802 | if ( logDwarf ) fprintf(stderr, "unknown CFA opcode 0x%02X\n", opcode); | |
803 | return false; | |
804 | } | |
805 | } | |
806 | } | |
807 | ||
808 | return true; | |
809 | } | |
810 | ||
811 | ||
812 | } // namespace libunwind | |
813 | ||
814 | ||
815 | #endif // __DWARF_PARSER_HPP__ | |
816 | ||
817 | ||
818 | ||
819 |