This is xnu-10002.1.13. See this file in:
/*
* Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
#include <libc.h>
#include <errno.h>
#include <ctype.h>
#include <mach/mach_init.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/mman.h>
#include <mach-o/arch.h>
#include <mach-o/fat.h>
#include <mach-o/loader.h>
#include <mach-o/nlist.h>
#include <mach-o/swap.h>
#include <uuid/uuid.h>
#include <stdbool.h>
#pragma mark Typedefs, Enums, Constants
/*********************************************************************
* Typedefs, Enums, Constants
*********************************************************************/
typedef enum {
kErrorNone = 0,
kError,
kErrorFileAccess,
kErrorDiskFull,
kErrorDuplicate
} ToolError;
#pragma mark Function Protos
/*********************************************************************
* Function Protos
*********************************************************************/
__private_extern__ ToolError
readFile(const char *path, vm_offset_t * objAddr, vm_size_t * objSize);
__private_extern__ ToolError
writeFile(int fd, const void * data, size_t length);
__private_extern__ ToolError
seekFile(int fd, off_t offset);
extern char* __cxa_demangle(const char* mangled_name,
char* buf,
size_t* n,
int* status);
#pragma mark Functions
/*********************************************************************
*********************************************************************/
__private_extern__ ToolError
writeFile(int fd, const void * data, size_t length)
{
ToolError err;
if (length != (size_t)write(fd, data, length)) {
err = kErrorDiskFull;
} else {
err = kErrorNone;
}
if (kErrorNone != err) {
perror("couldn't write output");
}
return err;
}
/*********************************************************************
*********************************************************************/
__private_extern__ ToolError
seekFile(int fd, off_t offset)
{
ToolError err;
if (offset != lseek(fd, offset, SEEK_SET)) {
err = kErrorDiskFull;
} else {
err = kErrorNone;
}
if (kErrorNone != err) {
perror("couldn't write output");
}
return err;
}
/*********************************************************************
*********************************************************************/
__private_extern__ ToolError
readFile(const char *path, vm_offset_t * objAddr, vm_size_t * objSize)
{
ToolError err = kErrorFileAccess;
int fd;
struct stat stat_buf;
*objAddr = 0;
*objSize = 0;
do{
if ((fd = open(path, O_RDONLY)) == -1) {
continue;
}
if (fstat(fd, &stat_buf) == -1) {
continue;
}
if (0 == (stat_buf.st_mode & S_IFREG)) {
continue;
}
/* Don't try to map an empty file, it fails now due to conformance
* stuff (PR 4611502).
*/
if (0 == stat_buf.st_size) {
err = kErrorNone;
continue;
}
*objSize = stat_buf.st_size;
*objAddr = (vm_offset_t)mmap(NULL /* address */, *objSize,
PROT_READ | PROT_WRITE, MAP_FILE | MAP_PRIVATE /* flags */,
fd, 0 /* offset */);
if ((void *)*objAddr == MAP_FAILED) {
*objAddr = 0;
*objSize = 0;
continue;
}
err = kErrorNone;
} while (false);
if (-1 != fd) {
close(fd);
}
if (kErrorNone != err) {
fprintf(stderr, "couldn't read %s: %s\n", path, strerror(errno));
}
return err;
}
enum { kExported = 0x00000001, kObsolete = 0x00000002 };
struct symbol {
char * name;
unsigned int name_len;
char * indirect;
unsigned int indirect_len;
unsigned int flags;
struct symbol * list;
unsigned int list_count;
};
static bool
issymchar( char c )
{
return (c > ' ') && (c <= '~') && (c != ':') && (c != '#');
}
static bool
iswhitespace( char c )
{
return (c == ' ') || (c == '\t');
}
/*
* Function for qsort for comparing symbol list names.
*/
static int
qsort_cmp(const void * _left, const void * _right)
{
struct symbol * left = (struct symbol *) _left;
struct symbol * right = (struct symbol *) _right;
return strcmp(left->name, right->name);
}
/*
* Function for bsearch for finding a symbol name.
*/
static int
bsearch_cmp( const void * _key, const void * _cmp)
{
char * key = (char *)_key;
struct symbol * cmp = (struct symbol *) _cmp;
return strcmp(key, cmp->name);
}
struct bsearch_key {
char * name;
unsigned int name_len;
};
static int
bsearch_cmp_prefix( const void * _key, const void * _cmp)
{
struct bsearch_key * key = (struct bsearch_key *)_key;
struct symbol * cmp = (struct symbol *) _cmp;
return strncmp(key->name, cmp->name, key->name_len);
}
static uint32_t
count_symbols(char * file, vm_size_t file_size)
{
uint32_t nsyms = 0;
char * scan;
char * eol;
char * next;
for (scan = file; true; scan = next) {
eol = memchr(scan, '\n', file_size - (scan - file));
if (eol == NULL) {
break;
}
next = eol + 1;
/* Skip empty lines.
*/
if (eol == scan) {
continue;
}
/* Skip comment lines.
*/
if (scan[0] == '#') {
continue;
}
/* Scan past any non-symbol characters at the beginning of the line. */
while ((scan < eol) && !issymchar(*scan)) {
scan++;
}
/* No symbol on line? Move along.
*/
if (scan == eol) {
continue;
}
/* Skip symbols starting with '.'.
*/
if (scan[0] == '.') {
continue;
}
nsyms++;
}
return nsyms;
}
static uint32_t
store_symbols(char * file, vm_size_t file_size, struct symbol * symbols, uint32_t idx, uint32_t max_symbols)
{
char * scan;
char * line;
char * eol;
char * next;
uint32_t strtabsize;
strtabsize = 0;
for (scan = file, line = file; true; scan = next, line = next) {
char * name = NULL;
char * name_term = NULL;
unsigned int name_len = 0;
char * indirect = NULL;
char * indirect_term = NULL;
unsigned int indirect_len = 0;
char * option = NULL;
char * option_term = NULL;
unsigned int option_len = 0;
char optionstr[256];
boolean_t obsolete = 0;
eol = memchr(scan, '\n', file_size - (scan - file));
if (eol == NULL) {
break;
}
next = eol + 1;
/* Skip empty lines.
*/
if (eol == scan) {
continue;
}
*eol = '\0';
/* Skip comment lines.
*/
if (scan[0] == '#') {
continue;
}
/* Scan past any non-symbol characters at the beginning of the line. */
while ((scan < eol) && !issymchar(*scan)) {
scan++;
}
/* No symbol on line? Move along.
*/
if (scan == eol) {
continue;
}
/* Skip symbols starting with '.'.
*/
if (scan[0] == '.') {
continue;
}
name = scan;
/* Find the end of the symbol.
*/
while ((*scan != '\0') && issymchar(*scan)) {
scan++;
}
/* Note char past end of symbol.
*/
name_term = scan;
/* Stored length must include the terminating nul char.
*/
name_len = name_term - name + 1;
/* Now look for an indirect.
*/
if (*scan != '\0') {
while ((*scan != '\0') && iswhitespace(*scan)) {
scan++;
}
if (*scan == ':') {
scan++;
while ((*scan != '\0') && iswhitespace(*scan)) {
scan++;
}
if (issymchar(*scan)) {
indirect = scan;
/* Find the end of the symbol.
*/
while ((*scan != '\0') && issymchar(*scan)) {
scan++;
}
/* Note char past end of symbol.
*/
indirect_term = scan;
/* Stored length must include the terminating nul char.
*/
indirect_len = indirect_term - indirect + 1;
} else if (*scan == '\0') {
fprintf(stderr, "bad format in symbol line: %s\n", line);
exit(1);
}
} else if (*scan != '\0' && *scan != '-') {
fprintf(stderr, "bad format in symbol line: %s\n", line);
exit(1);
}
}
/* Look for options.
*/
if (*scan != '\0') {
while ((*scan != '\0') && iswhitespace(*scan)) {
scan++;
}
if (*scan == '-') {
scan++;
if (isalpha(*scan)) {
option = scan;
/* Find the end of the option.
*/
while ((*scan != '\0') && isalpha(*scan)) {
scan++;
}
/* Note char past end of option.
*/
option_term = scan;
option_len = option_term - option;
if (option_len >= sizeof(optionstr)) {
fprintf(stderr, "option too long in symbol line: %s\n", line);
exit(1);
}
memcpy(optionstr, option, option_len);
optionstr[option_len] = '\0';
/* Find the option.
*/
if (!strncmp(optionstr, "obsolete", option_len)) {
obsolete = TRUE;
}
} else if (*scan == '\0') {
fprintf(stderr, "bad format in symbol line: %s\n", line);
exit(1);
}
}
}
if (idx >= max_symbols) {
fprintf(stderr, "symbol[%d/%d] overflow: %s\n", idx, max_symbols, line);
exit(1);
}
*name_term = '\0';
if (indirect_term) {
*indirect_term = '\0';
}
symbols[idx].name = name;
symbols[idx].name_len = name_len;
symbols[idx].indirect = indirect;
symbols[idx].indirect_len = indirect_len;
symbols[idx].flags = (obsolete) ? kObsolete : 0;
strtabsize += symbols[idx].name_len + symbols[idx].indirect_len;
idx++;
}
return strtabsize;
}
static const NXArchInfo *
lookup_arch(const char *archstring)
{
/*
* As new architectures are supported by xnu, add a mapping function
* without relying on host libraries.
*/
static const NXArchInfo archlist[] = {
{ "x86_64", 0x01000007 /* CPU_TYPE_X86_64 */, 3 /* CPU_SUBTYPE_X86_64_ALL */, NX_LittleEndian, NULL },
{ "x86_64h", 0x01000007 /* CPU_TYPE_X86_64 */, 8 /* CPU_SUBTYPE_X86_64_H */, NX_LittleEndian, NULL },
{ "armv7", 12 /* CPU_TYPE_ARM */, 9 /* CPU_SUBTYPE_ARM_V7 */, NX_LittleEndian, NULL },
{ "armv7s", 12 /* CPU_TYPE_ARM */, 11 /* CPU_SUBTYPE_ARM_V7S */, NX_LittleEndian, NULL },
{ "armv7k", 12 /* CPU_TYPE_ARM */, 12 /* CPU_SUBTYPE_ARM_V7K */, NX_LittleEndian, NULL },
{ "arm64", 0x0100000c /* CPU_TYPE_ARM64 */, 0 /* CPU_SUBTYPE_ARM64_ALL */, NX_LittleEndian, NULL },
{ "arm64e", 0x0100000c /* CPU_TYPE_ARM64 */, 2 /* CPU_SUBTYPE_ARM64_E */, NX_LittleEndian, NULL },
};
unsigned long i;
for (i = 0; i < sizeof(archlist) / sizeof(archlist[0]); i++) {
if (0 == strcmp(archstring, archlist[i].name)) {
return &archlist[i];
}
}
return NULL;
}
/*********************************************************************
*********************************************************************/
int
main(int argc, char * argv[])
{
ToolError err;
int i, fd;
const char * output_name = NULL;
uint32_t zero = 0, num_files = 0;
uint32_t filenum;
uint32_t strx, strtabsize, strtabpad;
struct symbol * import_symbols;
struct symbol * export_symbols;
uint32_t num_import_syms, num_export_syms;
uint32_t result_count, num_removed_syms;
uint32_t import_idx, export_idx;
const NXArchInfo * host_arch;
const NXArchInfo * target_arch;
boolean_t require_imports = true;
boolean_t diff = false;
struct file {
vm_offset_t mapped;
vm_size_t mapped_size;
uint32_t nsyms;
boolean_t import;
const char * path;
};
struct file files[64];
host_arch = NXGetLocalArchInfo();
target_arch = host_arch;
for (i = 1; i < argc; i += 2) {
boolean_t import;
if (!strcmp("-sect", argv[i])) {
require_imports = false;
i--;
continue;
}
if (!strcmp("-diff", argv[i])) {
require_imports = false;
diff = true;
i--;
continue;
}
if (i == (argc - 1)) {
fprintf(stderr, "bad arguments: %s\n", argv[i]);
exit(1);
}
if (!strcmp("-arch", argv[i])) {
target_arch = lookup_arch(argv[i + 1]);
if (!target_arch) {
fprintf(stderr, "unknown architecture name: %s\n", argv[i + 1]);
exit(1);
}
continue;
}
if (!strcmp("-output", argv[i])) {
output_name = argv[i + 1];
continue;
}
if (!strcmp("-import", argv[i])) {
import = true;
} else if (!strcmp("-export", argv[i])) {
import = false;
} else {
fprintf(stderr, "unknown option: %s\n", argv[i]);
exit(1);
}
err = readFile(argv[i + 1], &files[num_files].mapped, &files[num_files].mapped_size);
if (kErrorNone != err) {
exit(1);
}
if (files[num_files].mapped && files[num_files].mapped_size) {
files[num_files].import = import;
files[num_files].path = argv[i + 1];
num_files++;
}
}
if (!output_name) {
fprintf(stderr, "no output file\n");
exit(1);
}
num_import_syms = 0;
num_export_syms = 0;
for (filenum = 0; filenum < num_files; filenum++) {
files[filenum].nsyms = count_symbols((char *) files[filenum].mapped, files[filenum].mapped_size);
if (files[filenum].import) {
num_import_syms += files[filenum].nsyms;
} else {
num_export_syms += files[filenum].nsyms;
}
}
import_symbols = calloc(num_import_syms, sizeof(struct symbol));
export_symbols = calloc(num_export_syms, sizeof(struct symbol));
import_idx = 0;
export_idx = 0;
for (filenum = 0; filenum < num_files; filenum++) {
if (files[filenum].import) {
store_symbols((char *) files[filenum].mapped, files[filenum].mapped_size,
import_symbols, import_idx, num_import_syms);
import_idx += files[filenum].nsyms;
} else {
store_symbols((char *) files[filenum].mapped, files[filenum].mapped_size,
export_symbols, export_idx, num_export_syms);
export_idx += files[filenum].nsyms;
}
if (false && !files[filenum].nsyms) {
fprintf(stderr, "warning: file %s contains no names\n", files[filenum].path);
}
}
qsort(import_symbols, num_import_syms, sizeof(struct symbol), &qsort_cmp);
qsort(export_symbols, num_export_syms, sizeof(struct symbol), &qsort_cmp);
result_count = 0;
num_removed_syms = 0;
strtabsize = 4;
if (num_import_syms) {
for (export_idx = 0; export_idx < num_export_syms; export_idx++) {
struct symbol * result;
char * name;
size_t len;
boolean_t wild;
name = export_symbols[export_idx].indirect;
len = export_symbols[export_idx].indirect_len;
if (!name) {
name = export_symbols[export_idx].name;
len = export_symbols[export_idx].name_len;
}
wild = ((len > 2) && ('*' == name[len -= 2]));
if (wild) {
struct bsearch_key key;
key.name = name;
key.name_len = len;
result = bsearch(&key, import_symbols,
num_import_syms, sizeof(struct symbol), &bsearch_cmp_prefix);
if (result) {
struct symbol * first;
struct symbol * last;
strtabsize += (result->name_len + result->indirect_len);
first = result;
while (--first >= &import_symbols[0]) {
if (bsearch_cmp_prefix(&key, first)) {
break;
}
strtabsize += (first->name_len + first->indirect_len);
}
first++;
last = result;
while (++last < (&import_symbols[0] + num_import_syms)) {
if (bsearch_cmp_prefix(&key, last)) {
break;
}
strtabsize += (last->name_len + last->indirect_len);
}
result_count += last - first;
result = first;
export_symbols[export_idx].list = first;
export_symbols[export_idx].list_count = last - first;
export_symbols[export_idx].flags |= kExported;
}
} else {
result = bsearch(name, import_symbols,
num_import_syms, sizeof(struct symbol), &bsearch_cmp);
}
if (!result && require_imports) {
int status;
char * demangled_result =
__cxa_demangle(export_symbols[export_idx].name + 1, NULL, NULL, &status);
fprintf(stderr, "exported name not in import list: %s\n",
demangled_result ? demangled_result : export_symbols[export_idx].name);
// fprintf(stderr, " : %s\n", export_symbols[export_idx].name);
if (demangled_result) {
free(demangled_result);
}
num_removed_syms++;
}
if (diff) {
if (!result) {
result = &export_symbols[export_idx];
} else {
result = NULL;
}
}
if (result && !wild) {
export_symbols[export_idx].flags |= kExported;
strtabsize += (export_symbols[export_idx].name_len + export_symbols[export_idx].indirect_len);
result_count++;
export_symbols[export_idx].list = &export_symbols[export_idx];
export_symbols[export_idx].list_count = 1;
}
}
}
strtabpad = (strtabsize + 3) & ~3;
if (require_imports && num_removed_syms) {
err = kError;
goto finish;
}
fd = open(output_name, O_WRONLY | O_CREAT | O_TRUNC, 0755);
if (-1 == fd) {
perror("couldn't write output");
err = kErrorFileAccess;
goto finish;
}
struct symtab_command symcmd;
struct uuid_command uuidcmd;
off_t symsoffset;
symcmd.cmd = LC_SYMTAB;
symcmd.cmdsize = sizeof(symcmd);
symcmd.nsyms = result_count;
symcmd.strsize = strtabpad;
uuidcmd.cmd = LC_UUID;
uuidcmd.cmdsize = sizeof(uuidcmd);
uuid_generate(uuidcmd.uuid);
if (CPU_ARCH_ABI64 & target_arch->cputype) {
struct mach_header_64 hdr;
struct segment_command_64 segcmd;
hdr.magic = MH_MAGIC_64;
hdr.cputype = target_arch->cputype;
hdr.cpusubtype = target_arch->cpusubtype;
hdr.filetype = MH_KEXT_BUNDLE;
hdr.ncmds = 3;
hdr.sizeofcmds = sizeof(segcmd) + sizeof(symcmd) + sizeof(uuidcmd);
hdr.flags = MH_INCRLINK;
symsoffset = mach_vm_round_page(hdr.sizeofcmds);
segcmd.cmd = LC_SEGMENT_64;
segcmd.cmdsize = sizeof(segcmd);
strncpy(segcmd.segname, SEG_LINKEDIT, sizeof(segcmd.segname));
segcmd.vmaddr = 0;
segcmd.vmsize = result_count * sizeof(struct nlist_64) + strtabpad;
segcmd.fileoff = symsoffset;
segcmd.filesize = segcmd.vmsize;
segcmd.maxprot = PROT_READ;
segcmd.initprot = PROT_READ;
segcmd.nsects = 0;
segcmd.flags = SG_NORELOC;
symcmd.symoff = symsoffset;
symcmd.stroff = result_count * sizeof(struct nlist_64)
+ symcmd.symoff;
if (target_arch->byteorder != host_arch->byteorder) {
swap_mach_header_64(&hdr, target_arch->byteorder);
swap_segment_command_64(&segcmd, target_arch->byteorder);
}
err = writeFile(fd, &hdr, sizeof(hdr));
if (kErrorNone != err) {
goto finish;
}
err = writeFile(fd, &segcmd, sizeof(segcmd));
} else {
struct mach_header hdr;
struct segment_command segcmd;
hdr.magic = MH_MAGIC;
hdr.cputype = target_arch->cputype;
hdr.cpusubtype = target_arch->cpusubtype;
hdr.filetype = MH_KEXT_BUNDLE;
hdr.ncmds = 3;
hdr.sizeofcmds = sizeof(segcmd) + sizeof(symcmd) + sizeof(uuidcmd);
hdr.flags = MH_INCRLINK;
symsoffset = mach_vm_round_page(hdr.sizeofcmds);
segcmd.cmd = LC_SEGMENT;
segcmd.cmdsize = sizeof(segcmd);
strncpy(segcmd.segname, SEG_LINKEDIT, sizeof(segcmd.segname));
segcmd.vmaddr = 0;
segcmd.vmsize = result_count * sizeof(struct nlist) + strtabpad;
segcmd.fileoff = symsoffset;
segcmd.filesize = segcmd.vmsize;
segcmd.maxprot = PROT_READ;
segcmd.initprot = PROT_READ;
segcmd.nsects = 0;
segcmd.flags = SG_NORELOC;
symcmd.symoff = symsoffset;
symcmd.stroff = result_count * sizeof(struct nlist)
+ symcmd.symoff;
if (target_arch->byteorder != host_arch->byteorder) {
swap_mach_header(&hdr, target_arch->byteorder);
swap_segment_command(&segcmd, target_arch->byteorder);
}
err = writeFile(fd, &hdr, sizeof(hdr));
if (kErrorNone != err) {
goto finish;
}
err = writeFile(fd, &segcmd, sizeof(segcmd));
}
if (kErrorNone != err) {
goto finish;
}
if (target_arch->byteorder != host_arch->byteorder) {
swap_symtab_command(&symcmd, target_arch->byteorder);
swap_uuid_command(&uuidcmd, target_arch->byteorder);
}
err = writeFile(fd, &symcmd, sizeof(symcmd));
if (kErrorNone != err) {
goto finish;
}
err = writeFile(fd, &uuidcmd, sizeof(uuidcmd));
if (kErrorNone != err) {
goto finish;
}
err = seekFile(fd, symsoffset);
if (kErrorNone != err) {
goto finish;
}
strx = 4;
for (export_idx = 0; export_idx < num_export_syms; export_idx++) {
if (!export_symbols[export_idx].name) {
continue;
}
if (!(kExported & export_symbols[export_idx].flags)) {
continue;
}
if (export_idx
&& export_symbols[export_idx - 1].name
&& !strcmp(export_symbols[export_idx - 1].name, export_symbols[export_idx].name)) {
fprintf(stderr, "duplicate export: %s\n", export_symbols[export_idx - 1].name);
err = kErrorDuplicate;
goto finish;
}
for (import_idx = 0; import_idx < export_symbols[export_idx].list_count; import_idx++) {
if (export_symbols[export_idx].list != &export_symbols[export_idx]) {
printf("wild: %s, %s\n", export_symbols[export_idx].name,
export_symbols[export_idx].list[import_idx].name);
}
if (CPU_ARCH_ABI64 & target_arch->cputype) {
struct nlist_64 nl;
nl.n_sect = 0;
nl.n_desc = 0;
nl.n_un.n_strx = strx;
strx += export_symbols[export_idx].list[import_idx].name_len;
if (export_symbols[export_idx].flags & kObsolete) {
nl.n_desc |= N_DESC_DISCARDED;
}
if (export_symbols[export_idx].list[import_idx].indirect) {
nl.n_type = N_INDR | N_EXT;
nl.n_value = strx;
strx += export_symbols[export_idx].list[import_idx].indirect_len;
} else {
nl.n_type = N_UNDF | N_EXT;
nl.n_value = 0;
}
if (target_arch->byteorder != host_arch->byteorder) {
swap_nlist_64(&nl, 1, target_arch->byteorder);
}
err = writeFile(fd, &nl, sizeof(nl));
} else {
struct nlist nl;
nl.n_sect = 0;
nl.n_desc = 0;
nl.n_un.n_strx = strx;
strx += export_symbols[export_idx].list[import_idx].name_len;
if (export_symbols[export_idx].flags & kObsolete) {
nl.n_desc |= N_DESC_DISCARDED;
}
if (export_symbols[export_idx].list[import_idx].indirect) {
nl.n_type = N_INDR | N_EXT;
nl.n_value = strx;
strx += export_symbols[export_idx].list[import_idx].indirect_len;
} else {
nl.n_type = N_UNDF | N_EXT;
nl.n_value = 0;
}
if (target_arch->byteorder != host_arch->byteorder) {
swap_nlist(&nl, 1, target_arch->byteorder);
}
err = writeFile(fd, &nl, sizeof(nl));
}
}
if (kErrorNone != err) {
goto finish;
}
}
strx = sizeof(uint32_t);
err = writeFile(fd, &zero, strx);
if (kErrorNone != err) {
goto finish;
}
for (export_idx = 0; export_idx < num_export_syms; export_idx++) {
if (!export_symbols[export_idx].name) {
continue;
}
for (import_idx = 0; import_idx < export_symbols[export_idx].list_count; import_idx++) {
err = writeFile(fd, export_symbols[export_idx].list[import_idx].name,
export_symbols[export_idx].list[import_idx].name_len);
if (kErrorNone != err) {
goto finish;
}
if (export_symbols[export_idx].list[import_idx].indirect) {
err = writeFile(fd, export_symbols[export_idx].list[import_idx].indirect,
export_symbols[export_idx].list[import_idx].indirect_len);
if (kErrorNone != err) {
goto finish;
}
}
}
}
err = writeFile(fd, &zero, strtabpad - strtabsize);
if (kErrorNone != err) {
goto finish;
}
close(fd);
finish:
for (filenum = 0; filenum < num_files; filenum++) {
// unmap file
if (files[filenum].mapped_size) {
munmap((caddr_t)files[filenum].mapped, files[filenum].mapped_size);
files[filenum].mapped = 0;
files[filenum].mapped_size = 0;
}
}
if (kErrorNone != err) {
if (output_name && strncmp(output_name, "/dev/", 5)) {
unlink(output_name);
}
exit(1);
} else {
exit(0);
}
return 0;
}