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@
27 #include <mach/mach_init.h>
33 #include <mach-o/arch.h>
34 #include <mach-o/fat.h>
35 #include <mach-o/loader.h>
36 #include <mach-o/nlist.h>
37 #include <mach-o/swap.h>
39 #include <uuid/uuid.h>
42 #pragma mark Typedefs, Enums, Constants
43 /*********************************************************************
44 * Typedefs, Enums, Constants
45 *********************************************************************/
54 #pragma mark Function Protos
55 /*********************************************************************
57 *********************************************************************/
58 __private_extern__ ToolError
59 readFile(const char *path
, vm_offset_t
* objAddr
, vm_size_t
* objSize
);
61 __private_extern__ ToolError
62 writeFile(int fd
, const void * data
, size_t length
);
64 __private_extern__ ToolError
65 seekFile(int fd
, off_t offset
);
67 extern char* __cxa_demangle (const char* mangled_name
,
72 #pragma mark Functions
73 /*********************************************************************
74 *********************************************************************/
75 __private_extern__ ToolError
76 writeFile(int fd
, const void * data
, size_t length
)
80 if (length
!= (size_t)write(fd
, data
, length
))
85 if (kErrorNone
!= err
)
86 perror("couldn't write output");
91 /*********************************************************************
92 *********************************************************************/
93 __private_extern__ ToolError
94 seekFile(int fd
, off_t offset
)
98 if (offset
!= lseek(fd
, offset
, SEEK_SET
))
103 if (kErrorNone
!= err
)
104 perror("couldn't write output");
109 /*********************************************************************
110 *********************************************************************/
111 __private_extern__ ToolError
112 readFile(const char *path
, vm_offset_t
* objAddr
, vm_size_t
* objSize
)
114 ToolError err
= kErrorFileAccess
;
116 struct stat stat_buf
;
123 if((fd
= open(path
, O_RDONLY
)) == -1)
126 if(fstat(fd
, &stat_buf
) == -1)
129 if (0 == (stat_buf
.st_mode
& S_IFREG
))
132 /* Don't try to map an empty file, it fails now due to conformance
133 * stuff (PR 4611502).
135 if (0 == stat_buf
.st_size
) {
140 *objSize
= stat_buf
.st_size
;
142 *objAddr
= (vm_offset_t
)mmap(NULL
/* address */, *objSize
,
143 PROT_READ
|PROT_WRITE
, MAP_FILE
|MAP_PRIVATE
/* flags */,
146 if ((void *)*objAddr
== MAP_FAILED
) {
160 if (kErrorNone
!= err
)
162 fprintf(stderr
, "couldn't read %s: %s\n", path
, strerror(errno
));
169 enum { kExported
= 0x00000001, kObsolete
= 0x00000002 };
173 unsigned int name_len
;
175 unsigned int indirect_len
;
177 struct symbol
* list
;
178 unsigned int list_count
;
181 static bool issymchar( char c
)
183 return ((c
> ' ') && (c
<= '~') && (c
!= ':') && (c
!= '#'));
186 static bool iswhitespace( char c
)
188 return ((c
== ' ') || (c
== '\t'));
192 * Function for qsort for comparing symbol list names.
195 qsort_cmp(const void * _left
, const void * _right
)
197 struct symbol
* left
= (struct symbol
*) _left
;
198 struct symbol
* right
= (struct symbol
*) _right
;
200 return (strcmp(left
->name
, right
->name
));
204 * Function for bsearch for finding a symbol name.
208 bsearch_cmp( const void * _key
, const void * _cmp
)
210 char * key
= (char *)_key
;
211 struct symbol
* cmp
= (struct symbol
*) _cmp
;
213 return(strcmp(key
, cmp
->name
));
219 unsigned int name_len
;
223 bsearch_cmp_prefix( const void * _key
, const void * _cmp
)
225 struct bsearch_key
* key
= (struct bsearch_key
*)_key
;
226 struct symbol
* cmp
= (struct symbol
*) _cmp
;
228 return(strncmp(key
->name
, cmp
->name
, key
->name_len
));
232 count_symbols(char * file
, vm_size_t file_size
)
239 for (scan
= file
; true; scan
= next
) {
241 eol
= memchr(scan
, '\n', file_size
- (scan
- file
));
253 /* Skip comment lines.
255 if (scan
[0] == '#') {
259 /* Scan past any non-symbol characters at the beginning of the line. */
260 while ((scan
< eol
) && !issymchar(*scan
)) {
264 /* No symbol on line? Move along.
270 /* Skip symbols starting with '.'.
272 if (scan
[0] == '.') {
282 store_symbols(char * file
, vm_size_t file_size
, struct symbol
* symbols
, uint32_t idx
, uint32_t max_symbols
)
293 for (scan
= file
, line
= file
; true; scan
= next
, line
= next
) {
296 char * name_term
= NULL
;
297 unsigned int name_len
= 0;
298 char * indirect
= NULL
;
299 char * indirect_term
= NULL
;
300 unsigned int indirect_len
= 0;
301 char * option
= NULL
;
302 char * option_term
= NULL
;
303 unsigned int option_len
= 0;
305 boolean_t obsolete
= 0;
307 eol
= memchr(scan
, '\n', file_size
- (scan
- file
));
321 /* Skip comment lines.
323 if (scan
[0] == '#') {
327 /* Scan past any non-symbol characters at the beginning of the line. */
328 while ((scan
< eol
) && !issymchar(*scan
)) {
332 /* No symbol on line? Move along.
338 /* Skip symbols starting with '.'.
340 if (scan
[0] == '.') {
346 /* Find the end of the symbol.
348 while ((*scan
!= '\0') && issymchar(*scan
)) {
352 /* Note char past end of symbol.
356 /* Stored length must include the terminating nul char.
358 name_len
= name_term
- name
+ 1;
360 /* Now look for an indirect.
363 while ((*scan
!= '\0') && iswhitespace(*scan
)) {
368 while ((*scan
!= '\0') && iswhitespace(*scan
)) {
371 if (issymchar(*scan
)) {
374 /* Find the end of the symbol.
376 while ((*scan
!= '\0') && issymchar(*scan
)) {
380 /* Note char past end of symbol.
382 indirect_term
= scan
;
384 /* Stored length must include the terminating nul char.
386 indirect_len
= indirect_term
- indirect
+ 1;
388 } else if (*scan
== '\0') {
389 fprintf(stderr
, "bad format in symbol line: %s\n", line
);
392 } else if (*scan
!= '\0' && *scan
!= '-') {
393 fprintf(stderr
, "bad format in symbol line: %s\n", line
);
401 while ((*scan
!= '\0') && iswhitespace(*scan
)) {
408 if (isalpha(*scan
)) {
411 /* Find the end of the option.
413 while ((*scan
!= '\0') && isalpha(*scan
)) {
417 /* Note char past end of option.
420 option_len
= option_term
- option
;
422 if (option_len
>= sizeof(optionstr
)) {
423 fprintf(stderr
, "option too long in symbol line: %s\n", line
);
426 memcpy(optionstr
, option
, option_len
);
427 optionstr
[option_len
] = '\0';
431 if (!strncmp(optionstr
, "obsolete", option_len
)) {
435 } else if (*scan
== '\0') {
436 fprintf(stderr
, "bad format in symbol line: %s\n", line
);
444 if(idx
>= max_symbols
) {
445 fprintf(stderr
, "symbol[%d/%d] overflow: %s\n", idx
, max_symbols
, line
);
451 *indirect_term
= '\0';
454 symbols
[idx
].name
= name
;
455 symbols
[idx
].name_len
= name_len
;
456 symbols
[idx
].indirect
= indirect
;
457 symbols
[idx
].indirect_len
= indirect_len
;
458 symbols
[idx
].flags
= (obsolete
) ? kObsolete
: 0;
460 strtabsize
+= symbols
[idx
].name_len
+ symbols
[idx
].indirect_len
;
467 static const NXArchInfo
*
468 lookup_arch(const char *archstring
)
471 * As new architectures are supported by xnu, add a mapping function
472 * without relying on host libraries.
474 static const NXArchInfo archlist
[] = {
475 { "x86_64", 0x01000007 /* CPU_TYPE_X86_64 */, 3 /* CPU_SUBTYPE_X86_64_ALL */, NX_LittleEndian
, NULL
},
476 { "x86_64h", 0x01000007 /* CPU_TYPE_X86_64 */, 8 /* CPU_SUBTYPE_X86_64_H */, NX_LittleEndian
, NULL
},
480 for (i
=0; i
< sizeof(archlist
)/sizeof(archlist
[0]); i
++) {
481 if (0 == strcmp(archstring
, archlist
[i
].name
)) {
489 /*********************************************************************
490 *********************************************************************/
491 int main(int argc
, char * argv
[])
495 const char * output_name
= NULL
;
496 uint32_t zero
= 0, num_files
= 0;
498 uint32_t strx
, strtabsize
, strtabpad
;
499 struct symbol
* import_symbols
;
500 struct symbol
* export_symbols
;
501 uint32_t num_import_syms
, num_export_syms
;
502 uint32_t result_count
, num_removed_syms
;
503 uint32_t import_idx
, export_idx
;
504 const NXArchInfo
* host_arch
;
505 const NXArchInfo
* target_arch
;
506 boolean_t require_imports
= true;
507 boolean_t diff
= false;
512 vm_size_t mapped_size
;
517 struct file files
[64];
519 host_arch
= NXGetLocalArchInfo();
520 target_arch
= host_arch
;
522 for( i
= 1; i
< argc
; i
+= 2)
526 if (!strcmp("-sect", argv
[i
]))
528 require_imports
= false;
532 if (!strcmp("-diff", argv
[i
]))
534 require_imports
= false;
542 fprintf(stderr
, "bad arguments: %s\n", argv
[i
]);
546 if (!strcmp("-arch", argv
[i
]))
548 target_arch
= lookup_arch(argv
[i
+ 1]);
551 fprintf(stderr
, "unknown architecture name: %s\n", argv
[i
+1]);
556 if (!strcmp("-output", argv
[i
]))
558 output_name
= argv
[i
+1];
562 if (!strcmp("-import", argv
[i
]))
564 else if (!strcmp("-export", argv
[i
]))
568 fprintf(stderr
, "unknown option: %s\n", argv
[i
]);
572 err
= readFile(argv
[i
+1], &files
[num_files
].mapped
, &files
[num_files
].mapped_size
);
573 if (kErrorNone
!= err
)
576 if (files
[num_files
].mapped
&& files
[num_files
].mapped_size
)
578 files
[num_files
].import = import;
579 files
[num_files
].path
= argv
[i
+1];
586 fprintf(stderr
, "no output file\n");
592 for (filenum
= 0; filenum
< num_files
; filenum
++)
594 files
[filenum
].nsyms
= count_symbols((char *) files
[filenum
].mapped
, files
[filenum
].mapped_size
);
595 if (files
[filenum
].import)
596 num_import_syms
+= files
[filenum
].nsyms
;
598 num_export_syms
+= files
[filenum
].nsyms
;
601 import_symbols
= calloc(num_import_syms
, sizeof(struct symbol
));
602 export_symbols
= calloc(num_export_syms
, sizeof(struct symbol
));
607 for (filenum
= 0; filenum
< num_files
; filenum
++)
609 if (files
[filenum
].import)
611 store_symbols((char *) files
[filenum
].mapped
, files
[filenum
].mapped_size
,
612 import_symbols
, import_idx
, num_import_syms
);
613 import_idx
+= files
[filenum
].nsyms
;
617 store_symbols((char *) files
[filenum
].mapped
, files
[filenum
].mapped_size
,
618 export_symbols
, export_idx
, num_export_syms
);
619 export_idx
+= files
[filenum
].nsyms
;
621 if (false && !files
[filenum
].nsyms
)
623 fprintf(stderr
, "warning: file %s contains no names\n", files
[filenum
].path
);
628 qsort(import_symbols
, num_import_syms
, sizeof(struct symbol
), &qsort_cmp
);
629 qsort(export_symbols
, num_export_syms
, sizeof(struct symbol
), &qsort_cmp
);
632 num_removed_syms
= 0;
636 for (export_idx
= 0; export_idx
< num_export_syms
; export_idx
++)
638 struct symbol
* result
;
643 name
= export_symbols
[export_idx
].indirect
;
644 len
= export_symbols
[export_idx
].indirect_len
;
647 name
= export_symbols
[export_idx
].name
;
648 len
= export_symbols
[export_idx
].name_len
;
650 wild
= ((len
> 2) && ('*' == name
[len
-=2]));
653 struct bsearch_key key
;
656 result
= bsearch(&key
, import_symbols
,
657 num_import_syms
, sizeof(struct symbol
), &bsearch_cmp_prefix
);
661 struct symbol
* first
;
662 struct symbol
* last
;
664 strtabsize
+= (result
->name_len
+ result
->indirect_len
);
667 while (--first
>= &import_symbols
[0])
669 if (bsearch_cmp_prefix(&key
, first
))
671 strtabsize
+= (first
->name_len
+ first
->indirect_len
);
676 while (++last
< (&import_symbols
[0] + num_import_syms
))
678 if (bsearch_cmp_prefix(&key
, last
))
680 strtabsize
+= (last
->name_len
+ last
->indirect_len
);
682 result_count
+= last
- first
;
684 export_symbols
[export_idx
].list
= first
;
685 export_symbols
[export_idx
].list_count
= last
- first
;
686 export_symbols
[export_idx
].flags
|= kExported
;
690 result
= bsearch(name
, import_symbols
,
691 num_import_syms
, sizeof(struct symbol
), &bsearch_cmp
);
693 if (!result
&& require_imports
)
696 char * demangled_result
=
697 __cxa_demangle(export_symbols
[export_idx
].name
+ 1, NULL
, NULL
, &status
);
698 fprintf(stderr
, "exported name not in import list: %s\n",
699 demangled_result
? demangled_result
: export_symbols
[export_idx
].name
);
700 // fprintf(stderr, " : %s\n", export_symbols[export_idx].name);
701 if (demangled_result
) {
702 free(demangled_result
);
709 result
= &export_symbols
[export_idx
];
715 export_symbols
[export_idx
].flags
|= kExported
;
716 strtabsize
+= (export_symbols
[export_idx
].name_len
+ export_symbols
[export_idx
].indirect_len
);
718 export_symbols
[export_idx
].list
= &export_symbols
[export_idx
];
719 export_symbols
[export_idx
].list_count
= 1;
723 strtabpad
= (strtabsize
+ 3) & ~3;
725 if (require_imports
&& num_removed_syms
)
731 fd
= open(output_name
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0755);
734 perror("couldn't write output");
735 err
= kErrorFileAccess
;
739 struct symtab_command symcmd
;
740 struct uuid_command uuidcmd
;
743 symcmd
.cmd
= LC_SYMTAB
;
744 symcmd
.cmdsize
= sizeof(symcmd
);
745 symcmd
.nsyms
= result_count
;
746 symcmd
.strsize
= strtabpad
;
748 uuidcmd
.cmd
= LC_UUID
;
749 uuidcmd
.cmdsize
= sizeof(uuidcmd
);
750 uuid_generate(uuidcmd
.uuid
);
752 if (CPU_ARCH_ABI64
& target_arch
->cputype
)
754 struct mach_header_64 hdr
;
755 struct segment_command_64 segcmd
;
757 hdr
.magic
= MH_MAGIC_64
;
758 hdr
.cputype
= target_arch
->cputype
;
759 hdr
.cpusubtype
= target_arch
->cpusubtype
;
760 hdr
.filetype
= MH_KEXT_BUNDLE
;
762 hdr
.sizeofcmds
= sizeof(segcmd
) + sizeof(symcmd
) + sizeof(uuidcmd
);
763 hdr
.flags
= MH_INCRLINK
;
764 symsoffset
= mach_vm_round_page(hdr
.sizeofcmds
);
766 segcmd
.cmd
= LC_SEGMENT_64
;
767 segcmd
.cmdsize
= sizeof(segcmd
);
768 strncpy(segcmd
.segname
, SEG_LINKEDIT
, sizeof(segcmd
.segname
));
770 segcmd
.vmsize
= result_count
* sizeof(struct nlist_64
) + strtabpad
;
771 segcmd
.fileoff
= symsoffset
;
772 segcmd
.filesize
= segcmd
.vmsize
;
773 segcmd
.maxprot
= PROT_READ
;
774 segcmd
.initprot
= PROT_READ
;
776 segcmd
.flags
= SG_NORELOC
;
778 symcmd
.symoff
= symsoffset
;
779 symcmd
.stroff
= result_count
* sizeof(struct nlist_64
)
782 if (target_arch
->byteorder
!= host_arch
->byteorder
)
784 swap_mach_header_64(&hdr
, target_arch
->byteorder
);
785 swap_segment_command_64(&segcmd
, target_arch
->byteorder
);
787 err
= writeFile(fd
, &hdr
, sizeof(hdr
));
788 if (kErrorNone
!= err
)
790 err
= writeFile(fd
, &segcmd
, sizeof(segcmd
));
794 struct mach_header hdr
;
795 struct segment_command segcmd
;
797 hdr
.magic
= MH_MAGIC
;
798 hdr
.cputype
= target_arch
->cputype
;
799 hdr
.cpusubtype
= target_arch
->cpusubtype
;
800 hdr
.filetype
= MH_KEXT_BUNDLE
;
802 hdr
.sizeofcmds
= sizeof(segcmd
) + sizeof(symcmd
) + sizeof(uuidcmd
);
803 hdr
.flags
= MH_INCRLINK
;
804 symsoffset
= mach_vm_round_page(hdr
.sizeofcmds
);
806 segcmd
.cmd
= LC_SEGMENT
;
807 segcmd
.cmdsize
= sizeof(segcmd
);
808 strncpy(segcmd
.segname
, SEG_LINKEDIT
, sizeof(segcmd
.segname
));
810 segcmd
.vmsize
= result_count
* sizeof(struct nlist
) + strtabpad
;
811 segcmd
.fileoff
= symsoffset
;
812 segcmd
.filesize
= segcmd
.vmsize
;
813 segcmd
.maxprot
= PROT_READ
;
814 segcmd
.initprot
= PROT_READ
;
816 segcmd
.flags
= SG_NORELOC
;
818 symcmd
.symoff
= symsoffset
;
819 symcmd
.stroff
= result_count
* sizeof(struct nlist
)
822 if (target_arch
->byteorder
!= host_arch
->byteorder
)
824 swap_mach_header(&hdr
, target_arch
->byteorder
);
825 swap_segment_command(&segcmd
, target_arch
->byteorder
);
827 err
= writeFile(fd
, &hdr
, sizeof(hdr
));
828 if (kErrorNone
!= err
)
830 err
= writeFile(fd
, &segcmd
, sizeof(segcmd
));
833 if (kErrorNone
!= err
)
836 if (target_arch
->byteorder
!= host_arch
->byteorder
) {
837 swap_symtab_command(&symcmd
, target_arch
->byteorder
);
838 swap_uuid_command(&uuidcmd
, target_arch
->byteorder
);
840 err
= writeFile(fd
, &symcmd
, sizeof(symcmd
));
841 if (kErrorNone
!= err
)
843 err
= writeFile(fd
, &uuidcmd
, sizeof(uuidcmd
));
844 if (kErrorNone
!= err
)
847 err
= seekFile(fd
, symsoffset
);
848 if (kErrorNone
!= err
)
852 for (export_idx
= 0; export_idx
< num_export_syms
; export_idx
++)
854 if (!export_symbols
[export_idx
].name
)
856 if (!(kExported
& export_symbols
[export_idx
].flags
))
860 && export_symbols
[export_idx
- 1].name
861 && !strcmp(export_symbols
[export_idx
- 1].name
, export_symbols
[export_idx
].name
))
863 fprintf(stderr
, "duplicate export: %s\n", export_symbols
[export_idx
- 1].name
);
864 err
= kErrorDuplicate
;
868 for (import_idx
= 0; import_idx
< export_symbols
[export_idx
].list_count
; import_idx
++)
871 if (export_symbols
[export_idx
].list
!= &export_symbols
[export_idx
])
873 printf("wild: %s, %s\n", export_symbols
[export_idx
].name
,
874 export_symbols
[export_idx
].list
[import_idx
].name
);
876 if (CPU_ARCH_ABI64
& target_arch
->cputype
)
882 nl
.n_un
.n_strx
= strx
;
883 strx
+= export_symbols
[export_idx
].list
[import_idx
].name_len
;
885 if (export_symbols
[export_idx
].flags
& kObsolete
) {
886 nl
.n_desc
|= N_DESC_DISCARDED
;
889 if (export_symbols
[export_idx
].list
[import_idx
].indirect
)
891 nl
.n_type
= N_INDR
| N_EXT
;
893 strx
+= export_symbols
[export_idx
].list
[import_idx
].indirect_len
;
897 nl
.n_type
= N_UNDF
| N_EXT
;
901 if (target_arch
->byteorder
!= host_arch
->byteorder
)
902 swap_nlist_64(&nl
, 1, target_arch
->byteorder
);
904 err
= writeFile(fd
, &nl
, sizeof(nl
));
912 nl
.n_un
.n_strx
= strx
;
913 strx
+= export_symbols
[export_idx
].list
[import_idx
].name_len
;
915 if (export_symbols
[export_idx
].flags
& kObsolete
) {
916 nl
.n_desc
|= N_DESC_DISCARDED
;
919 if (export_symbols
[export_idx
].list
[import_idx
].indirect
)
921 nl
.n_type
= N_INDR
| N_EXT
;
923 strx
+= export_symbols
[export_idx
].list
[import_idx
].indirect_len
;
927 nl
.n_type
= N_UNDF
| N_EXT
;
931 if (target_arch
->byteorder
!= host_arch
->byteorder
)
932 swap_nlist(&nl
, 1, target_arch
->byteorder
);
934 err
= writeFile(fd
, &nl
, sizeof(nl
));
938 if (kErrorNone
!= err
)
942 strx
= sizeof(uint32_t);
943 err
= writeFile(fd
, &zero
, strx
);
944 if (kErrorNone
!= err
)
947 for (export_idx
= 0; export_idx
< num_export_syms
; export_idx
++)
949 if (!export_symbols
[export_idx
].name
)
952 for (import_idx
= 0; import_idx
< export_symbols
[export_idx
].list_count
; import_idx
++)
954 err
= writeFile(fd
, export_symbols
[export_idx
].list
[import_idx
].name
,
955 export_symbols
[export_idx
].list
[import_idx
].name_len
);
956 if (kErrorNone
!= err
)
958 if (export_symbols
[export_idx
].list
[import_idx
].indirect
)
960 err
= writeFile(fd
, export_symbols
[export_idx
].list
[import_idx
].indirect
,
961 export_symbols
[export_idx
].list
[import_idx
].indirect_len
);
962 if (kErrorNone
!= err
)
968 err
= writeFile(fd
, &zero
, strtabpad
- strtabsize
);
969 if (kErrorNone
!= err
)
976 for (filenum
= 0; filenum
< num_files
; filenum
++) {
978 if (files
[filenum
].mapped_size
)
980 munmap((caddr_t
)files
[filenum
].mapped
, files
[filenum
].mapped_size
);
981 files
[filenum
].mapped
= 0;
982 files
[filenum
].mapped_size
= 0;
987 if (kErrorNone
!= err
)
989 if (output_name
&& strncmp(output_name
, "/dev/", 5))