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
;
600 if (!num_export_syms
)
602 fprintf(stderr
, "no export names\n");
606 import_symbols
= calloc(num_import_syms
, sizeof(struct symbol
));
607 export_symbols
= calloc(num_export_syms
, sizeof(struct symbol
));
612 for (filenum
= 0; filenum
< num_files
; filenum
++)
614 if (files
[filenum
].import)
616 store_symbols((char *) files
[filenum
].mapped
, files
[filenum
].mapped_size
,
617 import_symbols
, import_idx
, num_import_syms
);
618 import_idx
+= files
[filenum
].nsyms
;
622 store_symbols((char *) files
[filenum
].mapped
, files
[filenum
].mapped_size
,
623 export_symbols
, export_idx
, num_export_syms
);
624 export_idx
+= files
[filenum
].nsyms
;
626 if (false && !files
[filenum
].nsyms
)
628 fprintf(stderr
, "warning: file %s contains no names\n", files
[filenum
].path
);
633 qsort(import_symbols
, num_import_syms
, sizeof(struct symbol
), &qsort_cmp
);
634 qsort(export_symbols
, num_export_syms
, sizeof(struct symbol
), &qsort_cmp
);
637 num_removed_syms
= 0;
641 for (export_idx
= 0; export_idx
< num_export_syms
; export_idx
++)
643 struct symbol
* result
;
648 name
= export_symbols
[export_idx
].indirect
;
649 len
= export_symbols
[export_idx
].indirect_len
;
652 name
= export_symbols
[export_idx
].name
;
653 len
= export_symbols
[export_idx
].name_len
;
655 wild
= ((len
> 2) && ('*' == name
[len
-=2]));
658 struct bsearch_key key
;
661 result
= bsearch(&key
, import_symbols
,
662 num_import_syms
, sizeof(struct symbol
), &bsearch_cmp_prefix
);
666 struct symbol
* first
;
667 struct symbol
* last
;
669 strtabsize
+= (result
->name_len
+ result
->indirect_len
);
672 while (--first
>= &import_symbols
[0])
674 if (bsearch_cmp_prefix(&key
, first
))
676 strtabsize
+= (first
->name_len
+ first
->indirect_len
);
681 while (++last
< (&import_symbols
[0] + num_import_syms
))
683 if (bsearch_cmp_prefix(&key
, last
))
685 strtabsize
+= (last
->name_len
+ last
->indirect_len
);
687 result_count
+= last
- first
;
689 export_symbols
[export_idx
].list
= first
;
690 export_symbols
[export_idx
].list_count
= last
- first
;
691 export_symbols
[export_idx
].flags
|= kExported
;
695 result
= bsearch(name
, import_symbols
,
696 num_import_syms
, sizeof(struct symbol
), &bsearch_cmp
);
698 if (!result
&& require_imports
)
701 char * demangled_result
=
702 __cxa_demangle(export_symbols
[export_idx
].name
+ 1, NULL
, NULL
, &status
);
703 fprintf(stderr
, "exported name not in import list: %s\n",
704 demangled_result
? demangled_result
: export_symbols
[export_idx
].name
);
705 // fprintf(stderr, " : %s\n", export_symbols[export_idx].name);
706 if (demangled_result
) {
707 free(demangled_result
);
714 result
= &export_symbols
[export_idx
];
720 export_symbols
[export_idx
].flags
|= kExported
;
721 strtabsize
+= (export_symbols
[export_idx
].name_len
+ export_symbols
[export_idx
].indirect_len
);
723 export_symbols
[export_idx
].list
= &export_symbols
[export_idx
];
724 export_symbols
[export_idx
].list_count
= 1;
728 strtabpad
= (strtabsize
+ 3) & ~3;
730 if (require_imports
&& num_removed_syms
)
736 fd
= open(output_name
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0755);
739 perror("couldn't write output");
740 err
= kErrorFileAccess
;
744 struct symtab_command symcmd
;
745 struct uuid_command uuidcmd
;
748 symcmd
.cmd
= LC_SYMTAB
;
749 symcmd
.cmdsize
= sizeof(symcmd
);
750 symcmd
.nsyms
= result_count
;
751 symcmd
.strsize
= strtabpad
;
753 uuidcmd
.cmd
= LC_UUID
;
754 uuidcmd
.cmdsize
= sizeof(uuidcmd
);
755 uuid_generate(uuidcmd
.uuid
);
757 if (CPU_ARCH_ABI64
& target_arch
->cputype
)
759 struct mach_header_64 hdr
;
760 struct segment_command_64 segcmd
;
762 hdr
.magic
= MH_MAGIC_64
;
763 hdr
.cputype
= target_arch
->cputype
;
764 hdr
.cpusubtype
= target_arch
->cpusubtype
;
765 hdr
.filetype
= MH_KEXT_BUNDLE
;
767 hdr
.sizeofcmds
= sizeof(segcmd
) + sizeof(symcmd
) + sizeof(uuidcmd
);
768 hdr
.flags
= MH_INCRLINK
;
769 symsoffset
= mach_vm_round_page(hdr
.sizeofcmds
);
771 segcmd
.cmd
= LC_SEGMENT_64
;
772 segcmd
.cmdsize
= sizeof(segcmd
);
773 strncpy(segcmd
.segname
, SEG_LINKEDIT
, sizeof(segcmd
.segname
));
775 segcmd
.vmsize
= result_count
* sizeof(struct nlist_64
) + strtabpad
;
776 segcmd
.fileoff
= symsoffset
;
777 segcmd
.filesize
= segcmd
.vmsize
;
778 segcmd
.maxprot
= PROT_READ
;
779 segcmd
.initprot
= PROT_READ
;
781 segcmd
.flags
= SG_NORELOC
;
783 symcmd
.symoff
= symsoffset
;
784 symcmd
.stroff
= result_count
* sizeof(struct nlist_64
)
787 if (target_arch
->byteorder
!= host_arch
->byteorder
)
789 swap_mach_header_64(&hdr
, target_arch
->byteorder
);
790 swap_segment_command_64(&segcmd
, target_arch
->byteorder
);
792 err
= writeFile(fd
, &hdr
, sizeof(hdr
));
793 if (kErrorNone
!= err
)
795 err
= writeFile(fd
, &segcmd
, sizeof(segcmd
));
799 struct mach_header hdr
;
800 struct segment_command segcmd
;
802 hdr
.magic
= MH_MAGIC
;
803 hdr
.cputype
= target_arch
->cputype
;
804 hdr
.cpusubtype
= target_arch
->cpusubtype
;
805 hdr
.filetype
= MH_KEXT_BUNDLE
;
807 hdr
.sizeofcmds
= sizeof(segcmd
) + sizeof(symcmd
) + sizeof(uuidcmd
);
808 hdr
.flags
= MH_INCRLINK
;
809 symsoffset
= mach_vm_round_page(hdr
.sizeofcmds
);
811 segcmd
.cmd
= LC_SEGMENT
;
812 segcmd
.cmdsize
= sizeof(segcmd
);
813 strncpy(segcmd
.segname
, SEG_LINKEDIT
, sizeof(segcmd
.segname
));
815 segcmd
.vmsize
= result_count
* sizeof(struct nlist
) + strtabpad
;
816 segcmd
.fileoff
= symsoffset
;
817 segcmd
.filesize
= segcmd
.vmsize
;
818 segcmd
.maxprot
= PROT_READ
;
819 segcmd
.initprot
= PROT_READ
;
821 segcmd
.flags
= SG_NORELOC
;
823 symcmd
.symoff
= symsoffset
;
824 symcmd
.stroff
= result_count
* sizeof(struct nlist
)
827 if (target_arch
->byteorder
!= host_arch
->byteorder
)
829 swap_mach_header(&hdr
, target_arch
->byteorder
);
830 swap_segment_command(&segcmd
, target_arch
->byteorder
);
832 err
= writeFile(fd
, &hdr
, sizeof(hdr
));
833 if (kErrorNone
!= err
)
835 err
= writeFile(fd
, &segcmd
, sizeof(segcmd
));
838 if (kErrorNone
!= err
)
841 if (target_arch
->byteorder
!= host_arch
->byteorder
) {
842 swap_symtab_command(&symcmd
, target_arch
->byteorder
);
843 swap_uuid_command(&uuidcmd
, target_arch
->byteorder
);
845 err
= writeFile(fd
, &symcmd
, sizeof(symcmd
));
846 if (kErrorNone
!= err
)
848 err
= writeFile(fd
, &uuidcmd
, sizeof(uuidcmd
));
849 if (kErrorNone
!= err
)
852 err
= seekFile(fd
, symsoffset
);
853 if (kErrorNone
!= err
)
857 for (export_idx
= 0; export_idx
< num_export_syms
; export_idx
++)
859 if (!export_symbols
[export_idx
].name
)
861 if (!(kExported
& export_symbols
[export_idx
].flags
))
865 && export_symbols
[export_idx
- 1].name
866 && !strcmp(export_symbols
[export_idx
- 1].name
, export_symbols
[export_idx
].name
))
868 fprintf(stderr
, "duplicate export: %s\n", export_symbols
[export_idx
- 1].name
);
869 err
= kErrorDuplicate
;
873 for (import_idx
= 0; import_idx
< export_symbols
[export_idx
].list_count
; import_idx
++)
876 if (export_symbols
[export_idx
].list
!= &export_symbols
[export_idx
])
878 printf("wild: %s, %s\n", export_symbols
[export_idx
].name
,
879 export_symbols
[export_idx
].list
[import_idx
].name
);
881 if (CPU_ARCH_ABI64
& target_arch
->cputype
)
887 nl
.n_un
.n_strx
= strx
;
888 strx
+= export_symbols
[export_idx
].list
[import_idx
].name_len
;
890 if (export_symbols
[export_idx
].flags
& kObsolete
) {
891 nl
.n_desc
|= N_DESC_DISCARDED
;
894 if (export_symbols
[export_idx
].list
[import_idx
].indirect
)
896 nl
.n_type
= N_INDR
| N_EXT
;
898 strx
+= export_symbols
[export_idx
].list
[import_idx
].indirect_len
;
902 nl
.n_type
= N_UNDF
| N_EXT
;
906 if (target_arch
->byteorder
!= host_arch
->byteorder
)
907 swap_nlist_64(&nl
, 1, target_arch
->byteorder
);
909 err
= writeFile(fd
, &nl
, sizeof(nl
));
917 nl
.n_un
.n_strx
= strx
;
918 strx
+= export_symbols
[export_idx
].list
[import_idx
].name_len
;
920 if (export_symbols
[export_idx
].flags
& kObsolete
) {
921 nl
.n_desc
|= N_DESC_DISCARDED
;
924 if (export_symbols
[export_idx
].list
[import_idx
].indirect
)
926 nl
.n_type
= N_INDR
| N_EXT
;
928 strx
+= export_symbols
[export_idx
].list
[import_idx
].indirect_len
;
932 nl
.n_type
= N_UNDF
| N_EXT
;
936 if (target_arch
->byteorder
!= host_arch
->byteorder
)
937 swap_nlist(&nl
, 1, target_arch
->byteorder
);
939 err
= writeFile(fd
, &nl
, sizeof(nl
));
943 if (kErrorNone
!= err
)
947 strx
= sizeof(uint32_t);
948 err
= writeFile(fd
, &zero
, strx
);
949 if (kErrorNone
!= err
)
952 for (export_idx
= 0; export_idx
< num_export_syms
; export_idx
++)
954 if (!export_symbols
[export_idx
].name
)
957 for (import_idx
= 0; import_idx
< export_symbols
[export_idx
].list_count
; import_idx
++)
959 err
= writeFile(fd
, export_symbols
[export_idx
].list
[import_idx
].name
,
960 export_symbols
[export_idx
].list
[import_idx
].name_len
);
961 if (kErrorNone
!= err
)
963 if (export_symbols
[export_idx
].list
[import_idx
].indirect
)
965 err
= writeFile(fd
, export_symbols
[export_idx
].list
[import_idx
].indirect
,
966 export_symbols
[export_idx
].list
[import_idx
].indirect_len
);
967 if (kErrorNone
!= err
)
973 err
= writeFile(fd
, &zero
, strtabpad
- strtabsize
);
974 if (kErrorNone
!= err
)
981 for (filenum
= 0; filenum
< num_files
; filenum
++) {
983 if (files
[filenum
].mapped_size
)
985 munmap((caddr_t
)files
[filenum
].mapped
, files
[filenum
].mapped_size
);
986 files
[filenum
].mapped
= 0;
987 files
[filenum
].mapped_size
= 0;
992 if (kErrorNone
!= err
)
994 if (output_name
&& strncmp(output_name
, "/dev/", 5))