2 * Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
31 #include <mach-o/arch.h>
32 #include <mach-o/fat.h>
33 #include <mach-o/loader.h>
34 #include <mach-o/nlist.h>
35 #include <mach-o/swap.h>
37 #include <uuid/uuid.h>
39 #include <IOKit/IOTypes.h>
41 #pragma mark Typedefs, Enums, Constants
42 /*********************************************************************
43 * Typedefs, Enums, Constants
44 *********************************************************************/
53 #pragma mark Function Protos
54 /*********************************************************************
56 *********************************************************************/
57 __private_extern__ ToolError
58 readFile(const char *path
, vm_offset_t
* objAddr
, vm_size_t
* objSize
);
60 __private_extern__ ToolError
61 writeFile(int fd
, const void * data
, size_t length
);
63 extern char* __cxa_demangle (const char* mangled_name
,
68 #pragma mark Functions
69 /*********************************************************************
70 *********************************************************************/
71 __private_extern__ ToolError
72 writeFile(int fd
, const void * data
, size_t length
)
76 if (length
!= (size_t)write(fd
, data
, length
))
81 if (kErrorNone
!= err
)
82 perror("couldn't write output");
87 /*********************************************************************
88 *********************************************************************/
89 __private_extern__ ToolError
90 readFile(const char *path
, vm_offset_t
* objAddr
, vm_size_t
* objSize
)
92 ToolError err
= kErrorFileAccess
;
101 if((fd
= open(path
, O_RDONLY
)) == -1)
104 if(fstat(fd
, &stat_buf
) == -1)
107 if (0 == (stat_buf
.st_mode
& S_IFREG
))
110 /* Don't try to map an empty file, it fails now due to conformance
111 * stuff (PR 4611502).
113 if (0 == stat_buf
.st_size
) {
118 *objSize
= stat_buf
.st_size
;
120 *objAddr
= (vm_offset_t
)mmap(NULL
/* address */, *objSize
,
121 PROT_READ
|PROT_WRITE
, MAP_FILE
|MAP_PRIVATE
/* flags */,
124 if ((void *)*objAddr
== MAP_FAILED
) {
138 if (kErrorNone
!= err
)
140 fprintf(stderr
, "couldn't read %s: %s\n", path
, strerror(errno
));
147 enum { kExported
= 0x00000001, kObsolete
= 0x00000002 };
151 unsigned int name_len
;
153 unsigned int indirect_len
;
155 struct symbol
* list
;
156 unsigned int list_count
;
159 static bool issymchar( char c
)
161 return ((c
> ' ') && (c
<= '~') && (c
!= ':') && (c
!= '#'));
164 static bool iswhitespace( char c
)
166 return ((c
== ' ') || (c
== '\t'));
170 * Function for qsort for comparing symbol list names.
173 qsort_cmp(const void * _left
, const void * _right
)
175 struct symbol
* left
= (struct symbol
*) _left
;
176 struct symbol
* right
= (struct symbol
*) _right
;
178 return (strcmp(left
->name
, right
->name
));
182 * Function for bsearch for finding a symbol name.
186 bsearch_cmp( const void * _key
, const void * _cmp
)
188 char * key
= (char *)_key
;
189 struct symbol
* cmp
= (struct symbol
*) _cmp
;
191 return(strcmp(key
, cmp
->name
));
197 unsigned int name_len
;
201 bsearch_cmp_prefix( const void * _key
, const void * _cmp
)
203 struct bsearch_key
* key
= (struct bsearch_key
*)_key
;
204 struct symbol
* cmp
= (struct symbol
*) _cmp
;
206 return(strncmp(key
->name
, cmp
->name
, key
->name_len
));
210 count_symbols(char * file
, vm_size_t file_size
)
217 for (scan
= file
; true; scan
= next
) {
219 eol
= memchr(scan
, '\n', file_size
- (scan
- file
));
231 /* Skip comment lines.
233 if (scan
[0] == '#') {
237 /* Scan past any non-symbol characters at the beginning of the line. */
238 while ((scan
< eol
) && !issymchar(*scan
)) {
242 /* No symbol on line? Move along.
248 /* Skip symbols starting with '.'.
250 if (scan
[0] == '.') {
260 store_symbols(char * file
, vm_size_t file_size
, struct symbol
* symbols
, uint32_t idx
, uint32_t max_symbols
)
271 for (scan
= file
, line
= file
; true; scan
= next
, line
= next
) {
274 char * name_term
= NULL
;
275 unsigned int name_len
= 0;
276 char * indirect
= NULL
;
277 char * indirect_term
= NULL
;
278 unsigned int indirect_len
= 0;
279 char * option
= NULL
;
280 char * option_term
= NULL
;
281 unsigned int option_len
= 0;
283 boolean_t obsolete
= 0;
285 eol
= memchr(scan
, '\n', file_size
- (scan
- file
));
299 /* Skip comment lines.
301 if (scan
[0] == '#') {
305 /* Scan past any non-symbol characters at the beginning of the line. */
306 while ((scan
< eol
) && !issymchar(*scan
)) {
310 /* No symbol on line? Move along.
316 /* Skip symbols starting with '.'.
318 if (scan
[0] == '.') {
324 /* Find the end of the symbol.
326 while ((*scan
!= '\0') && issymchar(*scan
)) {
330 /* Note char past end of symbol.
334 /* Stored length must include the terminating nul char.
336 name_len
= name_term
- name
+ 1;
338 /* Now look for an indirect.
341 while ((*scan
!= '\0') && iswhitespace(*scan
)) {
346 while ((*scan
!= '\0') && iswhitespace(*scan
)) {
349 if (issymchar(*scan
)) {
352 /* Find the end of the symbol.
354 while ((*scan
!= '\0') && issymchar(*scan
)) {
358 /* Note char past end of symbol.
360 indirect_term
= scan
;
362 /* Stored length must include the terminating nul char.
364 indirect_len
= indirect_term
- indirect
+ 1;
366 } else if (*scan
== '\0') {
367 fprintf(stderr
, "bad format in symbol line: %s\n", line
);
370 } else if (*scan
!= '\0' && *scan
!= '-') {
371 fprintf(stderr
, "bad format in symbol line: %s\n", line
);
379 while ((*scan
!= '\0') && iswhitespace(*scan
)) {
386 if (isalpha(*scan
)) {
389 /* Find the end of the option.
391 while ((*scan
!= '\0') && isalpha(*scan
)) {
395 /* Note char past end of option.
398 option_len
= option_term
- option
;
400 if (option_len
>= sizeof(optionstr
)) {
401 fprintf(stderr
, "option too long in symbol line: %s\n", line
);
404 memcpy(optionstr
, option
, option_len
);
405 optionstr
[option_len
] = '\0';
409 if (!strncmp(optionstr
, "obsolete", option_len
)) {
413 } else if (*scan
== '\0') {
414 fprintf(stderr
, "bad format in symbol line: %s\n", line
);
422 if(idx
>= max_symbols
) {
423 fprintf(stderr
, "symbol[%d/%d] overflow: %s\n", idx
, max_symbols
, line
);
429 *indirect_term
= '\0';
432 symbols
[idx
].name
= name
;
433 symbols
[idx
].name_len
= name_len
;
434 symbols
[idx
].indirect
= indirect
;
435 symbols
[idx
].indirect_len
= indirect_len
;
436 symbols
[idx
].flags
= (obsolete
) ? kObsolete
: 0;
438 strtabsize
+= symbols
[idx
].name_len
+ symbols
[idx
].indirect_len
;
445 /*********************************************************************
446 *********************************************************************/
447 int main(int argc
, char * argv
[])
451 const char * output_name
= NULL
;
452 uint32_t zero
= 0, num_files
= 0;
454 uint32_t strx
, strtabsize
, strtabpad
;
455 struct symbol
* import_symbols
;
456 struct symbol
* export_symbols
;
457 uint32_t num_import_syms
, num_export_syms
;
458 uint32_t result_count
, num_removed_syms
;
459 uint32_t import_idx
, export_idx
;
460 const NXArchInfo
* host_arch
;
461 const NXArchInfo
* target_arch
;
462 boolean_t require_imports
= true;
463 boolean_t diff
= false;
468 vm_size_t mapped_size
;
473 struct file files
[64];
475 host_arch
= NXGetLocalArchInfo();
476 target_arch
= host_arch
;
478 for( i
= 1; i
< argc
; i
+= 2)
482 if (!strcmp("-sect", argv
[i
]))
484 require_imports
= false;
488 if (!strcmp("-diff", argv
[i
]))
490 require_imports
= false;
498 fprintf(stderr
, "bad arguments: %s\n", argv
[i
]);
502 if (!strcmp("-arch", argv
[i
]))
504 target_arch
= NXGetArchInfoFromName(argv
[i
+ 1]);
507 fprintf(stderr
, "unknown architecture name: %s\n", argv
[i
+1]);
512 if (!strcmp("-output", argv
[i
]))
514 output_name
= argv
[i
+1];
518 if (!strcmp("-import", argv
[i
]))
520 else if (!strcmp("-export", argv
[i
]))
524 fprintf(stderr
, "unknown option: %s\n", argv
[i
]);
528 err
= readFile(argv
[i
+1], &files
[num_files
].mapped
, &files
[num_files
].mapped_size
);
529 if (kErrorNone
!= err
)
532 if (files
[num_files
].mapped
&& files
[num_files
].mapped_size
)
534 files
[num_files
].import = import;
535 files
[num_files
].path
= argv
[i
+1];
542 fprintf(stderr
, "no output file\n");
548 for (filenum
= 0; filenum
< num_files
; filenum
++)
550 files
[filenum
].nsyms
= count_symbols((char *) files
[filenum
].mapped
, files
[filenum
].mapped_size
);
551 if (files
[filenum
].import)
552 num_import_syms
+= files
[filenum
].nsyms
;
554 num_export_syms
+= files
[filenum
].nsyms
;
556 if (!num_export_syms
)
558 fprintf(stderr
, "no export names\n");
562 import_symbols
= calloc(num_import_syms
, sizeof(struct symbol
));
563 export_symbols
= calloc(num_export_syms
, sizeof(struct symbol
));
568 for (filenum
= 0; filenum
< num_files
; filenum
++)
570 if (files
[filenum
].import)
572 store_symbols((char *) files
[filenum
].mapped
, files
[filenum
].mapped_size
,
573 import_symbols
, import_idx
, num_import_syms
);
574 import_idx
+= files
[filenum
].nsyms
;
578 store_symbols((char *) files
[filenum
].mapped
, files
[filenum
].mapped_size
,
579 export_symbols
, export_idx
, num_export_syms
);
580 export_idx
+= files
[filenum
].nsyms
;
582 if (false && !files
[filenum
].nsyms
)
584 fprintf(stderr
, "warning: file %s contains no names\n", files
[filenum
].path
);
589 qsort(import_symbols
, num_import_syms
, sizeof(struct symbol
), &qsort_cmp
);
590 qsort(export_symbols
, num_export_syms
, sizeof(struct symbol
), &qsort_cmp
);
593 num_removed_syms
= 0;
597 for (export_idx
= 0; export_idx
< num_export_syms
; export_idx
++)
599 struct symbol
* result
;
604 name
= export_symbols
[export_idx
].indirect
;
605 len
= export_symbols
[export_idx
].indirect_len
;
608 name
= export_symbols
[export_idx
].name
;
609 len
= export_symbols
[export_idx
].name_len
;
611 wild
= ((len
> 2) && ('*' == name
[len
-=2]));
614 struct bsearch_key key
;
617 result
= bsearch(&key
, import_symbols
,
618 num_import_syms
, sizeof(struct symbol
), &bsearch_cmp_prefix
);
622 struct symbol
* first
;
623 struct symbol
* last
;
625 strtabsize
+= (result
->name_len
+ result
->indirect_len
);
628 while (--first
>= &import_symbols
[0])
630 if (bsearch_cmp_prefix(&key
, first
))
632 strtabsize
+= (first
->name_len
+ first
->indirect_len
);
637 while (++last
< (&import_symbols
[0] + num_import_syms
))
639 if (bsearch_cmp_prefix(&key
, last
))
641 strtabsize
+= (last
->name_len
+ last
->indirect_len
);
643 result_count
+= last
- first
;
645 export_symbols
[export_idx
].list
= first
;
646 export_symbols
[export_idx
].list_count
= last
- first
;
647 export_symbols
[export_idx
].flags
|= kExported
;
651 result
= bsearch(name
, import_symbols
,
652 num_import_syms
, sizeof(struct symbol
), &bsearch_cmp
);
654 if (!result
&& require_imports
)
657 char * demangled_result
=
658 __cxa_demangle(export_symbols
[export_idx
].name
+ 1, NULL
, NULL
, &status
);
659 fprintf(stderr
, "exported name not in import list: %s\n",
660 demangled_result
? demangled_result
: export_symbols
[export_idx
].name
);
661 // fprintf(stderr, " : %s\n", export_symbols[export_idx].name);
662 if (demangled_result
) {
663 free(demangled_result
);
670 result
= &export_symbols
[export_idx
];
676 export_symbols
[export_idx
].flags
|= kExported
;
677 strtabsize
+= (export_symbols
[export_idx
].name_len
+ export_symbols
[export_idx
].indirect_len
);
679 export_symbols
[export_idx
].list
= &export_symbols
[export_idx
];
680 export_symbols
[export_idx
].list_count
= 1;
684 strtabpad
= (strtabsize
+ 3) & ~3;
686 if (require_imports
&& num_removed_syms
)
692 fd
= open(output_name
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0755);
695 perror("couldn't write output");
696 err
= kErrorFileAccess
;
700 struct symtab_command symcmd
;
701 struct uuid_command uuidcmd
;
703 symcmd
.cmd
= LC_SYMTAB
;
704 symcmd
.cmdsize
= sizeof(symcmd
);
705 symcmd
.symoff
= sizeof(symcmd
) + sizeof(uuidcmd
);
706 symcmd
.nsyms
= result_count
;
707 symcmd
.strsize
= strtabpad
;
709 uuidcmd
.cmd
= LC_UUID
;
710 uuidcmd
.cmdsize
= sizeof(uuidcmd
);
711 uuid_generate(uuidcmd
.uuid
);
713 if (CPU_ARCH_ABI64
& target_arch
->cputype
)
715 struct mach_header_64 hdr
;
716 hdr
.magic
= MH_MAGIC_64
;
717 hdr
.cputype
= target_arch
->cputype
;
718 hdr
.cpusubtype
= target_arch
->cpusubtype
;
719 hdr
.filetype
= MH_KEXT_BUNDLE
;
721 hdr
.sizeofcmds
= sizeof(symcmd
) + sizeof(uuidcmd
);
722 hdr
.flags
= MH_INCRLINK
;
724 symcmd
.symoff
+= sizeof(hdr
);
725 symcmd
.stroff
= result_count
* sizeof(struct nlist_64
)
728 if (target_arch
->byteorder
!= host_arch
->byteorder
)
729 swap_mach_header_64(&hdr
, target_arch
->byteorder
);
730 err
= writeFile(fd
, &hdr
, sizeof(hdr
));
734 struct mach_header hdr
;
735 hdr
.magic
= MH_MAGIC
;
736 hdr
.cputype
= target_arch
->cputype
;
737 hdr
.cpusubtype
= target_arch
->cpusubtype
;
738 hdr
.filetype
= (target_arch
->cputype
== CPU_TYPE_I386
) ? MH_OBJECT
: MH_KEXT_BUNDLE
;
740 hdr
.sizeofcmds
= sizeof(symcmd
) + sizeof(uuidcmd
);
741 hdr
.flags
= MH_INCRLINK
;
743 symcmd
.symoff
+= sizeof(hdr
);
744 symcmd
.stroff
= result_count
* sizeof(struct nlist
)
747 if (target_arch
->byteorder
!= host_arch
->byteorder
)
748 swap_mach_header(&hdr
, target_arch
->byteorder
);
749 err
= writeFile(fd
, &hdr
, sizeof(hdr
));
752 if (kErrorNone
!= err
)
755 if (target_arch
->byteorder
!= host_arch
->byteorder
) {
756 swap_symtab_command(&symcmd
, target_arch
->byteorder
);
757 swap_uuid_command(&uuidcmd
, target_arch
->byteorder
);
759 err
= writeFile(fd
, &symcmd
, sizeof(symcmd
));
760 if (kErrorNone
!= err
)
762 err
= writeFile(fd
, &uuidcmd
, sizeof(uuidcmd
));
763 if (kErrorNone
!= err
)
767 for (export_idx
= 0; export_idx
< num_export_syms
; export_idx
++)
769 if (!export_symbols
[export_idx
].name
)
771 if (!(kExported
& export_symbols
[export_idx
].flags
))
775 && export_symbols
[export_idx
- 1].name
776 && !strcmp(export_symbols
[export_idx
- 1].name
, export_symbols
[export_idx
].name
))
778 fprintf(stderr
, "duplicate export: %s\n", export_symbols
[export_idx
- 1].name
);
779 err
= kErrorDuplicate
;
783 for (import_idx
= 0; import_idx
< export_symbols
[export_idx
].list_count
; import_idx
++)
786 if (export_symbols
[export_idx
].list
!= &export_symbols
[export_idx
])
788 printf("wild: %s, %s\n", export_symbols
[export_idx
].name
,
789 export_symbols
[export_idx
].list
[import_idx
].name
);
791 if (CPU_ARCH_ABI64
& target_arch
->cputype
)
797 nl
.n_un
.n_strx
= strx
;
798 strx
+= export_symbols
[export_idx
].list
[import_idx
].name_len
;
800 if (export_symbols
[export_idx
].flags
& kObsolete
) {
801 nl
.n_desc
|= N_DESC_DISCARDED
;
804 if (export_symbols
[export_idx
].list
[import_idx
].indirect
)
806 nl
.n_type
= N_INDR
| N_EXT
;
808 strx
+= export_symbols
[export_idx
].list
[import_idx
].indirect_len
;
812 nl
.n_type
= N_UNDF
| N_EXT
;
816 if (target_arch
->byteorder
!= host_arch
->byteorder
)
817 swap_nlist_64(&nl
, 1, target_arch
->byteorder
);
819 err
= writeFile(fd
, &nl
, sizeof(nl
));
827 nl
.n_un
.n_strx
= strx
;
828 strx
+= export_symbols
[export_idx
].list
[import_idx
].name_len
;
830 if (export_symbols
[export_idx
].flags
& kObsolete
) {
831 nl
.n_desc
|= N_DESC_DISCARDED
;
834 if (export_symbols
[export_idx
].list
[import_idx
].indirect
)
836 nl
.n_type
= N_INDR
| N_EXT
;
838 strx
+= export_symbols
[export_idx
].list
[import_idx
].indirect_len
;
842 nl
.n_type
= N_UNDF
| N_EXT
;
846 if (target_arch
->byteorder
!= host_arch
->byteorder
)
847 swap_nlist(&nl
, 1, target_arch
->byteorder
);
849 err
= writeFile(fd
, &nl
, sizeof(nl
));
853 if (kErrorNone
!= err
)
857 strx
= sizeof(uint32_t);
858 err
= writeFile(fd
, &zero
, strx
);
859 if (kErrorNone
!= err
)
862 for (export_idx
= 0; export_idx
< num_export_syms
; export_idx
++)
864 if (!export_symbols
[export_idx
].name
)
867 for (import_idx
= 0; import_idx
< export_symbols
[export_idx
].list_count
; import_idx
++)
869 err
= writeFile(fd
, export_symbols
[export_idx
].list
[import_idx
].name
,
870 export_symbols
[export_idx
].list
[import_idx
].name_len
);
871 if (kErrorNone
!= err
)
873 if (export_symbols
[export_idx
].list
[import_idx
].indirect
)
875 err
= writeFile(fd
, export_symbols
[export_idx
].list
[import_idx
].indirect
,
876 export_symbols
[export_idx
].list
[import_idx
].indirect_len
);
877 if (kErrorNone
!= err
)
883 err
= writeFile(fd
, &zero
, strtabpad
- strtabsize
);
884 if (kErrorNone
!= err
)
891 for (filenum
= 0; filenum
< num_files
; filenum
++) {
893 if (files
[filenum
].mapped_size
)
895 munmap((caddr_t
)files
[filenum
].mapped
, files
[filenum
].mapped_size
);
896 files
[filenum
].mapped
= 0;
897 files
[filenum
].mapped_size
= 0;
902 if (kErrorNone
!= err
)