summaryrefslogtreecommitdiff
path: root/utils/mcpu-strip.c
diff options
context:
space:
mode:
Diffstat (limited to 'utils/mcpu-strip.c')
-rw-r--r--utils/mcpu-strip.c332
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() */