#ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #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, "!\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() */