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