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