diff options
Diffstat (limited to 'misc/buildroot/toolchain/nxflat/mknxflat.c')
-rw-r--r-- | misc/buildroot/toolchain/nxflat/mknxflat.c | 956 |
1 files changed, 956 insertions, 0 deletions
diff --git a/misc/buildroot/toolchain/nxflat/mknxflat.c b/misc/buildroot/toolchain/nxflat/mknxflat.c new file mode 100644 index 000000000..7f1987486 --- /dev/null +++ b/misc/buildroot/toolchain/nxflat/mknxflat.c @@ -0,0 +1,956 @@ +/*********************************************************************** + * 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/arm.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 +#define DEFAULT_MAX_THREADS 6 +#define MIN_THREADS 3 + +/*********************************************************************** + * 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; +static int enable_trace = 0; +static int max_threads = DEFAULT_MAX_THREADS; + +/* Characteristics of things */ +static int calls_nonreturning_functions = 0; +static int calls_forkers = 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 */ + "err", /* Never returns */ + "errx", /* Never returns */ + "execl", /* Usually doesn't return */ + "execle", /* Usually doesn't return */ + "execlp", /* Usually doesn't return */ + "execv", /* Usually doesn't return */ + "execv", /* Usually doesn't return */ + "execve", /* Usually doesn't return */ + "execvp", /* Usually doesn't return */ + "execvp", /* Usually doesn't return */ + "exit", /* Never returns */ + "_exit", /* Never returns */ + "_Exit", /* Never returns */ + "longjmp", /* Never returns */ + "_longjmp", /* Never returns */ + "pthread_exit", /* Never returns */ + "siglongjmp", /* Never returns */ + "__uClibc_main", /* Never returns */ + "__uClibc_start_main", /* Never returns */ + "verr", /* Never returns */ + "verrx", /* Never returns */ + NULL /* End of list */ +}; + +/* This is the list of names of libc functions that behave very + * strangely: They return twice. + */ + +static const char *const forkers[] = { + "clone", + "fork", + "setjmp", + "_setjmp", + "__sigsetjmp", + "vfork", + 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, " -p <max_threads>\n"); + fprintf(stderr, + " The maximum number of threads that can make simultaneous\n"); + fprintf(stderr, " make calls into a shared library [6]\n"); + fprintf(stderr, " -t Enable tracing of outbound shared library function\n"); + fprintf(stderr, " calls. [no tracing]\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); +} + +/*********************************************************************** + * is_forker_name/sym + ***********************************************************************/ + +static int is_forker_name(const char *func_name) +{ + int i; + + /* Check every name in the list of forkers */ + + for (i = 0; forkers[i] != NULL; i++) + { + /* Is this function name in the list */ + + if (strcmp(func_name, forkers[i]) == 0) + { + /* Yes, return true now. */ + + return 1; + } + } + + /* Its not in the list, return false */ + + return 0; +} + +static int is_forker_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_forkers + ***********************************************************************/ + +static void check_for_forkers(void) +{ + calls_forkers = traverse_undefined_functions(NULL, is_forker_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 if (is_forker_name(func_name) != 0) + { + /* The special case for functions that fork */ + + sprintf(thunk, dyncall_format2, 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) +{ + /* Is it necessary to generate any thunk logic? */ + + if (number_undefined > 0) + { + /* Yes, was tracing enabled? */ + + if (enable_trace > 0) + { + /* Yes, generate the function to output the trace */ + + put_string(fd, dyntrace_function); + } + + /* Output macros for managing the frame storage */ + + put_string(fd, frame_macros); + + if (calls_nonreturning_functions) + { + /* Output special macros for managing frames for noreturning + * functions */ + + put_string(fd, nonreturning_frame_macros); + } + + /* Output the beginning of the dynamic call function. */ + + put_string(fd, dyncall_prologue); + + /* If tracing is enabled, then insert a call to the the trace generation + * function. */ + + if (enable_trace > 0) + { + put_string(fd, dyntrace_call); + } + + /* Output the rest of the dynamic call function. */ + + put_string(fd, dyncall_epilogue); + + /* Does the module call any non-returning functions? */ + + if (calls_nonreturning_functions) + { + /* Yes, output the beginning of a special dynamic call function. */ + + put_string(fd, nonreturning_dyncall_prologue); + + /* If tracing is enabled, then insert a call to the the trace + * generation function. */ + + if (enable_trace > 0) + { + put_string(fd, dyntrace_call); + } + + /* Output the rest of the special dynamic call function. */ + + put_string(fd, nonreturning_dyncall_epilogue); + } + } + put_string(fd, file_epilogue); +} + +/*********************************************************************** + * put_file_prologue + ***********************************************************************/ + +static void inline put_file_prologue(int fd) +{ + put_string(fd, file_prologue); + if (enable_trace > 0) + { + put_string(fd, dyntrace_enable); + } + if (number_undefined > 0) + { + char frame_size[1024]; + + put_string(fd, import_prologue); + sprintf(frame_size, import_frame_size, max_threads); + put_string(fd, frame_size); + put_string(fd, dynamic_frames); + + if (calls_nonreturning_functions) + { + put_string(fd, nonreturning_dynamic_frame); + } + } +} + +/*********************************************************************** + * 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 'p': + { + int nthreads = atoi(get_arg(&argno, argc, argv)); + if (nthreads < MIN_THREADS) + { + fprintf(stderr, "Invalid number of threads (%d) using %d\n", + nthreads, max_threads); + } + else + { + max_threads = nthreads; + } + } + break; + + case 't': + enable_trace++; + 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(); + check_for_forkers(); + + /* 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); +} |