9
0
Fork 0
nuttx-bb/misc/buildroot/toolchain/nxflat/mknxflat.c

957 lines
27 KiB
C

/***********************************************************************
* 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);
}