summaryrefslogtreecommitdiff
path: root/utils/mcpu-ld.c
diff options
context:
space:
mode:
Diffstat (limited to 'utils/mcpu-ld.c')
-rw-r--r--utils/mcpu-ld.c1038
1 files changed, 1038 insertions, 0 deletions
diff --git a/utils/mcpu-ld.c b/utils/mcpu-ld.c
new file mode 100644
index 0000000..cf793ea
--- /dev/null
+++ b/utils/mcpu-ld.c
@@ -0,0 +1,1038 @@
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libmcpu.h>
+
+#include "mcpu-asm.h"
+#include "mcpu-object.h"
+#include "mcpu-link.h"
+#include "mcpu-exec.h"
+#include "mcpu-dyn.h"
+#include "mcpu-archive.h"
+#include "mcpu-ld-options.h"
+
+
+typedef struct mcpu_ld_library
+{
+ char *path;
+ cpu_dyn_library_info info;
+} mcpu_ld_library;
+
+
+typedef enum mcpu_hll_program_kind
+{
+ MCPU_HLL_NONE = 0,
+ MCPU_HLL_PROGRAM,
+ MCPU_HLL_MODEL
+} mcpu_hll_program_kind;
+
+
+static void
+free_libraries( mcpu_ld_library *libraries, size_t count )
+{
+ size_t i;
+
+ for( i = 0; i < count; ++i )
+ {
+ free( libraries[i].path );
+ cpu_dyn_library_free( &libraries[i].info );
+ }
+ free( libraries );
+
+} /* End of free_libraries() */
+
+
+static int
+find_soname( const mcpu_ld_library *libraries, size_t count,
+ const char *soname )
+{
+ size_t i;
+
+ for( i = 0; i < count; ++i )
+ if( strcmp(libraries[i].info.soname, soname) == 0 )
+ return( (int)i );
+
+ return( -1 );
+
+} /* End of find_soname() */
+
+
+static int
+append_link_object( cpu_asm_object **objects, size_t *count, size_t *capacity,
+ cpu_asm_object *object, char *err, size_t es )
+{
+ cpu_asm_object *p;
+ size_t c;
+
+ if( *count == *capacity )
+ {
+ c = *capacity ? *capacity * 2U : 8U;
+ if( c < *capacity || c > SIZE_MAX / sizeof( *p ) )
+ {
+ snprintf( err, es, "too many input objects" );
+ return( -1 );
+ }
+
+ p = (cpu_asm_object *)realloc( *objects, c * sizeof( *p ) );
+ if( p == NULL )
+ {
+ snprintf( err, es, "out of memory" );
+ return( -1 );
+ }
+
+ *objects = p;
+ *capacity = c;
+ }
+
+ (*objects)[*count] = *object;
+ memset( object, 0, sizeof( *object ) );
+ ++*count;
+ return( 0 );
+
+} /* End of append_link_object() */
+
+
+static int
+hll_symbol_is_strong_func( const cpu_asm_symbol *symbol )
+{
+ return( symbol != NULL && symbol->section != CPU_OBJ_SEC_UNDEF &&
+ symbol->binding == CPU_OBJ_BIND_GLOBAL &&
+ symbol->type == CPU_OBJ_TYPE_FUNC );
+
+} /* End of hll_symbol_is_strong_func() */
+
+
+static int
+hll_record_definition( const cpu_asm_object *objects, size_t object_index,
+ size_t symbol_index, size_t *definition_object,
+ const cpu_asm_symbol **definition_symbol,
+ char *err, size_t es )
+{
+ const cpu_asm_symbol *symbol = &objects[object_index].symbols[symbol_index];
+
+ if( *definition_symbol != NULL )
+ {
+ snprintf( err, es, "multiple HLL complete-program definitions of %s",
+ symbol->name );
+ return( -1 );
+ }
+
+ *definition_object = object_index;
+ *definition_symbol = symbol;
+ return( 0 );
+
+} /* End of hll_record_definition() */
+
+
+static int
+classify_hll_program( const cpu_asm_object *objects, size_t count,
+ mcpu_hll_program_kind *kind,
+ char *err, size_t es )
+{
+ const cpu_asm_symbol *main_symbol = NULL;
+ const cpu_asm_symbol *init_symbol = NULL;
+ const cpu_asm_symbol *life_symbol = NULL;
+ const cpu_asm_symbol *fini_symbol = NULL;
+ size_t main_object = 0, init_object = 0, life_object = 0, fini_object = 0;
+ size_t i, j;
+ int raw_lifecycle = 0;
+
+ *kind = MCPU_HLL_NONE;
+
+ for( i = 0; i < count; ++i )
+ for( j = 0; j < objects[i].symbol_count; ++j )
+ {
+ const cpu_asm_symbol *symbol = &objects[i].symbols[j];
+
+ if( symbol->section == CPU_OBJ_SEC_UNDEF )
+ continue;
+
+ if( !strncmp(symbol->name, "__mcpu_crt_", 11) )
+ {
+ snprintf( err, es, "input object defines reserved CRT symbol: %s",
+ symbol->name );
+ return( -1 );
+ }
+
+ if( !strcmp(symbol->name, "_start") ||
+ !strcmp(symbol->name, "_life") ||
+ !strcmp(symbol->name, "_fini") )
+ {
+ raw_lifecycle = 1;
+ continue;
+ }
+
+ if( symbol->binding == CPU_OBJ_BIND_LOCAL )
+ continue;
+
+ if( !strcmp(symbol->name, "main") )
+ {
+ if( hll_record_definition(objects, i, j, &main_object, &main_symbol,
+ err, es) )
+ return( -1 );
+ }
+ else if( !strcmp(symbol->name, "init") )
+ {
+ if( hll_record_definition(objects, i, j, &init_object, &init_symbol,
+ err, es) )
+ return( -1 );
+ }
+ else if( !strcmp(symbol->name, "life") )
+ {
+ if( hll_record_definition(objects, i, j, &life_object, &life_symbol,
+ err, es) )
+ return( -1 );
+ }
+ else if( !strcmp(symbol->name, "fini") )
+ {
+ if( hll_record_definition(objects, i, j, &fini_object, &fini_symbol,
+ err, es) )
+ return( -1 );
+ }
+ }
+
+ if( raw_lifecycle &&
+ (main_symbol != NULL || init_symbol != NULL || life_symbol != NULL) )
+ {
+ snprintf( err, es, "cannot mix low-level lifecycle with HLL complete-program definitions" );
+ return( -1 );
+ }
+
+ if( main_symbol != NULL )
+ {
+ if( init_symbol != NULL || life_symbol != NULL )
+ {
+ snprintf( err, es, "HLL PROGRAM main is incompatible with MODEL init/life" );
+ return( -1 );
+ }
+ if( !hll_symbol_is_strong_func(main_symbol) )
+ {
+ snprintf( err, es, "HLL PROGRAM main must be strong GLOBAL/FUNC" );
+ return( -1 );
+ }
+ if( fini_symbol == NULL || fini_object != main_object ||
+ !hll_symbol_is_strong_func(fini_symbol) )
+ {
+ snprintf( err, es,
+ "HLL PROGRAM requires strong GLOBAL/FUNC main and fini in one ET_REL" );
+ return( -1 );
+ }
+ *kind = MCPU_HLL_PROGRAM;
+ return( 0 );
+ }
+
+ if( init_symbol != NULL || life_symbol != NULL )
+ {
+ if( init_symbol == NULL || life_symbol == NULL )
+ {
+ snprintf( err, es, "HLL MODEL requires both init and life" );
+ return( -1 );
+ }
+ if( init_object != life_object || fini_symbol == NULL ||
+ fini_object != init_object ||
+ !hll_symbol_is_strong_func(init_symbol) ||
+ !hll_symbol_is_strong_func(life_symbol) ||
+ !hll_symbol_is_strong_func(fini_symbol) )
+ {
+ snprintf( err, es,
+ "HLL MODEL requires strong GLOBAL/FUNC init, life and fini in one ET_REL" );
+ return( -1 );
+ }
+ *kind = MCPU_HLL_MODEL;
+ }
+
+ return( 0 );
+
+} /* End of classify_hll_program() */
+
+
+static char *
+hll_crt_path( const mcpu_ld_options *options, const char *name )
+{
+ const char *root;
+ char *path;
+ size_t n;
+
+ root = options->common.runtime.installation_root;
+ if( root == NULL || *root == 0 || name == NULL || *name == 0 )
+ return( NULL );
+
+ if( strlen(root) > SIZE_MAX - strlen(name) - sizeof("/lib/mcpu/") )
+ return( NULL );
+
+ n = strlen(root) + strlen(name) + sizeof("/lib/mcpu/");
+ path = (char *)malloc( n );
+ if( path != NULL )
+ snprintf( path, n, "%s/lib/mcpu/%s", root, name );
+
+ return( path );
+
+} /* End of hll_crt_path() */
+
+
+static int
+read_hll_crt_object( const mcpu_ld_options *options, const char *name,
+ cpu_asm_object *object, char *err, size_t es )
+{
+ char detail[512];
+ char *path;
+
+ memset( object, 0, sizeof( *object ) );
+ path = hll_crt_path( options, name );
+ if( path == NULL )
+ {
+ snprintf( err, es, "cannot construct HLL CRT path for %s", name );
+ return( -1 );
+ }
+
+ if( cpu_object_read(path, object, detail, sizeof(detail)) )
+ {
+ snprintf( err, es, "cannot load HLL CRT object %.120s: %.300s",
+ path, detail );
+ free( path );
+ return( -1 );
+ }
+
+ free( path );
+ return( 0 );
+
+} /* End of read_hll_crt_object() */
+
+
+static int
+inject_hll_crt( const mcpu_ld_options *options, mcpu_hll_program_kind kind,
+ cpu_asm_object **objects, size_t *count, size_t *capacity,
+ char *err, size_t es )
+{
+ cpu_asm_object adapter, crti, crtn;
+ cpu_asm_object *combined;
+ const char *adapter_name;
+ size_t new_count;
+
+ memset( &adapter, 0, sizeof(adapter) );
+ memset( &crti, 0, sizeof(crti) );
+ memset( &crtn, 0, sizeof(crtn) );
+
+ adapter_name = kind == MCPU_HLL_PROGRAM ? "crt0.o" : "crt1.o";
+ if( read_hll_crt_object(options, adapter_name, &adapter, err, es) ||
+ read_hll_crt_object(options, "crti.o", &crti, err, es) ||
+ read_hll_crt_object(options, "crtn.o", &crtn, err, es) )
+ goto bad;
+
+ if( *count > SIZE_MAX - 3 ||
+ *count + 3 > SIZE_MAX / sizeof(*combined) )
+ {
+ snprintf( err, es, "too many input objects after HLL CRT insertion" );
+ goto bad;
+ }
+
+ new_count = *count + 3;
+ combined = (cpu_asm_object *)calloc( new_count, sizeof(*combined) );
+ if( combined == NULL )
+ {
+ snprintf( err, es, "out of memory" );
+ goto bad;
+ }
+
+ combined[0] = adapter;
+ combined[1] = crti;
+ if( *count != 0 )
+ memcpy( combined + 2, *objects, *count * sizeof(*combined) );
+ combined[new_count - 1] = crtn;
+
+ memset( &adapter, 0, sizeof(adapter) );
+ memset( &crti, 0, sizeof(crti) );
+ memset( &crtn, 0, sizeof(crtn) );
+ free( *objects );
+ *objects = combined;
+ *count = new_count;
+ *capacity = new_count;
+ return( 0 );
+
+bad:
+ cpu_asm_object_free( &adapter );
+ cpu_asm_object_free( &crti );
+ cpu_asm_object_free( &crtn );
+ return( -1 );
+
+} /* End of inject_hll_crt() */
+
+
+static int
+object_defines_symbol( const cpu_asm_object *objects, size_t count,
+ const char *name )
+{
+ size_t i, j;
+
+ for( i = 0; i < count; ++i )
+ for( j = 0; j < objects[i].symbol_count; ++j )
+ {
+ const cpu_asm_symbol *s = &objects[i].symbols[j];
+
+ if( s->section != CPU_OBJ_SEC_UNDEF &&
+ s->binding != CPU_OBJ_BIND_LOCAL &&
+ strcmp(s->name, name) == 0 )
+ return( 1 );
+ }
+
+ return( 0 );
+
+} /* End of object_defines_symbol() */
+
+
+static int
+library_defines_symbol( const mcpu_ld_library *libraries, size_t count,
+ const char *name )
+{
+ size_t i;
+
+ for( i = 0; i < count; ++i )
+ if( cpu_dyn_library_find_symbol(&libraries[i].info, name, NULL) == 0 )
+ return( 1 );
+
+ return( 0 );
+
+} /* End of library_defines_symbol() */
+
+
+static int
+name_in_list( const char *const *names, size_t count, const char *name )
+{
+ size_t i;
+
+ for( i = 0; i < count; ++i )
+ if( strcmp(names[i], name) == 0 )
+ return( 1 );
+
+ return( 0 );
+
+} /* End of name_in_list() */
+
+
+static int
+collect_strong_unresolved( const cpu_asm_object *objects, size_t object_count,
+ const mcpu_ld_library *libraries,
+ size_t library_count,
+ const char ***names_out, size_t *count_out,
+ char *err, size_t es )
+{
+ const char **names = NULL;
+ size_t count = 0;
+ size_t i, j;
+
+ *names_out = NULL;
+ *count_out = 0;
+
+ for( i = 0; i < object_count; ++i )
+ for( j = 0; j < objects[i].symbol_count; ++j )
+ {
+ const cpu_asm_symbol *s = &objects[i].symbols[j];
+ const char **p;
+
+ if( s->section != CPU_OBJ_SEC_UNDEF ||
+ s->binding != CPU_OBJ_BIND_GLOBAL )
+ continue;
+
+ if( object_defines_symbol(objects, object_count, s->name) ||
+ library_defines_symbol(libraries, library_count, s->name) ||
+ name_in_list(names, count, s->name) )
+ continue;
+
+ if( count == SIZE_MAX / sizeof( *p ) )
+ {
+ free( names );
+ snprintf( err, es, "too many unresolved symbols" );
+ return( -1 );
+ }
+ p = (const char **)realloc( names, (count + 1) * sizeof( *p ) );
+ if( p == NULL )
+ {
+ free( names );
+ snprintf( err, es, "out of memory" );
+ return( -1 );
+ }
+ names = p;
+ names[count++] = s->name;
+ }
+
+ *names_out = names;
+ *count_out = count;
+ return( 0 );
+
+} /* End of collect_strong_unresolved() */
+
+
+static int
+archive_member_is_needed( const mcpu_archive_member *member,
+ const char *const *unresolved,
+ size_t unresolved_count )
+{
+ size_t i, j;
+
+ for( i = 0; i < member->symbol_count; ++i )
+ for( j = 0; j < unresolved_count; ++j )
+ if( strcmp(member->symbols[i], unresolved[j]) == 0 )
+ return( 1 );
+
+ return( 0 );
+
+} /* End of archive_member_is_needed() */
+
+
+static int
+extract_archive_members( const char *path, const mcpu_archive *archive,
+ cpu_asm_object **objects, size_t *object_count,
+ size_t *object_capacity,
+ const mcpu_ld_library *libraries,
+ size_t library_count,
+ char *err, size_t es )
+{
+ unsigned char *selected = NULL;
+ int changed;
+ size_t i;
+
+ if( archive->member_count != 0 )
+ {
+ selected = (unsigned char *)calloc( archive->member_count, 1 );
+ if( selected == NULL )
+ {
+ snprintf( err, es, "out of memory" );
+ return( -1 );
+ }
+ }
+
+ do
+ {
+ const char **unresolved = NULL;
+ size_t unresolved_count = 0;
+
+ changed = 0;
+ if( collect_strong_unresolved(*objects, *object_count,
+ libraries, library_count,
+ &unresolved, &unresolved_count,
+ err, es) )
+ {
+ free( selected );
+ return( -1 );
+ }
+
+ for( i = 0; i < archive->member_count && unresolved_count != 0; ++i )
+ {
+ cpu_asm_object object;
+ char detail[512];
+
+ if( selected[i] ||
+ !archive_member_is_needed(&archive->members[i],
+ unresolved, unresolved_count) )
+ continue;
+
+ memset( &object, 0, sizeof( object ) );
+ if( cpu_object_read_data(archive->members[i].data,
+ archive->members[i].size,
+ &object, detail, sizeof(detail)) )
+ {
+ snprintf( err, es, "%.160s(%.120s): %.180s",
+ path, archive->members[i].name, detail );
+ free( unresolved );
+ free( selected );
+ return( -1 );
+ }
+
+ if( append_link_object(objects, object_count, object_capacity,
+ &object, err, es) )
+ {
+ cpu_asm_object_free( &object );
+ free( unresolved );
+ free( selected );
+ return( -1 );
+ }
+
+ selected[i] = 1;
+ changed = 1;
+
+ free( unresolved );
+ unresolved = NULL;
+ unresolved_count = 0;
+ if( collect_strong_unresolved(*objects, *object_count,
+ libraries, library_count,
+ &unresolved, &unresolved_count,
+ err, es) )
+ {
+ free( selected );
+ return( -1 );
+ }
+ }
+
+ free( unresolved );
+ }
+ while( changed );
+
+ free( selected );
+ return( 0 );
+
+} /* End of extract_archive_members() */
+
+
+static char *
+library_filename( const char *name, const char *suffix )
+{
+ size_t n;
+ char *result;
+
+ if( strlen(name) > SIZE_MAX - strlen(suffix) - sizeof("lib") )
+ return( NULL );
+
+ n = strlen(name) + strlen(suffix) + sizeof("lib");
+ result = (char *)malloc( n );
+ if( result != NULL )
+ snprintf( result, n, "lib%s%s", name, suffix );
+
+ return( result );
+
+} /* End of library_filename() */
+
+
+static int
+process_libraries( const mcpu_ld_options *options,
+ cpu_asm_object **objects, size_t *object_count,
+ size_t *object_capacity,
+ mcpu_ld_library **libraries_out, size_t *count_out,
+ char *err, size_t es )
+{
+ mcpu_ld_library *libraries = NULL;
+ size_t count = 0, i;
+
+ *libraries_out = NULL;
+ *count_out = 0;
+
+ if( options->library_count == 0 )
+ return( 0 );
+
+ libraries = (mcpu_ld_library *)calloc( options->library_count,
+ sizeof(*libraries) );
+ if( libraries == NULL )
+ {
+ snprintf( err, es, "out of memory" );
+ return( -1 );
+ }
+
+ for( i = 0; i < options->library_count; ++i )
+ {
+ const char *name = options->libraries[i];
+ char *shared_name = NULL;
+ char *archive_name = NULL;
+ const char *alternatives[2];
+ size_t alternative_count = 0;
+ size_t selected_index = 0;
+ char *path = NULL;
+
+ if( !options->force_static &&
+ (options->mode == CPU_LINK_DYNAMIC_EXEC ||
+ options->mode == CPU_LINK_SHARED) )
+ {
+ shared_name = library_filename( name, ".so" );
+ if( shared_name == NULL )
+ {
+ snprintf( err, es, "library name is too long: %s", name );
+ goto bad;
+ }
+ alternatives[alternative_count++] = shared_name;
+ }
+
+ archive_name = library_filename( name, ".a" );
+ if( archive_name == NULL )
+ {
+ free( shared_name );
+ snprintf( err, es, "library name is too long: %s", name );
+ goto bad;
+ }
+ alternatives[alternative_count++] = archive_name;
+
+ if( mcpu_tool_resolve_library_alternatives(&options->common,
+ alternatives, alternative_count, &path, &selected_index) != 0 )
+ {
+ if( options->force_static ||
+ options->mode == CPU_LINK_STATIC_EXEC ||
+ options->mode == CPU_LINK_RELOCATABLE )
+ snprintf( err, es, "cannot find -l%s (%s): %s",
+ name, archive_name, strerror(errno) );
+ else
+ snprintf( err, es, "cannot find -l%s (%s or %s): %s",
+ name, shared_name, archive_name, strerror(errno) );
+ free( shared_name );
+ free( archive_name );
+ goto bad;
+ }
+
+ if( !options->force_static &&
+ (options->mode == CPU_LINK_DYNAMIC_EXEC ||
+ options->mode == CPU_LINK_SHARED) && selected_index == 0 )
+ {
+ cpu_dyn_library_info info;
+
+ memset( &info, 0, sizeof(info) );
+ if( cpu_dyn_library_read(path, &info, err, es) != 0 )
+ {
+ free( path );
+ free( shared_name );
+ free( archive_name );
+ goto bad;
+ }
+
+ if( find_soname(libraries, count, info.soname) >= 0 )
+ {
+ free( path );
+ cpu_dyn_library_free( &info );
+ }
+ else
+ {
+ libraries[count].path = path;
+ libraries[count].info = info;
+ ++count;
+ }
+ }
+ else
+ {
+ mcpu_archive archive;
+
+ mcpu_archive_init( &archive );
+ if( mcpu_archive_read(path, &archive, err, es) != 0 )
+ {
+ free( path );
+ free( shared_name );
+ free( archive_name );
+ goto bad;
+ }
+
+ if( extract_archive_members(path, &archive,
+ objects, object_count, object_capacity,
+ libraries, count, err, es) )
+ {
+ mcpu_archive_free( &archive );
+ free( path );
+ free( shared_name );
+ free( archive_name );
+ goto bad;
+ }
+
+ mcpu_archive_free( &archive );
+ free( path );
+ }
+
+ free( shared_name );
+ free( archive_name );
+ }
+
+ *libraries_out = libraries;
+ *count_out = count;
+ return( 0 );
+
+bad:
+ free_libraries( libraries, count );
+ return( -1 );
+
+} /* End of process_libraries() */
+
+
+static int
+validate_executable_imports( const cpu_asm_image *image,
+ const mcpu_ld_library *libraries, size_t count,
+ char *err, size_t es )
+{
+ size_t i, j;
+
+ for( i = 0; i < image->dyn_symbol_count; ++i )
+ {
+ const cpu_dyn_symbol *need = &image->dyn_symbols[i];
+ int found = 0;
+
+ if( need->section != CPU_OBJ_SEC_UNDEF ||
+ need->binding == CPU_OBJ_BIND_WEAK )
+ continue;
+
+ for( j = 0; j < count; ++j )
+ {
+ const cpu_dyn_library_symbol *have = NULL;
+
+ if( cpu_dyn_library_find_symbol(&libraries[j].info, need->name,
+ &have) == 0 )
+ {
+ if( need->type != CPU_OBJ_TYPE_NOTYPE &&
+ have->type != CPU_OBJ_TYPE_NOTYPE &&
+ need->type != have->type )
+ {
+ snprintf( err, es,
+ "symbol type mismatch for %s in %s",
+ need->name, libraries[j].path );
+ return( -1 );
+ }
+ found = 1;
+ break;
+ }
+ }
+
+ if( !found )
+ {
+ snprintf( err, es, "undefined symbol not provided by linked libraries: %s",
+ need->name );
+ return( -1 );
+ }
+ }
+
+ return( 0 );
+
+} /* End of validate_executable_imports() */
+
+
+static int
+mcpu_ld_link( const mcpu_ld_options *options )
+{
+ cpu_asm_object *objs = NULL;
+ cpu_asm_image im;
+ mcpu_ld_library *libraries = NULL;
+ size_t object_count;
+ size_t object_capacity;
+ size_t library_count = 0;
+ mcpu_hll_program_kind hll_kind = MCPU_HLL_NONE;
+ char err[512];
+ size_t i;
+ int rc = 1;
+
+ if( options == NULL )
+ return( 1 );
+
+ object_count = options->input_count;
+ object_capacity = options->input_count;
+ objs = (cpu_asm_object *)calloc( object_capacity, sizeof(*objs) );
+ if( objs == NULL )
+ {
+ perror( "calloc" );
+ return( 1 );
+ }
+
+ __mpu_init();
+ memset( &im, 0, sizeof(im) );
+
+ for( i = 0; i < options->input_count; ++i )
+ {
+ if( cpu_object_read(options->inputs[i], &objs[i], err, sizeof(err)) )
+ {
+ fprintf( stderr, "%s: %s\n", options->common.progname, err );
+ goto cleanup_mpu;
+ }
+ }
+
+ if( options->mode == CPU_LINK_STATIC_EXEC ||
+ options->mode == CPU_LINK_DYNAMIC_EXEC )
+ {
+ if( classify_hll_program(objs, object_count, &hll_kind,
+ err, sizeof(err)) )
+ {
+ fprintf( stderr, "%s: %s\n", options->common.progname, err );
+ goto cleanup_mpu;
+ }
+
+ if( hll_kind != MCPU_HLL_NONE &&
+ inject_hll_crt(options, hll_kind, &objs, &object_count,
+ &object_capacity, err, sizeof(err)) )
+ {
+ fprintf( stderr, "%s: %s\n", options->common.progname, err );
+ goto cleanup_mpu;
+ }
+ }
+
+ if( process_libraries(options, &objs, &object_count, &object_capacity,
+ &libraries, &library_count,
+ err, sizeof(err)) != 0 )
+ {
+ fprintf( stderr, "%s: %s\n", options->common.progname, err );
+ goto cleanup_mpu;
+ }
+
+ if( options->mode == CPU_LINK_RELOCATABLE )
+ {
+ cpu_asm_object merged;
+
+ memset( &merged, 0, sizeof(merged) );
+ if( cpu_link_relocatable(objs, object_count, &merged, err, sizeof(err)) )
+ {
+ fprintf( stderr, "%s: %s\n", options->common.progname, err );
+ goto cleanup_libraries;
+ }
+
+ if( cpu_object_write(options->out_fname, &merged, err, sizeof(err)) )
+ {
+ fprintf( stderr, "%s: %s\n", options->common.progname, err );
+ cpu_asm_object_free( &merged );
+ goto cleanup_libraries;
+ }
+
+ if( options->verbose )
+ printf( "MELF ET_REL partial link: %zu symbols, %zu relocations -> %s\n",
+ merged.symbol_count, merged.reloc_count, options->out_fname );
+ cpu_asm_object_free( &merged );
+ rc = 0;
+ goto cleanup_libraries;
+ }
+
+ if( cpu_link_objects_mode(objs, object_count, &im,
+ options->mode, err, sizeof(err)) )
+ {
+ fprintf( stderr, "%s: %s\n", options->common.progname, err );
+ goto cleanup_libraries;
+ }
+
+ if( options->mode == CPU_LINK_DYNAMIC_EXEC &&
+ options->library_count != 0 &&
+ validate_executable_imports(&im, libraries, library_count,
+ err, sizeof(err)) != 0 )
+ {
+ fprintf( stderr, "%s: %s\n", options->common.progname, err );
+ goto cleanup_image;
+ }
+
+ if( options->mode == CPU_LINK_STATIC_EXEC )
+ {
+ if( cpu_exec_write(options->out_fname, &im, &options->exec,
+ err, sizeof(err)) )
+ {
+ fprintf( stderr, "%s: %s\n", options->common.progname, err );
+ goto cleanup_image;
+ }
+
+ if( options->verbose )
+ {
+ printf( "MELF ABI1 ET_EXEC: PROGRAM=%zu DATA=%zu STACK=%llu",
+ im.code_size, im.data_size,
+ (unsigned long long)options->exec.stack_size );
+ if( options->exec.have_heap )
+ printf( " HEAP=%llu/%llu",
+ (unsigned long long)options->exec.heap_size,
+ (unsigned long long)options->exec.heap_limit );
+ printf( " -> %s\n", options->out_fname );
+ }
+ }
+ else
+ {
+ cpu_dyn_metadata metadata;
+ const char **needed = NULL;
+
+ memset( &metadata, 0, sizeof( metadata ) );
+ metadata.soname = options->soname;
+ if( library_count != 0 )
+ {
+ needed = (const char **)calloc( library_count, sizeof(*needed) );
+ if( needed == NULL )
+ {
+ fprintf( stderr, "%s: out of memory\n", options->common.progname );
+ goto cleanup_image;
+ }
+ for( i = 0; i < library_count; ++i )
+ needed[i] = libraries[i].info.soname;
+ metadata.needed = needed;
+ metadata.needed_count = library_count;
+ }
+
+ if( cpu_dyn_write_abi3(options->out_fname, &im,
+ options->mode == CPU_LINK_SHARED ? NULL : &options->exec,
+ options->mode == CPU_LINK_SHARED,
+ &metadata, err, sizeof(err)) )
+ {
+ free( needed );
+ fprintf( stderr, "%s: %s\n", options->common.progname, err );
+ goto cleanup_image;
+ }
+ free( needed );
+
+ if( options->verbose )
+ {
+ if( options->mode == CPU_LINK_SHARED )
+ printf( "MELF ABI3 ET_DYN: PROGRAM=%zu DATA=%zu DYNSYM=%zu RELA=%zu NEEDED=%zu -> %s\n",
+ im.code_size, im.data_size, im.dyn_symbol_count,
+ im.dyn_reloc_count, library_count, options->out_fname );
+ else
+ {
+ printf( "MELF ABI3 ET_EXEC: PROGRAM=%zu DATA=%zu DYNSYM=%zu RELA=%zu NEEDED=%zu STACK=%llu",
+ im.code_size, im.data_size, im.dyn_symbol_count,
+ im.dyn_reloc_count, library_count,
+ (unsigned long long)options->exec.stack_size );
+ if( options->exec.have_heap )
+ printf( " HEAP=%llu/%llu",
+ (unsigned long long)options->exec.heap_size,
+ (unsigned long long)options->exec.heap_limit );
+ printf( " -> %s\n", options->out_fname );
+ }
+ }
+ }
+
+ rc = 0;
+
+cleanup_image:
+ cpu_asm_image_free( &im );
+
+cleanup_libraries:
+ free_libraries( libraries, library_count );
+
+cleanup_mpu:
+ for( i = 0; i < object_count; ++i )
+ cpu_asm_object_free( &objs[i] );
+ free( objs );
+ __mpu_free_context();
+
+ return( rc );
+
+} /* End of mcpu_ld_link() */
+
+
+int
+main( int argc, char **argv )
+{
+ mcpu_ld_options options;
+ int rc = 1;
+
+ mcpu_ld_options_init( &options, argv[0] );
+
+ if( mcpu_ld_handle_options(&options, argc, argv) != 0 )
+ goto done;
+
+ if( options.common.help )
+ {
+ mcpu_ld_usage( &options );
+ rc = 0;
+ goto done;
+ }
+
+ if( options.common.version_action != MCPU_VERSION_NONE )
+ {
+ if( mcpu_version_print(options.common.progname,
+ options.common.version_action) != 0 )
+ goto done;
+ rc = 0;
+ goto done;
+ }
+
+ if( mcpu_tool_options_prepare(&options.common, argv[0]) != 0 )
+ goto done;
+
+ if( mcpu_tool_has_information_action(&options.common) )
+ {
+ if( options.common.dump_config &&
+ mcpu_tool_dump_config(&options.common, stdout) != 0 )
+ goto done;
+ if( options.common.print_search_dirs &&
+ mcpu_tool_print_search_dirs(&options.common, stdout) != 0 )
+ goto done;
+ rc = 0;
+ goto done;
+ }
+
+ rc = mcpu_ld_link( &options );
+
+done:
+ mcpu_ld_options_free( &options );
+ return( rc );
+
+} /* End of main() */