]> git.saurik.com Git - apple/ld64.git/blob - src/other/unwinddump.cpp
ld64-409.12.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
167 template <>
168 bool UnwindPrinter<arm>::validFile(const uint8_t* fileContent)
169 {
170 const macho_header<P>* header = (const macho_header<P>*)fileContent;
171 if ( header->magic() != MH_MAGIC )
172 return false;
173 if ( header->cputype() != CPU_TYPE_ARM )
174 return false;
175 switch (header->filetype()) {
176 case MH_EXECUTE:
177 case MH_DYLIB:
178 case MH_BUNDLE:
179 case MH_DYLINKER:
180 case MH_OBJECT:
181 return true;
182 }
183 return false;
184 }
185
186 template <typename A>
187 UnwindPrinter<A>::UnwindPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path, bool showFunctionNames)
188 : fHeader(NULL), fLength(fileLength), fUnwindSection(NULL),
189 fStrings(NULL), fStringsEnd(NULL), fSymbols(NULL), fSymbolCount(0), fMachHeaderAddress(0)
190 {
191 // sanity check
192 if ( ! validFile(fileContent) )
193 throw "not a mach-o file that can be checked";
194
195 fPath = strdup(path);
196 fHeader = (const macho_header<P>*)fileContent;
197
198 getSymbolTableInfo();
199
200 if ( findUnwindSection() ) {
201 if ( fHeader->filetype() == MH_OBJECT )
202 printObjectUnwindSection(showFunctionNames);
203 else
204 printUnwindSection(showFunctionNames);
205 }
206 }
207
208
209 template <typename A>
210 void UnwindPrinter<A>::getSymbolTableInfo()
211 {
212 const uint8_t* const endOfFile = (uint8_t*)fHeader + fLength;
213 const uint8_t* const endOfLoadCommands = (uint8_t*)fHeader + sizeof(macho_header<P>) + fHeader->sizeofcmds();
214 const uint32_t cmd_count = fHeader->ncmds();
215 const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
216 const macho_load_command<P>* cmd = cmds;
217 for (uint32_t i = 0; i < cmd_count; ++i) {
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 pint_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 const uint8_t* endOfCmd = ((uint8_t*)cmd)+cmd->cmdsize();
292 if ( endOfCmd > endOfLoadCommands )
293 throwf("load command #%d extends beyond the end of the load commands", i);
294 if ( endOfCmd > endOfFile )
295 throwf("load command #%d extends beyond the end of the file", i);
296 if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
297 const macho_segment_command<P>* segCmd = (const macho_segment_command<P>*)cmd;
298 const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
299 const macho_section<P>* const sectionsEnd = &sectionsStart[segCmd->nsects()];
300 for(const macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
301 if ( (strncmp(sect->sectname(), unwindSectionName, 16) == 0) && (strcmp(sect->segname(), unwindSegmentName) == 0) ) {
302 fUnwindSection = sect;
303 fMachHeaderAddress = segCmd->vmaddr();
304 return fUnwindSection;
305 }
306 }
307 }
308 cmd = (const macho_load_command<P>*)endOfCmd;
309 }
310 return false;
311 }
312
313 #define EXTRACT_BITS(value, mask) \
314 ( (value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask)))-1) )
315
316
317 template <>
318 void UnwindPrinter<x86_64>::decode(uint32_t encoding, const uint8_t* funcStart, char* str)
319 {
320 *str = '\0';
321 switch ( encoding & UNWIND_X86_64_MODE_MASK ) {
322 case UNWIND_X86_64_MODE_RBP_FRAME:
323 {
324 uint32_t savedRegistersOffset = EXTRACT_BITS(encoding, UNWIND_X86_64_RBP_FRAME_OFFSET);
325 uint32_t savedRegistersLocations = EXTRACT_BITS(encoding, UNWIND_X86_64_RBP_FRAME_REGISTERS);
326 if ( savedRegistersLocations == 0 ) {
327 strcpy(str, "rbp frame, no saved registers");
328 }
329 else {
330 sprintf(str, "rbp frame, at -%d:", savedRegistersOffset*8);
331 bool needComma = false;
332 for (int i=0; i < 5; ++i) {
333 if ( needComma )
334 strcat(str, ",");
335 else
336 needComma = true;
337 switch (savedRegistersLocations & 0x7) {
338 case UNWIND_X86_64_REG_NONE:
339 strcat(str, "-");
340 break;
341 case UNWIND_X86_64_REG_RBX:
342 strcat(str, "rbx");
343 break;
344 case UNWIND_X86_64_REG_R12:
345 strcat(str, "r12");
346 break;
347 case UNWIND_X86_64_REG_R13:
348 strcat(str, "r13");
349 break;
350 case UNWIND_X86_64_REG_R14:
351 strcat(str, "r14");
352 break;
353 case UNWIND_X86_64_REG_R15:
354 strcat(str, "r15");
355 break;
356 default:
357 strcat(str, "r?");
358 }
359 savedRegistersLocations = (savedRegistersLocations >> 3);
360 if ( savedRegistersLocations == 0 )
361 break;
362 }
363 }
364 }
365 break;
366 case UNWIND_X86_64_MODE_STACK_IMMD:
367 case UNWIND_X86_64_MODE_STACK_IND:
368 {
369 uint32_t stackSize = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE);
370 uint32_t stackAdjust = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST);
371 uint32_t regCount = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT);
372 uint32_t permutation = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION);
373 if ( (encoding & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_STACK_IND ) {
374 // stack size is encoded in subl $xxx,%esp instruction
375 uint32_t subl = x86_64::P::E::get32(*((uint32_t*)(funcStart+stackSize)));
376 sprintf(str, "stack size=0x%08X, ", subl + 8*stackAdjust);
377 }
378 else {
379 sprintf(str, "stack size=%d, ", stackSize*8);
380 }
381 if ( regCount == 0 ) {
382 strcat(str, "no registers saved");
383 }
384 else {
385 int permunreg[6];
386 switch ( regCount ) {
387 case 6:
388 permunreg[0] = permutation/120;
389 permutation -= (permunreg[0]*120);
390 permunreg[1] = permutation/24;
391 permutation -= (permunreg[1]*24);
392 permunreg[2] = permutation/6;
393 permutation -= (permunreg[2]*6);
394 permunreg[3] = permutation/2;
395 permutation -= (permunreg[3]*2);
396 permunreg[4] = permutation;
397 permunreg[5] = 0;
398 break;
399 case 5:
400 permunreg[0] = permutation/120;
401 permutation -= (permunreg[0]*120);
402 permunreg[1] = permutation/24;
403 permutation -= (permunreg[1]*24);
404 permunreg[2] = permutation/6;
405 permutation -= (permunreg[2]*6);
406 permunreg[3] = permutation/2;
407 permutation -= (permunreg[3]*2);
408 permunreg[4] = permutation;
409 break;
410 case 4:
411 permunreg[0] = permutation/60;
412 permutation -= (permunreg[0]*60);
413 permunreg[1] = permutation/12;
414 permutation -= (permunreg[1]*12);
415 permunreg[2] = permutation/3;
416 permutation -= (permunreg[2]*3);
417 permunreg[3] = permutation;
418 break;
419 case 3:
420 permunreg[0] = permutation/20;
421 permutation -= (permunreg[0]*20);
422 permunreg[1] = permutation/4;
423 permutation -= (permunreg[1]*4);
424 permunreg[2] = permutation;
425 break;
426 case 2:
427 permunreg[0] = permutation/5;
428 permutation -= (permunreg[0]*5);
429 permunreg[1] = permutation;
430 break;
431 case 1:
432 permunreg[0] = permutation;
433 break;
434 }
435 // renumber registers back to standard numbers
436 int registers[6];
437 bool used[7] = { false, false, false, false, false, false, false };
438 for (int i=0; i < regCount; ++i) {
439 int renum = 0;
440 for (int u=1; u < 7; ++u) {
441 if ( !used[u] ) {
442 if ( renum == permunreg[i] ) {
443 registers[i] = u;
444 used[u] = true;
445 break;
446 }
447 ++renum;
448 }
449 }
450 }
451 bool needComma = false;
452 for (int i=0; i < regCount; ++i) {
453 if ( needComma )
454 strcat(str, ",");
455 else
456 needComma = true;
457 switch ( registers[i] ) {
458 case UNWIND_X86_64_REG_RBX:
459 strcat(str, "rbx");
460 break;
461 case UNWIND_X86_64_REG_R12:
462 strcat(str, "r12");
463 break;
464 case UNWIND_X86_64_REG_R13:
465 strcat(str, "r13");
466 break;
467 case UNWIND_X86_64_REG_R14:
468 strcat(str, "r14");
469 break;
470 case UNWIND_X86_64_REG_R15:
471 strcat(str, "r15");
472 break;
473 case UNWIND_X86_64_REG_RBP:
474 strcat(str, "rbp");
475 break;
476 default:
477 strcat(str, "r??");
478 }
479 }
480 }
481 }
482 break;
483 case UNWIND_X86_64_MODE_DWARF:
484 sprintf(str, "dwarf offset 0x%08X, ", encoding & UNWIND_X86_64_DWARF_SECTION_OFFSET);
485 break;
486 default:
487 if ( encoding == 0 )
488 strcat(str, "no unwind information");
489 else
490 strcat(str, "tbd ");
491 }
492 if ( encoding & UNWIND_HAS_LSDA ) {
493 strcat(str, " LSDA");
494 }
495
496 }
497
498 template <>
499 void UnwindPrinter<x86>::decode(uint32_t encoding, const uint8_t* funcStart, char* str)
500 {
501 *str = '\0';
502 switch ( encoding & UNWIND_X86_MODE_MASK ) {
503 case UNWIND_X86_MODE_EBP_FRAME:
504 {
505 uint32_t savedRegistersOffset = EXTRACT_BITS(encoding, UNWIND_X86_EBP_FRAME_OFFSET);
506 uint32_t savedRegistersLocations = EXTRACT_BITS(encoding, UNWIND_X86_EBP_FRAME_REGISTERS);
507 if ( savedRegistersLocations == 0 ) {
508 strcpy(str, "ebp frame, no saved registers");
509 }
510 else {
511 sprintf(str, "ebp frame, at -%d:", savedRegistersOffset*4);
512 bool needComma = false;
513 for (int i=0; i < 5; ++i) {
514 if ( needComma )
515 strcat(str, ",");
516 else
517 needComma = true;
518 switch (savedRegistersLocations & 0x7) {
519 case UNWIND_X86_REG_NONE:
520 strcat(str, "-");
521 break;
522 case UNWIND_X86_REG_EBX:
523 strcat(str, "ebx");
524 break;
525 case UNWIND_X86_REG_ECX:
526 strcat(str, "ecx");
527 break;
528 case UNWIND_X86_REG_EDX:
529 strcat(str, "edx");
530 break;
531 case UNWIND_X86_REG_EDI:
532 strcat(str, "edi");
533 break;
534 case UNWIND_X86_REG_ESI:
535 strcat(str, "esi");
536 break;
537 default:
538 strcat(str, "e??");
539 }
540 savedRegistersLocations = (savedRegistersLocations >> 3);
541 if ( savedRegistersLocations == 0 )
542 break;
543 }
544 }
545 }
546 break;
547 case UNWIND_X86_MODE_STACK_IMMD:
548 case UNWIND_X86_MODE_STACK_IND:
549 {
550 uint32_t stackSize = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE);
551 uint32_t stackAdjust = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_ADJUST);
552 uint32_t regCount = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_COUNT);
553 uint32_t permutation = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION);
554 if ( (encoding & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_STACK_IND ) {
555 // stack size is encoded in subl $xxx,%esp instruction
556 uint32_t subl = x86::P::E::get32(*((uint32_t*)(funcStart+stackSize)));
557 sprintf(str, "stack size=0x%08X, ", subl+4*stackAdjust);
558 }
559 else {
560 sprintf(str, "stack size=%d, ", stackSize*4);
561 }
562 if ( regCount == 0 ) {
563 strcat(str, "no saved regs");
564 }
565 else {
566 int permunreg[6];
567 switch ( regCount ) {
568 case 6:
569 permunreg[0] = permutation/120;
570 permutation -= (permunreg[0]*120);
571 permunreg[1] = permutation/24;
572 permutation -= (permunreg[1]*24);
573 permunreg[2] = permutation/6;
574 permutation -= (permunreg[2]*6);
575 permunreg[3] = permutation/2;
576 permutation -= (permunreg[3]*2);
577 permunreg[4] = permutation;
578 permunreg[5] = 0;
579 break;
580 case 5:
581 permunreg[0] = permutation/120;
582 permutation -= (permunreg[0]*120);
583 permunreg[1] = permutation/24;
584 permutation -= (permunreg[1]*24);
585 permunreg[2] = permutation/6;
586 permutation -= (permunreg[2]*6);
587 permunreg[3] = permutation/2;
588 permutation -= (permunreg[3]*2);
589 permunreg[4] = permutation;
590 break;
591 case 4:
592 permunreg[0] = permutation/60;
593 permutation -= (permunreg[0]*60);
594 permunreg[1] = permutation/12;
595 permutation -= (permunreg[1]*12);
596 permunreg[2] = permutation/3;
597 permutation -= (permunreg[2]*3);
598 permunreg[3] = permutation;
599 break;
600 case 3:
601 permunreg[0] = permutation/20;
602 permutation -= (permunreg[0]*20);
603 permunreg[1] = permutation/4;
604 permutation -= (permunreg[1]*4);
605 permunreg[2] = permutation;
606 break;
607 case 2:
608 permunreg[0] = permutation/5;
609 permutation -= (permunreg[0]*5);
610 permunreg[1] = permutation;
611 break;
612 case 1:
613 permunreg[0] = permutation;
614 break;
615 }
616 // renumber registers back to standard numbers
617 int registers[6];
618 bool used[7] = { false, false, false, false, false, false, false };
619 for (int i=0; i < regCount; ++i) {
620 int renum = 0;
621 for (int u=1; u < 7; ++u) {
622 if ( !used[u] ) {
623 if ( renum == permunreg[i] ) {
624 registers[i] = u;
625 used[u] = true;
626 break;
627 }
628 ++renum;
629 }
630 }
631 }
632 bool needComma = false;
633 for (int i=0; i < regCount; ++i) {
634 if ( needComma )
635 strcat(str, ",");
636 else
637 needComma = true;
638 switch ( registers[i] ) {
639 case UNWIND_X86_REG_EBX:
640 strcat(str, "ebx");
641 break;
642 case UNWIND_X86_REG_ECX:
643 strcat(str, "ecx");
644 break;
645 case UNWIND_X86_REG_EDX:
646 strcat(str, "edx");
647 break;
648 case UNWIND_X86_REG_EDI:
649 strcat(str, "edi");
650 break;
651 case UNWIND_X86_REG_ESI:
652 strcat(str, "esi");
653 break;
654 case UNWIND_X86_REG_EBP:
655 strcat(str, "ebp");
656 break;
657 default:
658 strcat(str, "e??");
659 }
660 }
661 }
662 }
663 break;
664 case UNWIND_X86_MODE_DWARF:
665 sprintf(str, "dwarf offset 0x%08X, ", encoding & UNWIND_X86_DWARF_SECTION_OFFSET);
666 break;
667 default:
668 if ( encoding == 0 )
669 strcat(str, "no unwind information");
670 else
671 strcat(str, "tbd ");
672 }
673 if ( encoding & UNWIND_HAS_LSDA ) {
674 strcat(str, " LSDA");
675 }
676
677 }
678
679 #if SUPPORT_ARCH_arm64
680 template <>
681 void UnwindPrinter<arm64>::decode(uint32_t encoding, const uint8_t* funcStart, char* str)
682 {
683 uint32_t stackSize;
684 switch ( encoding & UNWIND_ARM64_MODE_MASK ) {
685 case UNWIND_ARM64_MODE_FRAMELESS:
686 stackSize = EXTRACT_BITS(encoding, UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK);
687 if ( stackSize == 0 )
688 strcpy(str, "no frame, no saved registers ");
689 else
690 sprintf(str, "stack size=%d: ", 16 * stackSize);
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 break;
711 case UNWIND_ARM64_MODE_DWARF:
712 sprintf(str, "dwarf offset 0x%08X, ", encoding & UNWIND_X86_64_DWARF_SECTION_OFFSET);
713 break;
714 case UNWIND_ARM64_MODE_FRAME:
715 strcpy(str, "std frame: ");
716 if ( encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR )
717 strcat(str, "x19/20 ");
718 if ( encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR )
719 strcat(str, "x21/22 ");
720 if ( encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR )
721 strcat(str, "x23/24 ");
722 if ( encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR )
723 strcat(str, "x25/26 ");
724 if ( encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR )
725 strcat(str, "x27/28 ");
726 if ( encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR )
727 strcat(str, "d8/9 ");
728 if ( encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR )
729 strcat(str, "d10/11 ");
730 if ( encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR )
731 strcat(str, "d12/13 ");
732 if ( encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR )
733 strcat(str, "d14/15 ");
734 break;
735 case UNWIND_ARM64_MODE_FRAME_OLD:
736 strcpy(str, "old frame: ");
737 if ( encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR_OLD )
738 strcat(str, "x21/22 ");
739 if ( encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR_OLD )
740 strcat(str, "x23/24 ");
741 if ( encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR_OLD )
742 strcat(str, "x25/26 ");
743 if ( encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR_OLD )
744 strcat(str, "x27/28 ");
745 if ( encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR_OLD )
746 strcat(str, "d8/9 ");
747 if ( encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR_OLD )
748 strcat(str, "d10/11 ");
749 if ( encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR_OLD )
750 strcat(str, "d12/13 ");
751 if ( encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR_OLD )
752 strcat(str, "d14/15 ");
753 break;
754 }
755 }
756 #endif
757
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
876 template <>
877 const char* UnwindPrinter<arm>::personalityName(const macho_relocation_info<arm::P>* reloc)
878 {
879 //assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section");
880 //assert((reloc->r_type() == GENERIC_RELOC_VANILLA) && "wrong reloc type on personality column in __compact_unwind section");
881 const macho_nlist<P>& sym = fSymbols[reloc->r_symbolnum()];
882 return &fStrings[sym.n_strx()];
883 }
884
885 template <typename A>
886 bool UnwindPrinter<A>::hasExernReloc(uint64_t sectionOffset, const char** personalityStr, pint_t* addr)
887 {
888 const macho_relocation_info<P>* relocs = (macho_relocation_info<P>*)((uint8_t*)fHeader + fUnwindSection->reloff());
889 const macho_relocation_info<P>* relocsEnd = &relocs[fUnwindSection->nreloc()];
890 for (const macho_relocation_info<P>* reloc = relocs; reloc < relocsEnd; ++reloc) {
891 if ( reloc->r_extern() && (reloc->r_address() == sectionOffset) ) {
892 *personalityStr = this->personalityName(reloc);
893 if ( addr != NULL )
894 *addr = fSymbols[reloc->r_symbolnum()].n_value();
895 return true;
896 }
897 }
898 return false;
899 }
900
901
902 template <typename A>
903 void UnwindPrinter<A>::printObjectUnwindSection(bool showFunctionNames)
904 {
905 printf("Arch: %s, Section: __LD,__compact_unwind (size=0x%08llX, => %lld entries)\n",
906 archName(), fUnwindSection->size(), fUnwindSection->size() / sizeof(macho_compact_unwind_entry<P>));
907
908 const macho_compact_unwind_entry<P>* const entriesStart = (macho_compact_unwind_entry<P>*)((uint8_t*)fHeader + fUnwindSection->offset());
909 const macho_compact_unwind_entry<P>* const entriesEnd = (macho_compact_unwind_entry<P>*)((uint8_t*)fHeader + fUnwindSection->offset() + fUnwindSection->size());
910 for (const macho_compact_unwind_entry<P>* entry=entriesStart; entry < entriesEnd; ++entry) {
911 uint64_t entryAddress = ((char*)entry - (char*)entriesStart) + fUnwindSection->addr();
912 printf("0x%08llX:\n", entryAddress);
913 const char* functionNameStr;
914 pint_t funcAddress;
915 uint32_t offsetInFunction;
916 if ( hasExernReloc(((char*)entry-(char*)entriesStart)+macho_compact_unwind_entry<P>::codeStartFieldOffset(), &functionNameStr, &funcAddress) ) {
917 offsetInFunction = entry->codeStart();
918 }
919 else {
920 functionNameStr = this->functionName(entry->codeStart(), &offsetInFunction);
921 funcAddress = entry->codeStart();
922 }
923 if ( offsetInFunction == 0 )
924 printf(" start: 0x%08llX %s\n", (uint64_t)funcAddress, functionNameStr);
925 else
926 printf(" start: 0x%08llX %s+0x%X\n", (uint64_t)funcAddress+offsetInFunction, functionNameStr, offsetInFunction);
927
928 printf(" end: 0x%08llX (len=0x%08X)\n", (uint64_t)(funcAddress+offsetInFunction+entry->codeLen()), entry->codeLen());
929
930 char encodingString[200];
931 this->decode(entry->compactUnwindInfo(), ((const uint8_t*)fHeader), encodingString);
932 printf(" unwind info: 0x%08X %s\n", entry->compactUnwindInfo(), encodingString);
933
934 const char* personalityNameStr;
935 if ( hasExernReloc(((char*)entry-(char*)entriesStart)+macho_compact_unwind_entry<P>::personalityFieldOffset(), &personalityNameStr) ) {
936 printf(" personality: %s\n", personalityNameStr);
937 }
938 else {
939 printf(" personality:\n");
940 }
941 if ( entry->lsda() == 0 ) {
942 printf(" lsda:\n");
943 }
944 else {
945 uint32_t lsdaOffset;
946 const char* lsdaName = this->functionName(entry->lsda(), &lsdaOffset);
947 if ( lsdaOffset == 0 )
948 printf(" lsda: 0x%08llX %s\n", (uint64_t)entry->lsda(), lsdaName);
949 else
950 printf(" lsda: 0x%08llX %s+0x%X\n", (uint64_t)entry->lsda(), lsdaName, lsdaOffset);
951 }
952 }
953 }
954
955
956
957 template <typename A>
958 void UnwindPrinter<A>::printUnwindSection(bool showFunctionNames)
959 {
960 const uint8_t* sectionContent = (uint8_t*)fHeader + fUnwindSection->offset();
961 macho_unwind_info_section_header<P>* sectionHeader = (macho_unwind_info_section_header<P>*)(sectionContent);
962
963 printf("Arch: %s, Section: __TEXT,__unwind_info (addr=0x%08llX, size=0x%08llX, fileOffset=0x%08X)\n",
964 archName(), fUnwindSection->addr(), fUnwindSection->size(), fUnwindSection->offset());
965 printf("\tversion=0x%08X\n", sectionHeader->version());
966 printf("\tcommonEncodingsArraySectionOffset=0x%08X\n", sectionHeader->commonEncodingsArraySectionOffset());
967 printf("\tcommonEncodingsArrayCount=0x%08X\n", sectionHeader->commonEncodingsArrayCount());
968 printf("\tpersonalityArraySectionOffset=0x%08X\n", sectionHeader->personalityArraySectionOffset());
969 printf("\tpersonalityArrayCount=0x%08X\n", sectionHeader->personalityArrayCount());
970 printf("\tindexSectionOffset=0x%08X\n", sectionHeader->indexSectionOffset());
971 printf("\tindexCount=0x%08X\n", sectionHeader->indexCount());
972 printf("\tcommon encodings: (count=%u)\n", sectionHeader->commonEncodingsArrayCount());
973 const uint32_t* commonEncodings = (uint32_t*)&sectionContent[sectionHeader->commonEncodingsArraySectionOffset()];
974 for (uint32_t i=0; i < sectionHeader->commonEncodingsArrayCount(); ++i) {
975 printf("\t\tencoding[%3u]=0x%08X\n", i, A::P::E::get32(commonEncodings[i]));
976 }
977 printf("\tpersonalities: (count=%u)\n", sectionHeader->personalityArrayCount());
978 const uint32_t* personalityArray = (uint32_t*)&sectionContent[sectionHeader->personalityArraySectionOffset()];
979 for (uint32_t i=0; i < sectionHeader->personalityArrayCount(); ++i) {
980 printf("\t\t[%2u]=0x%08X\n", i+1, A::P::E::get32(personalityArray[i]));
981 }
982 printf("\tfirst level index: (count=%u)\n", sectionHeader->indexCount());
983 macho_unwind_info_section_header_index_entry<P>* indexes = (macho_unwind_info_section_header_index_entry<P>*)&sectionContent[sectionHeader->indexSectionOffset()];
984 for (uint32_t i=0; i < sectionHeader->indexCount(); ++i) {
985 printf("\t\t[%2u] funcOffset=0x%08X, pageOffset=0x%08X, lsdaOffset=0x%08X\n",
986 i, indexes[i].functionOffset(), indexes[i].secondLevelPagesSectionOffset(), indexes[i].lsdaIndexArraySectionOffset());
987 }
988 uint32_t lsdaIndexArraySectionOffset = indexes[0].lsdaIndexArraySectionOffset();
989 uint32_t lsdaIndexArrayEndSectionOffset = indexes[sectionHeader->indexCount()-1].lsdaIndexArraySectionOffset();
990 uint32_t lsdaIndexArrayCount = (lsdaIndexArrayEndSectionOffset-lsdaIndexArraySectionOffset)/sizeof(macho_unwind_info_section_header_lsda_index_entry<P>);
991 printf("\tLSDA table: (section offset 0x%08X, count=%u)\n", lsdaIndexArraySectionOffset, lsdaIndexArrayCount);
992 macho_unwind_info_section_header_lsda_index_entry<P>* lindex = (macho_unwind_info_section_header_lsda_index_entry<P>*)&sectionContent[lsdaIndexArraySectionOffset];
993 for (uint32_t i=0; i < lsdaIndexArrayCount; ++i) {
994 const char* name = showFunctionNames ? functionName(lindex[i].functionOffset()+fMachHeaderAddress) : "";
995 printf("\t\t[%3u] funcOffset=0x%08X, lsdaOffset=0x%08X, %s\n", i, lindex[i].functionOffset(), lindex[i].lsdaOffset(), name);
996 if ( *(((uint8_t*)fHeader) + lindex[i].lsdaOffset()) != 0xFF )
997 fprintf(stderr, "BAD LSDA entry (does not start with 0xFF) for %s\n", functionName(lindex[i].functionOffset()+fMachHeaderAddress));
998 }
999 for (uint32_t i=0; i < sectionHeader->indexCount()-1; ++i) {
1000 printf("\tsecond level index[%u] sectionOffset=0x%08X, count=%u, fileOffset=0x%08X\n", i, indexes[i].secondLevelPagesSectionOffset(),
1001 sectionHeader->indexCount(), fUnwindSection->offset()+indexes[i].secondLevelPagesSectionOffset());
1002 macho_unwind_info_regular_second_level_page_header<P>* page = (macho_unwind_info_regular_second_level_page_header<P>*)&sectionContent[indexes[i].secondLevelPagesSectionOffset()];
1003 if ( page->kind() == UNWIND_SECOND_LEVEL_REGULAR ) {
1004 printf("\t\tkind=UNWIND_SECOND_LEVEL_REGULAR\n");
1005 printf("\t\tentryPageOffset=0x%08X\n", page->entryPageOffset());
1006 printf("\t\tentryCount=0x%08X\n", page->entryCount());
1007 const macho_unwind_info_regular_second_level_entry<P>* entry = (macho_unwind_info_regular_second_level_entry<P>*)((char*)page+page->entryPageOffset());
1008 for (uint32_t j=0; j < page->entryCount(); ++j) {
1009 uint32_t funcOffset = entry[j].functionOffset();
1010 if ( entry[j].encoding() & UNWIND_HAS_LSDA ) {
1011 // verify there is a corresponding entry in lsda table
1012 bool found = false;
1013 for (uint32_t k=0; k < lsdaIndexArrayCount; ++k) {
1014 if ( lindex[k].functionOffset() == funcOffset ) {
1015 found = true;
1016 break;
1017 }
1018 }
1019 if ( !found ) {
1020 fprintf(stderr, "MISSING LSDA entry for %s\n", functionName(funcOffset+fMachHeaderAddress));
1021 }
1022 }
1023 char encodingString[100];
1024 decode(entry[j].encoding(), ((const uint8_t*)fHeader)+funcOffset, encodingString);
1025 const char* name = showFunctionNames ? functionName(funcOffset+fMachHeaderAddress) : "";
1026 printf("\t\t\t[%3u] funcOffset=0x%08X, encoding=0x%08X (%-56s) %s\n",
1027 j, funcOffset, entry[j].encoding(), encodingString, name);
1028 }
1029 }
1030 else if ( page->kind() == UNWIND_SECOND_LEVEL_COMPRESSED ) {
1031 macho_unwind_info_compressed_second_level_page_header<P>* cp = (macho_unwind_info_compressed_second_level_page_header<P>*)page;
1032 printf("\t\tkind=UNWIND_SECOND_LEVEL_COMPRESSED\n");
1033 printf("\t\tentryPageOffset=0x%08X\n", cp->entryPageOffset());
1034 printf("\t\tentryCount=0x%08X\n", cp->entryCount());
1035 printf("\t\tencodingsPageOffset=0x%08X\n", cp->encodingsPageOffset());
1036 printf("\t\tencodingsCount=0x%08X\n", cp->encodingsCount());
1037 const uint32_t* entries = (uint32_t*)(((uint8_t*)page)+cp->entryPageOffset());
1038 const uint32_t* encodings = (uint32_t*)(((uint8_t*)page)+cp->encodingsPageOffset());
1039 const uint32_t baseFunctionOffset = indexes[i].functionOffset();
1040 for (uint32_t j=0; j < cp->entryCount(); ++j) {
1041 uint8_t encodingIndex = UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(entries[j]);
1042 uint32_t encoding;
1043 if ( encodingIndex < sectionHeader->commonEncodingsArrayCount() )
1044 encoding = A::P::E::get32(commonEncodings[encodingIndex]);
1045 else
1046 encoding = A::P::E::get32(encodings[encodingIndex-sectionHeader->commonEncodingsArrayCount()]);
1047 char encodingString[100];
1048 uint32_t funcOff = UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entries[j])+baseFunctionOffset;
1049 decode(encoding, ((const uint8_t*)fHeader)+funcOff, encodingString);
1050 const char* name = showFunctionNames ? functionName(funcOff+fMachHeaderAddress) : "";
1051 if ( encoding & UNWIND_HAS_LSDA ) {
1052 // verify there is a corresponding entry in lsda table
1053 bool found = false;
1054 for (uint32_t k=0; k < lsdaIndexArrayCount; ++k) {
1055 if ( lindex[k].functionOffset() == funcOff ) {
1056 found = true;
1057 break;
1058 }
1059 }
1060 if ( !found ) {
1061 fprintf(stderr, "MISSING LSDA entry for %s\n", name);
1062 }
1063 }
1064 printf("\t\t\t[%3u] funcOffset=0x%08X, encoding[%3u]=0x%08X (%-56s) %s\n",
1065 j, funcOff, encodingIndex, encoding, encodingString, name);
1066 }
1067 }
1068 else {
1069 fprintf(stderr, "\t\tbad page header\n");
1070 }
1071 }
1072
1073 }
1074
1075 static void dump(const char* path, const std::set<cpu_type_t>& onlyArchs, bool showFunctionNames)
1076 {
1077 struct stat stat_buf;
1078
1079 try {
1080 int fd = ::open(path, O_RDONLY, 0);
1081 if ( fd == -1 )
1082 throw "cannot open file";
1083 if ( ::fstat(fd, &stat_buf) != 0 )
1084 throwf("fstat(%s) failed, errno=%d\n", path, errno);
1085 uint32_t length = stat_buf.st_size;
1086 uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
1087 if ( p == ((uint8_t*)(-1)) )
1088 throw "cannot map file";
1089 ::close(fd);
1090 const mach_header* mh = (mach_header*)p;
1091 if ( mh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
1092 const struct fat_header* fh = (struct fat_header*)p;
1093 const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header));
1094 for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) {
1095 size_t offset = OSSwapBigToHostInt32(archs[i].offset);
1096 size_t size = OSSwapBigToHostInt32(archs[i].size);
1097 unsigned int cputype = OSSwapBigToHostInt32(archs[i].cputype);
1098 if ( onlyArchs.count(cputype) ) {
1099 switch(cputype) {
1100 case CPU_TYPE_I386:
1101 if ( UnwindPrinter<x86>::validFile(p + offset) )
1102 UnwindPrinter<x86>::make(p + offset, size, path, showFunctionNames);
1103 else
1104 throw "in universal file, i386 slice does not contain i386 mach-o";
1105 break;
1106 case CPU_TYPE_X86_64:
1107 if ( UnwindPrinter<x86_64>::validFile(p + offset) )
1108 UnwindPrinter<x86_64>::make(p + offset, size, path, showFunctionNames);
1109 else
1110 throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
1111 break;
1112 #if SUPPORT_ARCH_arm64
1113 case CPU_TYPE_ARM64:
1114 if ( UnwindPrinter<arm64>::validFile(p + offset) )
1115 UnwindPrinter<arm64>::make(p + offset, size, path, showFunctionNames);
1116 else
1117 throw "in universal file, arm64 slice does not contain arm64 mach-o";
1118 break;
1119 #endif
1120 case CPU_TYPE_ARM:
1121 if ( UnwindPrinter<arm>::validFile(p + offset) )
1122 UnwindPrinter<arm>::make(p + offset, size, path, showFunctionNames);
1123 else
1124 throw "in universal file, arm slice does not contain arm mach-o";
1125 break;
1126 default:
1127 throwf("in universal file, unknown architecture slice 0x%x\n", cputype);
1128 }
1129 }
1130 }
1131 }
1132 else if ( UnwindPrinter<x86>::validFile(p) && onlyArchs.count(CPU_TYPE_I386) ) {
1133 UnwindPrinter<x86>::make(p, length, path, showFunctionNames);
1134 }
1135 else if ( UnwindPrinter<x86_64>::validFile(p) && onlyArchs.count(CPU_TYPE_X86_64) ) {
1136 UnwindPrinter<x86_64>::make(p, length, path, showFunctionNames);
1137 }
1138 #if SUPPORT_ARCH_arm64
1139 else if ( UnwindPrinter<arm64>::validFile(p) && onlyArchs.count(CPU_TYPE_ARM64) ) {
1140 UnwindPrinter<arm64>::make(p, length, path, showFunctionNames);
1141 }
1142 #endif
1143 else if ( UnwindPrinter<arm>::validFile(p) && onlyArchs.count(CPU_TYPE_ARM) ) {
1144 UnwindPrinter<arm>::make(p, length, path, showFunctionNames);
1145 }
1146 else {
1147 throw "not a known file type";
1148 }
1149 }
1150 catch (const char* msg) {
1151 throwf("%s in %s", msg, path);
1152 }
1153 }
1154
1155
1156 int main(int argc, const char* argv[])
1157 {
1158 std::set<cpu_type_t> onlyArchs;
1159 std::vector<const char*> files;
1160 bool showFunctionNames = true;
1161
1162 try {
1163 for(int i=1; i < argc; ++i) {
1164 const char* arg = argv[i];
1165 if ( arg[0] == '-' ) {
1166 if ( strcmp(arg, "-arch") == 0 ) {
1167 const char* arch = argv[++i];
1168 if ( strcmp(arch, "i386") == 0 )
1169 onlyArchs.insert(CPU_TYPE_I386);
1170 else if ( strcmp(arch, "x86_64") == 0 )
1171 onlyArchs.insert(CPU_TYPE_X86_64);
1172 #if SUPPORT_ARCH_arm64
1173 else if ( strcmp(arch, "arm64") == 0 )
1174 onlyArchs.insert(CPU_TYPE_ARM64);
1175 #endif
1176 else if ( strcmp(arch, "armv7k") == 0 )
1177 onlyArchs.insert(CPU_TYPE_ARM);
1178 else
1179 throwf("unknown architecture %s", arch);
1180 }
1181 else if ( strcmp(arg, "-no_symbols") == 0 ) {
1182 showFunctionNames = false;
1183 }
1184 else {
1185 throwf("unknown option: %s\n", arg);
1186 }
1187 }
1188 else {
1189 files.push_back(arg);
1190 }
1191 }
1192
1193 // use all architectures if no restrictions specified
1194 if ( onlyArchs.size() == 0 ) {
1195 onlyArchs.insert(CPU_TYPE_I386);
1196 onlyArchs.insert(CPU_TYPE_X86_64);
1197 #if SUPPORT_ARCH_arm64
1198 onlyArchs.insert(CPU_TYPE_ARM64);
1199 #endif
1200 onlyArchs.insert(CPU_TYPE_ARM);
1201 }
1202
1203 // process each file
1204 for(std::vector<const char*>::iterator it=files.begin(); it != files.end(); ++it) {
1205 dump(*it, onlyArchs, showFunctionNames);
1206 }
1207
1208 }
1209 catch (const char* msg) {
1210 fprintf(stderr, "UnwindDump failed: %s\n", msg);
1211 return 1;
1212 }
1213
1214 return 0;
1215 }
1216
1217
1218