236-3
[darwin-xtools.git] / ld64 / src / other / unwinddump.cpp
blob3da01c540f39391561697034d58a65b3c3e7c776
1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
3 * Copyright (c) 2008-2011 Apple Inc. All rights reserved.
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.
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.
22 * @APPLE_LICENSE_HEADER_END@
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>
34 #include <vector>
35 #include <set>
36 #include <unordered_set>
38 #include "configure.h"
39 #include "MachOFileAbstraction.hpp"
40 #include "Architectures.hpp"
43 __attribute__((noreturn))
44 void throwf(const char* format, ...)
46 va_list list;
47 char* p;
48 va_start(list, format);
49 vasprintf(&p, format, list);
50 va_end(list);
52 const char* t = p;
53 throw t;
57 template <typename A>
58 class UnwindPrinter
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() {}
69 private:
70 typedef typename A::P P;
71 typedef typename A::P::E E;
72 typedef typename A::P::uint_t pint_t;
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);
84 static const char* archName();
85 static void decode(uint32_t encoding, const uint8_t* funcStart, char* str);
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;
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
106 template <>
107 bool UnwindPrinter<x86>::validFile(const uint8_t* fileContent)
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;
122 return false;
125 template <>
126 bool UnwindPrinter<x86_64>::validFile(const uint8_t* fileContent)
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;
141 return false;
145 #if SUPPORT_ARCH_arm64
146 template <>
147 bool UnwindPrinter<arm64>::validFile(const uint8_t* fileContent)
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;
162 return false;
164 #endif
166 template <typename A>
167 UnwindPrinter<A>::UnwindPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path, bool showFunctionNames)
168 : fHeader(NULL), fLength(fileLength), fUnwindSection(NULL),
169 fStrings(NULL), fStringsEnd(NULL), fSymbols(NULL), fSymbolCount(0), fMachHeaderAddress(0)
171 // sanity check
172 if ( ! validFile(fileContent) )
173 throw "not a mach-o file that can be checked";
175 fPath = strdup(path);
176 fHeader = (const macho_header<P>*)fileContent;
178 getSymbolTableInfo();
180 if ( findUnwindSection() ) {
181 if ( fHeader->filetype() == MH_OBJECT )
182 printObjectUnwindSection(showFunctionNames);
183 else
184 printUnwindSection(showFunctionNames);
189 template <typename A>
190 void UnwindPrinter<A>::getSymbolTableInfo()
192 const uint8_t* const endOfFile = (uint8_t*)fHeader + fLength;
193 const uint8_t* const endOfLoadCommands = (uint8_t*)fHeader + sizeof(macho_header<P>) + fHeader->sizeofcmds();
194 const uint32_t cmd_count = fHeader->ncmds();
195 const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
196 const macho_load_command<P>* cmd = cmds;
197 for (uint32_t i = 0; i < cmd_count; ++i) {
198 uint32_t size = cmd->cmdsize();
199 const uint8_t* endOfCmd = ((uint8_t*)cmd)+cmd->cmdsize();
200 if ( endOfCmd > endOfLoadCommands )
201 throwf("load command #%d extends beyond the end of the load commands", i);
202 if ( endOfCmd > endOfFile )
203 throwf("load command #%d extends beyond the end of the file", i);
204 if ( cmd->cmd() == LC_SYMTAB) {
205 const macho_symtab_command<P>* symtab = (macho_symtab_command<P>*)cmd;
206 fSymbolCount = symtab->nsyms();
207 fSymbols = (const macho_nlist<P>*)((char*)fHeader + symtab->symoff());
208 fStrings = (char*)fHeader + symtab->stroff();
209 fStringsEnd = fStrings + symtab->strsize();
211 cmd = (const macho_load_command<P>*)endOfCmd;
215 template <typename A>
216 const char* UnwindPrinter<A>::functionName(pint_t addr, uint32_t* offset)
218 const macho_nlist<P>* closestSymbol = NULL;
219 if ( offset != NULL )
220 *offset = 0;
221 for (uint32_t i=0; i < fSymbolCount; ++i) {
222 uint8_t type = fSymbols[i].n_type();
223 if ( ((type & N_STAB) == 0) && ((type & N_TYPE) == N_SECT) ) {
224 if ( fSymbols[i].n_value() == addr ) {
225 const char* r = &fStrings[fSymbols[i].n_strx()];
226 //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);
227 return r;
229 else if ( offset != NULL ) {
230 if ( closestSymbol == NULL ) {
231 if ( fSymbols[i].n_value() < addr )
232 closestSymbol = &fSymbols[i];
234 else {
235 if ( (fSymbols[i].n_value() < addr) && (fSymbols[i].n_value() > closestSymbol->n_value()) )
236 closestSymbol = &fSymbols[i];
241 if ( closestSymbol != NULL ) {
242 *offset = addr - closestSymbol->n_value();
243 return &fStrings[closestSymbol->n_strx()];
245 return "--anonymous function--";
250 template <typename A>
251 bool UnwindPrinter<A>::findUnwindSection()
253 const char* unwindSectionName = "__unwind_info";
254 const char* unwindSegmentName = "__TEXT";
255 if ( fHeader->filetype() == MH_OBJECT ) {
256 unwindSectionName = "__compact_unwind";
257 unwindSegmentName = "__LD";
259 const uint8_t* const endOfFile = (uint8_t*)fHeader + fLength;
260 const uint8_t* const endOfLoadCommands = (uint8_t*)fHeader + sizeof(macho_header<P>) + fHeader->sizeofcmds();
261 const uint32_t cmd_count = fHeader->ncmds();
262 const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
263 const macho_load_command<P>* cmd = cmds;
264 for (uint32_t i = 0; i < cmd_count; ++i) {
265 uint32_t size = cmd->cmdsize();
266 const uint8_t* endOfCmd = ((uint8_t*)cmd)+cmd->cmdsize();
267 if ( endOfCmd > endOfLoadCommands )
268 throwf("load command #%d extends beyond the end of the load commands", i);
269 if ( endOfCmd > endOfFile )
270 throwf("load command #%d extends beyond the end of the file", i);
271 if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
272 const macho_segment_command<P>* segCmd = (const macho_segment_command<P>*)cmd;
273 const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
274 const macho_section<P>* const sectionsEnd = &sectionsStart[segCmd->nsects()];
275 for(const macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
276 if ( (strncmp(sect->sectname(), unwindSectionName, 16) == 0) && (strcmp(sect->segname(), unwindSegmentName) == 0) ) {
277 fUnwindSection = sect;
278 fMachHeaderAddress = segCmd->vmaddr();
279 return fUnwindSection;
283 cmd = (const macho_load_command<P>*)endOfCmd;
285 return false;
288 #define EXTRACT_BITS(value, mask) \
289 ( (value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask)))-1) )
292 template <>
293 void UnwindPrinter<x86_64>::decode(uint32_t encoding, const uint8_t* funcStart, char* str)
295 *str = '\0';
296 switch ( encoding & UNWIND_X86_64_MODE_MASK ) {
297 case UNWIND_X86_64_MODE_RBP_FRAME:
299 uint32_t savedRegistersOffset = EXTRACT_BITS(encoding, UNWIND_X86_64_RBP_FRAME_OFFSET);
300 uint32_t savedRegistersLocations = EXTRACT_BITS(encoding, UNWIND_X86_64_RBP_FRAME_REGISTERS);
301 if ( savedRegistersLocations == 0 ) {
302 strcpy(str, "rbp frame, no saved registers");
304 else {
305 sprintf(str, "rbp frame, at -%d:", savedRegistersOffset*8);
306 bool needComma = false;
307 for (int i=0; i < 5; ++i) {
308 if ( needComma )
309 strcat(str, ",");
310 else
311 needComma = true;
312 switch (savedRegistersLocations & 0x7) {
313 case UNWIND_X86_64_REG_NONE:
314 strcat(str, "-");
315 break;
316 case UNWIND_X86_64_REG_RBX:
317 strcat(str, "rbx");
318 break;
319 case UNWIND_X86_64_REG_R12:
320 strcat(str, "r12");
321 break;
322 case UNWIND_X86_64_REG_R13:
323 strcat(str, "r13");
324 break;
325 case UNWIND_X86_64_REG_R14:
326 strcat(str, "r14");
327 break;
328 case UNWIND_X86_64_REG_R15:
329 strcat(str, "r15");
330 break;
331 default:
332 strcat(str, "r?");
334 savedRegistersLocations = (savedRegistersLocations >> 3);
335 if ( savedRegistersLocations == 0 )
336 break;
340 break;
341 case UNWIND_X86_64_MODE_STACK_IMMD:
342 case UNWIND_X86_64_MODE_STACK_IND:
344 uint32_t stackSize = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE);
345 uint32_t stackAdjust = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST);
346 uint32_t regCount = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT);
347 uint32_t permutation = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION);
348 if ( (encoding & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_STACK_IND ) {
349 // stack size is encoded in subl $xxx,%esp instruction
350 uint32_t subl = x86_64::P::E::get32(*((uint32_t*)(funcStart+stackSize)));
351 sprintf(str, "stack size=0x%08X, ", subl + 8*stackAdjust);
353 else {
354 sprintf(str, "stack size=%d, ", stackSize*8);
356 if ( regCount == 0 ) {
357 strcat(str, "no registers saved");
359 else {
360 int permunreg[6];
361 switch ( regCount ) {
362 case 6:
363 permunreg[0] = permutation/120;
364 permutation -= (permunreg[0]*120);
365 permunreg[1] = permutation/24;
366 permutation -= (permunreg[1]*24);
367 permunreg[2] = permutation/6;
368 permutation -= (permunreg[2]*6);
369 permunreg[3] = permutation/2;
370 permutation -= (permunreg[3]*2);
371 permunreg[4] = permutation;
372 permunreg[5] = 0;
373 break;
374 case 5:
375 permunreg[0] = permutation/120;
376 permutation -= (permunreg[0]*120);
377 permunreg[1] = permutation/24;
378 permutation -= (permunreg[1]*24);
379 permunreg[2] = permutation/6;
380 permutation -= (permunreg[2]*6);
381 permunreg[3] = permutation/2;
382 permutation -= (permunreg[3]*2);
383 permunreg[4] = permutation;
384 break;
385 case 4:
386 permunreg[0] = permutation/60;
387 permutation -= (permunreg[0]*60);
388 permunreg[1] = permutation/12;
389 permutation -= (permunreg[1]*12);
390 permunreg[2] = permutation/3;
391 permutation -= (permunreg[2]*3);
392 permunreg[3] = permutation;
393 break;
394 case 3:
395 permunreg[0] = permutation/20;
396 permutation -= (permunreg[0]*20);
397 permunreg[1] = permutation/4;
398 permutation -= (permunreg[1]*4);
399 permunreg[2] = permutation;
400 break;
401 case 2:
402 permunreg[0] = permutation/5;
403 permutation -= (permunreg[0]*5);
404 permunreg[1] = permutation;
405 break;
406 case 1:
407 permunreg[0] = permutation;
408 break;
410 // renumber registers back to standard numbers
411 int registers[6];
412 bool used[7] = { false, false, false, false, false, false, false };
413 for (int i=0; i < regCount; ++i) {
414 int renum = 0;
415 for (int u=1; u < 7; ++u) {
416 if ( !used[u] ) {
417 if ( renum == permunreg[i] ) {
418 registers[i] = u;
419 used[u] = true;
420 break;
422 ++renum;
426 bool needComma = false;
427 for (int i=0; i < regCount; ++i) {
428 if ( needComma )
429 strcat(str, ",");
430 else
431 needComma = true;
432 switch ( registers[i] ) {
433 case UNWIND_X86_64_REG_RBX:
434 strcat(str, "rbx");
435 break;
436 case UNWIND_X86_64_REG_R12:
437 strcat(str, "r12");
438 break;
439 case UNWIND_X86_64_REG_R13:
440 strcat(str, "r13");
441 break;
442 case UNWIND_X86_64_REG_R14:
443 strcat(str, "r14");
444 break;
445 case UNWIND_X86_64_REG_R15:
446 strcat(str, "r15");
447 break;
448 case UNWIND_X86_64_REG_RBP:
449 strcat(str, "rbp");
450 break;
451 default:
452 strcat(str, "r??");
457 break;
458 case UNWIND_X86_64_MODE_DWARF:
459 sprintf(str, "dwarf offset 0x%08X, ", encoding & UNWIND_X86_64_DWARF_SECTION_OFFSET);
460 break;
461 default:
462 if ( encoding == 0 )
463 strcat(str, "no unwind information");
464 else
465 strcat(str, "tbd ");
467 if ( encoding & UNWIND_HAS_LSDA ) {
468 strcat(str, " LSDA");
473 template <>
474 void UnwindPrinter<x86>::decode(uint32_t encoding, const uint8_t* funcStart, char* str)
476 *str = '\0';
477 switch ( encoding & UNWIND_X86_MODE_MASK ) {
478 case UNWIND_X86_MODE_EBP_FRAME:
480 uint32_t savedRegistersOffset = EXTRACT_BITS(encoding, UNWIND_X86_EBP_FRAME_OFFSET);
481 uint32_t savedRegistersLocations = EXTRACT_BITS(encoding, UNWIND_X86_EBP_FRAME_REGISTERS);
482 if ( savedRegistersLocations == 0 ) {
483 strcpy(str, "ebp frame, no saved registers");
485 else {
486 sprintf(str, "ebp frame, at -%d:", savedRegistersOffset*4);
487 bool needComma = false;
488 for (int i=0; i < 5; ++i) {
489 if ( needComma )
490 strcat(str, ",");
491 else
492 needComma = true;
493 switch (savedRegistersLocations & 0x7) {
494 case UNWIND_X86_REG_NONE:
495 strcat(str, "-");
496 break;
497 case UNWIND_X86_REG_EBX:
498 strcat(str, "ebx");
499 break;
500 case UNWIND_X86_REG_ECX:
501 strcat(str, "ecx");
502 break;
503 case UNWIND_X86_REG_EDX:
504 strcat(str, "edx");
505 break;
506 case UNWIND_X86_REG_EDI:
507 strcat(str, "edi");
508 break;
509 case UNWIND_X86_REG_ESI:
510 strcat(str, "esi");
511 break;
512 default:
513 strcat(str, "e??");
515 savedRegistersLocations = (savedRegistersLocations >> 3);
516 if ( savedRegistersLocations == 0 )
517 break;
521 break;
522 case UNWIND_X86_MODE_STACK_IMMD:
523 case UNWIND_X86_MODE_STACK_IND:
525 uint32_t stackSize = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE);
526 uint32_t stackAdjust = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_ADJUST);
527 uint32_t regCount = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_COUNT);
528 uint32_t permutation = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION);
529 if ( (encoding & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_STACK_IND ) {
530 // stack size is encoded in subl $xxx,%esp instruction
531 uint32_t subl = x86::P::E::get32(*((uint32_t*)(funcStart+stackSize)));
532 sprintf(str, "stack size=0x%08X, ", subl+4*stackAdjust);
534 else {
535 sprintf(str, "stack size=%d, ", stackSize*4);
537 if ( regCount == 0 ) {
538 strcat(str, "no saved regs");
540 else {
541 int permunreg[6];
542 switch ( regCount ) {
543 case 6:
544 permunreg[0] = permutation/120;
545 permutation -= (permunreg[0]*120);
546 permunreg[1] = permutation/24;
547 permutation -= (permunreg[1]*24);
548 permunreg[2] = permutation/6;
549 permutation -= (permunreg[2]*6);
550 permunreg[3] = permutation/2;
551 permutation -= (permunreg[3]*2);
552 permunreg[4] = permutation;
553 permunreg[5] = 0;
554 break;
555 case 5:
556 permunreg[0] = permutation/120;
557 permutation -= (permunreg[0]*120);
558 permunreg[1] = permutation/24;
559 permutation -= (permunreg[1]*24);
560 permunreg[2] = permutation/6;
561 permutation -= (permunreg[2]*6);
562 permunreg[3] = permutation/2;
563 permutation -= (permunreg[3]*2);
564 permunreg[4] = permutation;
565 break;
566 case 4:
567 permunreg[0] = permutation/60;
568 permutation -= (permunreg[0]*60);
569 permunreg[1] = permutation/12;
570 permutation -= (permunreg[1]*12);
571 permunreg[2] = permutation/3;
572 permutation -= (permunreg[2]*3);
573 permunreg[3] = permutation;
574 break;
575 case 3:
576 permunreg[0] = permutation/20;
577 permutation -= (permunreg[0]*20);
578 permunreg[1] = permutation/4;
579 permutation -= (permunreg[1]*4);
580 permunreg[2] = permutation;
581 break;
582 case 2:
583 permunreg[0] = permutation/5;
584 permutation -= (permunreg[0]*5);
585 permunreg[1] = permutation;
586 break;
587 case 1:
588 permunreg[0] = permutation;
589 break;
591 // renumber registers back to standard numbers
592 int registers[6];
593 bool used[7] = { false, false, false, false, false, false, false };
594 for (int i=0; i < regCount; ++i) {
595 int renum = 0;
596 for (int u=1; u < 7; ++u) {
597 if ( !used[u] ) {
598 if ( renum == permunreg[i] ) {
599 registers[i] = u;
600 used[u] = true;
601 break;
603 ++renum;
607 bool needComma = false;
608 for (int i=0; i < regCount; ++i) {
609 if ( needComma )
610 strcat(str, ",");
611 else
612 needComma = true;
613 switch ( registers[i] ) {
614 case UNWIND_X86_REG_EBX:
615 strcat(str, "ebx");
616 break;
617 case UNWIND_X86_REG_ECX:
618 strcat(str, "ecx");
619 break;
620 case UNWIND_X86_REG_EDX:
621 strcat(str, "edx");
622 break;
623 case UNWIND_X86_REG_EDI:
624 strcat(str, "edi");
625 break;
626 case UNWIND_X86_REG_ESI:
627 strcat(str, "esi");
628 break;
629 case UNWIND_X86_REG_EBP:
630 strcat(str, "ebp");
631 break;
632 default:
633 strcat(str, "e??");
638 break;
639 case UNWIND_X86_MODE_DWARF:
640 sprintf(str, "dwarf offset 0x%08X, ", encoding & UNWIND_X86_DWARF_SECTION_OFFSET);
641 break;
642 default:
643 if ( encoding == 0 )
644 strcat(str, "no unwind information");
645 else
646 strcat(str, "tbd ");
648 if ( encoding & UNWIND_HAS_LSDA ) {
649 strcat(str, " LSDA");
654 #if SUPPORT_ARCH_arm64
655 template <>
656 void UnwindPrinter<arm64>::decode(uint32_t encoding, const uint8_t* funcStart, char* str)
658 uint32_t stackSize;
659 switch ( encoding & UNWIND_ARM64_MODE_MASK ) {
660 case UNWIND_ARM64_MODE_FRAMELESS:
661 stackSize = EXTRACT_BITS(encoding, UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK);
662 if ( stackSize == 0 )
663 strcpy(str, "no frame, no saved registers ");
664 else
665 sprintf(str, "stack size=%d: ", 16 * stackSize);
666 if ( encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR )
667 strcat(str, "x19/20 ");
668 if ( encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR )
669 strcat(str, "x21/22 ");
670 if ( encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR )
671 strcat(str, "x23/24 ");
672 if ( encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR )
673 strcat(str, "x25/26 ");
674 if ( encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR )
675 strcat(str, "x27/28 ");
676 if ( encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR )
677 strcat(str, "d8/9 ");
678 if ( encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR )
679 strcat(str, "d10/11 ");
680 if ( encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR )
681 strcat(str, "d12/13 ");
682 if ( encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR )
683 strcat(str, "d14/15 ");
684 break;
685 break;
686 case UNWIND_ARM64_MODE_DWARF:
687 sprintf(str, "dwarf offset 0x%08X, ", encoding & UNWIND_X86_64_DWARF_SECTION_OFFSET);
688 break;
689 case UNWIND_ARM64_MODE_FRAME:
690 strcpy(str, "std frame: ");
691 if ( encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR )
692 strcat(str, "x19/20 ");
693 if ( encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR )
694 strcat(str, "x21/22 ");
695 if ( encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR )
696 strcat(str, "x23/24 ");
697 if ( encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR )
698 strcat(str, "x25/26 ");
699 if ( encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR )
700 strcat(str, "x27/28 ");
701 if ( encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR )
702 strcat(str, "d8/9 ");
703 if ( encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR )
704 strcat(str, "d10/11 ");
705 if ( encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR )
706 strcat(str, "d12/13 ");
707 if ( encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR )
708 strcat(str, "d14/15 ");
709 break;
710 case UNWIND_ARM64_MODE_FRAME_OLD:
711 strcpy(str, "old frame: ");
712 if ( encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR_OLD )
713 strcat(str, "x21/22 ");
714 if ( encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR_OLD )
715 strcat(str, "x23/24 ");
716 if ( encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR_OLD )
717 strcat(str, "x25/26 ");
718 if ( encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR_OLD )
719 strcat(str, "x27/28 ");
720 if ( encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR_OLD )
721 strcat(str, "d8/9 ");
722 if ( encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR_OLD )
723 strcat(str, "d10/11 ");
724 if ( encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR_OLD )
725 strcat(str, "d12/13 ");
726 if ( encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR_OLD )
727 strcat(str, "d14/15 ");
728 break;
731 #endif
733 template <>
734 const char* UnwindPrinter<x86_64>::personalityName(const macho_relocation_info<x86_64::P>* reloc)
736 //assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section");
737 //assert((reloc->r_type() == X86_64_RELOC_UNSIGNED) && "wrong reloc type on personality column in __compact_unwind section");
738 const macho_nlist<P>& sym = fSymbols[reloc->r_symbolnum()];
739 return &fStrings[sym.n_strx()];
742 template <>
743 const char* UnwindPrinter<x86>::personalityName(const macho_relocation_info<x86::P>* reloc)
745 //assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section");
746 //assert((reloc->r_type() == GENERIC_RELOC_VANILLA) && "wrong reloc type on personality column in __compact_unwind section");
747 const macho_nlist<P>& sym = fSymbols[reloc->r_symbolnum()];
748 return &fStrings[sym.n_strx()];
751 #if SUPPORT_ARCH_arm64
752 template <>
753 const char* UnwindPrinter<arm64>::personalityName(const macho_relocation_info<arm64::P>* reloc)
755 //assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section");
756 //assert((reloc->r_type() == ARM64_RELOC_UNSIGNED) && "wrong reloc type on personality column in __compact_unwind section");
757 const macho_nlist<P>& sym = fSymbols[reloc->r_symbolnum()];
758 return &fStrings[sym.n_strx()];
760 #endif
762 template <typename A>
763 bool UnwindPrinter<A>::hasExernReloc(uint64_t sectionOffset, const char** personalityStr, pint_t* addr)
765 const macho_relocation_info<P>* relocs = (macho_relocation_info<P>*)((uint8_t*)fHeader + fUnwindSection->reloff());
766 const macho_relocation_info<P>* relocsEnd = &relocs[fUnwindSection->nreloc()];
767 for (const macho_relocation_info<P>* reloc = relocs; reloc < relocsEnd; ++reloc) {
768 if ( reloc->r_extern() && (reloc->r_address() == sectionOffset) ) {
769 *personalityStr = this->personalityName(reloc);
770 if ( addr != NULL )
771 *addr = fSymbols[reloc->r_symbolnum()].n_value();
772 return true;
775 return false;
779 template <typename A>
780 void UnwindPrinter<A>::printObjectUnwindSection(bool showFunctionNames)
782 printf("Arch: %s, Section: __LD,__compact_unwind (size=0x%08llX, => %lld entries)\n",
783 archName(), fUnwindSection->size(), fUnwindSection->size() / sizeof(macho_compact_unwind_entry<P>));
785 const macho_compact_unwind_entry<P>* const entriesStart = (macho_compact_unwind_entry<P>*)((uint8_t*)fHeader + fUnwindSection->offset());
786 const macho_compact_unwind_entry<P>* const entriesEnd = (macho_compact_unwind_entry<P>*)((uint8_t*)fHeader + fUnwindSection->offset() + fUnwindSection->size());
787 for (const macho_compact_unwind_entry<P>* entry=entriesStart; entry < entriesEnd; ++entry) {
788 uint64_t entryAddress = ((char*)entry - (char*)entriesStart) + fUnwindSection->addr();
789 printf("0x%08llX:\n", entryAddress);
790 const char* functionNameStr;
791 pint_t funcAddress;
792 uint32_t offsetInFunction;
793 if ( hasExernReloc(((char*)entry-(char*)entriesStart)+macho_compact_unwind_entry<P>::codeStartFieldOffset(), &functionNameStr, &funcAddress) ) {
794 offsetInFunction = entry->codeStart();
796 else {
797 functionNameStr = this->functionName(entry->codeStart(), &offsetInFunction);
799 if ( offsetInFunction == 0 )
800 printf(" start: 0x%08llX %s\n", (uint64_t)funcAddress, functionNameStr);
801 else
802 printf(" start: 0x%08llX %s+0x%X\n", (uint64_t)funcAddress+offsetInFunction, functionNameStr, offsetInFunction);
804 printf(" end: 0x%08llX (len=0x%08X)\n", (uint64_t)(funcAddress+offsetInFunction+entry->codeLen()), entry->codeLen());
806 char encodingString[200];
807 this->decode(entry->compactUnwindInfo(), ((const uint8_t*)fHeader), encodingString);
808 printf(" unwind info: 0x%08X %s\n", entry->compactUnwindInfo(), encodingString);
810 const char* personalityNameStr;
811 if ( hasExernReloc(((char*)entry-(char*)entriesStart)+macho_compact_unwind_entry<P>::personalityFieldOffset(), &personalityNameStr) ) {
812 printf(" personality: %s\n", personalityNameStr);
814 else {
815 printf(" personality:\n");
817 if ( entry->lsda() == 0 ) {
818 printf(" lsda:\n");
820 else {
821 uint32_t lsdaOffset;
822 const char* lsdaName = this->functionName(entry->lsda(), &lsdaOffset);
823 if ( lsdaOffset == 0 )
824 printf(" lsda: 0x%08llX %s\n", (uint64_t)entry->lsda(), lsdaName);
825 else
826 printf(" lsda: 0x%08llX %s+0x%X\n", (uint64_t)entry->lsda(), lsdaName, lsdaOffset);
833 template <typename A>
834 void UnwindPrinter<A>::printUnwindSection(bool showFunctionNames)
836 const uint8_t* sectionContent = (uint8_t*)fHeader + fUnwindSection->offset();
837 macho_unwind_info_section_header<P>* sectionHeader = (macho_unwind_info_section_header<P>*)(sectionContent);
839 printf("Arch: %s, Section: __TEXT,__unwind_info (addr=0x%08llX, size=0x%08llX, fileOffset=0x%08X)\n",
840 archName(), fUnwindSection->addr(), fUnwindSection->size(), fUnwindSection->offset());
841 printf("\tversion=0x%08X\n", sectionHeader->version());
842 printf("\tcommonEncodingsArraySectionOffset=0x%08X\n", sectionHeader->commonEncodingsArraySectionOffset());
843 printf("\tcommonEncodingsArrayCount=0x%08X\n", sectionHeader->commonEncodingsArrayCount());
844 printf("\tpersonalityArraySectionOffset=0x%08X\n", sectionHeader->personalityArraySectionOffset());
845 printf("\tpersonalityArrayCount=0x%08X\n", sectionHeader->personalityArrayCount());
846 printf("\tindexSectionOffset=0x%08X\n", sectionHeader->indexSectionOffset());
847 printf("\tindexCount=0x%08X\n", sectionHeader->indexCount());
848 printf("\tcommon encodings: (count=%u)\n", sectionHeader->commonEncodingsArrayCount());
849 const uint32_t* commonEncodings = (uint32_t*)&sectionContent[sectionHeader->commonEncodingsArraySectionOffset()];
850 for (uint32_t i=0; i < sectionHeader->commonEncodingsArrayCount(); ++i) {
851 printf("\t\tencoding[%3u]=0x%08X\n", i, A::P::E::get32(commonEncodings[i]));
853 printf("\tpersonalities: (count=%u)\n", sectionHeader->personalityArrayCount());
854 const uint32_t* personalityArray = (uint32_t*)&sectionContent[sectionHeader->personalityArraySectionOffset()];
855 for (uint32_t i=0; i < sectionHeader->personalityArrayCount(); ++i) {
856 printf("\t\t[%2u]=0x%08X\n", i+1, A::P::E::get32(personalityArray[i]));
858 printf("\tfirst level index: (count=%u)\n", sectionHeader->indexCount());
859 macho_unwind_info_section_header_index_entry<P>* indexes = (macho_unwind_info_section_header_index_entry<P>*)&sectionContent[sectionHeader->indexSectionOffset()];
860 for (uint32_t i=0; i < sectionHeader->indexCount(); ++i) {
861 printf("\t\t[%2u] funcOffset=0x%08X, pageOffset=0x%08X, lsdaOffset=0x%08X\n",
862 i, indexes[i].functionOffset(), indexes[i].secondLevelPagesSectionOffset(), indexes[i].lsdaIndexArraySectionOffset());
864 uint32_t lsdaIndexArraySectionOffset = indexes[0].lsdaIndexArraySectionOffset();
865 uint32_t lsdaIndexArrayEndSectionOffset = indexes[sectionHeader->indexCount()-1].lsdaIndexArraySectionOffset();
866 uint32_t lsdaIndexArrayCount = (lsdaIndexArrayEndSectionOffset-lsdaIndexArraySectionOffset)/sizeof(macho_unwind_info_section_header_lsda_index_entry<P>);
867 printf("\tLSDA table: (section offset 0x%08X, count=%u)\n", lsdaIndexArraySectionOffset, lsdaIndexArrayCount);
868 macho_unwind_info_section_header_lsda_index_entry<P>* lindex = (macho_unwind_info_section_header_lsda_index_entry<P>*)&sectionContent[lsdaIndexArraySectionOffset];
869 for (uint32_t i=0; i < lsdaIndexArrayCount; ++i) {
870 const char* name = showFunctionNames ? functionName(lindex[i].functionOffset()+fMachHeaderAddress) : "";
871 printf("\t\t[%3u] funcOffset=0x%08X, lsdaOffset=0x%08X, %s\n", i, lindex[i].functionOffset(), lindex[i].lsdaOffset(), name);
872 if ( *(((uint8_t*)fHeader) + lindex[i].lsdaOffset()) != 0xFF )
873 fprintf(stderr, "BAD LSDA entry (does not start with 0xFF) for %s\n", functionName(lindex[i].functionOffset()+fMachHeaderAddress));
875 for (uint32_t i=0; i < sectionHeader->indexCount()-1; ++i) {
876 printf("\tsecond level index[%u] sectionOffset=0x%08X, count=%u, fileOffset=0x%08X\n", i, indexes[i].secondLevelPagesSectionOffset(),
877 sectionHeader->indexCount(), fUnwindSection->offset()+indexes[i].secondLevelPagesSectionOffset());
878 macho_unwind_info_regular_second_level_page_header<P>* page = (macho_unwind_info_regular_second_level_page_header<P>*)&sectionContent[indexes[i].secondLevelPagesSectionOffset()];
879 if ( page->kind() == UNWIND_SECOND_LEVEL_REGULAR ) {
880 printf("\t\tkind=UNWIND_SECOND_LEVEL_REGULAR\n");
881 printf("\t\tentryPageOffset=0x%08X\n", page->entryPageOffset());
882 printf("\t\tentryCount=0x%08X\n", page->entryCount());
883 const macho_unwind_info_regular_second_level_entry<P>* entry = (macho_unwind_info_regular_second_level_entry<P>*)((char*)page+page->entryPageOffset());
884 for (uint32_t j=0; j < page->entryCount(); ++j) {
885 uint32_t funcOffset = entry[j].functionOffset();
886 if ( entry[j].encoding() & UNWIND_HAS_LSDA ) {
887 // verify there is a corresponding entry in lsda table
888 bool found = false;
889 for (uint32_t k=0; k < lsdaIndexArrayCount; ++k) {
890 if ( lindex[k].functionOffset() == funcOffset ) {
891 found = true;
892 break;
895 if ( !found ) {
896 fprintf(stderr, "MISSING LSDA entry for %s\n", functionName(funcOffset+fMachHeaderAddress));
899 char encodingString[100];
900 decode(entry[j].encoding(), ((const uint8_t*)fHeader)+funcOffset, encodingString);
901 const char* name = showFunctionNames ? functionName(funcOffset+fMachHeaderAddress) : "";
902 printf("\t\t\t[%3u] funcOffset=0x%08X, encoding=0x%08X (%-56s) %s\n",
903 j, funcOffset, entry[j].encoding(), encodingString, name);
906 else if ( page->kind() == UNWIND_SECOND_LEVEL_COMPRESSED ) {
907 macho_unwind_info_compressed_second_level_page_header<P>* cp = (macho_unwind_info_compressed_second_level_page_header<P>*)page;
908 printf("\t\tkind=UNWIND_SECOND_LEVEL_COMPRESSED\n");
909 printf("\t\tentryPageOffset=0x%08X\n", cp->entryPageOffset());
910 printf("\t\tentryCount=0x%08X\n", cp->entryCount());
911 printf("\t\tencodingsPageOffset=0x%08X\n", cp->encodingsPageOffset());
912 printf("\t\tencodingsCount=0x%08X\n", cp->encodingsCount());
913 const uint32_t* entries = (uint32_t*)(((uint8_t*)page)+cp->entryPageOffset());
914 const uint32_t* encodings = (uint32_t*)(((uint8_t*)page)+cp->encodingsPageOffset());
915 const uint32_t baseFunctionOffset = indexes[i].functionOffset();
916 for (uint32_t j=0; j < cp->entryCount(); ++j) {
917 uint8_t encodingIndex = UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(entries[j]);
918 uint32_t encoding;
919 if ( encodingIndex < sectionHeader->commonEncodingsArrayCount() )
920 encoding = A::P::E::get32(commonEncodings[encodingIndex]);
921 else
922 encoding = A::P::E::get32(encodings[encodingIndex-sectionHeader->commonEncodingsArrayCount()]);
923 char encodingString[100];
924 uint32_t funcOff = UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entries[j])+baseFunctionOffset;
925 decode(encoding, ((const uint8_t*)fHeader)+funcOff, encodingString);
926 const char* name = showFunctionNames ? functionName(funcOff+fMachHeaderAddress) : "";
927 if ( encoding & UNWIND_HAS_LSDA ) {
928 // verify there is a corresponding entry in lsda table
929 bool found = false;
930 for (uint32_t k=0; k < lsdaIndexArrayCount; ++k) {
931 if ( lindex[k].functionOffset() == funcOff ) {
932 found = true;
933 break;
936 if ( !found ) {
937 fprintf(stderr, "MISSING LSDA entry for %s\n", name);
940 printf("\t\t\t[%3u] funcOffset=0x%08X, encoding[%3u]=0x%08X (%-56s) %s\n",
941 j, funcOff, encodingIndex, encoding, encodingString, name);
944 else {
945 fprintf(stderr, "\t\tbad page header\n");
951 static void dump(const char* path, const std::set<cpu_type_t>& onlyArchs, bool showFunctionNames)
953 struct stat stat_buf;
955 try {
956 int fd = ::open(path, O_RDONLY, 0);
957 if ( fd == -1 )
958 throw "cannot open file";
959 if ( ::fstat(fd, &stat_buf) != 0 )
960 throwf("fstat(%s) failed, errno=%d\n", path, errno);
961 uint32_t length = stat_buf.st_size;
962 uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
963 if ( p == ((uint8_t*)(-1)) )
964 throw "cannot map file";
965 ::close(fd);
966 const mach_header* mh = (mach_header*)p;
967 if ( mh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
968 const struct fat_header* fh = (struct fat_header*)p;
969 const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header));
970 for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) {
971 size_t offset = OSSwapBigToHostInt32(archs[i].offset);
972 size_t size = OSSwapBigToHostInt32(archs[i].size);
973 unsigned int cputype = OSSwapBigToHostInt32(archs[i].cputype);
974 if ( onlyArchs.count(cputype) ) {
975 switch(cputype) {
976 case CPU_TYPE_I386:
977 if ( UnwindPrinter<x86>::validFile(p + offset) )
978 UnwindPrinter<x86>::make(p + offset, size, path, showFunctionNames);
979 else
980 throw "in universal file, i386 slice does not contain i386 mach-o";
981 break;
982 case CPU_TYPE_X86_64:
983 if ( UnwindPrinter<x86_64>::validFile(p + offset) )
984 UnwindPrinter<x86_64>::make(p + offset, size, path, showFunctionNames);
985 else
986 throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
987 break;
988 #if SUPPORT_ARCH_arm64
989 case CPU_TYPE_ARM64:
990 if ( UnwindPrinter<arm64>::validFile(p + offset) )
991 UnwindPrinter<arm64>::make(p + offset, size, path, showFunctionNames);
992 else
993 throw "in universal file, arm64 slice does not contain arm mach-o";
994 break;
995 #endif
996 default:
997 throwf("in universal file, unknown architecture slice 0x%x\n", cputype);
1002 else if ( UnwindPrinter<x86>::validFile(p) && onlyArchs.count(CPU_TYPE_I386) ) {
1003 UnwindPrinter<x86>::make(p, length, path, showFunctionNames);
1005 else if ( UnwindPrinter<x86_64>::validFile(p) && onlyArchs.count(CPU_TYPE_X86_64) ) {
1006 UnwindPrinter<x86_64>::make(p, length, path, showFunctionNames);
1008 #if SUPPORT_ARCH_arm64
1009 else if ( UnwindPrinter<arm64>::validFile(p) && onlyArchs.count(CPU_TYPE_ARM64) ) {
1010 UnwindPrinter<arm64>::make(p, length, path, showFunctionNames);
1012 #endif
1013 else {
1014 throw "not a known file type";
1017 catch (const char* msg) {
1018 throwf("%s in %s", msg, path);
1023 int main(int argc, const char* argv[])
1025 std::set<cpu_type_t> onlyArchs;
1026 std::vector<const char*> files;
1027 bool showFunctionNames = true;
1029 try {
1030 for(int i=1; i < argc; ++i) {
1031 const char* arg = argv[i];
1032 if ( arg[0] == '-' ) {
1033 if ( strcmp(arg, "-arch") == 0 ) {
1034 const char* arch = argv[++i];
1035 if ( strcmp(arch, "i386") == 0 )
1036 onlyArchs.insert(CPU_TYPE_I386);
1037 else if ( strcmp(arch, "x86_64") == 0 )
1038 onlyArchs.insert(CPU_TYPE_X86_64);
1039 #if SUPPORT_ARCH_arm64
1040 else if ( strcmp(arch, "arm64") == 0 )
1041 onlyArchs.insert(CPU_TYPE_ARM64);
1042 #endif
1043 else
1044 throwf("unknown architecture %s", arch);
1046 else if ( strcmp(arg, "-no_symbols") == 0 ) {
1047 showFunctionNames = false;
1049 else {
1050 throwf("unknown option: %s\n", arg);
1053 else {
1054 files.push_back(arg);
1058 // use all architectures if no restrictions specified
1059 if ( onlyArchs.size() == 0 ) {
1060 onlyArchs.insert(CPU_TYPE_I386);
1061 onlyArchs.insert(CPU_TYPE_X86_64);
1062 #if SUPPORT_ARCH_arm64
1063 onlyArchs.insert(CPU_TYPE_ARM64);
1064 #endif
1067 // process each file
1068 for(std::vector<const char*>::iterator it=files.begin(); it != files.end(); ++it) {
1069 dump(*it, onlyArchs, showFunctionNames);
1073 catch (const char* msg) {
1074 fprintf(stderr, "UnwindDump failed: %s\n", msg);
1075 return 1;
1078 return 0;