summaryrefslogtreecommitdiff
path: root/utils/mcpu-ld-options.c
diff options
context:
space:
mode:
Diffstat (limited to 'utils/mcpu-ld-options.c')
-rw-r--r--utils/mcpu-ld-options.c463
1 files changed, 463 insertions, 0 deletions
diff --git a/utils/mcpu-ld-options.c b/utils/mcpu-ld-options.c
new file mode 100644
index 0000000..6b64654
--- /dev/null
+++ b/utils/mcpu-ld-options.c
@@ -0,0 +1,463 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libmcpu.h>
+
+#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 <dir>, -L<dir> - add explicit MCPU library search directory\n"
+ " -l <name>, -l<name> - link against lib<name>; ABI-v1 and -r search .a,\n"
+ " ABI-v3 searches .so then .a in each directory\n"
+ " --static - force every -l<name> lookup to use lib<name>.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 <file> - write output to <file>\n"
+ " --stack-size=<size> - set fixed STACK size; positive and %u-byte aligned\n"
+ " (default for executables: 1048576)\n"
+ " --heap-size=<size> - set initial HEAP size (default: 1048576)\n"
+ " --heap-limit=<size> - 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"
+ "<size> 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() */