871 lines
22 KiB
C
871 lines
22 KiB
C
/****************************************************************************
|
|
* tools/wrapsymbol.c
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#include <stdbool.h>
|
|
#include <libelf.h>
|
|
#include <gelf.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <ctype.h>
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
#define MAX_LINE_LENGTH 1024
|
|
#define OPTIONS_FROM_FILE 256
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
typedef struct
|
|
{
|
|
char **options;
|
|
size_t count;
|
|
size_t capacity;
|
|
} option_list_t;
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static int g_verbose = 0;
|
|
const char *g_input_filename;
|
|
const char *g_output_filename;
|
|
|
|
/****************************************************************************
|
|
* Private Functions prototypes
|
|
****************************************************************************/
|
|
|
|
static int read_option_file(const char *filename, option_list_t *wrap_list);
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: log_verbose
|
|
*
|
|
* Description:
|
|
* Print verbose log messages when verbose mode is enabled
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void log_verbose(const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
|
|
if (g_verbose)
|
|
{
|
|
va_start(args, fmt);
|
|
vprintf(fmt, args);
|
|
va_end(args);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: print_usage
|
|
*
|
|
* Description:
|
|
* Print usage information
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void print_usage(const char *progname)
|
|
{
|
|
fprintf(stderr, "Usage: %s <input-elf-file> [--wrap=<symbol>]... "
|
|
"[--output=<outputfile> [-v] [@options-file]\n", progname);
|
|
fprintf(stderr, " --wrap=<symbol>: Wrap symbol references and "
|
|
"definitions\n");
|
|
fprintf(stderr, " - Defined symbols: renamed to "
|
|
"__real_<symbol>\n");
|
|
fprintf(stderr, " - Undefined symbols: renamed to "
|
|
"__wrap_<symbol>\n");
|
|
fprintf(stderr, " -o <output-elf-file>: Output to a new file "
|
|
"(optional)\n");
|
|
fprintf(stderr, " -v, --verbose: Enable verbose output\n");
|
|
fprintf(stderr, " @options-file: Read options from file "
|
|
"(one option per line)\n");
|
|
fprintf(stderr, " If -o is not specified, the input file will be "
|
|
"modified in place.\n");
|
|
fprintf(stderr, "\nExample:\n");
|
|
fprintf(stderr, " %s input.o --wrap=malloc --wrap=free "
|
|
"-o output.o\n", progname);
|
|
fprintf(stderr, " %s input.o @wrap_options.txt -o output.o -v\n",
|
|
progname);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: free_option_list
|
|
****************************************************************************/
|
|
|
|
static void free_option_list(option_list_t *list)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < list->count; i++)
|
|
{
|
|
free(list->options[i]);
|
|
}
|
|
|
|
free(list->options);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: add_option
|
|
****************************************************************************/
|
|
|
|
static int add_option(option_list_t *list, const char *option)
|
|
{
|
|
void *newoption;
|
|
|
|
if (list->count >= list->capacity)
|
|
{
|
|
newoption = realloc(list->options,
|
|
sizeof(char *) * (list->capacity +
|
|
OPTIONS_FROM_FILE));
|
|
if (!newoption)
|
|
{
|
|
fprintf(stderr, "Error: realloc failed while adding option\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
list->options = newoption;
|
|
list->capacity += OPTIONS_FROM_FILE;
|
|
}
|
|
|
|
list->options[list->count] = strdup(option);
|
|
if (!list->options[list->count])
|
|
{
|
|
perror("Failed to allocate memory for option");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
list->count++;
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: trim_line
|
|
*
|
|
* Description:
|
|
* Remove newline character ,trim leading and trailing whitespace
|
|
* from string
|
|
*
|
|
****************************************************************************/
|
|
|
|
static char *trim_line(char *str)
|
|
{
|
|
size_t len;
|
|
|
|
/* Remove newline character */
|
|
|
|
len = strlen(str);
|
|
if (len > 0 && str[len - 1] == '\n')
|
|
{
|
|
str[--len] = '\0';
|
|
}
|
|
|
|
/* Remove leading whitespace */
|
|
|
|
while (isspace(*str))
|
|
{
|
|
str++;
|
|
}
|
|
|
|
if (*str == 0)
|
|
{
|
|
return str;
|
|
}
|
|
|
|
/* Remove trailing whitespace */
|
|
|
|
while (len > 0 && isspace(str[--len]))
|
|
{
|
|
str[len] = '\0';
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: process_single_option
|
|
*
|
|
* Description:
|
|
* Process a single option (for both command line and file options)
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int process_single_option(const char *opt,
|
|
option_list_t *wrap_list)
|
|
{
|
|
if (strncmp(opt, "@", sizeof("@") - 1) == 0)
|
|
{
|
|
/* Process options file */
|
|
|
|
opt += sizeof("@") - 1;
|
|
if (opt[0] == '\0')
|
|
{
|
|
fprintf(stderr, "Error: @ requires a filename\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
if (read_option_file(opt, wrap_list) != 0)
|
|
{
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
else if (strncmp(opt, "--wrap=", sizeof("--wrap=") - 1) == 0)
|
|
{
|
|
opt += sizeof("--wrap=") - 1;
|
|
if (opt[0] == '\0')
|
|
{
|
|
fprintf(stderr, "Error: --wrap= requires a symbol\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
if (add_option(wrap_list, opt) != 0)
|
|
{
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
else if (strncmp(opt, "--output=", sizeof("--output=") - 1) == 0 &&
|
|
g_output_filename == NULL)
|
|
{
|
|
g_output_filename = strdup(opt + sizeof("--output=") - 1);
|
|
if (g_output_filename && g_output_filename[0] == '\0')
|
|
{
|
|
fprintf(stderr, "Error: --output= requires a filename\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
else if (strcmp(opt, "-v") == 0 || strcmp(opt, "--verbose") == 0)
|
|
{
|
|
g_verbose = 1;
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "Error: Unknown option '%s'\n", opt);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: read_option_file
|
|
*
|
|
* Description:
|
|
* Read options from file and process them directly
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int read_option_file(const char *filename, option_list_t *wrap_list)
|
|
{
|
|
FILE *file = fopen(filename, "r");
|
|
char line[MAX_LINE_LENGTH];
|
|
size_t count = 0;
|
|
char *trimmed;
|
|
|
|
if (!file)
|
|
{
|
|
fprintf(stderr, "Error: Failed to open options file '%s': ",
|
|
filename);
|
|
perror("");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
while (fgets(line, sizeof(line), file))
|
|
{
|
|
/* Trim whitespace */
|
|
|
|
trimmed = trim_line(line);
|
|
|
|
/* Skip empty lines and comment lines (starting with #) */
|
|
|
|
if (trimmed[0] == '\0' || trimmed[0] == '#')
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/* Process option directly */
|
|
|
|
if (process_single_option(trimmed, wrap_list) != 0)
|
|
{
|
|
fclose(file);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
count++;
|
|
}
|
|
|
|
fclose(file);
|
|
log_verbose("Read %d options from file '%s'\n", count, filename);
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: is_wrap_symbol
|
|
****************************************************************************/
|
|
|
|
static bool is_wrap_symbol(const option_list_t *list, const char *symbol)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < list->count; i++)
|
|
{
|
|
if (strcmp(list->options[i], symbol) == 0)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: copy_file
|
|
****************************************************************************/
|
|
|
|
static int copy_file(const char *dst, const char *src)
|
|
{
|
|
char buffer[4096];
|
|
FILE *src_file;
|
|
FILE *dst_file;
|
|
size_t bytes;
|
|
|
|
src_file = fopen(src, "rb");
|
|
if (!src_file)
|
|
{
|
|
perror("Failed to open source file");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
dst_file = fopen(dst, "wb");
|
|
if (!dst_file)
|
|
{
|
|
perror("Failed to create destination file");
|
|
fclose(src_file);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
while ((bytes = fread(buffer, 1, sizeof(buffer), src_file)) > 0)
|
|
{
|
|
if (fwrite(buffer, 1, bytes, dst_file) != bytes)
|
|
{
|
|
perror("Failed to write to destination file");
|
|
fclose(src_file);
|
|
fclose(dst_file);
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
|
|
fclose(src_file);
|
|
fclose(dst_file);
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: parse_arguments
|
|
*
|
|
* Description:
|
|
* Parse command line arguments
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int parse_arguments(int argc, char **argv,
|
|
option_list_t *wrap_list)
|
|
{
|
|
int i;
|
|
|
|
if (argc < 2)
|
|
{
|
|
print_usage(argv[0]);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
g_input_filename = argv[1];
|
|
g_output_filename = NULL;
|
|
|
|
/* Process command line arguments */
|
|
|
|
for (i = 2; i < argc; i++)
|
|
{
|
|
if (process_single_option(argv[i], wrap_list) != 0)
|
|
{
|
|
print_usage(argv[0]);
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
|
|
if (wrap_list->count == 0)
|
|
{
|
|
fprintf(stderr, "Error: At least one --wrap=<symbol> is "
|
|
"required\n");
|
|
print_usage(argv[0]);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: find_symbol_string_tables
|
|
*
|
|
* Description:
|
|
* Find symbol table and string table sections
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int find_symbol_string_tables(Elf *elf,
|
|
Elf_Scn **symtab_scn,
|
|
Elf_Scn **strtab_scn,
|
|
GElf_Shdr *symtab_shdr,
|
|
GElf_Shdr *strtab_shdr,
|
|
Elf_Data **symtab_data,
|
|
Elf_Data **strtab_data)
|
|
{
|
|
Elf_Scn *scn = NULL;
|
|
char *section_name;
|
|
size_t shstrndx;
|
|
GElf_Shdr shdr;
|
|
|
|
*symtab_scn = NULL;
|
|
*strtab_scn = NULL;
|
|
|
|
while ((scn = elf_nextscn(elf, scn)) != NULL)
|
|
{
|
|
if (gelf_getshdr(scn, &shdr) != &shdr)
|
|
{
|
|
fprintf(stderr, "gelf_getshdr failed: %s\n", elf_errmsg(-1));
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
if (shdr.sh_type == SHT_SYMTAB)
|
|
{
|
|
*symtab_scn = scn;
|
|
*symtab_shdr = shdr;
|
|
}
|
|
else if (shdr.sh_type == SHT_STRTAB)
|
|
{
|
|
if (elf_getshdrstrndx(elf, &shstrndx) == 0)
|
|
{
|
|
section_name = elf_strptr(elf, shstrndx, shdr.sh_name);
|
|
if (section_name && strcmp(section_name, ".strtab") == 0)
|
|
{
|
|
*strtab_scn = scn;
|
|
*strtab_shdr = shdr;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!*symtab_scn)
|
|
{
|
|
fprintf(stderr, "Error: Symbol table not found\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
if (!*strtab_scn)
|
|
{
|
|
fprintf(stderr, "Error: String table not found\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
/* Get symbol table data */
|
|
|
|
*symtab_data = elf_getdata(*symtab_scn, NULL);
|
|
if (!*symtab_data)
|
|
{
|
|
fprintf(stderr, "Error: Failed to get symbol table data: %s\n",
|
|
elf_errmsg(-1));
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
/* Get string table data */
|
|
|
|
*strtab_data = elf_getdata(*strtab_scn, NULL);
|
|
if (!*strtab_data)
|
|
{
|
|
fprintf(stderr, "Error: Failed to get string table data: %s\n",
|
|
elf_errmsg(-1));
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: compute_strtab_expansion_size
|
|
*
|
|
* Description:
|
|
* Calculate expansion string table size needed
|
|
*
|
|
****************************************************************************/
|
|
|
|
static size_t compute_strtab_expansion_size(Elf_Data *symtab_data,
|
|
Elf_Data *strtab_data,
|
|
GElf_Shdr *symtab_shdr,
|
|
option_list_t *wrap_list)
|
|
{
|
|
size_t sym_count = symtab_shdr->sh_size / symtab_shdr->sh_entsize;
|
|
size_t expansion_size = 0;
|
|
GElf_Sym sym;
|
|
char *name;
|
|
size_t i;
|
|
|
|
for (i = 0; i < sym_count; i++)
|
|
{
|
|
if (!gelf_getsym(symtab_data, i, &sym))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
name = (char *)strtab_data->d_buf + sym.st_name;
|
|
if (is_wrap_symbol(wrap_list, name))
|
|
{
|
|
/* __wrap_ or __real_ prefix (7 chars) + original name +
|
|
* null terminator
|
|
*/
|
|
|
|
expansion_size += strlen(name) + 8;
|
|
}
|
|
}
|
|
|
|
return expansion_size;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: modify_symbols
|
|
*
|
|
* Description:
|
|
* Modify symbols in the symbol table
|
|
*
|
|
****************************************************************************/
|
|
|
|
static size_t modify_symbols(Elf_Data *symtab_data,
|
|
Elf_Data *strtab_data,
|
|
GElf_Shdr *symtab_shdr,
|
|
option_list_t *wrap_list)
|
|
{
|
|
size_t sym_count = symtab_shdr->sh_size / symtab_shdr->sh_entsize;
|
|
size_t strtab_offset = strtab_data->d_size;
|
|
size_t modified = 0;
|
|
size_t new_name_len;
|
|
char new_name[512];
|
|
const char *name;
|
|
GElf_Sym sym;
|
|
size_t i;
|
|
|
|
for (i = 0; i < sym_count; i++)
|
|
{
|
|
if (!gelf_getsym(symtab_data, i, &sym))
|
|
{
|
|
fprintf(stderr, "gelf_getsym failed: %s\n", elf_errmsg(-1));
|
|
continue;
|
|
}
|
|
|
|
name = (const char *)strtab_data->d_buf + sym.st_name;
|
|
if (!is_wrap_symbol(wrap_list, name))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/* Check if symbol is defined or undefined
|
|
* SHN_UNDEF (0) indicates undefined symbol
|
|
*/
|
|
|
|
if (sym.st_shndx == SHN_UNDEF)
|
|
{
|
|
/* Undefined symbol -> __wrap_xxxx */
|
|
|
|
snprintf(new_name, sizeof(new_name), "__wrap_%s", name);
|
|
log_verbose("Renaming undefined symbol '%s' to '%s'\n",
|
|
name, new_name);
|
|
}
|
|
else
|
|
{
|
|
/* Defined symbol -> __real_xxxx */
|
|
|
|
snprintf(new_name, sizeof(new_name), "__real_%s", name);
|
|
log_verbose("Renaming defined symbol '%s' to '%s'\n",
|
|
name, new_name);
|
|
}
|
|
|
|
/* Add new symbol name to string table */
|
|
|
|
new_name_len = strlen(new_name) + 1;
|
|
strcpy((char *)strtab_data->d_buf + strtab_offset,
|
|
new_name);
|
|
|
|
/* Update symbol's string index */
|
|
|
|
sym.st_name = strtab_offset;
|
|
strtab_offset += new_name_len;
|
|
|
|
/* Update symbol table */
|
|
|
|
if (!gelf_update_sym(symtab_data, i, &sym))
|
|
{
|
|
fprintf(stderr, "gelf_update_sym failed: %s\n",
|
|
elf_errmsg(-1));
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
modified++;
|
|
}
|
|
|
|
return modified;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: process_elf_file
|
|
*
|
|
* Description:
|
|
* Process the ELF file and modify symbols
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int process_elf_file(const char *filename, option_list_t *wrap_list)
|
|
{
|
|
size_t expansion_strtab_size;
|
|
char *new_strtab = NULL;
|
|
int ret = EXIT_FAILURE;
|
|
GElf_Shdr symtab_shdr;
|
|
GElf_Shdr strtab_shdr;
|
|
Elf_Data *symtab_data;
|
|
Elf_Data *strtab_data;
|
|
Elf_Scn *symtab_scn;
|
|
Elf_Scn *strtab_scn;
|
|
size_t modified;
|
|
Elf *elf;
|
|
int fd;
|
|
|
|
/* Initialize libelf */
|
|
|
|
if (elf_version(EV_CURRENT) == EV_NONE)
|
|
{
|
|
fprintf(stderr, "Error: ELF library initialization failed: %s\n",
|
|
elf_errmsg(-1));
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
/* Open ELF file */
|
|
|
|
fd = open(filename, O_RDWR);
|
|
if (fd < 0)
|
|
{
|
|
perror("Failed to open file");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
elf = elf_begin(fd, ELF_C_RDWR, NULL);
|
|
if (!elf)
|
|
{
|
|
fprintf(stderr, "Error: elf_begin failed: %s\n", elf_errmsg(-1));
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Find symbol table and string table */
|
|
|
|
if (find_symbol_string_tables(elf, &symtab_scn, &strtab_scn,
|
|
&symtab_shdr, &strtab_shdr,
|
|
&symtab_data, &strtab_data) != 0)
|
|
{
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Calculate additional string table size needed */
|
|
|
|
expansion_strtab_size = compute_strtab_expansion_size(symtab_data,
|
|
strtab_data,
|
|
&symtab_shdr,
|
|
wrap_list);
|
|
|
|
/* Expand string table if needed */
|
|
|
|
if (expansion_strtab_size > 0)
|
|
{
|
|
new_strtab = malloc(strtab_data->d_size + expansion_strtab_size);
|
|
if (!new_strtab)
|
|
{
|
|
fprintf(stderr, "Error: Failed to allocate memory for new "
|
|
"string table\n");
|
|
goto cleanup;
|
|
}
|
|
|
|
memcpy(new_strtab, strtab_data->d_buf,
|
|
strtab_data->d_size);
|
|
memset(new_strtab + strtab_data->d_size, 0,
|
|
expansion_strtab_size);
|
|
|
|
strtab_data->d_buf = new_strtab;
|
|
}
|
|
|
|
/* Modify symbols */
|
|
|
|
modified = modify_symbols(symtab_data, strtab_data,
|
|
&symtab_shdr, wrap_list);
|
|
if (modified == 0)
|
|
{
|
|
log_verbose("No symbols were modified.\n");
|
|
ret = EXIT_SUCCESS;
|
|
}
|
|
else if (modified > 0)
|
|
{
|
|
log_verbose("Modified %d symbol(s).\n", modified);
|
|
|
|
/* Update string table size */
|
|
|
|
strtab_data->d_size += expansion_strtab_size;
|
|
strtab_shdr.sh_size = strtab_data->d_size;
|
|
|
|
/* Mark data as modified */
|
|
|
|
elf_flagdata(strtab_data, ELF_C_SET, ELF_F_DIRTY);
|
|
elf_flagdata(symtab_data, ELF_C_SET, ELF_F_DIRTY);
|
|
|
|
/* Update string table section header */
|
|
|
|
if (!gelf_update_shdr(strtab_scn, &strtab_shdr))
|
|
{
|
|
fprintf(stderr, "Error: Failed to update string table header: "
|
|
"%s\n", elf_errmsg(-1));
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Write modifications */
|
|
|
|
if (elf_update(elf, ELF_C_WRITE) < 0)
|
|
{
|
|
fprintf(stderr, "Error: elf_update failed: %s\n",
|
|
elf_errmsg(-1));
|
|
goto cleanup;
|
|
}
|
|
|
|
log_verbose("Successfully wrote changes to '%s'\n", filename);
|
|
ret = EXIT_SUCCESS;
|
|
}
|
|
|
|
cleanup:
|
|
if (new_strtab)
|
|
{
|
|
free(new_strtab);
|
|
}
|
|
|
|
if (elf)
|
|
{
|
|
elf_end(elf);
|
|
}
|
|
|
|
close(fd);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: main
|
|
****************************************************************************/
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
int ret = EXIT_FAILURE;
|
|
const char *filename;
|
|
size_t i;
|
|
option_list_t wrap_list =
|
|
{
|
|
0
|
|
};
|
|
|
|
/* Parse command line arguments */
|
|
|
|
if (parse_arguments(argc, argv, &wrap_list) != 0)
|
|
{
|
|
free_option_list(&wrap_list);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
/* Print wrap symbols list */
|
|
|
|
log_verbose("Processing %d wrap symbol(s):\n", wrap_list.count);
|
|
for (i = 0; i < wrap_list.count; i++)
|
|
{
|
|
log_verbose(" - %s\n", wrap_list.options[i]);
|
|
}
|
|
|
|
/* Copy input file to output file if specified */
|
|
|
|
if (g_output_filename)
|
|
{
|
|
log_verbose("Copying '%s' to '%s'...\n", g_input_filename,
|
|
g_output_filename);
|
|
|
|
if (copy_file(g_output_filename, g_input_filename) != 0)
|
|
{
|
|
fprintf(stderr, "Error: Failed to copy file\n");
|
|
free_option_list(&wrap_list);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
filename = g_output_filename;
|
|
}
|
|
else
|
|
{
|
|
filename = g_input_filename;
|
|
}
|
|
|
|
/* Process ELF file */
|
|
|
|
ret = process_elf_file(filename, &wrap_list);
|
|
|
|
/* If processing failed and output file was created, delete it */
|
|
|
|
if (ret != EXIT_SUCCESS && g_output_filename)
|
|
{
|
|
log_verbose("Error occurred, removing output file '%s'\n",
|
|
g_output_filename);
|
|
unlink(g_output_filename);
|
|
}
|
|
|
|
/* Cleanup */
|
|
|
|
free_option_list(&wrap_list);
|
|
if (g_output_filename)
|
|
{
|
|
free((void *)g_output_filename);
|
|
}
|
|
|
|
return ret;
|
|
}
|