]> git.saurik.com Git - apple/ld64.git/blob - src/other/unwinddump.cpp
ld64-253.6.tar.gz
[apple/ld64.git] / src / other / unwinddump.cpp
1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
2 *
3 * Copyright (c) 2008-2011 Apple Inc. All rights reserved.
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 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <sys/mman.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include <errno.h>
33
34 #include <vector>
35 #include <set>
36 #include <unordered_set>
37
38 #include "configure.h"
39 #include "MachOFileAbstraction.hpp"
40 #include "Architectures.hpp"
41
42
43 __attribute__((noreturn))
44 void throwf(const char* format, ...)
45 {
46 va_list list;
47 char* p;
48 va_start(list, format);
49 vasprintf(&p, format, list);
50 va_end(list);
51
52 const char* t = p;
53 throw t;
54 }
55
56
57 template <typename A>
58 class UnwindPrinter
59 {
60 public:
61 static bool validFile(const uint8_t* fileContent);
62 static UnwindPrinter<A>* make(const uint8_t* fileContent, uint32_t fileLength,
63 const char* path, bool showFunctionNames)
64 { return new UnwindPrinter<A>(fileContent, fileLength,
65 path, showFunctionNames); }
66 virtual ~UnwindPrinter() {}
67
68
69 private:
70 typedef typename A::P P;
71 typedef typename A::P::E E;
72 typedef typename A::P::uint_t pint_t;
73
74 UnwindPrinter(const uint8_t* fileContent, uint32_t fileLength,
75 const char* path, bool showFunctionNames);
76 bool findUnwindSection();
77 void printUnwindSection(bool showFunctionNames);
78 void printObjectUnwindSection(bool showFunctionNames);
79 void getSymbolTableInfo();
80 const char* functionName(pint_t addr, uint32_t* offset=NULL);
81 const char* personalityName(const macho_relocation_info<typename A::P>* reloc);
82 bool hasExernReloc(uint64_t sectionOffset, const char** personalityStr, pint_t* addr=NULL);
83
84 static const char* archName();
85 static void decode(uint32_t encoding, const uint8_t* funcStart, char* str);
86
87 const char* fPath;
88 const macho_header<P>* fHeader;
89 uint64_t fLength;
90 const macho_section<P>* fUnwindSection;
91 const char* fStrings;
92 const char* fStringsEnd;
93 const macho_nlist<P>* fSymbols;
94 uint32_t fSymbolCount;
95 pint_t fMachHeaderAddress;
96 };
97
98
99 template <> const char* UnwindPrinter<x86>::archName() { return "i386"; }
100 template <> const char* UnwindPrinter<x86_64>::archName() { return "x86_64"; }
101 template <> const char* UnwindPrinter<arm>::archName() { return "arm"; }
102 #if SUPPORT_ARCH_arm64
103 template <> const char* UnwindPrinter<arm64>::archName() { return "arm64"; }
104 #endif
105
106 template <>
107 bool UnwindPrinter<x86>::validFile(const uint8_t* fileContent)
108 {
109 const macho_header<P>* header = (const macho_header<P>*)fileContent;
110 if ( header->magic() != MH_MAGIC )
111 return false;
112 if ( header->cputype() != CPU_TYPE_I386 )
113 return false;
114 switch (header->filetype()) {
115 case MH_EXECUTE:
116 case MH_DYLIB:
117 case MH_BUNDLE:
118 case MH_DYLINKER:
119 case MH_OBJECT:
120 return true;
121 }
122 return false;
123 }
124
125 template <>
126 bool UnwindPrinter<x86_64>::validFile(const uint8_t* fileContent)
127 {
128 const macho_header<P>* header = (const macho_header<P>*)fileContent;
129 if ( header->magic() != MH_MAGIC_64 )
130 return false;
131 if ( header->cputype() != CPU_TYPE_X86_64 )
132 return false;
133 switch (header->filetype()) {
134 case MH_EXECUTE:
135 case MH_DYLIB:
136 case MH_BUNDLE:
137 case MH_DYLINKER:
138 case MH_OBJECT:
139 return true;
140 }
141 return false;
142 }
143
144
145 #if SUPPORT_ARCH_arm64
146 template <>
147 bool UnwindPrinter<arm64>::validFile(const uint8_t* fileContent)
148 {
149 const macho_header<P>* header = (const macho_header<P>*)fileContent;
150 if ( header->magic() != MH_MAGIC_64 )
151 return false;
152 if ( header->cputype() != CPU_TYPE_ARM64 )
153 return false;
154 switch (header->filetype()) {
155 case MH_EXECUTE:
156 case MH_DYLIB:
157 case MH_BUNDLE:
158 case MH_DYLINKER:
159 case MH_OBJECT:
160 return true;
161 }
162 return false;
163 }
164 #endif
165
166 template <>
167 bool UnwindPrinter<arm>::validFile(const uint8_t* fileContent)
168 {
169 const macho_header<P>* header = (const macho_header<P>*)fileContent;
170 if ( header->magic() != MH_MAGIC )
171 return false;
172 if ( header->cputype() != CPU_TYPE_ARM )
173 return false;
174 switch (header->filetype()) {
175 case MH_EXECUTE:
176 case MH_DYLIB:
177 case MH_BUNDLE:
178 case MH_DYLINKER:
179 case MH_OBJECT:
180 return true;
181 }
182 return false;
183 }
184
185 template <typename A>
186 UnwindPrinter<A>::UnwindPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path, bool showFunctionNames)
187 : fHeader(NULL), fLength(fileLength), fUnwindSection(NULL),
188 fStrings(NULL), fStringsEnd(NULL), fSymbols(NULL), fSymbolCount(0), fMachHeaderAddress(0)
189 {
190 // sanity check
191 if ( ! validFile(fileContent) )
192 throw "not a mach-o file that can be checked";
193
194 fPath = strdup(path);
195 fHeader = (const macho_header<P>*)fileContent;
196
197 getSymbolTableInfo();
198
199 if ( findUnwindSection() ) {
200 if ( fHeader->filetype() == MH_OBJECT )
201 printObjectUnwindSection(showFunctionNames);
202 else
203 printUnwindSection(showFunctionNames);
204 }
205 }
206
207
208 template <typename A>
209 void UnwindPrinter<A>::getSymbolTableInfo()
210 {
211 const uint8_t* const endOfFile = (uint8_t*)fHeader + fLength;
212 const uint8_t* const endOfLoadCommands = (uint8_t*)fHeader + sizeof(macho_header<P>) + fHeader->sizeofcmds();
213 const uint32_t cmd_count = fHeader->ncmds();
214 const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
215 const macho_load_command<P>* cmd = cmds;
216 for (uint32_t i = 0; i < cmd_count; ++i) {
217 uint32_t size = cmd->cmdsize();
218 const uint8_t* endOfCmd = ((uint8_t*)cmd)+cmd->cmdsize();
219 if ( endOfCmd > endOfLoadCommands )
220 throwf("load command #%d extends beyond the end of the load commands", i);
221 if ( endOfCmd > endOfFile )
222 throwf("load command #%d extends beyond the end of the file", i);
223 if ( cmd->cmd() == LC_SYMTAB) {
224 const macho_symtab_command<P>* symtab = (macho_symtab_command<P>*)cmd;
225 fSymbolCount = symtab->nsyms();
226 fSymbols = (const macho_nlist<P>*)((char*)fHeader + symtab->symoff());
227 fStrings = (char*)fHeader + symtab->stroff();
228 fStringsEnd = fStrings + symtab->strsize();
229 }
230 cmd = (const macho_load_command<P>*)endOfCmd;
231 }
232 }
233
234 template <typename A>
235 const char* UnwindPrinter<A>::functionName(pint_t addr, uint32_t* offset)
236 {
237 const macho_nlist<P>* closestSymbol = NULL;
238 if ( offset != NULL )
239 *offset = 0;
240 for (uint32_t i=0; i < fSymbolCount; ++i) {
241 uint8_t type = fSymbols[i].n_type();
242 if ( ((type & N_STAB) == 0) && ((type & N_TYPE) == N_SECT) ) {
243 uint32_t value = fSymbols[i].n_value();
244 if ( value == addr ) {
245 const char* r = &fStrings[fSymbols[i].n_strx()];
246 return r;
247 }
248 if ( fSymbols[i].n_desc() & N_ARM_THUMB_DEF )
249 value |= 1;
250 if ( value == addr ) {
251 const char* r = &fStrings[fSymbols[i].n_strx()];
252 //fprintf(stderr, "addr=0x%08llX, i=%u, n_type=0x%0X, r=%s\n", (long long)(fSymbols[i].n_value()), i, fSymbols[i].n_type(), r);
253 return r;
254 }
255 else if ( offset != NULL ) {
256 if ( closestSymbol == NULL ) {
257 if ( fSymbols[i].n_value() < addr )
258 closestSymbol = &fSymbols[i];
259 }
260 else {
261 if ( (fSymbols[i].n_value() < addr) && (fSymbols[i].n_value() > closestSymbol->n_value()) )
262 closestSymbol = &fSymbols[i];
263 }
264 }
265 }
266 }
267 if ( closestSymbol != NULL ) {
268 *offset = addr - closestSymbol->n_value();
269 return &fStrings[closestSymbol->n_strx()];
270 }
271 return "--anonymous function--";
272 }
273
274
275
276 template <typename A>
277 bool UnwindPrinter<A>::findUnwindSection()
278 {
279 const char* unwindSectionName = "__unwind_info";
280 const char* unwindSegmentName = "__TEXT";
281 if ( fHeader->filetype() == MH_OBJECT ) {
282 unwindSectionName = "__compact_unwind";
283 unwindSegmentName = "__LD";
284 }
285 const uint8_t* const endOfFile = (uint8_t*)fHeader + fLength;
286 const uint8_t* const endOfLoadCommands = (uint8_t*)fHeader + sizeof(macho_header<P>) + fHeader->sizeofcmds();
287 const uint32_t cmd_count = fHeader->ncmds();
288 const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
289 const macho_load_command<P>* cmd = cmds;
290 for (uint32_t i = 0; i < cmd_count; ++i) {
291 uint32_t size = cmd->cmdsize();
292 const uint8_t* endOfCmd = ((uint8_t*)cmd)+cmd->cmdsize();
293 if ( endOfCmd > endOfLoadCommands )
294 throwf("load command #%d extends beyond the end of the load commands", i);
295 if ( endOfCmd > endOfFile )
296 throwf("load command #%d extends beyond the end of the file", i);
297 if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
298 const macho_segment_command<P>* segCmd = (const macho_segment_command<P>*)cmd;
299 const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
300 const macho_section<P>* const sectionsEnd = &sectionsStart[segCmd->nsects()];
301 for(const macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
302 if ( (strncmp(sect->sectname(), unwindSectionName, 16) == 0) && (strcmp(sect->segname(), unwindSegmentName) == 0) ) {
303 fUnwindSection = sect;
304 fMachHeaderAddress = segCmd->vmaddr();
305 return fUnwindSection;
306 }
307 }
308 }
309 cmd = (const macho_load_command<P>*)endOfCmd;
310 }
311 return false;
312 }
313
314 #define EXTRACT_BITS(value, mask) \
315 ( (value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask)))-1) )
316
317
318 template <>
319 void UnwindPrinter<x86_64>::decode(uint32_t encoding, const uint8_t* funcStart, char* str)
320 {
321 *str = '\0';
322 switch ( encoding & UNWIND_X86_64_MODE_MASK ) {
323 case UNWIND_X86_64_MODE_RBP_FRAME:
324 {
325 uint32_t savedRegistersOffset = EXTRACT_BITS(encoding, UNWIND_X86_64_RBP_FRAME_OFFSET);
326 uint32_t savedRegistersLocations = EXTRACT_BITS(encoding, UNWIND_X86_64_RBP_FRAME_REGISTERS);
327 if ( savedRegistersLocations == 0 ) {
328 strcpy(str, "rbp frame, no saved registers");
329 }
330 else {
331 sprintf(str, "rbp frame, at -%d:", savedRegistersOffset*8);
332 bool needComma = false;
333 for (int i=0; i < 5; ++i) {
334 if ( needComma )
335 strcat(str, ",");
336 else
337 needComma = true;
338 switch (savedRegistersLocations & 0x7) {
339 case UNWIND_X86_64_REG_NONE:
340 strcat(str, "-");
341 break;
342 case UNWIND_X86_64_REG_RBX:
343 strcat(str, "rbx");
344 break;
345 case UNWIND_X86_64_REG_R12:
346 strcat(str, "r12");
347 break;
348 case UNWIND_X86_64_REG_R13:
349 strcat(str, "r13");
350 break;
351 case UNWIND_X86_64_REG_R14:
352 strcat(str, "r14");
353 break;
354 case UNWIND_X86_64_REG_R15:
355 strcat(str, "r15");
356 break;
357 default:
358 strcat(str, "r?");
359 }
360 savedRegistersLocations = (savedRegistersLocations >> 3);
361 if ( savedRegistersLocations == 0 )
362 break;
363 }
364 }
365 }
366 break;
367 case UNWIND_X86_64_MODE_STACK_IMMD:
368 case UNWIND_X86_64_MODE_STACK_IND:
369 {
370 uint32_t stackSize = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE);
371 uint32_t stackAdjust = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST);
372 uint32_t regCount = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT);
373 uint32_t permutation = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION);
374 if ( (encoding & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_STACK_IND ) {
375 // stack size is encoded in subl $xxx,%esp instruction
376 uint32_t subl = x86_64::P::E::get32(*((uint32_t*)(funcStart+stackSize)));
377 sprintf(str, "stack size=0x%08X, ", subl + 8*stackAdjust);
378 }
379 else {
380 sprintf(str, "stack size=%d, ", stackSize*8);
381 }
382 if ( regCount == 0 ) {
383 strcat(str, "no registers saved");
384 }
385 else {
386 int permunreg[6];
387 switch ( regCount ) {
388 case 6:
389 permunreg[0] = permutation/120;
390 permutation -= (permunreg[0]*120);
391 permunreg[1] = permutation/24;
392 permutation -= (permunreg[1]*24);
393 permunreg[2] = permutation/6;
394 permutation -= (permunreg[2]*6);
395 permunreg[3] = permutation/2;
396 permutation -= (permunreg[3]*2);
397 permunreg[4] = permutation;
398 permunreg[5] = 0;
399 break;
400 case 5:
401 permunreg[0] = permutation/120;
402 permutation -= (permunreg[0]*120);
403 permunreg[1] = permutation/24;
404 permutation -= (permunreg[1]*24);
405 permunreg[2] = permutation/6;
406 permutation -= (permunreg[2]*6);
407 permunreg[3] = permutation/2;
408 permutation -= (permunreg[3]*2);
409 permunreg[4] = permutation;
410 break;
411 case 4:
412 permunreg[0] = permutation/60;
413 permutation -= (permunreg[0]*60);
414 permunreg[1] = permutation/12;
415 permutation -= (permunreg[1]*12);
416 permunreg[2] = permutation/3;
417 permutation -= (permunreg[2]*3);
418 permunreg[3] = permutation;
419 break;
420 case 3:
421 permunreg[0] = permutation/20;
422 permutation -= (permunreg[0]*20);
423 permunreg[1] = permutation/4;
424 permutation -= (permunreg[1]*4);
425 permunreg[2] = permutation;
426 break;
427 case 2:
428 permunreg[0] = permutation/5;
429 permutation -= (permunreg[0]*5);
430 permunreg[1] = permutation;
431 break;
432 case 1:
433 permunreg[0] = permutation;
434 break;
435 }
436 // renumber registers back to standard numbers
437 int registers[6];
438 bool used[7] = { false, false, false, false, false, false, false };
439 for (int i=0; i < regCount; ++i) {
440 int renum = 0;
441 for (int u=1; u < 7; ++u) {
442 if ( !used[u] ) {
443 if ( renum == permunreg[i] ) {
444 registers[i] = u;
445 used[u] = true;
446 break;
447 }
448 ++renum;
449 }
450 }
451 }
452 bool needComma = false;
453 for (int i=0; i < regCount; ++i) {
454 if ( needComma )
455 strcat(str, ",");
456 else
457 needComma = true;
458 switch ( registers[i] ) {
459 case UNWIND_X86_64_REG_RBX:
460 strcat(str, "rbx");
461 break;
462 case UNWIND_X86_64_REG_R12:
463 strcat(str, "r12");
464 break;
465 case UNWIND_X86_64_REG_R13:
466 strcat(str, "r13");
467 break;
468 case UNWIND_X86_64_REG_R14:
469 strcat(str, "r14");
470 break;
471 case UNWIND_X86_64_REG_R15:
472 strcat(str, "r15");
473 break;
474 case UNWIND_X86_64_REG_RBP:
475 strcat(str, "rbp");
476 break;
477 default:
478 strcat(str, "r??");
479 }
480 }
481 }
482 }
483 break;
484 case UNWIND_X86_64_MODE_DWARF:
485 sprintf(str, "dwarf offset 0x%08X, ", encoding & UNWIND_X86_64_DWARF_SECTION_OFFSET);
486 break;
487 default:
488 if ( encoding == 0 )
489 strcat(str, "no unwind information");
490 else
491 strcat(str, "tbd ");
492 }
493 if ( encoding & UNWIND_HAS_LSDA ) {
494 strcat(str, " LSDA");
495 }
496
497 }
498
499 template <>
500 void UnwindPrinter<x86>::decode(uint32_t encoding, const uint8_t* funcStart, char* str)
501 {
502 *str = '\0';
503 switch ( encoding & UNWIND_X86_MODE_MASK ) {
504 case UNWIND_X86_MODE_EBP_FRAME:
505 {
506 uint32_t savedRegistersOffset = EXTRACT_BITS(encoding, UNWIND_X86_EBP_FRAME_OFFSET);
507 uint32_t savedRegistersLocations = EXTRACT_BITS(encoding, UNWIND_X86_EBP_FRAME_REGISTERS);
508 if ( savedRegistersLocations == 0 ) {
509 strcpy(str, "ebp frame, no saved registers");
510 }
511 else {
512 sprintf(str, "ebp frame, at -%d:", savedRegistersOffset*4);
513 bool needComma = false;
514 for (int i=0; i < 5; ++i) {
515 if ( needComma )
516 strcat(str, ",");
517 else
518 needComma = true;
519 switch (savedRegistersLocations & 0x7) {
520 case UNWIND_X86_REG_NONE:
521 strcat(str, "-");
522 break;
523 case UNWIND_X86_REG_EBX:
524 strcat(str, "ebx");
525 break;
526 case UNWIND_X86_REG_ECX:
527 strcat(str, "ecx");
528 break;
529 case UNWIND_X86_REG_EDX:
530 strcat(str, "edx");
531 break;
532 case UNWIND_X86_REG_EDI:
533 strcat(str, "edi");
534 break;
535 case UNWIND_X86_REG_ESI:
536 strcat(str, "esi");
537 break;
538 default:
539 strcat(str, "e??");
540 }
541 savedRegistersLocations = (savedRegistersLocations >> 3);
542 if ( savedRegistersLocations == 0 )
543 break;
544 }
545 }
546 }
547 break;
548 case UNWIND_X86_MODE_STACK_IMMD:
549 case UNWIND_X86_MODE_STACK_IND:
550 {
551 uint32_t stackSize = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE);
552 uint32_t stackAdjust = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_ADJUST);
553 uint32_t regCount = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_COUNT);
554 uint32_t permutation = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION);
555 if ( (encoding & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_STACK_IND ) {
556 // stack size is encoded in subl $xxx,%esp instruction
557 uint32_t subl = x86::P::E::get32(*((uint32_t*)(funcStart+stackSize)));
558 sprintf(str, "stack size=0x%08X, ", subl+4*stackAdjust);
559 }
560 else {
561 sprintf(str, "stack size=%d, ", stackSize*4);
562 }
563 if ( regCount == 0 ) {
564 strcat(str, "no saved regs");
565 }
566 else {
567 int permunreg[6];
568 switch ( regCount ) {
569 case 6:
570 permunreg[0] = permutation/120;
571 permutation -= (permunreg[0]*120);
572 permunreg[1] = permutation/24;
573 permutation -= (permunreg[1]*24);
574 permunreg[2] = permutation/6;
575 permutation -= (permunreg[2]*6);
576 permunreg[3] = permutation/2;
577 permutation -= (permunreg[3]*2);
578 permunreg[4] = permutation;
579 permunreg[5] = 0;
580 break;
581 case 5:
582 permunreg[0] = permutation/120;
583 permutation -= (permunreg[0]*120);
584 permunreg[1] = permutation/24;
585 permutation -= (permunreg[1]*24);
586 permunreg[2] = permutation/6;
587 permutation -= (permunreg[2]*6);
588 permunreg[3] = permutation/2;
589 permutation -= (permunreg[3]*2);
590 permunreg[4] = permutation;
591 break;
592 case 4:
593 permunreg[0] = permutation/60;
594 permutation -= (permunreg[0]*60);
595 permunreg[1] = permutation/12;
596 permutation -= (permunreg[1]*12);
597 permunreg[2] = permutation/3;
598 permutation -= (permunreg[2]*3);
599 permunreg[3] = permutation;
600 break;
601 case 3:
602 permunreg[0] = permutation/20;
603 permutation -= (permunreg[0]*20);
604 permunreg[1] = permutation/4;
605 permutation -= (permunreg[1]*4);
606 permunreg[2] = permutation;
607 break;
608 case 2:
609 permunreg[0] = permutation/5;
610 permutation -= (permunreg[0]*5);
611 permunreg[1] = permutation;
612 break;
613 case 1:
614 permunreg[0] = permutation;
615 break;
616 }
617 // renumber registers back to standard numbers
618 int registers[6];
619 bool used[7] = { false, false, false, false, false, false, false };
620 for (int i=0; i < regCount; ++i) {
621 int renum = 0;
622 for (int u=1; u < 7; ++u) {
623 if ( !used[u] ) {
624 if ( renum == permunreg[i] ) {
625 registers[i] = u;
626 used[u] = true;
627 break;
628 }
629 ++renum;
630 }
631 }
632 }
633 bool needComma = false;
634 for (int i=0; i < regCount; ++i) {
635 if ( needComma )
636 strcat(str, ",");
637 else
638 needComma = true;
639 switch ( registers[i] ) {
640 case UNWIND_X86_REG_EBX:
641 strcat(str, "ebx");
642 break;
643 case UNWIND_X86_REG_ECX:
644 strcat(str, "ecx");
645 break;
646 case UNWIND_X86_REG_EDX:
647 strcat(str, "edx");
648 break;
649 case UNWIND_X86_REG_EDI:
650 strcat(str, "edi");
651 break;
652 case UNWIND_X86_REG_ESI:
653 strcat(str, "esi");
654 break;
655 case UNWIND_X86_REG_EBP:
656 strcat(str, "ebp");
657 break;
658 default:
659 strcat(str, "e??");
660 }
661 }
662 }
663 }
664 break;
665 case UNWIND_X86_MODE_DWARF:
666 sprintf(str, "dwarf offset 0x%08X, ", encoding & UNWIND_X86_DWARF_SECTION_OFFSET);
667 break;
668 default:
669 if ( encoding == 0 )
670 strcat(str, "no unwind information");
671 else
672 strcat(str, "tbd ");
673 }
674 if ( encoding & UNWIND_HAS_LSDA ) {
675 strcat(str, " LSDA");
676 }
677
678 }
679
680 #if SUPPORT_ARCH_arm64
681 template <>
682 void UnwindPrinter<arm64>::decode(uint32_t encoding, const uint8_t* funcStart, char* str)
683 {
684 uint32_t stackSize;
685 switch ( encoding & UNWIND_ARM64_MODE_MASK ) {
686 case UNWIND_ARM64_MODE_FRAMELESS:
687 stackSize = EXTRACT_BITS(encoding, UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK);
688 if ( stackSize == 0 )
689 strcpy(str, "no frame, no saved registers ");
690 else
691 sprintf(str, "stack size=%d: ", 16 * stackSize);
692 if ( encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR )
693 strcat(str, "x19/20 ");
694 if ( encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR )
695 strcat(str, "x21/22 ");
696 if ( encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR )
697 strcat(str, "x23/24 ");
698 if ( encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR )
699 strcat(str, "x25/26 ");
700 if ( encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR )
701 strcat(str, "x27/28 ");
702 if ( encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR )
703 strcat(str, "d8/9 ");
704 if ( encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR )
705 strcat(str, "d10/11 ");
706 if ( encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR )
707 strcat(str, "d12/13 ");
708 if ( encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR )
709 strcat(str, "d14/15 ");
710 break;
711 break;
712 case UNWIND_ARM64_MODE_DWARF:
713 sprintf(str, "dwarf offset 0x%08X, ", encoding & UNWIND_X86_64_DWARF_SECTION_OFFSET);
714 break;
715 case UNWIND_ARM64_MODE_FRAME:
716 strcpy(str, "std frame: ");
717 if ( encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR )
718 strcat(str, "x19/20 ");
719 if ( encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR )
720 strcat(str, "x21/22 ");
721 if ( encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR )
722 strcat(str, "x23/24 ");
723 if ( encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR )
724 strcat(str, "x25/26 ");
725 if ( encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR )
726 strcat(str, "x27/28 ");
727 if ( encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR )
728 strcat(str, "d8/9 ");
729 if ( encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR )
730 strcat(str, "d10/11 ");
731 if ( encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR )
732 strcat(str, "d12/13 ");
733 if ( encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR )
734 strcat(str, "d14/15 ");
735 break;
736 case UNWIND_ARM64_MODE_FRAME_OLD:
737 strcpy(str, "old frame: ");
738 if ( encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR_OLD )
739 strcat(str, "x21/22 ");
740 if ( encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR_OLD )
741 strcat(str, "x23/24 ");
742 if ( encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR_OLD )
743 strcat(str, "x25/26 ");
744 if ( encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR_OLD )
745 strcat(str, "x27/28 ");
746 if ( encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR_OLD )
747 strcat(str, "d8/9 ");
748 if ( encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR_OLD )
749 strcat(str, "d10/11 ");
750 if ( encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR_OLD )
751 strcat(str, "d12/13 ");
752 if ( encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR_OLD )
753 strcat(str, "d14/15 ");
754 break;
755 }
756 }
757 #endif
758
759 template <>
760 void UnwindPrinter<arm>::decode(uint32_t encoding, const uint8_t* funcStart, char* str)
761 {
762 *str = '\0';
763 switch ( encoding & UNWIND_ARM_MODE_MASK ) {
764 case UNWIND_ARM_MODE_DWARF:
765 sprintf(str, "dwarf offset 0x%08X, ", encoding & UNWIND_ARM_DWARF_SECTION_OFFSET);
766 break;
767 case UNWIND_ARM_MODE_FRAME:
768 case UNWIND_ARM_MODE_FRAME_D:
769 switch ( encoding & UNWIND_ARM_FRAME_STACK_ADJUST_MASK ) {
770 case 0x00000000:
771 strcpy(str, "std frame: ");
772 break;
773 case 0x00400000:
774 strcat(str, "std frame(sp adj 4): ");
775 break;
776 case 0x00800000:
777 strcat(str, "std frame(sp adj 8): ");
778 break;
779 case 0x00C00000:
780 strcat(str, "std frame(sp adj 12): ");
781 break;
782 }
783 if ( encoding & UNWIND_ARM_FRAME_FIRST_PUSH_R4 )
784 strcat(str, "r4 ");
785 if ( encoding & UNWIND_ARM_FRAME_FIRST_PUSH_R5 )
786 strcat(str, "r5 ");
787 if ( encoding & UNWIND_ARM_FRAME_FIRST_PUSH_R6 )
788 strcat(str, "r6 ");
789
790 if ( encoding & 0x000000F8)
791 strcat(str, " / ");
792 if ( encoding & UNWIND_ARM_FRAME_SECOND_PUSH_R8 )
793 strcat(str, "r8 ");
794 if ( encoding & UNWIND_ARM_FRAME_SECOND_PUSH_R9 )
795 strcat(str, "r9 ");
796 if ( encoding & UNWIND_ARM_FRAME_SECOND_PUSH_R10 )
797 strcat(str, "r10 ");
798 if ( encoding & UNWIND_ARM_FRAME_SECOND_PUSH_R11 )
799 strcat(str, "r11 ");
800 if ( encoding & UNWIND_ARM_FRAME_SECOND_PUSH_R12 )
801 strcat(str, "r12 ");
802
803 if ( (encoding & UNWIND_ARM_MODE_MASK) == UNWIND_ARM_MODE_FRAME_D ) {
804 switch ( encoding & UNWIND_ARM_FRAME_D_REG_COUNT_MASK ) {
805 case 0x00000000:
806 strcat(str, " / d8 ");
807 break;
808 case 0x00000100:
809 strcat(str, " / d8,d10 ");
810 break;
811 case 0x00000200:
812 strcat(str, " / d8,d10,d12 ");
813 break;
814 case 0x00000300:
815 strcat(str, " / d8,d10,d12,d14 ");
816 break;
817 case 0x00000400:
818 strcat(str, " / d12,d14 / d8,d9,d10 ");
819 break;
820 case 0x00000500:
821 strcat(str, " / d14 / d8,d9,d10,d11,d12");
822 break;
823 case 0x00000600:
824 strcat(str, " / d8,d9,d10,d11,d12,d13,d14 ");
825 break;
826 case 0x00000700:
827 strcat(str, " / d8,d9,d10,d11,d12,d13,d14 ");
828 break;
829 default:
830 strcat(str, " / unknown D register usage ");
831 break;
832 }
833 }
834
835 break;
836 default:
837 if ( encoding == 0 )
838 strcpy(str, "no unwind information");
839 else
840 strcpy(str, "unsupported compact unwind");
841 break;
842 }
843 }
844
845
846 template <>
847 const char* UnwindPrinter<x86_64>::personalityName(const macho_relocation_info<x86_64::P>* reloc)
848 {
849 //assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section");
850 //assert((reloc->r_type() == X86_64_RELOC_UNSIGNED) && "wrong reloc type on personality column in __compact_unwind section");
851 const macho_nlist<P>& sym = fSymbols[reloc->r_symbolnum()];
852 return &fStrings[sym.n_strx()];
853 }
854
855 template <>
856 const char* UnwindPrinter<x86>::personalityName(const macho_relocation_info<x86::P>* reloc)
857 {
858 //assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section");
859 //assert((reloc->r_type() == GENERIC_RELOC_VANILLA) && "wrong reloc type on personality column in __compact_unwind section");
860 const macho_nlist<P>& sym = fSymbols[reloc->r_symbolnum()];
861 return &fStrings[sym.n_strx()];
862 }
863
864 #if SUPPORT_ARCH_arm64
865 template <>
866 const char* UnwindPrinter<arm64>::personalityName(const macho_relocation_info<arm64::P>* reloc)
867 {
868 //assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section");
869 //assert((reloc->r_type() == ARM64_RELOC_UNSIGNED) && "wrong reloc type on personality column in __compact_unwind section");
870 const macho_nlist<P>& sym = fSymbols[reloc->r_symbolnum()];
871 return &fStrings[sym.n_strx()];
872 }
873 #endif
874
875 template <>
876 const char* UnwindPrinter<arm>::personalityName(const macho_relocation_info<arm::P>* reloc)
877 {
878 //assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section");
879 //assert((reloc->r_type() == GENERIC_RELOC_VANILLA) && "wrong reloc type on personality column in __compact_unwind section");
880 const macho_nlist<P>& sym = fSymbols[reloc->r_symbolnum()];
881 return &fStrings[sym.n_strx()];
882 }
883
884 template <typename A>
885 bool UnwindPrinter<A>::hasExernReloc(uint64_t sectionOffset, const char** personalityStr, pint_t* addr)
886 {
887 const macho_relocation_info<P>* relocs = (macho_relocation_info<P>*)((uint8_t*)fHeader + fUnwindSection->reloff());
888 const macho_relocation_info<P>* relocsEnd = &relocs[fUnwindSection->nreloc()];
889 for (const macho_relocation_info<P>* reloc = relocs; reloc < relocsEnd; ++reloc) {
890 if ( reloc->r_extern() && (reloc->r_address() == sectionOffset) ) {
891 *personalityStr = this->personalityName(reloc);
892 if ( addr != NULL )
893 *addr = fSymbols[reloc->r_symbolnum()].n_value();
894 return true;
895 }
896 }
897 return false;
898 }
899
900
901 template <typename A>
902 void UnwindPrinter<A>::printObjectUnwindSection(bool showFunctionNames)
903 {
904 printf("Arch: %s, Section: __LD,__compact_unwind (size=0x%08llX, => %lld entries)\n",
905 archName(), fUnwindSection->size(), fUnwindSection->size() / sizeof(macho_compact_unwind_entry<P>));
906
907 const macho_compact_unwind_entry<P>* const entriesStart = (macho_compact_unwind_entry<P>*)((uint8_t*)fHeader + fUnwindSection->offset());
908 const macho_compact_unwind_entry<P>* const entriesEnd = (macho_compact_unwind_entry<P>*)((uint8_t*)fHeader + fUnwindSection->offset() + fUnwindSection->size());
909 for (const macho_compact_unwind_entry<P>* entry=entriesStart; entry < entriesEnd; ++entry) {
910 uint64_t entryAddress = ((char*)entry - (char*)entriesStart) + fUnwindSection->addr();
911 printf("0x%08llX:\n", entryAddress);
912 const char* functionNameStr;
913 pint_t funcAddress;
914 uint32_t offsetInFunction;
915 if ( hasExernReloc(((char*)entry-(char*)entriesStart)+macho_compact_unwind_entry<P>::codeStartFieldOffset(), &functionNameStr, &funcAddress) ) {
916 offsetInFunction = entry->codeStart();
917 }
918 else {
919 functionNameStr = this->functionName(entry->codeStart(), &offsetInFunction);
920 funcAddress = entry->codeStart();
921 }
922 if ( offsetInFunction == 0 )
923 printf(" start: 0x%08llX %s\n", (uint64_t)funcAddress, functionNameStr);
924 else
925 printf(" start: 0x%08llX %s+0x%X\n", (uint64_t)funcAddress+offsetInFunction, functionNameStr, offsetInFunction);
926
927 printf(" end: 0x%08llX (len=0x%08X)\n", (uint64_t)(funcAddress+offsetInFunction+entry->codeLen()), entry->codeLen());
928
929 char encodingString[200];
930 this->decode(entry->compactUnwindInfo(), ((const uint8_t*)fHeader), encodingString);
931 printf(" unwind info: 0x%08X %s\n", entry->compactUnwindInfo(), encodingString);
932
933 const char* personalityNameStr;
934 if ( hasExernReloc(((char*)entry-(char*)entriesStart)+macho_compact_unwind_entry<P>::personalityFieldOffset(), &personalityNameStr) ) {
935 printf(" personality: %s\n", personalityNameStr);
936 }
937 else {
938 printf(" personality:\n");
939 }
940 if ( entry->lsda() == 0 ) {
941 printf(" lsda:\n");
942 }
943 else {
944 uint32_t lsdaOffset;
945 const char* lsdaName = this->functionName(entry->lsda(), &lsdaOffset);
946 if ( lsdaOffset == 0 )
947 printf(" lsda: 0x%08llX %s\n", (uint64_t)entry->lsda(), lsdaName);
948 else
949 printf(" lsda: 0x%08llX %s+0x%X\n", (uint64_t)entry->lsda(), lsdaName, lsdaOffset);
950 }
951 }
952 }
953
954
955
956 template <typename A>
957 void UnwindPrinter<A>::printUnwindSection(bool showFunctionNames)
958 {
959 const uint8_t* sectionContent = (uint8_t*)fHeader + fUnwindSection->offset();
960 macho_unwind_info_section_header<P>* sectionHeader = (macho_unwind_info_section_header<P>*)(sectionContent);
961
962 printf("Arch: %s, Section: __TEXT,__unwind_info (addr=0x%08llX, size=0x%08llX, fileOffset=0x%08X)\n",
963 archName(), fUnwindSection->addr(), fUnwindSection->size(), fUnwindSection->offset());
964 printf("\tversion=0x%08X\n", sectionHeader->version());
965 printf("\tcommonEncodingsArraySectionOffset=0x%08X\n", sectionHeader->commonEncodingsArraySectionOffset());
966 printf("\tcommonEncodingsArrayCount=0x%08X\n", sectionHeader->commonEncodingsArrayCount());
967 printf("\tpersonalityArraySectionOffset=0x%08X\n", sectionHeader->personalityArraySectionOffset());
968 printf("\tpersonalityArrayCount=0x%08X\n", sectionHeader->personalityArrayCount());
969 printf("\tindexSectionOffset=0x%08X\n", sectionHeader->indexSectionOffset());
970 printf("\tindexCount=0x%08X\n", sectionHeader->indexCount());
971 printf("\tcommon encodings: (count=%u)\n", sectionHeader->commonEncodingsArrayCount());
972 const uint32_t* commonEncodings = (uint32_t*)&sectionContent[sectionHeader->commonEncodingsArraySectionOffset()];
973 for (uint32_t i=0; i < sectionHeader->commonEncodingsArrayCount(); ++i) {
974 printf("\t\tencoding[%3u]=0x%08X\n", i, A::P::E::get32(commonEncodings[i]));
975 }
976 printf("\tpersonalities: (count=%u)\n", sectionHeader->personalityArrayCount());
977 const uint32_t* personalityArray = (uint32_t*)&sectionContent[sectionHeader->personalityArraySectionOffset()];
978 for (uint32_t i=0; i < sectionHeader->personalityArrayCount(); ++i) {
979 printf("\t\t[%2u]=0x%08X\n", i+1, A::P::E::get32(personalityArray[i]));
980 }
981 printf("\tfirst level index: (count=%u)\n", sectionHeader->indexCount());
982 macho_unwind_info_section_header_index_entry<P>* indexes = (macho_unwind_info_section_header_index_entry<P>*)&sectionContent[sectionHeader->indexSectionOffset()];
983 for (uint32_t i=0; i < sectionHeader->indexCount(); ++i) {
984 printf("\t\t[%2u] funcOffset=0x%08X, pageOffset=0x%08X, lsdaOffset=0x%08X\n",
985 i, indexes[i].functionOffset(), indexes[i].secondLevelPagesSectionOffset(), indexes[i].lsdaIndexArraySectionOffset());
986 }
987 uint32_t lsdaIndexArraySectionOffset = indexes[0].lsdaIndexArraySectionOffset();
988 uint32_t lsdaIndexArrayEndSectionOffset = indexes[sectionHeader->indexCount()-1].lsdaIndexArraySectionOffset();
989 uint32_t lsdaIndexArrayCount = (lsdaIndexArrayEndSectionOffset-lsdaIndexArraySectionOffset)/sizeof(macho_unwind_info_section_header_lsda_index_entry<P>);
990 printf("\tLSDA table: (section offset 0x%08X, count=%u)\n", lsdaIndexArraySectionOffset, lsdaIndexArrayCount);
991 macho_unwind_info_section_header_lsda_index_entry<P>* lindex = (macho_unwind_info_section_header_lsda_index_entry<P>*)&sectionContent[lsdaIndexArraySectionOffset];
992 for (uint32_t i=0; i < lsdaIndexArrayCount; ++i) {
993 const char* name = showFunctionNames ? functionName(lindex[i].functionOffset()+fMachHeaderAddress) : "";
994 printf("\t\t[%3u] funcOffset=0x%08X, lsdaOffset=0x%08X, %s\n", i, lindex[i].functionOffset(), lindex[i].lsdaOffset(), name);
995 if ( *(((uint8_t*)fHeader) + lindex[i].lsdaOffset()) != 0xFF )
996 fprintf(stderr, "BAD LSDA entry (does not start with 0xFF) for %s\n", functionName(lindex[i].functionOffset()+fMachHeaderAddress));
997 }
998 for (uint32_t i=0; i < sectionHeader->indexCount()-1; ++i) {
999 printf("\tsecond level index[%u] sectionOffset=0x%08X, count=%u, fileOffset=0x%08X\n", i, indexes[i].secondLevelPagesSectionOffset(),
1000 sectionHeader->indexCount(), fUnwindSection->offset()+indexes[i].secondLevelPagesSectionOffset());
1001 macho_unwind_info_regular_second_level_page_header<P>* page = (macho_unwind_info_regular_second_level_page_header<P>*)&sectionContent[indexes[i].secondLevelPagesSectionOffset()];
1002 if ( page->kind() == UNWIND_SECOND_LEVEL_REGULAR ) {
1003 printf("\t\tkind=UNWIND_SECOND_LEVEL_REGULAR\n");
1004 printf("\t\tentryPageOffset=0x%08X\n", page->entryPageOffset());
1005 printf("\t\tentryCount=0x%08X\n", page->entryCount());
1006 const macho_unwind_info_regular_second_level_entry<P>* entry = (macho_unwind_info_regular_second_level_entry<P>*)((char*)page+page->entryPageOffset());
1007 for (uint32_t j=0; j < page->entryCount(); ++j) {
1008 uint32_t funcOffset = entry[j].functionOffset();
1009 if ( entry[j].encoding() & UNWIND_HAS_LSDA ) {
1010 // verify there is a corresponding entry in lsda table
1011 bool found = false;
1012 for (uint32_t k=0; k < lsdaIndexArrayCount; ++k) {
1013 if ( lindex[k].functionOffset() == funcOffset ) {
1014 found = true;
1015 break;
1016 }
1017 }
1018 if ( !found ) {
1019 fprintf(stderr, "MISSING LSDA entry for %s\n", functionName(funcOffset+fMachHeaderAddress));
1020 }
1021 }
1022 char encodingString[100];
1023 decode(entry[j].encoding(), ((const uint8_t*)fHeader)+funcOffset, encodingString);
1024 const char* name = showFunctionNames ? functionName(funcOffset+fMachHeaderAddress) : "";
1025 printf("\t\t\t[%3u] funcOffset=0x%08X, encoding=0x%08X (%-56s) %s\n",
1026 j, funcOffset, entry[j].encoding(), encodingString, name);
1027 }
1028 }
1029 else if ( page->kind() == UNWIND_SECOND_LEVEL_COMPRESSED ) {
1030 macho_unwind_info_compressed_second_level_page_header<P>* cp = (macho_unwind_info_compressed_second_level_page_header<P>*)page;
1031 printf("\t\tkind=UNWIND_SECOND_LEVEL_COMPRESSED\n");
1032 printf("\t\tentryPageOffset=0x%08X\n", cp->entryPageOffset());
1033 printf("\t\tentryCount=0x%08X\n", cp->entryCount());
1034 printf("\t\tencodingsPageOffset=0x%08X\n", cp->encodingsPageOffset());
1035 printf("\t\tencodingsCount=0x%08X\n", cp->encodingsCount());
1036 const uint32_t* entries = (uint32_t*)(((uint8_t*)page)+cp->entryPageOffset());
1037 const uint32_t* encodings = (uint32_t*)(((uint8_t*)page)+cp->encodingsPageOffset());
1038 const uint32_t baseFunctionOffset = indexes[i].functionOffset();
1039 for (uint32_t j=0; j < cp->entryCount(); ++j) {
1040 uint8_t encodingIndex = UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(entries[j]);
1041 uint32_t encoding;
1042 if ( encodingIndex < sectionHeader->commonEncodingsArrayCount() )
1043 encoding = A::P::E::get32(commonEncodings[encodingIndex]);
1044 else
1045 encoding = A::P::E::get32(encodings[encodingIndex-sectionHeader->commonEncodingsArrayCount()]);
1046 char encodingString[100];
1047 uint32_t funcOff = UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entries[j])+baseFunctionOffset;
1048 decode(encoding, ((const uint8_t*)fHeader)+funcOff, encodingString);
1049 const char* name = showFunctionNames ? functionName(funcOff+fMachHeaderAddress) : "";
1050 if ( encoding & UNWIND_HAS_LSDA ) {
1051 // verify there is a corresponding entry in lsda table
1052 bool found = false;
1053 for (uint32_t k=0; k < lsdaIndexArrayCount; ++k) {
1054 if ( lindex[k].functionOffset() == funcOff ) {
1055 found = true;
1056 break;
1057 }
1058 }
1059 if ( !found ) {
1060 fprintf(stderr, "MISSING LSDA entry for %s\n", name);
1061 }
1062 }
1063 printf("\t\t\t[%3u] funcOffset=0x%08X, encoding[%3u]=0x%08X (%-56s) %s\n",
1064 j, funcOff, encodingIndex, encoding, encodingString, name);
1065 }
1066 }
1067 else {
1068 fprintf(stderr, "\t\tbad page header\n");
1069 }
1070 }
1071
1072 }
1073
1074 static void dump(const char* path, const std::set<cpu_type_t>& onlyArchs, bool showFunctionNames)
1075 {
1076 struct stat stat_buf;
1077
1078 try {
1079 int fd = ::open(path, O_RDONLY, 0);
1080 if ( fd == -1 )
1081 throw "cannot open file";
1082 if ( ::fstat(fd, &stat_buf) != 0 )
1083 throwf("fstat(%s) failed, errno=%d\n", path, errno);
1084 uint32_t length = stat_buf.st_size;
1085 uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
1086 if ( p == ((uint8_t*)(-1)) )
1087 throw "cannot map file";
1088 ::close(fd);
1089 const mach_header* mh = (mach_header*)p;
1090 if ( mh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
1091 const struct fat_header* fh = (struct fat_header*)p;
1092 const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header));
1093 for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) {
1094 size_t offset = OSSwapBigToHostInt32(archs[i].offset);
1095 size_t size = OSSwapBigToHostInt32(archs[i].size);
1096 unsigned int cputype = OSSwapBigToHostInt32(archs[i].cputype);
1097 if ( onlyArchs.count(cputype) ) {
1098 switch(cputype) {
1099 case CPU_TYPE_I386:
1100 if ( UnwindPrinter<x86>::validFile(p + offset) )
1101 UnwindPrinter<x86>::make(p + offset, size, path, showFunctionNames);
1102 else
1103 throw "in universal file, i386 slice does not contain i386 mach-o";
1104 break;
1105 case CPU_TYPE_X86_64:
1106 if ( UnwindPrinter<x86_64>::validFile(p + offset) )
1107 UnwindPrinter<x86_64>::make(p + offset, size, path, showFunctionNames);
1108 else
1109 throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
1110 break;
1111 #if SUPPORT_ARCH_arm64
1112 case CPU_TYPE_ARM64:
1113 if ( UnwindPrinter<arm64>::validFile(p + offset) )
1114 UnwindPrinter<arm64>::make(p + offset, size, path, showFunctionNames);
1115 else
1116 throw "in universal file, arm64 slice does not contain arm64 mach-o";
1117 break;
1118 #endif
1119 case CPU_TYPE_ARM:
1120 if ( UnwindPrinter<arm>::validFile(p + offset) )
1121 UnwindPrinter<arm>::make(p + offset, size, path, showFunctionNames);
1122 else
1123 throw "in universal file, arm slice does not contain arm mach-o";
1124 break;
1125 default:
1126 throwf("in universal file, unknown architecture slice 0x%x\n", cputype);
1127 }
1128 }
1129 }
1130 }
1131 else if ( UnwindPrinter<x86>::validFile(p) && onlyArchs.count(CPU_TYPE_I386) ) {
1132 UnwindPrinter<x86>::make(p, length, path, showFunctionNames);
1133 }
1134 else if ( UnwindPrinter<x86_64>::validFile(p) && onlyArchs.count(CPU_TYPE_X86_64) ) {
1135 UnwindPrinter<x86_64>::make(p, length, path, showFunctionNames);
1136 }
1137 #if SUPPORT_ARCH_arm64
1138 else if ( UnwindPrinter<arm64>::validFile(p) && onlyArchs.count(CPU_TYPE_ARM64) ) {
1139 UnwindPrinter<arm64>::make(p, length, path, showFunctionNames);
1140 }
1141 #endif
1142 else if ( UnwindPrinter<arm>::validFile(p) && onlyArchs.count(CPU_TYPE_ARM) ) {
1143 UnwindPrinter<arm>::make(p, length, path, showFunctionNames);
1144 }
1145 else {
1146 throw "not a known file type";
1147 }
1148 }
1149 catch (const char* msg) {
1150 throwf("%s in %s", msg, path);
1151 }
1152 }
1153
1154
1155 int main(int argc, const char* argv[])
1156 {
1157 std::set<cpu_type_t> onlyArchs;
1158 std::vector<const char*> files;
1159 bool showFunctionNames = true;
1160
1161 try {
1162 for(int i=1; i < argc; ++i) {
1163 const char* arg = argv[i];
1164 if ( arg[0] == '-' ) {
1165 if ( strcmp(arg, "-arch") == 0 ) {
1166 const char* arch = argv[++i];
1167 if ( strcmp(arch, "i386") == 0 )
1168 onlyArchs.insert(CPU_TYPE_I386);
1169 else if ( strcmp(arch, "x86_64") == 0 )
1170 onlyArchs.insert(CPU_TYPE_X86_64);
1171 #if SUPPORT_ARCH_arm64
1172 else if ( strcmp(arch, "arm64") == 0 )
1173 onlyArchs.insert(CPU_TYPE_ARM64);
1174 #endif
1175 else if ( strcmp(arch, "armv7k") == 0 )
1176 onlyArchs.insert(CPU_TYPE_ARM);
1177 else
1178 throwf("unknown architecture %s", arch);
1179 }
1180 else if ( strcmp(arg, "-no_symbols") == 0 ) {
1181 showFunctionNames = false;
1182 }
1183 else {
1184 throwf("unknown option: %s\n", arg);
1185 }
1186 }
1187 else {
1188 files.push_back(arg);
1189 }
1190 }
1191
1192 // use all architectures if no restrictions specified
1193 if ( onlyArchs.size() == 0 ) {
1194 onlyArchs.insert(CPU_TYPE_I386);
1195 onlyArchs.insert(CPU_TYPE_X86_64);
1196 #if SUPPORT_ARCH_arm64
1197 onlyArchs.insert(CPU_TYPE_ARM64);
1198 #endif
1199 onlyArchs.insert(CPU_TYPE_ARM);
1200 }
1201
1202 // process each file
1203 for(std::vector<const char*>::iterator it=files.begin(); it != files.end(); ++it) {
1204 dump(*it, onlyArchs, showFunctionNames);
1205 }
1206
1207 }
1208 catch (const char* msg) {
1209 fprintf(stderr, "UnwindDump failed: %s\n", msg);
1210 return 1;
1211 }
1212
1213 return 0;
1214 }
1215
1216
1217