You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
769 lines
22 KiB
769 lines
22 KiB
/*********************************************************************** |
|
* xflat/tools/mknxflat.c |
|
* |
|
* Copyright (C) 2009 Gregory Nutt. All rights reserved. |
|
* Author: Gregory Nutt <spudmonkey@racsa.co.cr> |
|
* |
|
* Modified from ldelflib (see http://xflat.org): |
|
* |
|
* Copyright (c) 2002, 2006, Cadenux, LLC. All rights reserved. |
|
* Copyright (c) 2002, 2006, Gregory Nutt. All rights reserved. |
|
* Author: Gregory Nutt <spudmonkey@racsa.co.cr> |
|
* |
|
* Redistribution and use in source and binary forms, with or without |
|
* modification, are permitted provided that the following conditions |
|
* are met: |
|
* |
|
* 1. Redistributions of source code must retain the above copyright |
|
* notice, this list of conditions and the following disclaimer. |
|
* 2. Redistributions in binary form must reproduce the above copyright |
|
* notice, this list of conditions and the following disclaimer in |
|
* the documentation and/or other materials provided with the |
|
* distribution. |
|
* 3. Neither the name NuttX nor the names of its contributors may be |
|
* used to endorse or promote products derived from this software |
|
* without specific prior written permission. |
|
* |
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS |
|
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
* POSSIBILITY OF SUCH DAMAGE. |
|
* |
|
***********************************************************************/ |
|
|
|
/*********************************************************************** |
|
* Included Files |
|
***********************************************************************/ |
|
|
|
#include <stdio.h> |
|
#include <unistd.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <fcntl.h> |
|
#include <errno.h> |
|
#include <sys/types.h> |
|
#include <sys/stat.h> |
|
#include <bfd.h> |
|
|
|
#include "nxflat.h" |
|
#include "arch/arch.h" |
|
|
|
/*********************************************************************** |
|
* Definitions |
|
***********************************************************************/ |
|
|
|
#define dbg(format, arg...) \ |
|
if (verbose) printf(format, ## arg) |
|
|
|
#define BSF_GLOBL_FUNC (BSF_GLOBAL|BSF_FUNCTION) |
|
#define BSF_WEAK_FUNC (BSF_WEAK|BSF_FUNCTION) |
|
#define BSF_DEFINED (BSF_LOCAL|BSF_GLOBAL) |
|
|
|
#define IS_GLOBL_FUNC(x) ((((x)->flags)&(BSF_GLOBL_FUNC))==(BSF_GLOBL_FUNC)) |
|
#define IS_WEAK_FUNC(x) ((((x)->flags)&(BSF_WEAK_FUNC))==(BSF_WEAK_FUNC)) |
|
#define IS_DEFINED(x) ((((x)->flags)&(BSF_DEFINED))!=0) |
|
#define IS_OBJECT(x) ((((x)->flags)&(BSF_OBJECT))!=0) |
|
#define IS_WEAK(x) ((((x)->flags)&(BSF_WEAK))!=0) |
|
|
|
#define MAX_EXPORT_NAMES 1024 |
|
|
|
/*********************************************************************** |
|
* Private Types |
|
***********************************************************************/ |
|
|
|
typedef int (*symfunc_type) (asymbol * sym, void *arg); |
|
typedef int (*namefunc_type) (const char *name, void *arg); |
|
|
|
/*********************************************************************** |
|
* Private Variables |
|
***********************************************************************/ |
|
|
|
/* Command line settings (counters but treated like booleans) */ |
|
|
|
static int verbose = 0; |
|
static int weak_imports = 0; |
|
static int dsyms = 0; |
|
|
|
/* Characteristics of things */ |
|
static int calls_nonreturning_functions = 0; |
|
|
|
/* Sizes of things */ |
|
|
|
static long number_of_symbols = 0; |
|
static long number_undefined = 0; |
|
|
|
/* Names of things */ |
|
|
|
static const char *program_name = NULL; |
|
static const char *bfd_filename = NULL; |
|
static const char *out_filename = NULL; |
|
|
|
/* The symbol table. */ |
|
|
|
static asymbol **symbol_table = NULL; |
|
|
|
/* handle to included file */ |
|
|
|
static FILE *include_stream = NULL; |
|
static char token[1024]; |
|
|
|
static int counter; |
|
|
|
/*********************************************************************** |
|
* Private constant data |
|
***********************************************************************/ |
|
|
|
/* All of the strings defining the generated file are here: */ |
|
|
|
#include "arch/dyncall_skeleton.def" |
|
|
|
static const char dyn_symbol_prefix[] = "__dyn"; |
|
#define DYN_SYMBOL_PREFIX_LEN 5 |
|
|
|
/* This is the list of names of libc and libpthread functions that |
|
* do not return. These may require some special handling -- at a |
|
* minimum, they must tie up resources that can only be released |
|
* when the function returns. |
|
*/ |
|
|
|
static const char *const nonreturners[] = { |
|
"abort", /* Never returns */ |
|
"exit", /* Never returns */ |
|
"_exit", /* Never returns */ |
|
"longjmp", /* Never returns */ |
|
"_longjmp", /* Never returns */ |
|
"pthread_exit", /* Never returns */ |
|
"siglongjmp", /* Never returns */ |
|
NULL /* End of list */ |
|
}; |
|
|
|
/*********************************************************************** |
|
* Private Functions |
|
***********************************************************************/ |
|
|
|
/*********************************************************************** |
|
* show_usage |
|
***********************************************************************/ |
|
|
|
static void show_usage(void) |
|
{ |
|
fprintf(stderr, "Usage: %s [options] <bfd-filename>\n\n", program_name); |
|
fprintf(stderr, "Where options are one or more of the following. Note\n"); |
|
fprintf(stderr, "that a space is always required between the option and\n"); |
|
fprintf(stderr, "any following arguments.\n\n"); |
|
fprintf(stderr, " -d Use dynamic symbol table. [symtab]\n"); |
|
fprintf(stderr, " -f <cmd-filename>\n"); |
|
fprintf(stderr, " Take next commands from <cmd-filename> [cmd-line]\n"); |
|
fprintf(stderr, " -o <out-filename>\n"); |
|
fprintf(stderr, " Output to <out-filename> [stdout]\n"); |
|
fprintf(stderr, " -v Verbose output [no output]\n"); |
|
fprintf(stderr, " -w Import weakly declared functions, i.e., weakly\n"); |
|
fprintf(stderr, " declared functions are expected to be provided at\n"); |
|
fprintf(stderr, " load-time [not imported]\n"); |
|
fprintf(stderr, "\n"); |
|
exit(1); |
|
} |
|
|
|
/*********************************************************************** |
|
* get_symbols |
|
***********************************************************************/ |
|
|
|
static asymbol **get_symbols(bfd * abfd, long *num) |
|
{ |
|
long storage_needed; |
|
asymbol **symbol_table; |
|
long number_of_symbols; |
|
|
|
if (dsyms) |
|
storage_needed = bfd_get_dynamic_symtab_upper_bound(abfd); |
|
else |
|
storage_needed = bfd_get_symtab_upper_bound(abfd); |
|
|
|
if (storage_needed < 0) |
|
abort(); |
|
|
|
if (storage_needed == 0) |
|
return NULL; |
|
|
|
symbol_table = (asymbol **) malloc(storage_needed); |
|
|
|
if (dsyms) |
|
number_of_symbols = bfd_canonicalize_dynamic_symtab(abfd, symbol_table); |
|
else |
|
number_of_symbols = bfd_canonicalize_symtab(abfd, symbol_table); |
|
if (number_of_symbols < 0) |
|
abort(); |
|
|
|
*num = number_of_symbols; |
|
return symbol_table; |
|
} |
|
|
|
/*********************************************************************** |
|
* traverse_undefined_functions |
|
***********************************************************************/ |
|
|
|
static int traverse_undefined_functions(void *arg, symfunc_type fn) |
|
{ |
|
int i; |
|
|
|
for (i = 0; i < number_of_symbols; i++) |
|
{ |
|
/* Check if it is undefined and not an object. I have found that symbol |
|
* typing can be misleading: Imported functions are not marked as |
|
* BSF_FUNCTION; weakly defined objects are listed as undefined objects. |
|
* Conclusion: We will error if a object is truly undefined! */ |
|
|
|
if ((symbol_table[i]->value == 0) && |
|
(!IS_DEFINED(symbol_table[i])) && (!IS_OBJECT(symbol_table[i]))) |
|
{ |
|
/* Is is imported as a "weak" symbol? If so, we will process the |
|
* symbol only if we were requested to do so from the command line. */ |
|
|
|
if ((!IS_WEAK(symbol_table[i])) || (weak_imports > 0)) |
|
{ |
|
/* Yes, process the symbol */ |
|
|
|
if (fn(symbol_table[i], arg) != 0) |
|
{ |
|
/* If the function returns a non-zero value, then we |
|
* terminate the traversal and return a non-zero value also. */ |
|
|
|
return 1; |
|
} |
|
} |
|
} |
|
} |
|
|
|
/* Return 0 meaning that all undefined symbols were examined successfully. */ |
|
|
|
return 0; |
|
} |
|
|
|
/*********************************************************************** |
|
* put_string |
|
***********************************************************************/ |
|
|
|
static void put_string(int fd, const char *string) |
|
{ |
|
ssize_t bytes_available = strlen(string); |
|
ssize_t bytes_written = write(fd, string, bytes_available); |
|
if (bytes_written < 0) |
|
{ |
|
fprintf(stderr, |
|
"Failed to write %ld bytes of string to output, errno=%d\n", |
|
(long)bytes_available, errno); |
|
exit(5); |
|
} |
|
else if (bytes_written != bytes_available) |
|
{ |
|
fprintf(stderr, "Only wrote %ld of %ld bytes of string to output\n", |
|
(long)bytes_written, (long)bytes_available); |
|
exit(6); |
|
} |
|
} |
|
|
|
/*********************************************************************** |
|
* does_not_return_name/sym |
|
***********************************************************************/ |
|
|
|
static int does_not_return_name(const char *func_name) |
|
{ |
|
int i; |
|
|
|
/* Check every name in the list of (usually) non-returning function */ |
|
|
|
for (i = 0; nonreturners[i] != NULL; i++) |
|
{ |
|
/* Is this function name in the list */ |
|
|
|
if (strcmp(func_name, nonreturners[i]) == 0) |
|
{ |
|
/* Yes, return true now. */ |
|
|
|
return 1; |
|
} |
|
} |
|
|
|
/* Its not in the list, return false */ |
|
|
|
return 0; |
|
} |
|
|
|
static int does_not_return_sym(asymbol * sym, void *arg) |
|
{ |
|
const char *func_name = sym->name; |
|
if (func_name) |
|
return does_not_return_name(func_name); |
|
else |
|
return 0; |
|
} |
|
|
|
/*********************************************************************** |
|
* check_for_nonreturning_functions |
|
***********************************************************************/ |
|
|
|
static void check_for_nonreturning_functions(void) |
|
{ |
|
calls_nonreturning_functions = |
|
traverse_undefined_functions(NULL, does_not_return_sym); |
|
} |
|
|
|
/*********************************************************************** |
|
* count_undefined |
|
***********************************************************************/ |
|
|
|
static int count_undefined(asymbol * sym, void *arg) |
|
{ |
|
number_undefined++; |
|
return 0; |
|
} |
|
|
|
/*********************************************************************** |
|
* put_dynimport_decl |
|
***********************************************************************/ |
|
|
|
static int put_dynimport_decl(asymbol * sym, void *arg) |
|
{ |
|
char dynimport_decl[1024]; |
|
const char *func_name = sym->name; |
|
int fd = (int)arg; |
|
|
|
/* Put the declaration for the dynamic info structure */ |
|
if (func_name) |
|
{ |
|
sprintf(dynimport_decl, dynimport_decl_format, |
|
MKINFODECLARGS(func_name, counter)); |
|
put_string(fd, dynimport_decl); |
|
counter++; |
|
} |
|
return 0; |
|
} |
|
|
|
/*********************************************************************** |
|
* put_dynimport_array |
|
***********************************************************************/ |
|
|
|
static int put_dynimport_array(asymbol * sym, void *arg) |
|
{ |
|
char dynimport_array[1024]; |
|
const char *func_name = sym->name; |
|
int fd = (int)arg; |
|
|
|
/* Create the dynimport_array */ |
|
|
|
if (func_name) |
|
{ |
|
sprintf(dynimport_array, dynimport_array_format, |
|
MKINFOARGS(func_name, counter)); |
|
put_string(fd, dynimport_array); |
|
counter++; |
|
} |
|
return 0; |
|
} |
|
|
|
/*********************************************************************** |
|
* put_nxflat_import |
|
***********************************************************************/ |
|
|
|
static int put_nxflat_import(asymbol * sym, void *arg) |
|
{ |
|
char thunk[4096]; |
|
const char *func_name = sym->name; |
|
int fd = (int)arg; |
|
|
|
if (func_name) |
|
{ |
|
/* Create the thunk */ |
|
|
|
if (does_not_return_name(func_name) != 0) |
|
{ |
|
/* The special case for functions that may not return */ |
|
|
|
sprintf(thunk, nonreturning_dyncall_format, MKCALLARGS(func_name, counter)); |
|
} |
|
else |
|
{ |
|
/* The normal case */ |
|
|
|
sprintf(thunk, dyncall_format, MKCALLARGS(func_name, counter)); |
|
} |
|
|
|
put_string(fd, thunk); |
|
counter++; |
|
} |
|
return 0; |
|
} |
|
|
|
/*********************************************************************** |
|
* put_all_nxflat_import |
|
***********************************************************************/ |
|
|
|
static void put_all_nxflat_import(int fd) |
|
{ |
|
if (number_undefined > 0) |
|
{ |
|
/* Put all of the declarations for the dynimport structures together. */ |
|
|
|
put_string(fd, dynimport_decl_prologue); |
|
counter = 0; |
|
(void)traverse_undefined_functions((void *)fd, put_dynimport_decl); |
|
|
|
/* Put all of the dynimport structures together as an array */ |
|
|
|
put_string(fd, dynimport_array_prologue); |
|
counter = 0; |
|
(void)traverse_undefined_functions((void *)fd, put_dynimport_array); |
|
put_string(fd, dynimport_array_epilogue); |
|
|
|
/* Put all of the dyncall logic together */ |
|
|
|
put_string(fd, dyncall_decl_prologue); |
|
counter = 0; |
|
(void)traverse_undefined_functions((void *)fd, put_nxflat_import); |
|
} |
|
} |
|
|
|
/*********************************************************************** |
|
* put_import_name |
|
***********************************************************************/ |
|
|
|
static int put_import_name(asymbol * sym, void *arg) |
|
{ |
|
char import_name[512]; |
|
const char *func_name = sym->name; |
|
int fd = (int)arg; |
|
|
|
/* Create the import_name */ |
|
|
|
if (func_name) |
|
{ |
|
sprintf(import_name, import_name_strtab_format, |
|
MKIMPSTRTABARG(func_name, counter)); |
|
put_string(fd, import_name); |
|
counter++; |
|
} |
|
return 0; |
|
} |
|
|
|
/*********************************************************************** |
|
* put_import_name_strtab |
|
***********************************************************************/ |
|
|
|
static void inline put_import_name_strtab(int fd) |
|
{ |
|
if (number_undefined > 0) |
|
{ |
|
counter = 0; |
|
put_string(fd, import_name_strtab_prologue); |
|
(void)traverse_undefined_functions((void *)fd, put_import_name); |
|
} |
|
} |
|
|
|
/*********************************************************************** |
|
* put_file_epilogue |
|
***********************************************************************/ |
|
|
|
static void inline put_file_epilogue(int fd) |
|
{ |
|
put_string(fd, file_epilogue); |
|
} |
|
|
|
/*********************************************************************** |
|
* put_file_prologue |
|
***********************************************************************/ |
|
|
|
static void inline put_file_prologue(int fd) |
|
{ |
|
put_string(fd, file_prologue); |
|
|
|
if (number_undefined > 0) |
|
{ |
|
put_string(fd, import_prologue); |
|
} |
|
} |
|
|
|
/*********************************************************************** |
|
* get_file_token |
|
***********************************************************************/ |
|
|
|
#define ISSPACE(c) \ |
|
((c==' ')||(c=='\f')||(c=='\n')||(c=='\r')||(c=='\t')||(c=='\v')) |
|
|
|
#define ISTERMINATOR(c) (ISSPACE(c)||(c==EOF)) |
|
|
|
static int get_file_token(FILE * in_stream) |
|
{ |
|
int i; |
|
int c; |
|
|
|
/* Skip over leading whitespace */ |
|
|
|
do |
|
c = getc(in_stream); |
|
while ISSPACE |
|
(c); |
|
|
|
if (c == EOF) |
|
return EOF; |
|
|
|
/* Add the token to the buffer. Copy characters until the buffer is full, or |
|
* a terminator is encountered. */ |
|
|
|
for (i = 0; ((i < 1023) && !ISTERMINATOR(c)); i++, c = getc(in_stream)) |
|
{ |
|
token[i] = (char)c; |
|
} |
|
|
|
/* Handle the string truncation case. */ |
|
|
|
token[i] = '\0'; |
|
while (!ISTERMINATOR(c)) |
|
c = getc(in_stream); |
|
|
|
/* Return success. On next entry, we will get the next character after the |
|
* terminator. If the terminator was EOF, we should get EOF again. */ |
|
|
|
return 0; |
|
} |
|
|
|
/*********************************************************************** |
|
* get_token |
|
***********************************************************************/ |
|
|
|
static char *get_token(int *argno, int argc, char **argv) |
|
{ |
|
char *retval = NULL; |
|
|
|
if (include_stream) |
|
{ |
|
if (get_file_token(include_stream) == EOF) |
|
{ |
|
fclose(include_stream); |
|
include_stream = NULL; |
|
retval = get_token(argno, argc, argv); |
|
} |
|
else |
|
{ |
|
retval = strdup(token); |
|
} |
|
} |
|
else if (*argno >= argc) |
|
{ |
|
retval = NULL; |
|
} |
|
else |
|
{ |
|
retval = argv[*argno]; |
|
(*argno)++; |
|
} |
|
return retval; |
|
} |
|
|
|
/*********************************************************************** |
|
* get_arg |
|
***********************************************************************/ |
|
|
|
static char *get_arg(int *argno, int argc, char **argv) |
|
{ |
|
char *retval; |
|
|
|
/* Get the next argument */ |
|
|
|
retval = get_token(argno, argc, argv); |
|
if ((retval == NULL) || (retval[0] == '-')) |
|
{ |
|
fprintf(stderr, "Option requires an argument\n\n"); |
|
show_usage(); |
|
} |
|
return retval; |
|
} |
|
|
|
/*********************************************************************** |
|
* get_opt |
|
***********************************************************************/ |
|
|
|
static int get_opt(int *argno, int argc, char **argv) |
|
{ |
|
char *opt; |
|
int len; |
|
int retval = -1; |
|
|
|
/* Get the next argument */ |
|
|
|
opt = get_token(argno, argc, argv); |
|
if (opt != NULL) |
|
{ |
|
/* It must be of length 2 and start with a '-' */ |
|
|
|
len = strlen(opt); |
|
if ((len == 2) && (opt[0] == '-')) |
|
{ |
|
retval = (int)opt[1]; |
|
} |
|
else |
|
{ |
|
fprintf(stderr, "%s Unrecognized option\n\n", opt); |
|
show_usage(); |
|
} |
|
} |
|
return retval; |
|
} |
|
|
|
/*********************************************************************** |
|
* parse_args |
|
***********************************************************************/ |
|
|
|
static void parse_args(int argc, char **argv) |
|
{ |
|
int argno = 1; |
|
int opt; |
|
|
|
/* Save our name (for show_usage) */ |
|
|
|
program_name = argv[0]; |
|
|
|
if (argc < 2) |
|
{ |
|
fprintf(stderr, "ERROR: Missing required arguments\n\n"); |
|
show_usage(); |
|
} |
|
|
|
/* Get the name of the input BFD file. This is always the last thing in the |
|
* argument list. We decrement argc so that the parsing logic will not look |
|
* at it. */ |
|
|
|
bfd_filename = argv[argc - 1]; |
|
argc--; |
|
|
|
/* Get miscellaneous options from the command line. */ |
|
|
|
while ((opt = get_opt(&argno, argc, argv)) != -1) |
|
{ |
|
switch (opt) |
|
{ |
|
case 'd': |
|
dsyms++; |
|
break; |
|
|
|
case 'f': |
|
{ |
|
char *filename = get_arg(&argno, argc, argv); |
|
if (include_stream) |
|
{ |
|
fprintf(stderr, "Cannot use -f from within a cmd-file\n\n"); |
|
show_usage(); |
|
} |
|
else |
|
{ |
|
include_stream = fopen(filename, "r"); |
|
if (!include_stream) |
|
{ |
|
fprintf(stderr, "Could not open cmd-file %s\n\n", filename); |
|
show_usage(); |
|
} |
|
} |
|
} |
|
break; |
|
|
|
case 'o': |
|
out_filename = get_arg(&argno, argc, argv); |
|
break; |
|
|
|
case 'v': |
|
verbose++; |
|
break; |
|
|
|
case 'w': |
|
weak_imports++; |
|
break; |
|
|
|
default: |
|
fprintf(stderr, "%s Unknown option\n\n", argv[0]); |
|
show_usage(); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
/*********************************************************************** |
|
* Public Functions |
|
***********************************************************************/ |
|
|
|
/*********************************************************************** |
|
* main |
|
***********************************************************************/ |
|
|
|
int main(int argc, char **argv, char **envp) |
|
{ |
|
bfd *bf; |
|
int out_fd = 0; |
|
|
|
/* Get the input arguments */ |
|
|
|
parse_args(argc, argv); |
|
|
|
/* Make sure that we can option the BFD file */ |
|
|
|
dbg("Opening BFD file: %s\n", bfd_filename); |
|
if (!(bf = bfd_openr(bfd_filename, 0))) |
|
{ |
|
fprintf(stderr, "Failed to open BFD file: %s, errno=%d\n", |
|
bfd_filename, errno); |
|
exit(2); |
|
} |
|
|
|
dbg("Checking format\n"); |
|
if (bfd_check_format(bf, bfd_object) == 0) |
|
{ |
|
fprintf(stderr, "BFD file %s is not an object file\n", bfd_filename); |
|
exit(3); |
|
} |
|
|
|
dbg("Loading symbol table from BFD file %s\n", bfd_filename); |
|
symbol_table = get_symbols(bf, &number_of_symbols); |
|
|
|
/* Count the number of undefined function symbols */ |
|
|
|
(void)traverse_undefined_functions(NULL, count_undefined); |
|
|
|
/* Check if the module calls any non-returning functions (like exit). These |
|
* will require some additional setup. */ |
|
|
|
check_for_nonreturning_functions(); |
|
|
|
/* Make sure that we can open the output file if one is specified. If no |
|
* out_filename is specified, we'll use stdout. */ |
|
|
|
if (out_filename) |
|
{ |
|
out_fd = open(out_filename, O_WRONLY | O_CREAT | O_TRUNC, 0644); |
|
if (out_fd < 0) |
|
{ |
|
fprintf(stderr, "Failed to open output file: %s, errno=%d\n", |
|
out_filename, errno); |
|
exit(4); |
|
} |
|
} |
|
|
|
/* Output the thunk file in three pieces: 1. The constant file prologue 2. |
|
* Library path information (if any) 3. Library file name information (if |
|
* any) 4. Exported symbole information (if any) 5. Imported symbole |
|
* information (if any) 6. The constant file epilogue. */ |
|
|
|
put_file_prologue(out_fd); |
|
put_import_name_strtab(out_fd); |
|
put_all_nxflat_import(out_fd); |
|
put_file_epilogue(out_fd); |
|
|
|
if (out_fd > 0) |
|
close(out_fd); |
|
exit(0); |
|
}
|
|
|