diff options
Diffstat (limited to 'utils/mcpu-strip.c')
| -rw-r--r-- | utils/mcpu-strip.c | 332 |
1 files changed, 332 insertions, 0 deletions
diff --git a/utils/mcpu-strip.c b/utils/mcpu-strip.c new file mode 100644 index 0000000..14f4b08 --- /dev/null +++ b/utils/mcpu-strip.c @@ -0,0 +1,332 @@ +#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" + + +static void +usage( FILE *fp, const char *name ) +{ + fprintf( fp, + "usage: %s [options] MELF-file\n\n" + "Strip non-runtime symbol metadata from LibMCPU MELF files and archives.\n\n" + "The current safe policy removes ordinary SHT_SYMTAB/SHT_STRTAB metadata\n" + "from ET_EXEC/ET_DYN only when no retained section depends on it. ET_REL\n" + "symbol tables, dynamic-linking tables, relocations and MCPU metadata are\n" + "always preserved.\n\n" + "Options:\n" + " -o, --output=FILE - write result to FILE instead of replacing input\n" + " --help - show this help\n" + " --version - show program version\n" + " --dump-version - show version number only\n", + name ); + +} /* End of usage() */ + + +static int +make_temp_name( const char *path, char **name, char *err, size_t errsz ) +{ + size_t n = strlen( path ); + char *p; + + if( n > SIZE_MAX - 32 ) + { + snprintf( err, errsz, "output path is too long" ); + return( -1 ); + } + p = (char *)malloc( n + 32 ); + if( !p ) + { + snprintf( err, errsz, "out of memory" ); + return( -1 ); + } + snprintf( p, n + 32, "%s.mcpu-strip-XXXXXX", path ); + *name = p; + return( 0 ); + +} /* End of make_temp_name() */ + + +static int +commit_bytes( const char *input, const char *output, + const unsigned char *data, size_t size, + char *err, size_t errsz ) +{ + struct stat st; + char *tmp = NULL; + int fd = -1; + int rc = -1; + + if( stat( input, &st ) ) + { + snprintf( err, errsz, "%s: %s", input, strerror( errno ) ); + return( -1 ); + } + if( make_temp_name( output, &tmp, err, errsz ) ) + return( -1 ); + + fd = mkstemp( tmp ); + if( fd < 0 ) + { + snprintf( err, errsz, "%s: %s", tmp, strerror( errno ) ); + goto out; + } + if( close( fd ) ) + { + fd = -1; + snprintf( err, errsz, "%s: %s", tmp, strerror( errno ) ); + goto out; + } + fd = -1; + + if( cpu_melf_writeall( tmp, data, size, err, errsz ) ) + goto out; + if( chmod( tmp, st.st_mode & 07777 ) ) + { + snprintf( err, errsz, "%s: %s", tmp, strerror( errno ) ); + goto out; + } + if( rename( tmp, output ) ) + { + snprintf( err, errsz, "%s: %s", output, strerror( errno ) ); + goto out; + } + rc = 0; + +out: + if( fd >= 0 ) + close( fd ); + if( rc && tmp ) + unlink( tmp ); + free( tmp ); + return( rc ); + +} /* End of commit_bytes() */ + + +static int +commit_archive( const char *input, const char *output, + const mcpu_archive *archive, int index, + char *err, size_t errsz ) +{ + struct stat st; + char *tmp = NULL; + int fd = -1; + int rc = -1; + + if( stat( input, &st ) ) + { + snprintf( err, errsz, "%s: %s", input, strerror( errno ) ); + return( -1 ); + } + if( make_temp_name( output, &tmp, err, errsz ) ) + return( -1 ); + + fd = mkstemp( tmp ); + if( fd < 0 ) + { + snprintf( err, errsz, "%s: %s", tmp, strerror( errno ) ); + goto out; + } + if( close( fd ) ) + { + fd = -1; + snprintf( err, errsz, "%s: %s", tmp, strerror( errno ) ); + goto out; + } + fd = -1; + + if( mcpu_archive_write( tmp, archive, index, err, errsz ) ) + goto out; + if( chmod( tmp, st.st_mode & 07777 ) ) + { + snprintf( err, errsz, "%s: %s", tmp, strerror( errno ) ); + goto out; + } + if( rename( tmp, output ) ) + { + snprintf( err, errsz, "%s: %s", output, strerror( errno ) ); + goto out; + } + rc = 0; + +out: + if( fd >= 0 ) + close( fd ); + if( rc && tmp ) + unlink( tmp ); + free( tmp ); + return( rc ); + +} /* End of commit_archive() */ + + +static int +strip_archive( const char *input, const char *output, + const unsigned char *original, size_t original_size, + char *err, size_t errsz ) +{ + mcpu_archive archive; + size_t i; + int any = 0; + int rc = -1; + + mcpu_archive_init( &archive ); + 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_strip_data( archive.members[i].data, + archive.members[i].size, + &p, &n, &changed, err, errsz ) ) + goto out; + if( changed ) + { + free( archive.members[i].data ); + archive.members[i].data = p; + archive.members[i].size = n; + any = 1; + } + else + free( p ); + } + + if( !any ) + rc = commit_bytes( input, output, original, original_size, err, errsz ); + else + rc = commit_archive( input, output, &archive, archive.had_symbol_index, + err, errsz ); + +out: + mcpu_archive_free( &archive ); + return( rc ); + +} /* End of strip_archive() */ + + +int +main( int argc, char **argv ) +{ + const char *prog = argv[0]; + const char *input = NULL; + const char *output = NULL; + unsigned char *data = NULL; + size_t size = 0; + int i; + char err[512]; + int rc = EXIT_FAILURE; + + for( i = 1; i < argc; ++i ) + { + const char *arg = argv[i]; + + if( !strcmp( arg, "--help" ) ) + { + usage( stdout, prog ); + return( EXIT_SUCCESS ); + } + if( !strcmp( arg, "--version" ) || + !strcmp( arg, "--dump-version" ) ) + { + mcpu_version_action version_action; + + mcpu_version_parse_option( arg, &version_action ); + return( mcpu_version_print(prog, version_action) == 0 ? + EXIT_SUCCESS : EXIT_FAILURE ); + } + if( !strcmp( arg, "-o" ) ) + { + if( ++i >= argc || output ) + { + usage( stderr, prog ); + return( EXIT_FAILURE ); + } + output = argv[i]; + continue; + } + if( !strncmp( arg, "--output=", 9 ) ) + { + if( output || !arg[9] ) + { + usage( stderr, prog ); + return( EXIT_FAILURE ); + } + output = arg + 9; + continue; + } + if( arg[0] == '-' && arg[1] ) + { + fprintf( stderr, "%s: unknown option: %s\n", prog, arg ); + usage( stderr, prog ); + return( EXIT_FAILURE ); + } + if( input ) + { + fprintf( stderr, "%s: exactly one input file is required\n", prog ); + usage( stderr, prog ); + return( EXIT_FAILURE ); + } + input = arg; + } + + if( !input ) + { + usage( stderr, prog ); + return( EXIT_FAILURE ); + } + if( !output ) + output = input; + + if( cpu_melf_readall( input, &data, &size, err, sizeof( err ) ) ) + { + fprintf( stderr, "%s: %s\n", prog, err ); + return( EXIT_FAILURE ); + } + + if( size >= 8 && !memcmp( data, "!<arch>\n", 8 ) ) + { + if( strip_archive( input, output, data, size, err, sizeof( err ) ) ) + fprintf( stderr, "%s: %s: %s\n", prog, input, err ); + else + rc = EXIT_SUCCESS; + } + else + { + unsigned char *stripped = NULL; + size_t stripped_size = 0; + int changed = 0; + + if( mcpu_object_strip_data( data, size, &stripped, &stripped_size, + &changed, err, sizeof( err ) ) ) + fprintf( stderr, "%s: %s: %s\n", prog, input, err ); + else if( commit_bytes( input, output, stripped, stripped_size, + err, sizeof( err ) ) ) + fprintf( stderr, "%s: %s\n", prog, err ); + else + rc = EXIT_SUCCESS; + free( stripped ); + (void)changed; + } + + free( data ); + return( rc ); + +} /* End of main() */ |
