#ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include "mcpu-ld-options.h" static int parse_size( const char *s, __mpu_uint64_t *v ) { unsigned long long x; __mpu_uint64_t m = 1; char *e; if( !s || !*s || *s == '-' ) return( -1 ); errno = 0; x = strtoull( s, &e, 0 ); if( errno || e == s ) return( -1 ); if( *e == '\0' ) m = 1; else if( !strcmp( e, "K" ) || !strcmp( e, "KiB" ) ) m = __mpu_UINT64_C( 1024 ); else if( !strcmp( e, "M" ) || !strcmp( e, "MiB" ) ) m = __mpu_UINT64_C( 1024 ) * __mpu_UINT64_C( 1024 ); else if( !strcmp( e, "G" ) || !strcmp( e, "GiB" ) ) m = __mpu_UINT64_C( 1024 ) * __mpu_UINT64_C( 1024 ) * __mpu_UINT64_C( 1024 ); else return( -1 ); if( (__mpu_uint64_t)x != x || (__mpu_uint64_t)x > UINT64_MAX / m ) return( -1 ); *v = (__mpu_uint64_t)x * m; return( 0 ); } /* End of parse_size() */ static int append_input( mcpu_ld_options *options, char *name ) { char **p; size_t capacity; if( options->input_count == options->input_capacity ) { capacity = options->input_capacity ? options->input_capacity * 2U : 8U; if( capacity < options->input_capacity || capacity > SIZE_MAX / sizeof(*p) ) { errno = EOVERFLOW; return( -1 ); } p = (char **)realloc( options->inputs, capacity * sizeof(*p) ); if( p == NULL ) return( -1 ); options->inputs = p; options->input_capacity = capacity; } options->inputs[options->input_count++] = name; return( 0 ); } /* End of append_input() */ static int append_library( mcpu_ld_options *options, char *name ) { char **p; size_t capacity; if( options->library_count == options->library_capacity ) { capacity = options->library_capacity ? options->library_capacity * 2U : 8U; if( capacity < options->library_capacity || capacity > SIZE_MAX / sizeof(*p) ) { errno = EOVERFLOW; return( -1 ); } p = (char **)realloc( options->libraries, capacity * sizeof(*p) ); if( p == NULL ) return( -1 ); options->libraries = p; options->library_capacity = capacity; } options->libraries[options->library_count++] = name; return( 0 ); } /* End of append_library() */ void mcpu_ld_options_init( mcpu_ld_options *options, const char *progname ) { if( options == NULL ) return; memset( options, 0, sizeof(*options) ); mcpu_tool_options_init( &options->common, progname ); options->exec.stack_size = 1048576U; options->exec.have_heap = 1; options->exec.heap_size = 1048576U; options->exec.heap_limit = 1048576U; options->mode = CPU_LINK_STATIC_EXEC; } /* End of mcpu_ld_options_init() */ void mcpu_ld_options_free( mcpu_ld_options *options ) { if( options == NULL ) return; free( options->inputs ); options->inputs = NULL; options->input_count = 0; options->input_capacity = 0; free( options->libraries ); options->libraries = NULL; options->library_count = 0; options->library_capacity = 0; mcpu_tool_options_free( &options->common ); } /* End of mcpu_ld_options_free() */ void mcpu_ld_usage( const mcpu_ld_options *options ) { const char *name = options ? options->common.progname : "mcpu-ld"; fprintf( stderr, "usage: %s [link-options] -o output input.o ...\n\n" "Link LibMCPU MELF ET_REL object files.\n\n" "Output mode (at most one):\n" " -r - produce one partial-link MELF ET_REL object\n" " --shared - produce ABI-v3 MELF ET_DYN shared object\n" " --dynamic - produce ABI-v3 startup-dynamic MELF ET_EXEC\n" " (default without these options: ABI-v1 static ET_EXEC)\n\n" "MCPU system options:\n" " --no-config - do not read mcpu-ld.conf files\n" " --sys-root=PATH - use PATH as MCPU root and do not read config files\n" " --dump-config - print effective MCPU configuration and exit\n" " --print-search-dirs - print effective library search directories and exit\n\n" "Library dependency options:\n" " -L , -L - add explicit MCPU library search directory\n" " -l , -l - link against lib; ABI-v1 and -r search .a,\n" " ABI-v3 searches .so then .a in each directory\n" " --static - force every -l lookup to use lib.a\n" " without changing the selected output mode\n" " (selected .so files alone create DT_NEEDED)\n\n" "Shared-library identity:\n" " --soname=NAME - set mandatory ABI-v3 ET_DYN SONAME\n" " --soname NAME - NAME must be a logical name without '/'\n\n" "Link options:\n" " -o - write output to \n" " --stack-size= - set fixed STACK size; positive and %u-byte aligned\n" " (default for executables: 1048576)\n" " --heap-size= - set initial HEAP size (default: 1048576)\n" " --heap-limit= - set immutable maximum HEAP size (default: 1048576)\n" " --no-heap - omit PT_MCPU_HEAP from an executable\n" " -v, --verbose - print output-object summary\n" " -h, --help - show this help\n" " --version - show program version\n" " --dump-version - show version number only\n\n" " is bytes by default; K/KiB, M/MiB and G/GiB suffixes are accepted.\n" "K, M and G use binary multipliers (1024, 1024^2 and 1024^3).\n\n" "Partial -r output is MELF ET_REL: STACK/HEAP/SONAME and shared objects are\n" "not part of this mode; unresolved symbols and RELA records are preserved.\n" "Shared objects may contain only .text/.data/.bss allocated sections,\n" "must define --soname and must not use STACK/HEAP sizing options. ABI-v3\n" "dynamic executables retain preemptible GLOBAL/WEAK relocations; mcpu-run\n" "continues to accept explicit --ldpreload and legacy ABI-v2 files.\n", name, CPU_ABI_STACK_ALIGN ); } /* End of mcpu_ld_usage() */ int mcpu_ld_handle_options( mcpu_ld_options *options, int argc, char **argv ) { int i; if( options == NULL || argv == NULL ) { errno = EINVAL; return( -1 ); } for( i = 1; i < argc; ++i ) { int common; if( !strcmp( argv[i], "-h" ) || !strcmp( argv[i], "--help" ) ) { options->common.help = 1; return( 0 ); } common = mcpu_tool_parse_common_option( &options->common, argc, argv, &i ); if( common < 0 ) return( -1 ); if( common > 0 ) { if( options->common.version_action != MCPU_VERSION_NONE ) return( 0 ); continue; } if( !strcmp( argv[i], "-v" ) || !strcmp( argv[i], "--verbose" ) ) { options->verbose = 1; } else if( !strcmp( argv[i], "-r" ) ) { if( options->mode_seen++ ) { fprintf( stderr, "%s: -r/--shared/--dynamic are mutually exclusive\n", options->common.progname ); return( -1 ); } options->mode = CPU_LINK_RELOCATABLE; } else if( !strcmp( argv[i], "--shared" ) ) { if( options->mode_seen++ ) { fprintf( stderr, "%s: -r/--shared/--dynamic are mutually exclusive\n", options->common.progname ); return( -1 ); } options->mode = CPU_LINK_SHARED; } else if( !strcmp( argv[i], "--dynamic" ) ) { if( options->mode_seen++ ) { fprintf( stderr, "%s: -r/--shared/--dynamic are mutually exclusive\n", options->common.progname ); return( -1 ); } options->mode = CPU_LINK_DYNAMIC_EXEC; } else if( !strcmp( argv[i], "--static" ) ) { options->force_static = 1; } else if( !strncmp( argv[i], "--soname=", 9 ) ) { const char *name = argv[i] + 9; if( options->soname != NULL || name[0] == 0 || strchr( name, '/' ) != NULL ) { fprintf( stderr, "%s: invalid or duplicate --soname\n", options->common.progname ); return( -1 ); } options->soname = name; } else if( !strcmp( argv[i], "--soname" ) ) { const char *name; if( ++i >= argc ) { fprintf( stderr, "%s: argument missing after --soname\n", options->common.progname ); return( -1 ); } name = argv[i]; if( options->soname != NULL || name[0] == 0 || strchr( name, '/' ) != NULL ) { fprintf( stderr, "%s: invalid or duplicate --soname\n", options->common.progname ); return( -1 ); } options->soname = name; } else if( !strcmp( argv[i], "-L" ) ) { if( ++i >= argc || argv[i][0] == 0 || mcpu_tool_add_library_directory(&options->common, argv[i]) != 0 ) { fprintf( stderr, "%s: argument missing or invalid after -L\n", options->common.progname ); return( -1 ); } } else if( !strncmp( argv[i], "-L", 2 ) ) { if( argv[i][2] == 0 || mcpu_tool_add_library_directory(&options->common, argv[i] + 2) != 0 ) { fprintf( stderr, "%s: bad -L directory: %s\n", options->common.progname, argv[i] + 2 ); return( -1 ); } } else if( !strcmp( argv[i], "-l" ) ) { char *name; if( ++i >= argc ) { fprintf( stderr, "%s: argument missing after -l\n", options->common.progname ); return( -1 ); } name = argv[i]; if( name[0] == 0 || strchr(name, '/') != NULL || append_library(options, name) != 0 ) { fprintf( stderr, "%s: invalid library name after -l: %s\n", options->common.progname, name ); return( -1 ); } } else if( !strncmp( argv[i], "-l", 2 ) ) { char *name = argv[i] + 2; if( name[0] == 0 || strchr(name, '/') != NULL || append_library(options, name) != 0 ) { fprintf( stderr, "%s: invalid library option: %s\n", options->common.progname, argv[i] ); return( -1 ); } } else if( !strcmp( argv[i], "-o" ) ) { if( ++i >= argc || options->out_fname != NULL ) { mcpu_ld_usage( options ); return( -1 ); } options->out_fname = argv[i]; } else if( !strncmp( argv[i], "--stack-size=", 13 ) ) { options->size_option_seen = 1; if( parse_size(argv[i] + 13, &options->exec.stack_size) || !options->exec.stack_size || options->exec.stack_size % CPU_ABI_STACK_ALIGN ) { fprintf( stderr, "%s: stack size must be a positive multiple of %u\n", options->common.progname, CPU_ABI_STACK_ALIGN ); return( -1 ); } } else if( !strncmp( argv[i], "--heap-size=", 12 ) ) { options->size_option_seen = 1; if( parse_size(argv[i] + 12, &options->exec.heap_size) || options->exec.heap_size < CPU_HEAP_BEGIN ) { fprintf( stderr, "%s: heap size must be at least %u\n", options->common.progname, CPU_HEAP_BEGIN ); return( -1 ); } options->exec.have_heap = 1; } else if( !strncmp( argv[i], "--heap-limit=", 13 ) ) { options->size_option_seen = 1; if( parse_size(argv[i] + 13, &options->exec.heap_limit) || options->exec.heap_limit < CPU_HEAP_BEGIN ) { fprintf( stderr, "%s: bad heap limit\n", options->common.progname ); return( -1 ); } options->exec.have_heap = 1; } else if( !strcmp( argv[i], "--no-heap" ) ) { options->size_option_seen = 1; options->exec.have_heap = 0; options->exec.heap_size = options->exec.heap_limit = 0; } else if( argv[i][0] == '-' ) { fprintf( stderr, "%s: unknown option %s\n", options->common.progname, argv[i] ); return( -1 ); } else if( append_input(options, argv[i]) != 0 ) { perror( "realloc" ); return( -1 ); } } if( options->common.help || mcpu_tool_has_information_action(&options->common) ) return( 0 ); if( options->out_fname == NULL || options->input_count == 0 ) { mcpu_ld_usage( options ); return( -1 ); } if( options->mode == CPU_LINK_RELOCATABLE && options->size_option_seen ) { fprintf( stderr, "%s: STACK/HEAP options are invalid with -r\n", options->common.progname ); return( -1 ); } if( options->mode == CPU_LINK_SHARED && options->size_option_seen ) { fprintf( stderr, "%s: STACK/HEAP options are invalid with --shared\n", options->common.progname ); return( -1 ); } if( options->mode == CPU_LINK_SHARED && options->soname == NULL ) { fprintf( stderr, "%s: ABI-v3 --shared requires --soname=NAME\n", options->common.progname ); return( -1 ); } if( options->mode != CPU_LINK_SHARED && options->soname != NULL ) { fprintf( stderr, "%s: --soname is valid only with --shared\n", options->common.progname ); return( -1 ); } if( options->mode != CPU_LINK_SHARED && options->mode != CPU_LINK_RELOCATABLE && options->exec.have_heap && options->exec.heap_limit < options->exec.heap_size ) { fprintf( stderr, "%s: heap limit is smaller than heap size\n", options->common.progname ); return( -1 ); } return( 0 ); } /* End of mcpu_ld_handle_options() */