xref: /xnu-12377.81.4/SETUP/kextsymboltool/kextsymboltool.c (revision 043036a2b3718f7f0be807e2870f8f47d3fa0796)
1 /*
2  * Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
3  *
4  * @APPLE_LICENSE_HEADER_START@
5  *
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
11  * file.
12  *
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.
20  *
21  * @APPLE_LICENSE_HEADER_END@
22  */
23 #include <libc.h>
24 #include <errno.h>
25 #include <ctype.h>
26 
27 #include <mach/mach_init.h>
28 
29 #include <sys/stat.h>
30 #include <sys/file.h>
31 #include <sys/mman.h>
32 
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>
38 
39 #include <uuid/uuid.h>
40 #include <stdbool.h>
41 
42 #pragma mark Typedefs, Enums, Constants
43 /*********************************************************************
44 * Typedefs, Enums, Constants
45 *********************************************************************/
46 typedef enum {
47 	kErrorNone = 0,
48 	kError,
49 	kErrorFileAccess,
50 	kErrorDiskFull,
51 	kErrorDuplicate
52 } ToolError;
53 
54 #pragma mark Function Protos
55 /*********************************************************************
56 * Function Protos
57 *********************************************************************/
58 __private_extern__ ToolError
59 readFile(const char *path, vm_offset_t * objAddr, vm_size_t * objSize);
60 
61 __private_extern__ ToolError
62 writeFile(int fd, const void * data, size_t length);
63 
64 __private_extern__ ToolError
65 seekFile(int fd, off_t offset);
66 
67 extern char* __cxa_demangle(const char* mangled_name,
68     char* buf,
69     size_t* n,
70     int* status);
71 
72 #pragma mark Functions
73 /*********************************************************************
74 *********************************************************************/
75 __private_extern__ ToolError
writeFile(int fd,const void * data,size_t length)76 writeFile(int fd, const void * data, size_t length)
77 {
78 	ToolError err;
79 
80 	if (length != (size_t)write(fd, data, length)) {
81 		err = kErrorDiskFull;
82 	} else {
83 		err = kErrorNone;
84 	}
85 
86 	if (kErrorNone != err) {
87 		perror("couldn't write output");
88 	}
89 
90 	return err;
91 }
92 
93 /*********************************************************************
94 *********************************************************************/
95 __private_extern__ ToolError
seekFile(int fd,off_t offset)96 seekFile(int fd, off_t offset)
97 {
98 	ToolError err;
99 
100 	if (offset != lseek(fd, offset, SEEK_SET)) {
101 		err = kErrorDiskFull;
102 	} else {
103 		err = kErrorNone;
104 	}
105 
106 	if (kErrorNone != err) {
107 		perror("couldn't write output");
108 	}
109 
110 	return err;
111 }
112 
113 /*********************************************************************
114 *********************************************************************/
115 __private_extern__ ToolError
readFile(const char * path,vm_offset_t * objAddr,vm_size_t * objSize)116 readFile(const char *path, vm_offset_t * objAddr, vm_size_t * objSize)
117 {
118 	ToolError err = kErrorFileAccess;
119 	int fd;
120 	struct stat stat_buf;
121 
122 	*objAddr = 0;
123 	*objSize = 0;
124 
125 	do{
126 		if ((fd = open(path, O_RDONLY)) == -1) {
127 			continue;
128 		}
129 
130 		if (fstat(fd, &stat_buf) == -1) {
131 			continue;
132 		}
133 
134 		if (0 == (stat_buf.st_mode & S_IFREG)) {
135 			continue;
136 		}
137 
138 		/* Don't try to map an empty file, it fails now due to conformance
139 		 * stuff (PR 4611502).
140 		 */
141 		if (0 == stat_buf.st_size) {
142 			err = kErrorNone;
143 			continue;
144 		}
145 
146 		*objSize = stat_buf.st_size;
147 
148 		*objAddr = (vm_offset_t)mmap(NULL /* address */, *objSize,
149 		    PROT_READ | PROT_WRITE, MAP_FILE | MAP_PRIVATE /* flags */,
150 		    fd, 0 /* offset */);
151 
152 		if ((void *)*objAddr == MAP_FAILED) {
153 			*objAddr = 0;
154 			*objSize = 0;
155 			continue;
156 		}
157 
158 		err = kErrorNone;
159 	} while (false);
160 
161 	if (-1 != fd) {
162 		close(fd);
163 	}
164 	if (kErrorNone != err) {
165 		fprintf(stderr, "couldn't read %s: %s\n", path, strerror(errno));
166 	}
167 
168 	return err;
169 }
170 
171 
172 enum { kExported = 0x00000001, kObsolete = 0x00000002 };
173 
174 struct symbol {
175 	char * name;
176 	unsigned int name_len;
177 	char * indirect;
178 	unsigned int indirect_len;
179 	unsigned int flags;
180 	struct symbol * list;
181 	unsigned int list_count;
182 };
183 
184 static bool
issymchar(char c)185 issymchar( char c )
186 {
187 	return (c > ' ') && (c <= '~') && (c != ':') && (c != '#');
188 }
189 
190 static bool
iswhitespace(char c)191 iswhitespace( char c )
192 {
193 	return (c == ' ') || (c == '\t');
194 }
195 
196 /*
197  * Function for qsort for comparing symbol list names.
198  */
199 static int
qsort_cmp(const void * _left,const void * _right)200 qsort_cmp(const void * _left, const void * _right)
201 {
202 	struct symbol * left  = (struct symbol *) _left;
203 	struct symbol * right = (struct symbol *) _right;
204 
205 	return strcmp(left->name, right->name);
206 }
207 
208 /*
209  * Function for bsearch for finding a symbol name.
210  */
211 
212 static int
bsearch_cmp(const void * _key,const void * _cmp)213 bsearch_cmp( const void * _key, const void * _cmp)
214 {
215 	char * key = (char *)_key;
216 	struct symbol * cmp = (struct symbol *) _cmp;
217 
218 	return strcmp(key, cmp->name);
219 }
220 
221 struct bsearch_key {
222 	char * name;
223 	unsigned int name_len;
224 };
225 
226 static int
bsearch_cmp_prefix(const void * _key,const void * _cmp)227 bsearch_cmp_prefix( const void * _key, const void * _cmp)
228 {
229 	struct bsearch_key * key = (struct bsearch_key *)_key;
230 	struct symbol *      cmp = (struct symbol *) _cmp;
231 
232 	return strncmp(key->name, cmp->name, key->name_len);
233 }
234 
235 static uint32_t
count_symbols(char * file,vm_size_t file_size)236 count_symbols(char * file, vm_size_t file_size)
237 {
238 	uint32_t nsyms = 0;
239 	char *   scan;
240 	char *   eol;
241 	char *   next;
242 
243 	for (scan = file; true; scan = next) {
244 		eol = memchr(scan, '\n', file_size - (scan - file));
245 		if (eol == NULL) {
246 			break;
247 		}
248 		next = eol + 1;
249 
250 		/* Skip empty lines.
251 		 */
252 		if (eol == scan) {
253 			continue;
254 		}
255 
256 		/* Skip comment lines.
257 		 */
258 		if (scan[0] == '#') {
259 			continue;
260 		}
261 
262 		/* Scan past any non-symbol characters at the beginning of the line. */
263 		while ((scan < eol) && !issymchar(*scan)) {
264 			scan++;
265 		}
266 
267 		/* No symbol on line? Move along.
268 		 */
269 		if (scan == eol) {
270 			continue;
271 		}
272 
273 		/* Skip symbols starting with '.'.
274 		 */
275 		if (scan[0] == '.') {
276 			continue;
277 		}
278 		nsyms++;
279 	}
280 
281 	return nsyms;
282 }
283 
284 static uint32_t
store_symbols(char * file,vm_size_t file_size,struct symbol * symbols,uint32_t idx,uint32_t max_symbols)285 store_symbols(char * file, vm_size_t file_size, struct symbol * symbols, uint32_t idx, uint32_t max_symbols)
286 {
287 	char *   scan;
288 	char *   line;
289 	char *   eol;
290 	char *   next;
291 
292 	uint32_t strtabsize;
293 
294 	strtabsize = 0;
295 
296 	for (scan = file, line = file; true; scan = next, line = next) {
297 		char *       name = NULL;
298 		char *       name_term = NULL;
299 		unsigned int name_len = 0;
300 		char *       indirect = NULL;
301 		char *       indirect_term = NULL;
302 		unsigned int indirect_len = 0;
303 		char *       option = NULL;
304 		char *       option_term = NULL;
305 		unsigned int option_len = 0;
306 		char         optionstr[256];
307 		boolean_t    obsolete = 0;
308 
309 		eol = memchr(scan, '\n', file_size - (scan - file));
310 		if (eol == NULL) {
311 			break;
312 		}
313 		next = eol + 1;
314 
315 		/* Skip empty lines.
316 		 */
317 		if (eol == scan) {
318 			continue;
319 		}
320 
321 		*eol = '\0';
322 
323 		/* Skip comment lines.
324 		 */
325 		if (scan[0] == '#') {
326 			continue;
327 		}
328 
329 		/* Scan past any non-symbol characters at the beginning of the line. */
330 		while ((scan < eol) && !issymchar(*scan)) {
331 			scan++;
332 		}
333 
334 		/* No symbol on line? Move along.
335 		 */
336 		if (scan == eol) {
337 			continue;
338 		}
339 
340 		/* Skip symbols starting with '.'.
341 		 */
342 		if (scan[0] == '.') {
343 			continue;
344 		}
345 
346 		name = scan;
347 
348 		/* Find the end of the symbol.
349 		 */
350 		while ((*scan != '\0') && issymchar(*scan)) {
351 			scan++;
352 		}
353 
354 		/* Note char past end of symbol.
355 		 */
356 		name_term = scan;
357 
358 		/* Stored length must include the terminating nul char.
359 		 */
360 		name_len = name_term - name + 1;
361 
362 		/* Now look for an indirect.
363 		 */
364 		if (*scan != '\0') {
365 			while ((*scan != '\0') && iswhitespace(*scan)) {
366 				scan++;
367 			}
368 			if (*scan == ':') {
369 				scan++;
370 				while ((*scan != '\0') && iswhitespace(*scan)) {
371 					scan++;
372 				}
373 				if (issymchar(*scan)) {
374 					indirect = scan;
375 
376 					/* Find the end of the symbol.
377 					 */
378 					while ((*scan != '\0') && issymchar(*scan)) {
379 						scan++;
380 					}
381 
382 					/* Note char past end of symbol.
383 					 */
384 					indirect_term = scan;
385 
386 					/* Stored length must include the terminating nul char.
387 					 */
388 					indirect_len = indirect_term - indirect + 1;
389 				} else if (*scan == '\0') {
390 					fprintf(stderr, "bad format in symbol line: %s\n", line);
391 					exit(1);
392 				}
393 			} else if (*scan != '\0' && *scan != '-') {
394 				fprintf(stderr, "bad format in symbol line: %s\n", line);
395 				exit(1);
396 			}
397 		}
398 
399 		/* Look for options.
400 		 */
401 		if (*scan != '\0') {
402 			while ((*scan != '\0') && iswhitespace(*scan)) {
403 				scan++;
404 			}
405 
406 			if (*scan == '-') {
407 				scan++;
408 
409 				if (isalpha(*scan)) {
410 					option = scan;
411 
412 					/* Find the end of the option.
413 					 */
414 					while ((*scan != '\0') && isalpha(*scan)) {
415 						scan++;
416 					}
417 
418 					/* Note char past end of option.
419 					 */
420 					option_term = scan;
421 					option_len = option_term - option;
422 
423 					if (option_len >= sizeof(optionstr)) {
424 						fprintf(stderr, "option too long in symbol line: %s\n", line);
425 						exit(1);
426 					}
427 					memcpy(optionstr, option, option_len);
428 					optionstr[option_len] = '\0';
429 
430 					/* Find the option.
431 					 */
432 					if (!strncmp(optionstr, "obsolete", option_len)) {
433 						obsolete = TRUE;
434 					}
435 				} else if (*scan == '\0') {
436 					fprintf(stderr, "bad format in symbol line: %s\n", line);
437 					exit(1);
438 				}
439 			}
440 		}
441 
442 		if (idx >= max_symbols) {
443 			fprintf(stderr, "symbol[%d/%d] overflow: %s\n", idx, max_symbols, line);
444 			exit(1);
445 		}
446 
447 		*name_term = '\0';
448 		if (indirect_term) {
449 			*indirect_term = '\0';
450 		}
451 
452 		symbols[idx].name = name;
453 		symbols[idx].name_len = name_len;
454 		symbols[idx].indirect = indirect;
455 		symbols[idx].indirect_len = indirect_len;
456 		symbols[idx].flags = (obsolete) ? kObsolete : 0;
457 
458 		strtabsize += symbols[idx].name_len + symbols[idx].indirect_len;
459 		idx++;
460 	}
461 
462 	return strtabsize;
463 }
464 
465 static const NXArchInfo *
lookup_arch(const char * archstring)466 lookup_arch(const char *archstring)
467 {
468 	/*
469 	 * As new architectures are supported by xnu, add a mapping function
470 	 * without relying on host libraries.
471 	 */
472 	static const NXArchInfo archlist[] = {
473 		{ "x86_64", 0x01000007 /* CPU_TYPE_X86_64 */, 3 /* CPU_SUBTYPE_X86_64_ALL */, NX_LittleEndian, NULL },
474 		{ "x86_64h", 0x01000007 /* CPU_TYPE_X86_64 */, 8 /* CPU_SUBTYPE_X86_64_H */, NX_LittleEndian, NULL },
475 		{ "armv7", 12 /* CPU_TYPE_ARM */, 9 /* CPU_SUBTYPE_ARM_V7 */, NX_LittleEndian, NULL },
476 		{ "armv7s", 12 /* CPU_TYPE_ARM */, 11 /* CPU_SUBTYPE_ARM_V7S */, NX_LittleEndian, NULL },
477 		{ "armv7k", 12 /* CPU_TYPE_ARM */, 12 /* CPU_SUBTYPE_ARM_V7K */, NX_LittleEndian, NULL },
478 		{ "arm64", 0x0100000c /* CPU_TYPE_ARM64 */, 0 /* CPU_SUBTYPE_ARM64_ALL */, NX_LittleEndian, NULL },
479 		{ "arm64e", 0x0100000c /* CPU_TYPE_ARM64 */, 2 /* CPU_SUBTYPE_ARM64_E */, NX_LittleEndian, NULL },
480 	};
481 	unsigned long i;
482 
483 	for (i = 0; i < sizeof(archlist) / sizeof(archlist[0]); i++) {
484 		if (0 == strcmp(archstring, archlist[i].name)) {
485 			return &archlist[i];
486 		}
487 	}
488 
489 	return NULL;
490 }
491 
492 /*********************************************************************
493 *********************************************************************/
494 int
main(int argc,char * argv[])495 main(int argc, char * argv[])
496 {
497 	ToolError   err;
498 	int                 i, fd;
499 	const char *        output_name = NULL;
500 	uint32_t            zero = 0, num_files = 0;
501 	uint32_t            filenum;
502 	uint32_t            strx, strtabsize, strtabpad;
503 	struct symbol *     import_symbols;
504 	struct symbol *     export_symbols;
505 	uint32_t            num_import_syms, num_export_syms;
506 	uint32_t            result_count, num_removed_syms;
507 	uint32_t            import_idx, export_idx;
508 	const NXArchInfo *  host_arch;
509 	const NXArchInfo *  target_arch;
510 	boolean_t           require_imports = true;
511 	boolean_t           diff = false;
512 
513 
514 	struct file {
515 		vm_offset_t  mapped;
516 		vm_size_t    mapped_size;
517 		uint32_t     nsyms;
518 		boolean_t    import;
519 		const char * path;
520 	};
521 	struct file files[64];
522 
523 	host_arch = NXGetLocalArchInfo();
524 	target_arch = host_arch;
525 
526 	for (i = 1; i < argc; i += 2) {
527 		boolean_t import;
528 
529 		if (!strcmp("-sect", argv[i])) {
530 			require_imports = false;
531 			i--;
532 			continue;
533 		}
534 		if (!strcmp("-diff", argv[i])) {
535 			require_imports = false;
536 			diff = true;
537 			i--;
538 			continue;
539 		}
540 
541 		if (i == (argc - 1)) {
542 			fprintf(stderr, "bad arguments: %s\n", argv[i]);
543 			exit(1);
544 		}
545 
546 		if (!strcmp("-arch", argv[i])) {
547 			target_arch = lookup_arch(argv[i + 1]);
548 			if (!target_arch) {
549 				fprintf(stderr, "unknown architecture name: %s\n", argv[i + 1]);
550 				exit(1);
551 			}
552 			continue;
553 		}
554 		if (!strcmp("-output", argv[i])) {
555 			output_name = argv[i + 1];
556 			continue;
557 		}
558 
559 		if (!strcmp("-import", argv[i])) {
560 			import = true;
561 		} else if (!strcmp("-export", argv[i])) {
562 			import = false;
563 		} else {
564 			fprintf(stderr, "unknown option: %s\n", argv[i]);
565 			exit(1);
566 		}
567 
568 		err = readFile(argv[i + 1], &files[num_files].mapped, &files[num_files].mapped_size);
569 		if (kErrorNone != err) {
570 			exit(1);
571 		}
572 
573 		if (files[num_files].mapped && files[num_files].mapped_size) {
574 			files[num_files].import = import;
575 			files[num_files].path   = argv[i + 1];
576 			num_files++;
577 		}
578 	}
579 
580 	if (!output_name) {
581 		fprintf(stderr, "no output file\n");
582 		exit(1);
583 	}
584 
585 	num_import_syms = 0;
586 	num_export_syms = 0;
587 	for (filenum = 0; filenum < num_files; filenum++) {
588 		files[filenum].nsyms = count_symbols((char *) files[filenum].mapped, files[filenum].mapped_size);
589 		if (files[filenum].import) {
590 			num_import_syms += files[filenum].nsyms;
591 		} else {
592 			num_export_syms += files[filenum].nsyms;
593 		}
594 	}
595 
596 	import_symbols = calloc(num_import_syms, sizeof(struct symbol));
597 	export_symbols = calloc(num_export_syms, sizeof(struct symbol));
598 
599 	import_idx = 0;
600 	export_idx = 0;
601 
602 	for (filenum = 0; filenum < num_files; filenum++) {
603 		if (files[filenum].import) {
604 			store_symbols((char *) files[filenum].mapped, files[filenum].mapped_size,
605 			    import_symbols, import_idx, num_import_syms);
606 			import_idx += files[filenum].nsyms;
607 		} else {
608 			store_symbols((char *) files[filenum].mapped, files[filenum].mapped_size,
609 			    export_symbols, export_idx, num_export_syms);
610 			export_idx += files[filenum].nsyms;
611 		}
612 		if (false && !files[filenum].nsyms) {
613 			fprintf(stderr, "warning: file %s contains no names\n", files[filenum].path);
614 		}
615 	}
616 
617 
618 	qsort(import_symbols, num_import_syms, sizeof(struct symbol), &qsort_cmp);
619 	qsort(export_symbols, num_export_syms, sizeof(struct symbol), &qsort_cmp);
620 
621 	result_count = 0;
622 	num_removed_syms = 0;
623 	strtabsize = 4;
624 	if (num_import_syms) {
625 		for (export_idx = 0; export_idx < num_export_syms; export_idx++) {
626 			struct symbol * result;
627 			char * name;
628 			size_t len;
629 			boolean_t wild;
630 
631 			name = export_symbols[export_idx].indirect;
632 			len  = export_symbols[export_idx].indirect_len;
633 			if (!name) {
634 				name = export_symbols[export_idx].name;
635 				len  = export_symbols[export_idx].name_len;
636 			}
637 			wild = ((len > 2) && ('*' == name[len -= 2]));
638 			if (wild) {
639 				struct bsearch_key key;
640 				key.name = name;
641 				key.name_len = len;
642 				result = bsearch(&key, import_symbols,
643 				    num_import_syms, sizeof(struct symbol), &bsearch_cmp_prefix);
644 
645 				if (result) {
646 					struct symbol * first;
647 					struct symbol * last;
648 
649 					strtabsize += (result->name_len + result->indirect_len);
650 
651 					first = result;
652 					while (--first >= &import_symbols[0]) {
653 						if (bsearch_cmp_prefix(&key, first)) {
654 							break;
655 						}
656 						strtabsize += (first->name_len + first->indirect_len);
657 					}
658 					first++;
659 
660 					last = result;
661 					while (++last < (&import_symbols[0] + num_import_syms)) {
662 						if (bsearch_cmp_prefix(&key, last)) {
663 							break;
664 						}
665 						strtabsize += (last->name_len + last->indirect_len);
666 					}
667 					result_count += last - first;
668 					result = first;
669 					export_symbols[export_idx].list = first;
670 					export_symbols[export_idx].list_count = last - first;
671 					export_symbols[export_idx].flags |= kExported;
672 				}
673 			} else {
674 				result = bsearch(name, import_symbols,
675 				    num_import_syms, sizeof(struct symbol), &bsearch_cmp);
676 			}
677 
678 			if (!result && require_imports) {
679 				int status;
680 				char * demangled_result =
681 				    __cxa_demangle(export_symbols[export_idx].name + 1, NULL, NULL, &status);
682 				fprintf(stderr, "exported name not in import list: %s\n",
683 				    demangled_result ? demangled_result : export_symbols[export_idx].name);
684 //		fprintf(stderr, "                                : %s\n", export_symbols[export_idx].name);
685 				if (demangled_result) {
686 					free(demangled_result);
687 				}
688 				num_removed_syms++;
689 			}
690 			if (diff) {
691 				if (!result) {
692 					result = &export_symbols[export_idx];
693 				} else {
694 					result = NULL;
695 				}
696 			}
697 			if (result && !wild) {
698 				export_symbols[export_idx].flags |= kExported;
699 				strtabsize += (export_symbols[export_idx].name_len + export_symbols[export_idx].indirect_len);
700 				result_count++;
701 				export_symbols[export_idx].list = &export_symbols[export_idx];
702 				export_symbols[export_idx].list_count = 1;
703 			}
704 		}
705 	}
706 	strtabpad = (strtabsize + 3) & ~3;
707 
708 	if (require_imports && num_removed_syms) {
709 		err = kError;
710 		goto finish;
711 	}
712 
713 	fd = open(output_name, O_WRONLY | O_CREAT | O_TRUNC, 0755);
714 	if (-1 == fd) {
715 		perror("couldn't write output");
716 		err = kErrorFileAccess;
717 		goto finish;
718 	}
719 
720 	struct symtab_command symcmd;
721 	struct uuid_command uuidcmd;
722 	off_t  symsoffset;
723 
724 	symcmd.cmd          = LC_SYMTAB;
725 	symcmd.cmdsize      = sizeof(symcmd);
726 	symcmd.nsyms        = result_count;
727 	symcmd.strsize      = strtabpad;
728 
729 	uuidcmd.cmd         = LC_UUID;
730 	uuidcmd.cmdsize     = sizeof(uuidcmd);
731 	uuid_generate(uuidcmd.uuid);
732 
733 	if (CPU_ARCH_ABI64 & target_arch->cputype) {
734 		struct mach_header_64     hdr;
735 		struct segment_command_64 segcmd;
736 
737 		hdr.magic       = MH_MAGIC_64;
738 		hdr.cputype     = target_arch->cputype;
739 		hdr.cpusubtype  = target_arch->cpusubtype;
740 		hdr.filetype    = MH_KEXT_BUNDLE;
741 		hdr.ncmds       = 3;
742 		hdr.sizeofcmds  = sizeof(segcmd) + sizeof(symcmd) + sizeof(uuidcmd);
743 		hdr.flags       = MH_INCRLINK;
744 		symsoffset      = mach_vm_round_page(hdr.sizeofcmds);
745 
746 		segcmd.cmd      = LC_SEGMENT_64;
747 		segcmd.cmdsize  = sizeof(segcmd);
748 		strncpy(segcmd.segname, SEG_LINKEDIT, sizeof(segcmd.segname));
749 		segcmd.vmaddr   = 0;
750 		segcmd.vmsize   = result_count * sizeof(struct nlist_64) + strtabpad;
751 		segcmd.fileoff  = symsoffset;
752 		segcmd.filesize = segcmd.vmsize;
753 		segcmd.maxprot  = PROT_READ;
754 		segcmd.initprot = PROT_READ;
755 		segcmd.nsects   = 0;
756 		segcmd.flags    = SG_NORELOC;
757 
758 		symcmd.symoff   = symsoffset;
759 		symcmd.stroff   = result_count * sizeof(struct nlist_64)
760 		    + symcmd.symoff;
761 
762 		if (target_arch->byteorder != host_arch->byteorder) {
763 			swap_mach_header_64(&hdr, target_arch->byteorder);
764 			swap_segment_command_64(&segcmd, target_arch->byteorder);
765 		}
766 		err = writeFile(fd, &hdr, sizeof(hdr));
767 		if (kErrorNone != err) {
768 			goto finish;
769 		}
770 		err = writeFile(fd, &segcmd, sizeof(segcmd));
771 	} else {
772 		struct mach_header     hdr;
773 		struct segment_command segcmd;
774 
775 		hdr.magic       = MH_MAGIC;
776 		hdr.cputype     = target_arch->cputype;
777 		hdr.cpusubtype  = target_arch->cpusubtype;
778 		hdr.filetype    = MH_KEXT_BUNDLE;
779 		hdr.ncmds       = 3;
780 		hdr.sizeofcmds  = sizeof(segcmd) + sizeof(symcmd) + sizeof(uuidcmd);
781 		hdr.flags       = MH_INCRLINK;
782 		symsoffset      = mach_vm_round_page(hdr.sizeofcmds);
783 
784 		segcmd.cmd      = LC_SEGMENT;
785 		segcmd.cmdsize  = sizeof(segcmd);
786 		strncpy(segcmd.segname, SEG_LINKEDIT, sizeof(segcmd.segname));
787 		segcmd.vmaddr   = 0;
788 		segcmd.vmsize   = result_count * sizeof(struct nlist) + strtabpad;
789 		segcmd.fileoff  = symsoffset;
790 		segcmd.filesize = segcmd.vmsize;
791 		segcmd.maxprot  = PROT_READ;
792 		segcmd.initprot = PROT_READ;
793 		segcmd.nsects   = 0;
794 		segcmd.flags    = SG_NORELOC;
795 
796 		symcmd.symoff   = symsoffset;
797 		symcmd.stroff   = result_count * sizeof(struct nlist)
798 		    + symcmd.symoff;
799 
800 		if (target_arch->byteorder != host_arch->byteorder) {
801 			swap_mach_header(&hdr, target_arch->byteorder);
802 			swap_segment_command(&segcmd, target_arch->byteorder);
803 		}
804 		err = writeFile(fd, &hdr, sizeof(hdr));
805 		if (kErrorNone != err) {
806 			goto finish;
807 		}
808 		err = writeFile(fd, &segcmd, sizeof(segcmd));
809 	}
810 
811 	if (kErrorNone != err) {
812 		goto finish;
813 	}
814 
815 	if (target_arch->byteorder != host_arch->byteorder) {
816 		swap_symtab_command(&symcmd, target_arch->byteorder);
817 		swap_uuid_command(&uuidcmd, target_arch->byteorder);
818 	}
819 	err = writeFile(fd, &symcmd, sizeof(symcmd));
820 	if (kErrorNone != err) {
821 		goto finish;
822 	}
823 	err = writeFile(fd, &uuidcmd, sizeof(uuidcmd));
824 	if (kErrorNone != err) {
825 		goto finish;
826 	}
827 
828 	err = seekFile(fd, symsoffset);
829 	if (kErrorNone != err) {
830 		goto finish;
831 	}
832 
833 	strx = 4;
834 	for (export_idx = 0; export_idx < num_export_syms; export_idx++) {
835 		if (!export_symbols[export_idx].name) {
836 			continue;
837 		}
838 		if (!(kExported & export_symbols[export_idx].flags)) {
839 			continue;
840 		}
841 
842 		if (export_idx
843 		    && export_symbols[export_idx - 1].name
844 		    && !strcmp(export_symbols[export_idx - 1].name, export_symbols[export_idx].name)) {
845 			fprintf(stderr, "duplicate export: %s\n", export_symbols[export_idx - 1].name);
846 			err = kErrorDuplicate;
847 			goto finish;
848 		}
849 
850 		for (import_idx = 0; import_idx < export_symbols[export_idx].list_count; import_idx++) {
851 			if (export_symbols[export_idx].list != &export_symbols[export_idx]) {
852 				printf("wild: %s, %s\n", export_symbols[export_idx].name,
853 				    export_symbols[export_idx].list[import_idx].name);
854 			}
855 			if (CPU_ARCH_ABI64 & target_arch->cputype) {
856 				struct nlist_64 nl;
857 
858 				nl.n_sect  = 0;
859 				nl.n_desc  = 0;
860 				nl.n_un.n_strx = strx;
861 				strx += export_symbols[export_idx].list[import_idx].name_len;
862 
863 				if (export_symbols[export_idx].flags & kObsolete) {
864 					nl.n_desc |= N_DESC_DISCARDED;
865 				}
866 
867 				if (export_symbols[export_idx].list[import_idx].indirect) {
868 					nl.n_type  = N_INDR | N_EXT;
869 					nl.n_value = strx;
870 					strx += export_symbols[export_idx].list[import_idx].indirect_len;
871 				} else {
872 					nl.n_type  = N_UNDF | N_EXT;
873 					nl.n_value = 0;
874 				}
875 
876 				if (target_arch->byteorder != host_arch->byteorder) {
877 					swap_nlist_64(&nl, 1, target_arch->byteorder);
878 				}
879 
880 				err = writeFile(fd, &nl, sizeof(nl));
881 			} else {
882 				struct nlist nl;
883 
884 				nl.n_sect  = 0;
885 				nl.n_desc  = 0;
886 				nl.n_un.n_strx = strx;
887 				strx += export_symbols[export_idx].list[import_idx].name_len;
888 
889 				if (export_symbols[export_idx].flags & kObsolete) {
890 					nl.n_desc |= N_DESC_DISCARDED;
891 				}
892 
893 				if (export_symbols[export_idx].list[import_idx].indirect) {
894 					nl.n_type  = N_INDR | N_EXT;
895 					nl.n_value = strx;
896 					strx += export_symbols[export_idx].list[import_idx].indirect_len;
897 				} else {
898 					nl.n_type  = N_UNDF | N_EXT;
899 					nl.n_value = 0;
900 				}
901 
902 				if (target_arch->byteorder != host_arch->byteorder) {
903 					swap_nlist(&nl, 1, target_arch->byteorder);
904 				}
905 
906 				err = writeFile(fd, &nl, sizeof(nl));
907 			}
908 		}
909 
910 		if (kErrorNone != err) {
911 			goto finish;
912 		}
913 	}
914 
915 	strx = sizeof(uint32_t);
916 	err = writeFile(fd, &zero, strx);
917 	if (kErrorNone != err) {
918 		goto finish;
919 	}
920 
921 	for (export_idx = 0; export_idx < num_export_syms; export_idx++) {
922 		if (!export_symbols[export_idx].name) {
923 			continue;
924 		}
925 
926 		for (import_idx = 0; import_idx < export_symbols[export_idx].list_count; import_idx++) {
927 			err = writeFile(fd, export_symbols[export_idx].list[import_idx].name,
928 			    export_symbols[export_idx].list[import_idx].name_len);
929 			if (kErrorNone != err) {
930 				goto finish;
931 			}
932 			if (export_symbols[export_idx].list[import_idx].indirect) {
933 				err = writeFile(fd, export_symbols[export_idx].list[import_idx].indirect,
934 				    export_symbols[export_idx].list[import_idx].indirect_len);
935 				if (kErrorNone != err) {
936 					goto finish;
937 				}
938 			}
939 		}
940 	}
941 
942 	err = writeFile(fd, &zero, strtabpad - strtabsize);
943 	if (kErrorNone != err) {
944 		goto finish;
945 	}
946 
947 	close(fd);
948 
949 
950 finish:
951 	for (filenum = 0; filenum < num_files; filenum++) {
952 		// unmap file
953 		if (files[filenum].mapped_size) {
954 			munmap((caddr_t)files[filenum].mapped, files[filenum].mapped_size);
955 			files[filenum].mapped     = 0;
956 			files[filenum].mapped_size = 0;
957 		}
958 	}
959 
960 	if (kErrorNone != err) {
961 		if (output_name && strncmp(output_name, "/dev/", 5)) {
962 			unlink(output_name);
963 		}
964 		exit(1);
965 	} else {
966 		exit(0);
967 	}
968 	return 0;
969 }
970