1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
3 * Copyright (c) 2008 Apple Inc. All rights reserved.
5 * @APPLE_LICENSE_HEADER_START@
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
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>
36 #include <ext/hash_set>
39 #include "MachOFileAbstraction.hpp"
40 #include "Architectures.hpp"
43 __attribute__((noreturn
))
44 void throwf(const char* format
, ...)
48 va_start(list
, format
);
49 vasprintf(&p
, format
, list
);
61 static bool validFile(const uint8_t* fileContent
);
62 static UnwindPrinter
<A
>* make(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
)
63 { return new UnwindPrinter
<A
>(fileContent
, fileLength
, path
); }
64 virtual ~UnwindPrinter() {}
68 typedef typename
A::P P
;
69 typedef typename
A::P::E E
;
70 typedef typename
A::P::uint_t pint_t
;
75 bool operator()(const char* left
, const char* right
) const { return (strcmp(left
, right
) == 0); }
78 typedef __gnu_cxx::hash_set
<const char*, __gnu_cxx::hash
<const char*>, CStringEquals
> StringSet
;
80 UnwindPrinter(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
);
81 bool findUnwindSection();
82 void printUnwindSection();
83 void getSymbolTableInfo();
84 const char* functionName(pint_t addr
);
85 static const char* archName();
88 const macho_header
<P
>* fHeader
;
90 const macho_section
<P
>* fUnwindSection
;
92 const char* fStringsEnd
;
93 const macho_nlist
<P
>* fSymbols
;
94 uint32_t fSymbolCount
;
95 pint_t fMachHeaderAddress
;
99 template <> const char* UnwindPrinter
<ppc
>::archName() { return "ppc"; }
100 template <> const char* UnwindPrinter
<ppc64
>::archName() { return "ppc64"; }
101 template <> const char* UnwindPrinter
<x86
>::archName() { return "i386"; }
102 template <> const char* UnwindPrinter
<x86_64
>::archName() { return "x86_64"; }
103 template <> const char* UnwindPrinter
<arm
>::archName() { return "arm"; }
106 bool UnwindPrinter
<ppc
>::validFile(const uint8_t* fileContent
)
108 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
109 if ( header
->magic() != MH_MAGIC
)
111 if ( header
->cputype() != CPU_TYPE_POWERPC
)
113 switch (header
->filetype()) {
124 bool UnwindPrinter
<ppc64
>::validFile(const uint8_t* fileContent
)
126 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
127 if ( header
->magic() != MH_MAGIC_64
)
129 if ( header
->cputype() != CPU_TYPE_POWERPC64
)
131 switch (header
->filetype()) {
142 bool UnwindPrinter
<x86
>::validFile(const uint8_t* fileContent
)
144 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
145 if ( header
->magic() != MH_MAGIC
)
147 if ( header
->cputype() != CPU_TYPE_I386
)
149 switch (header
->filetype()) {
160 bool UnwindPrinter
<x86_64
>::validFile(const uint8_t* fileContent
)
162 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
163 if ( header
->magic() != MH_MAGIC_64
)
165 if ( header
->cputype() != CPU_TYPE_X86_64
)
167 switch (header
->filetype()) {
178 bool UnwindPrinter
<arm
>::validFile(const uint8_t* fileContent
)
180 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
181 if ( header
->magic() != MH_MAGIC
)
183 if ( header
->cputype() != CPU_TYPE_ARM
)
185 switch (header
->filetype()) {
196 template <typename A
>
197 UnwindPrinter
<A
>::UnwindPrinter(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
)
198 : fHeader(NULL
), fLength(fileLength
), fUnwindSection(NULL
),
199 fStrings(NULL
), fStringsEnd(NULL
), fSymbols(NULL
), fSymbolCount(0), fMachHeaderAddress(0)
202 if ( ! validFile(fileContent
) )
203 throw "not a mach-o file that can be checked";
205 fPath
= strdup(path
);
206 fHeader
= (const macho_header
<P
>*)fileContent
;
208 getSymbolTableInfo();
210 if ( findUnwindSection() )
211 printUnwindSection();
215 template <typename A
>
216 void UnwindPrinter
<A
>::getSymbolTableInfo()
218 const uint8_t* const endOfFile
= (uint8_t*)fHeader
+ fLength
;
219 const uint8_t* const endOfLoadCommands
= (uint8_t*)fHeader
+ sizeof(macho_header
<P
>) + fHeader
->sizeofcmds();
220 const uint32_t cmd_count
= fHeader
->ncmds();
221 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
222 const macho_load_command
<P
>* cmd
= cmds
;
223 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
224 uint32_t size
= cmd
->cmdsize();
225 const uint8_t* endOfCmd
= ((uint8_t*)cmd
)+cmd
->cmdsize();
226 if ( endOfCmd
> endOfLoadCommands
)
227 throwf("load command #%d extends beyond the end of the load commands", i
);
228 if ( endOfCmd
> endOfFile
)
229 throwf("load command #%d extends beyond the end of the file", i
);
230 if ( cmd
->cmd() == LC_SYMTAB
) {
231 const macho_symtab_command
<P
>* symtab
= (macho_symtab_command
<P
>*)cmd
;
232 fSymbolCount
= symtab
->nsyms();
233 fSymbols
= (const macho_nlist
<P
>*)((char*)fHeader
+ symtab
->symoff());
234 fStrings
= (char*)fHeader
+ symtab
->stroff();
235 fStringsEnd
= fStrings
+ symtab
->strsize();
237 cmd
= (const macho_load_command
<P
>*)endOfCmd
;
241 template <typename A
>
242 const char* UnwindPrinter
<A
>::functionName(pint_t addr
)
244 for (uint32_t i
=0; i
< fSymbolCount
; ++i
) {
245 uint8_t type
= fSymbols
[i
].n_type();
246 if ( ((type
& N_STAB
) == 0) && ((type
& N_TYPE
) == N_SECT
) ) {
247 if ( fSymbols
[i
].n_value() == addr
) {
248 const char* r
= &fStrings
[fSymbols
[i
].n_strx()];
249 //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);
259 template <typename A
>
260 bool UnwindPrinter
<A
>::findUnwindSection()
262 const uint8_t* const endOfFile
= (uint8_t*)fHeader
+ fLength
;
263 const uint8_t* const endOfLoadCommands
= (uint8_t*)fHeader
+ sizeof(macho_header
<P
>) + fHeader
->sizeofcmds();
264 const uint32_t cmd_count
= fHeader
->ncmds();
265 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
266 const macho_load_command
<P
>* cmd
= cmds
;
267 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
268 uint32_t size
= cmd
->cmdsize();
269 const uint8_t* endOfCmd
= ((uint8_t*)cmd
)+cmd
->cmdsize();
270 if ( endOfCmd
> endOfLoadCommands
)
271 throwf("load command #%d extends beyond the end of the load commands", i
);
272 if ( endOfCmd
> endOfFile
)
273 throwf("load command #%d extends beyond the end of the file", i
);
274 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
275 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
276 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
277 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
278 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
279 if ( (strcmp(sect
->sectname(), "__unwind_info") == 0) && (strcmp(sect
->segname(), "__TEXT") == 0) ) {
280 fUnwindSection
= sect
;
281 fMachHeaderAddress
= segCmd
->vmaddr();
282 return fUnwindSection
;
286 cmd
= (const macho_load_command
<P
>*)endOfCmd
;
294 template <typename A
>
295 void UnwindPrinter
<A
>::printUnwindSection()
297 const uint8_t* sectionContent
= (uint8_t*)fHeader
+ fUnwindSection
->offset();
298 macho_unwind_info_section_header
<P
>* sectionHeader
= (macho_unwind_info_section_header
<P
>*)(sectionContent
);
300 printf("Arch: %s, Section: __TEXT,__unwind_info (addr=0x%08llX, size=0x%08llX, fileOffset=0x%08X)\n",
301 archName(), fUnwindSection
->addr(), fUnwindSection
->size(), fUnwindSection
->offset());
302 printf("\tversion=0x%08X\n", sectionHeader
->version());
303 printf("\tcommonEncodingsArraySectionOffset=0x%08X\n", sectionHeader
->commonEncodingsArraySectionOffset());
304 printf("\tcommonEncodingsArrayCount=0x%08X\n", sectionHeader
->commonEncodingsArrayCount());
305 printf("\tpersonalityArraySectionOffset=0x%08X\n", sectionHeader
->personalityArraySectionOffset());
306 printf("\tpersonalityArrayCount=0x%08X\n", sectionHeader
->personalityArrayCount());
307 printf("\tindexSectionOffset=0x%08X\n", sectionHeader
->indexSectionOffset());
308 printf("\tindexCount=0x%08X\n", sectionHeader
->indexCount());
309 printf("\tcommon encodings: (count=%u)\n", sectionHeader
->commonEncodingsArrayCount());
310 const uint32_t* commonEncodings
= (uint32_t*)§ionContent
[sectionHeader
->commonEncodingsArraySectionOffset()];
311 for (uint32_t i
=0; i
< sectionHeader
->commonEncodingsArrayCount(); ++i
) {
312 printf("\t\tencoding[%2u]=0x%08X\n", i
, A::P::E::get32(commonEncodings
[i
]));
314 printf("\tpersonalities: (count=%u)\n", sectionHeader
->personalityArrayCount());
315 const uint32_t* personalityArray
= (uint32_t*)§ionContent
[sectionHeader
->personalityArraySectionOffset()];
316 for (uint32_t i
=0; i
< sectionHeader
->personalityArrayCount(); ++i
) {
317 printf("\t\t[%2u]=0x%08X\n", i
+1, A::P::E::get32(personalityArray
[i
]));
319 printf("\tfirst level index: (count=%u)\n", sectionHeader
->indexCount());
320 macho_unwind_info_section_header_index_entry
<P
>* indexes
= (macho_unwind_info_section_header_index_entry
<P
>*)§ionContent
[sectionHeader
->indexSectionOffset()];
321 for (uint32_t i
=0; i
< sectionHeader
->indexCount(); ++i
) {
322 printf("\t\t[%2u] funcOffset=0x%08X, pageOffset=0x%08X, lsdaOffset=0x%08X\n",
323 i
, indexes
[i
].functionOffset(), indexes
[i
].secondLevelPagesSectionOffset(), indexes
[i
].lsdaIndexArraySectionOffset());
325 uint32_t lsdaIndexArraySectionOffset
= indexes
[0].lsdaIndexArraySectionOffset();
326 uint32_t lsdaIndexArrayEndSectionOffset
= indexes
[sectionHeader
->indexCount()-1].lsdaIndexArraySectionOffset();
327 uint32_t lsdaIndexArrayCount
= (lsdaIndexArrayEndSectionOffset
-lsdaIndexArraySectionOffset
)/sizeof(macho_unwind_info_section_header_lsda_index_entry
<P
>);
328 printf("\tLSDA table: (section offset 0x%08X, count=%u)\n", lsdaIndexArraySectionOffset
, lsdaIndexArrayCount
);
329 macho_unwind_info_section_header_lsda_index_entry
<P
>* lindex
= (macho_unwind_info_section_header_lsda_index_entry
<P
>*)§ionContent
[lsdaIndexArraySectionOffset
];
330 for (uint32_t i
=0; i
< lsdaIndexArrayCount
; ++i
) {
331 printf("\t\t[%3u] funcOffset=0x%08X, lsdaOffset=0x%08X, %s\n",
332 i
, lindex
[i
].functionOffset(), lindex
[i
].lsdaOffset(), functionName(lindex
[i
].functionOffset()+fMachHeaderAddress
));
333 if ( *(((uint8_t*)fHeader
) + lindex
[i
].lsdaOffset()) != 0xFF )
334 fprintf(stderr
, "BAD LSDA entry (does not start with 0xFF) for %s\n", functionName(lindex
[i
].functionOffset()+fMachHeaderAddress
));
336 for (uint32_t i
=0; i
< sectionHeader
->indexCount()-1; ++i
) {
337 printf("\tsecond level index[%u] sectionOffset=0x%08X, count=%u, fileOffset=0x%08X\n", i
, indexes
[i
].secondLevelPagesSectionOffset(),
338 sectionHeader
->indexCount(), fUnwindSection
->offset()+indexes
[i
].secondLevelPagesSectionOffset());
339 macho_unwind_info_regular_second_level_page_header
<P
>* page
= (macho_unwind_info_regular_second_level_page_header
<P
>*)§ionContent
[indexes
[i
].secondLevelPagesSectionOffset()];
340 if ( page
->kind() == UNWIND_SECOND_LEVEL_REGULAR
) {
341 printf("\t\tkind=UNWIND_SECOND_LEVEL_REGULAR\n");
342 printf("\t\tentryPageOffset=0x%08X\n", page
->entryPageOffset());
343 printf("\t\tentryCount=0x%08X\n", page
->entryCount());
344 const macho_unwind_info_regular_second_level_entry
<P
>* entry
= (macho_unwind_info_regular_second_level_entry
<P
>*)((char*)page
+page
->entryPageOffset());
345 for (uint32_t j
=0; j
< page
->entryCount(); ++j
) {
346 if ( entry
[j
].encoding() & UNWIND_HAS_LSDA
) {
347 // verify there is a corresponding entry in lsda table
349 uint32_t funcOffset
= entry
[j
].functionOffset();
350 for (uint32_t k
=0; k
< lsdaIndexArrayCount
; ++k
) {
351 if ( lindex
[k
].functionOffset() == funcOffset
) {
357 fprintf(stderr
, "MISSING LSDA entry for %s\n", functionName(funcOffset
+fMachHeaderAddress
));
360 printf("\t\t\t[%3u] funcOffset=0x%08X, encoding=0x%08X %s\n",
361 j
, entry
[j
].functionOffset(), entry
[j
].encoding(), functionName(entry
[j
].functionOffset()+fMachHeaderAddress
));
364 else if ( page
->kind() == UNWIND_SECOND_LEVEL_COMPRESSED
) {
365 macho_unwind_info_compressed_second_level_page_header
<P
>* cp
= (macho_unwind_info_compressed_second_level_page_header
<P
>*)page
;
366 printf("\t\tkind=UNWIND_SECOND_LEVEL_COMPRESSED\n");
367 printf("\t\tentryPageOffset=0x%08X\n", cp
->entryPageOffset());
368 printf("\t\tentryCount=0x%08X\n", cp
->entryCount());
369 printf("\t\tencodingsPageOffset=0x%08X\n", cp
->encodingsPageOffset());
370 printf("\t\tencodingsCount=0x%08X\n", cp
->encodingsCount());
371 const uint32_t* entries
= (uint32_t*)(((uint8_t*)page
)+cp
->entryPageOffset());
372 const uint32_t* encodings
= (uint32_t*)(((uint8_t*)page
)+cp
->encodingsPageOffset());
373 const uint32_t baseFunctionOffset
= indexes
[i
].functionOffset();
374 for (uint32_t j
=0; j
< cp
->entryCount(); ++j
) {
375 uint8_t encodingIndex
= UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(entries
[j
]);
377 if ( encodingIndex
< sectionHeader
->commonEncodingsArrayCount() )
378 encoding
= A::P::E::get32(commonEncodings
[encodingIndex
]);
380 encoding
= A::P::E::get32(encodings
[encodingIndex
-sectionHeader
->commonEncodingsArrayCount()]);
381 uint32_t funcOff
= UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entries
[j
])+baseFunctionOffset
;
382 const char* name
= functionName(funcOff
+fMachHeaderAddress
);
383 if ( encoding
& UNWIND_HAS_LSDA
) {
384 // verify there is a corresponding entry in lsda table
386 for (uint32_t k
=0; k
< lsdaIndexArrayCount
; ++k
) {
387 if ( lindex
[k
].functionOffset() == funcOff
) {
393 fprintf(stderr
, "MISSING LSDA entry for %s\n", name
);
396 printf("\t\t\t[%3u] funcOffset=0x%08X, encoding[%2u]=0x%08X %s\n",
397 j
, funcOff
, encodingIndex
, encoding
, name
);
401 fprintf(stderr
, "\t\tbad page header\n");
407 static void dump(const char* path
, const std::set
<cpu_type_t
>& onlyArchs
)
409 struct stat stat_buf
;
412 int fd
= ::open(path
, O_RDONLY
, 0);
414 throw "cannot open file";
415 if ( ::fstat(fd
, &stat_buf
) != 0 )
416 throwf("fstat(%s) failed, errno=%d\n", path
, errno
);
417 uint32_t length
= stat_buf
.st_size
;
418 uint8_t* p
= (uint8_t*)::mmap(NULL
, stat_buf
.st_size
, PROT_READ
, MAP_FILE
| MAP_PRIVATE
, fd
, 0);
419 if ( p
== ((uint8_t*)(-1)) )
420 throw "cannot map file";
422 const mach_header
* mh
= (mach_header
*)p
;
423 if ( mh
->magic
== OSSwapBigToHostInt32(FAT_MAGIC
) ) {
424 const struct fat_header
* fh
= (struct fat_header
*)p
;
425 const struct fat_arch
* archs
= (struct fat_arch
*)(p
+ sizeof(struct fat_header
));
426 for (unsigned long i
=0; i
< OSSwapBigToHostInt32(fh
->nfat_arch
); ++i
) {
427 size_t offset
= OSSwapBigToHostInt32(archs
[i
].offset
);
428 size_t size
= OSSwapBigToHostInt32(archs
[i
].size
);
429 unsigned int cputype
= OSSwapBigToHostInt32(archs
[i
].cputype
);
430 if ( onlyArchs
.count(cputype
) ) {
432 case CPU_TYPE_POWERPC
:
433 if ( UnwindPrinter
<ppc
>::validFile(p
+ offset
) )
434 UnwindPrinter
<ppc
>::make(p
+ offset
, size
, path
);
436 throw "in universal file, ppc slice does not contain ppc mach-o";
439 if ( UnwindPrinter
<x86
>::validFile(p
+ offset
) )
440 UnwindPrinter
<x86
>::make(p
+ offset
, size
, path
);
442 throw "in universal file, i386 slice does not contain i386 mach-o";
444 case CPU_TYPE_POWERPC64
:
445 if ( UnwindPrinter
<ppc64
>::validFile(p
+ offset
) )
446 UnwindPrinter
<ppc64
>::make(p
+ offset
, size
, path
);
448 throw "in universal file, ppc64 slice does not contain ppc64 mach-o";
450 case CPU_TYPE_X86_64
:
451 if ( UnwindPrinter
<x86_64
>::validFile(p
+ offset
) )
452 UnwindPrinter
<x86_64
>::make(p
+ offset
, size
, path
);
454 throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
457 if ( UnwindPrinter
<arm
>::validFile(p
+ offset
) )
458 UnwindPrinter
<arm
>::make(p
+ offset
, size
, path
);
460 throw "in universal file, arm slice does not contain arm mach-o";
463 throwf("in universal file, unknown architecture slice 0x%x\n", cputype
);
468 else if ( UnwindPrinter
<x86
>::validFile(p
) && onlyArchs
.count(CPU_TYPE_I386
) ) {
469 UnwindPrinter
<x86
>::make(p
, length
, path
);
471 else if ( UnwindPrinter
<ppc
>::validFile(p
) && onlyArchs
.count(CPU_TYPE_POWERPC
) ) {
472 UnwindPrinter
<ppc
>::make(p
, length
, path
);
474 else if ( UnwindPrinter
<ppc64
>::validFile(p
) && onlyArchs
.count(CPU_TYPE_POWERPC64
) ) {
475 UnwindPrinter
<ppc64
>::make(p
, length
, path
);
477 else if ( UnwindPrinter
<x86_64
>::validFile(p
) && onlyArchs
.count(CPU_TYPE_X86_64
) ) {
478 UnwindPrinter
<x86_64
>::make(p
, length
, path
);
480 else if ( UnwindPrinter
<arm
>::validFile(p
) && onlyArchs
.count(CPU_TYPE_ARM
) ) {
481 UnwindPrinter
<arm
>::make(p
, length
, path
);
484 throw "not a known file type";
487 catch (const char* msg
) {
488 throwf("%s in %s", msg
, path
);
493 int main(int argc
, const char* argv
[])
495 std::set
<cpu_type_t
> onlyArchs
;
496 std::vector
<const char*> files
;
499 for(int i
=1; i
< argc
; ++i
) {
500 const char* arg
= argv
[i
];
501 if ( arg
[0] == '-' ) {
502 if ( strcmp(arg
, "-arch") == 0 ) {
503 const char* arch
= argv
[++i
];
504 if ( strcmp(arch
, "ppc") == 0 )
505 onlyArchs
.insert(CPU_TYPE_POWERPC
);
506 else if ( strcmp(arch
, "ppc64") == 0 )
507 onlyArchs
.insert(CPU_TYPE_POWERPC64
);
508 else if ( strcmp(arch
, "i386") == 0 )
509 onlyArchs
.insert(CPU_TYPE_I386
);
510 else if ( strcmp(arch
, "x86_64") == 0 )
511 onlyArchs
.insert(CPU_TYPE_X86_64
);
512 else if ( strcmp(arch
, "arm") == 0 )
513 onlyArchs
.insert(CPU_TYPE_ARM
);
515 throwf("unknown architecture %s", arch
);
518 throwf("unknown option: %s\n", arg
);
522 files
.push_back(arg
);
526 // use all architectures if no restrictions specified
527 if ( onlyArchs
.size() == 0 ) {
528 onlyArchs
.insert(CPU_TYPE_POWERPC
);
529 onlyArchs
.insert(CPU_TYPE_POWERPC64
);
530 onlyArchs
.insert(CPU_TYPE_I386
);
531 onlyArchs
.insert(CPU_TYPE_X86_64
);
532 onlyArchs
.insert(CPU_TYPE_ARM
);
536 for(std::vector
<const char*>::iterator it
=files
.begin(); it
!= files
.end(); ++it
) {
537 dump(*it
, onlyArchs
);
541 catch (const char* msg
) {
542 fprintf(stderr
, "UnwindDump failed: %s\n", msg
);