diff options
| author | kx <kx@radix-linux.su> | 2026-10-09 18:00:15 +0300 |
|---|---|---|
| committer | kx <kx@radix-linux.su> | 2026-10-09 18:00:15 +0300 |
| commit | 065da3509019b15dd0e678072e6be629e5330bef (patch) | |
| tree | 69c934c9e786ac048b4fbe8f4c4c5571661521f4 /utils/mcpu-objcopy.c | |
| parent | d130fe27fa753be02ed76fd9b277aa3c3d928eb9 (diff) | |
| download | libmcpu-0.4.0.tar.xz | |
Version 0.4.00.4.0
Diffstat (limited to 'utils/mcpu-objcopy.c')
| -rw-r--r-- | utils/mcpu-objcopy.c | 596 |
1 files changed, 596 insertions, 0 deletions
diff --git a/utils/mcpu-objcopy.c b/utils/mcpu-objcopy.c new file mode 100644 index 0000000..c977140 --- /dev/null +++ b/utils/mcpu-objcopy.c @@ -0,0 +1,596 @@ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <errno.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "mcpu-archive.h" +#include "mcpu-melf.h" +#include "mcpu-object-edit.h" +#include "mcpu-version.h" + + +typedef struct cli_action +{ + mcpu_object_edit_action edit; + char *name; + char *new_name; + unsigned char *data; +} cli_action; + + +typedef struct dump_action +{ + char *name; + char *path; +} dump_action; + + +static void +usage( FILE *fp, const char *name ) +{ + fprintf( fp, + "usage: %s [options] INPUT OUTPUT\n\n" + "Copy and safely edit LibMCPU MELF files and deterministic archives.\n" + "The output format is always MELF. Runtime-visible sections and frozen\n" + "MELF ABI3 execution metadata cannot be removed, renamed or updated.\n\n" + "Options:\n" + " -j, --only-section=NAME - copy only the named sections\n" + " -R, --remove-section=NAME - remove a safe non-runtime section\n" + " --dump-section=NAME=FILE - write section contents to FILE\n" + " --add-section=NAME=FILE - add non-ALLOC SHT_PROGBITS metadata\n" + " --update-section=NAME=FILE - replace safe non-ALLOC section contents\n" + " --rename-section=OLD=NEW - rename a safe non-ALLOC section\n" + " --help - show this help\n" + " --version - show program version\n" + " --dump-version - show version number only\n", + name ); + +} /* End of usage() */ + + +static int +append_string( char ***items, size_t *count, const char *s ) +{ + char **q; + char *p; + + if( !s || !*s ) + return( -1 ); + q = (char **)realloc( *items, (*count + 1) * sizeof( *q ) ); + if( !q ) + return( -1 ); + p = strdup( s ); + if( !p ) + return( -1 ); + *items = q; + (*items)[(*count)++] = p; + return( 0 ); + +} /* End of append_string() */ + + +static void +free_strings( char **items, size_t count ) +{ + size_t i; + + for( i = 0; i < count; ++i ) + free( items[i] ); + free( items ); + +} /* End of free_strings() */ + + +static int +split_pair( const char *arg, char **left, char **right ) +{ + const char *eq = strchr( arg, '=' ); + size_t n; + + if( !eq || eq == arg || !eq[1] ) + return( -1 ); + n = (size_t)( eq - arg ); + *left = (char *)malloc( n + 1 ); + if( !*left ) + return( -1 ); + memcpy( *left, arg, n ); + (*left)[n] = 0; + *right = strdup( eq + 1 ); + if( !*right ) + { + free( *left ); + *left = NULL; + return( -1 ); + } + return( 0 ); + +} /* End of split_pair() */ + + +static int +append_edit_action( cli_action **actions, size_t *count, + mcpu_object_edit_action_type type, + const char *spec, int needs_file, + char *err, size_t errsz ) +{ + cli_action *q; + cli_action *a; + char *left = NULL; + char *right = NULL; + + if( split_pair( spec, &left, &right ) ) + { + snprintf( err, errsz, "expected NAME=VALUE: %s", spec ); + return( -1 ); + } + q = (cli_action *)realloc( *actions, (*count + 1) * sizeof( *q ) ); + if( !q ) + { + free( left ); + free( right ); + snprintf( err, errsz, "out of memory" ); + return( -1 ); + } + *actions = q; + a = &q[*count]; + memset( a, 0, sizeof( *a ) ); + a->name = left; + a->edit.type = type; + a->edit.name = a->name; + + if( needs_file ) + { + if( cpu_melf_readall( right, &a->data, &a->edit.size, err, errsz ) ) + { + free( right ); + free( a->name ); + memset( a, 0, sizeof( *a ) ); + return( -1 ); + } + a->edit.data = a->data; + free( right ); + } + else + { + a->new_name = right; + a->edit.new_name = a->new_name; + } + ++*count; + return( 0 ); + +} /* End of append_edit_action() */ + + +static int +append_remove_action( cli_action **actions, size_t *count, + const char *name, char *err, size_t errsz ) +{ + cli_action *q; + cli_action *a; + + if( !name || !*name ) + { + snprintf( err, errsz, "empty section name" ); + return( -1 ); + } + q = (cli_action *)realloc( *actions, (*count + 1) * sizeof( *q ) ); + if( !q ) + { + snprintf( err, errsz, "out of memory" ); + return( -1 ); + } + *actions = q; + a = &q[*count]; + memset( a, 0, sizeof( *a ) ); + a->name = strdup( name ); + if( !a->name ) + { + snprintf( err, errsz, "out of memory" ); + return( -1 ); + } + a->edit.type = MCPU_OBJECT_EDIT_REMOVE; + a->edit.name = a->name; + ++*count; + return( 0 ); + +} /* End of append_remove_action() */ + + +static void +free_edit_actions( cli_action *actions, size_t count ) +{ + size_t i; + + for( i = 0; i < count; ++i ) + { + free( actions[i].name ); + free( actions[i].new_name ); + free( actions[i].data ); + } + free( actions ); + +} /* End of free_edit_actions() */ + + +static int +append_dump_action( dump_action **actions, size_t *count, + const char *spec, char *err, size_t errsz ) +{ + dump_action *q; + char *name = NULL; + char *path = NULL; + + if( split_pair( spec, &name, &path ) ) + { + snprintf( err, errsz, "expected NAME=FILE: %s", spec ); + return( -1 ); + } + q = (dump_action *)realloc( *actions, (*count + 1) * sizeof( *q ) ); + if( !q ) + { + free( name ); + free( path ); + snprintf( err, errsz, "out of memory" ); + return( -1 ); + } + *actions = q; + q[*count].name = name; + q[*count].path = path; + ++*count; + return( 0 ); + +} /* End of append_dump_action() */ + + +static void +free_dump_actions( dump_action *actions, size_t count ) +{ + size_t i; + + for( i = 0; i < count; ++i ) + { + free( actions[i].name ); + free( actions[i].path ); + } + free( actions ); + +} /* End of free_dump_actions() */ + + +static int +write_output( const char *input, const char *output, + const unsigned char *data, size_t size, + char *err, size_t errsz ) +{ + struct stat st; + + if( stat( input, &st ) ) + { + snprintf( err, errsz, "%s: %s", input, strerror( errno ) ); + return( -1 ); + } + if( cpu_melf_writeall( output, data, size, err, errsz ) ) + return( -1 ); + if( chmod( output, st.st_mode & 07777 ) ) + { + snprintf( err, errsz, "%s: %s", output, strerror( errno ) ); + unlink( output ); + return( -1 ); + } + return( 0 ); + +} /* End of write_output() */ + + +static int +perform_dumps( const unsigned char *data, size_t size, + const dump_action *actions, size_t count, + char *err, size_t errsz ) +{ + size_t i; + + for( i = 0; i < count; ++i ) + { + unsigned char *p = NULL; + size_t n = 0; + + if( mcpu_object_dump_section( data, size, actions[i].name, + &p, &n, err, errsz ) ) + return( -1 ); + if( cpu_melf_writeall( actions[i].path, p, n, err, errsz ) ) + { + free( p ); + return( -1 ); + } + free( p ); + } + return( 0 ); + +} /* End of perform_dumps() */ + + +static int +copy_archive( const char *input, const char *output, + const mcpu_object_edit_options *options, + char *err, size_t errsz ) +{ + mcpu_archive archive; + struct stat st; + size_t i; + int rc = -1; + + mcpu_archive_init( &archive ); + if( stat( input, &st ) ) + { + snprintf( err, errsz, "%s: %s", input, strerror( errno ) ); + return( -1 ); + } + if( mcpu_archive_read( input, &archive, err, errsz ) ) + return( -1 ); + + for( i = 0; i < archive.member_count; ++i ) + { + unsigned char *p = NULL; + size_t n = 0; + int changed = 0; + + if( mcpu_object_edit_data( archive.members[i].data, + archive.members[i].size, + options, &p, &n, &changed, err, errsz ) ) + goto out; + free( archive.members[i].data ); + archive.members[i].data = p; + archive.members[i].size = n; + (void)changed; + } + + if( mcpu_archive_write( output, &archive, archive.had_symbol_index, + err, errsz ) ) + goto out; + if( chmod( output, st.st_mode & 07777 ) ) + { + snprintf( err, errsz, "%s: %s", output, strerror( errno ) ); + unlink( output ); + goto out; + } + rc = 0; + +out: + mcpu_archive_free( &archive ); + return( rc ); + +} /* End of copy_archive() */ + + +int +main( int argc, char **argv ) +{ + const char *prog = argv[0]; + const char *input = NULL; + const char *output = NULL; + char **only = NULL; + size_t only_count = 0; + cli_action *actions = NULL; + size_t action_count = 0; + dump_action *dumps = NULL; + size_t dump_count = 0; + unsigned char *data = NULL; + size_t size = 0; + mcpu_object_edit_options options; + mcpu_object_edit_action *edit_actions = NULL; + char err[512]; + int i; + int rc = EXIT_FAILURE; + + err[0] = 0; + for( i = 1; i < argc; ++i ) + { + const char *arg = argv[i]; + + if( !strcmp( arg, "--help" ) ) + { + usage( stdout, prog ); + rc = EXIT_SUCCESS; + goto out; + } + if( !strcmp( arg, "--version" ) || + !strcmp( arg, "--dump-version" ) ) + { + mcpu_version_action version_action; + + mcpu_version_parse_option( arg, &version_action ); + rc = mcpu_version_print(prog, version_action) == 0 ? + EXIT_SUCCESS : EXIT_FAILURE; + goto out; + } + if( !strcmp( arg, "-j" ) ) + { + if( ++i >= argc || append_string( &only, &only_count, argv[i] ) ) + { + snprintf( err, sizeof( err ), "option -j requires a section name" ); + goto bad_usage; + } + continue; + } + if( !strncmp( arg, "-j", 2 ) && arg[2] ) + { + if( append_string( &only, &only_count, arg + 2 ) ) + { + snprintf( err, sizeof( err ), "out of memory or empty section name" ); + goto bad_usage; + } + continue; + } + if( !strncmp( arg, "--only-section=", 15 ) ) + { + if( append_string( &only, &only_count, arg + 15 ) ) + { + snprintf( err, sizeof( err ), "--only-section requires a section name" ); + goto bad_usage; + } + continue; + } + if( !strcmp( arg, "-R" ) ) + { + if( ++i >= argc || append_remove_action( &actions, &action_count, + argv[i], err, sizeof( err ) ) ) + goto bad_usage; + continue; + } + if( !strncmp( arg, "-R", 2 ) && arg[2] ) + { + if( append_remove_action( &actions, &action_count, arg + 2, + err, sizeof( err ) ) ) + goto bad_usage; + continue; + } + if( !strncmp( arg, "--remove-section=", 17 ) ) + { + if( append_remove_action( &actions, &action_count, arg + 17, + err, sizeof( err ) ) ) + goto bad_usage; + continue; + } + if( !strncmp( arg, "--dump-section=", 15 ) ) + { + if( append_dump_action( &dumps, &dump_count, arg + 15, + err, sizeof( err ) ) ) + goto bad_usage; + continue; + } + if( !strncmp( arg, "--add-section=", 14 ) ) + { + if( append_edit_action( &actions, &action_count, MCPU_OBJECT_EDIT_ADD, + arg + 14, 1, err, sizeof( err ) ) ) + goto bad_usage; + continue; + } + if( !strncmp( arg, "--update-section=", 17 ) ) + { + if( append_edit_action( &actions, &action_count, MCPU_OBJECT_EDIT_UPDATE, + arg + 17, 1, err, sizeof( err ) ) ) + goto bad_usage; + continue; + } + if( !strncmp( arg, "--rename-section=", 17 ) ) + { + if( append_edit_action( &actions, &action_count, MCPU_OBJECT_EDIT_RENAME, + arg + 17, 0, err, sizeof( err ) ) ) + goto bad_usage; + continue; + } + if( arg[0] == '-' && arg[1] ) + { + snprintf( err, sizeof( err ), "unknown option: %s", arg ); + goto bad_usage; + } + if( !input ) + input = arg; + else if( !output ) + output = arg; + else + { + snprintf( err, sizeof( err ), "exactly INPUT and OUTPUT are required" ); + goto bad_usage; + } + } + + if( !input || !output ) + { + snprintf( err, sizeof( err ), "both INPUT and OUTPUT are required" ); + goto bad_usage; + } + if( !strcmp( input, output ) ) + { + snprintf( err, sizeof( err ), "INPUT and OUTPUT must be different files" ); + goto fail; + } + + if( action_count ) + { + size_t k; + + edit_actions = (mcpu_object_edit_action *)calloc( action_count, + sizeof( *edit_actions ) ); + if( !edit_actions ) + { + snprintf( err, sizeof( err ), "out of memory" ); + goto fail; + } + for( k = 0; k < action_count; ++k ) + edit_actions[k] = actions[k].edit; + } + memset( &options, 0, sizeof( options ) ); + options.only_sections = (const char *const *)only; + options.only_count = only_count; + options.actions = edit_actions; + options.action_count = action_count; + + if( cpu_melf_readall( input, &data, &size, err, sizeof( err ) ) ) + goto fail; + + if( size >= 8 && !memcmp( data, "!<arch>\n", 8 ) ) + { + if( dump_count ) + { + snprintf( err, sizeof( err ), + "--dump-section is ambiguous for archive input" ); + goto fail; + } + if( !only_count && !action_count ) + { + if( write_output( input, output, data, size, err, sizeof( err ) ) ) + goto fail; + } + else if( copy_archive( input, output, &options, err, sizeof( err ) ) ) + goto fail; + } + else + { + unsigned char *result = NULL; + size_t result_size = 0; + int changed = 0; + + if( perform_dumps( data, size, dumps, dump_count, err, sizeof( err ) ) ) + goto fail; + if( mcpu_object_edit_data( data, size, &options, + &result, &result_size, &changed, + err, sizeof( err ) ) ) + goto fail; + if( write_output( input, output, result, result_size, + err, sizeof( err ) ) ) + { + free( result ); + goto fail; + } + free( result ); + (void)changed; + } + + rc = EXIT_SUCCESS; + goto out; + +bad_usage: + fprintf( stderr, "%s: %s\n", prog, err ); + usage( stderr, prog ); + goto out; + +fail: + fprintf( stderr, "%s: %s\n", prog, err ); + +out: + free( data ); + free( edit_actions ); + free_dump_actions( dumps, dump_count ); + free_edit_actions( actions, action_count ); + free_strings( only, only_count ); + return( rc ); + +} /* End of main() */ |
