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-object-edit.c | |
| parent | d130fe27fa753be02ed76fd9b277aa3c3d928eb9 (diff) | |
| download | libmcpu-0.4.0.tar.xz | |
Version 0.4.00.4.0
Diffstat (limited to 'utils/mcpu-object-edit.c')
| -rw-r--r-- | utils/mcpu-object-edit.c | 1195 |
1 files changed, 1195 insertions, 0 deletions
diff --git a/utils/mcpu-object-edit.c b/utils/mcpu-object-edit.c new file mode 100644 index 0000000..ac0db3c --- /dev/null +++ b/utils/mcpu-object-edit.c @@ -0,0 +1,1195 @@ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <libmcpu.h> +#include <mcpu-elf.h> + +#include "mcpu-melf.h" +#include "mcpu-object-edit.h" + + +typedef struct mcpu_edit_shdr +{ + __mpu_uint32_t name; + __mpu_uint32_t type; + __mpu_uint64_t flags; + __mpu_uint64_t addr; + __mpu_uint64_t offset; + __mpu_uint64_t size; + __mpu_uint32_t link; + __mpu_uint32_t info; + __mpu_uint64_t align; + __mpu_uint64_t entsize; +} mcpu_edit_shdr; + + +typedef struct mcpu_edit_file +{ + const unsigned char *data; + size_t size; + int encoding; + __mpu_uint16_t type; + __mpu_uint64_t phoff; + __mpu_uint16_t phnum; + __mpu_uint64_t shoff; + __mpu_uint16_t shnum; + __mpu_uint16_t shstrndx; + mcpu_edit_shdr *shdr; + const unsigned char *shstr; + size_t shstr_size; +} mcpu_edit_file; + + +typedef struct mcpu_edit_section +{ + mcpu_edit_shdr shdr; + char *name; + const unsigned char *data; + unsigned char *owned_data; + size_t data_size; + __mpu_uint16_t old_index; + int is_new; + int removed; +} mcpu_edit_section; + + +static void +get_shdr( const unsigned char *p, int data, mcpu_edit_shdr *sh ) +{ + memset( sh, 0, sizeof( *sh ) ); + sh->name = cpu_melf_get32( p + MELF64_SHDR_SH_NAME_OFF, data ); + sh->type = cpu_melf_get32( p + MELF64_SHDR_SH_TYPE_OFF, data ); + sh->flags = cpu_melf_get64( p + MELF64_SHDR_SH_FLAGS_OFF, data ); + sh->addr = cpu_melf_get64( p + MELF64_SHDR_SH_ADDR_OFF, data ); + sh->offset = cpu_melf_get64( p + MELF64_SHDR_SH_OFFSET_OFF, data ); + sh->size = cpu_melf_get64( p + MELF64_SHDR_SH_SIZE_OFF, data ); + sh->link = cpu_melf_get32( p + MELF64_SHDR_SH_LINK_OFF, data ); + sh->info = cpu_melf_get32( p + MELF64_SHDR_SH_INFO_OFF, data ); + sh->align = cpu_melf_get64( p + MELF64_SHDR_SH_ADDRALIGN_OFF, data ); + sh->entsize = cpu_melf_get64( p + MELF64_SHDR_SH_ENTSIZE_OFF, data ); + +} /* End of get_shdr() */ + + +static void +put_shdr( unsigned char *p, int data, const mcpu_edit_shdr *sh ) +{ + memset( p, 0, MELF64_SHDR_SIZE ); + cpu_melf_put32( p + MELF64_SHDR_SH_NAME_OFF, sh->name, data ); + cpu_melf_put32( p + MELF64_SHDR_SH_TYPE_OFF, sh->type, data ); + cpu_melf_put64( p + MELF64_SHDR_SH_FLAGS_OFF, sh->flags, data ); + cpu_melf_put64( p + MELF64_SHDR_SH_ADDR_OFF, sh->addr, data ); + cpu_melf_put64( p + MELF64_SHDR_SH_OFFSET_OFF, sh->offset, data ); + cpu_melf_put64( p + MELF64_SHDR_SH_SIZE_OFF, sh->size, data ); + cpu_melf_put32( p + MELF64_SHDR_SH_LINK_OFF, sh->link, data ); + cpu_melf_put32( p + MELF64_SHDR_SH_INFO_OFF, sh->info, data ); + cpu_melf_put64( p + MELF64_SHDR_SH_ADDRALIGN_OFF, sh->align, data ); + cpu_melf_put64( p + MELF64_SHDR_SH_ENTSIZE_OFF, sh->entsize, data ); + +} /* End of put_shdr() */ + + +static int +shdr_is_zero( const mcpu_edit_shdr *sh ) +{ + return( sh->name == 0 && sh->type == MELF_SHT_NULL && sh->flags == 0 && + sh->addr == 0 && sh->offset == 0 && sh->size == 0 && + sh->link == 0 && sh->info == 0 && sh->align == 0 && + sh->entsize == 0 ); + +} /* End of shdr_is_zero() */ + + +static const char * +name_at( const mcpu_edit_file *file, __mpu_uint32_t offset ) +{ + const unsigned char *p; + + if( offset >= file->shstr_size ) + return( NULL ); + p = file->shstr + offset; + if( !memchr( p, 0, file->shstr_size - offset ) ) + return( NULL ); + return( (const char *)p ); + +} /* End of name_at() */ + + +static void +edit_file_free( mcpu_edit_file *file ) +{ + if( !file ) + return; + free( file->shdr ); + memset( file, 0, sizeof( *file ) ); + +} /* End of edit_file_free() */ + + +static int +edit_file_open( const unsigned char *data, size_t size, mcpu_edit_file *file, + char *err, size_t errsz ) +{ + __mpu_uint16_t ehsize; + __mpu_uint16_t phentsize; + __mpu_uint16_t shentsize; + size_t i; + + memset( file, 0, sizeof( *file ) ); + file->data = data; + file->size = size; + + if( size < MELF64_EHDR_SIZE ) + { + snprintf( err, errsz, "file is too small for a MELF64 header" ); + return( -1 ); + } + if( data[MELF_EI_MAG0] != MELFMAG0 || data[MELF_EI_MAG1] != MELFMAG1 || + data[MELF_EI_MAG2] != MELFMAG2 || data[MELF_EI_MAG3] != MELFMAG3 ) + { + snprintf( err, errsz, "not a MELF file" ); + return( -1 ); + } + if( data[MELF_EI_CLASS] != MELF_CLASS_64 ) + { + snprintf( err, errsz, "unsupported MELF class %u", + (unsigned int)data[MELF_EI_CLASS] ); + return( -1 ); + } + + file->encoding = data[MELF_EI_DATA]; + if( file->encoding != MELF_DATA_LSB && file->encoding != MELF_DATA_MSB ) + { + snprintf( err, errsz, "invalid MELF data encoding %d", file->encoding ); + return( -1 ); + } + if( data[MELF_EI_VERSION] != MELF_EV_CURRENT || + data[MELF_EI_OSABI] != MELF_OSABI_MCPU || + cpu_melf_get16( data + MELF64_EHDR_E_MACHINE_OFF, file->encoding ) != + MELF_EM_MCPU || + cpu_melf_get32( data + MELF64_EHDR_E_VERSION_OFF, file->encoding ) != + MELF_EV_CURRENT ) + { + snprintf( err, errsz, "unsupported MELF header" ); + return( -1 ); + } + + ehsize = cpu_melf_get16( data + MELF64_EHDR_E_EHSIZE_OFF, file->encoding ); + phentsize = cpu_melf_get16( data + MELF64_EHDR_E_PHENTSIZE_OFF, + file->encoding ); + shentsize = cpu_melf_get16( data + MELF64_EHDR_E_SHENTSIZE_OFF, + file->encoding ); + file->type = cpu_melf_get16( data + MELF64_EHDR_E_TYPE_OFF, + file->encoding ); + file->phoff = cpu_melf_get64( data + MELF64_EHDR_E_PHOFF_OFF, + file->encoding ); + file->phnum = cpu_melf_get16( data + MELF64_EHDR_E_PHNUM_OFF, + file->encoding ); + file->shoff = cpu_melf_get64( data + MELF64_EHDR_E_SHOFF_OFF, + file->encoding ); + file->shnum = cpu_melf_get16( data + MELF64_EHDR_E_SHNUM_OFF, + file->encoding ); + file->shstrndx = cpu_melf_get16( data + MELF64_EHDR_E_SHSTRNDX_OFF, + file->encoding ); + + if( ehsize != MELF64_EHDR_SIZE || + ( file->type != MELF_ET_REL && file->type != MELF_ET_EXEC && + file->type != MELF_ET_DYN ) ) + { + snprintf( err, errsz, "unsupported MELF64 file type/header size" ); + return( -1 ); + } + + if( file->phnum ) + { + if( phentsize != MELF64_PHDR_SIZE || + !cpu_melf_span( size, file->phoff, + (__mpu_uint64_t)file->phnum * MELF64_PHDR_SIZE ) ) + { + snprintf( err, errsz, "malformed program-header table" ); + return( -1 ); + } + } + else if( phentsize != 0 && file->type == MELF_ET_REL ) + { + snprintf( err, errsz, "ET_REL has unexpected program-header entry size" ); + return( -1 ); + } + + if( !file->shnum || shentsize != MELF64_SHDR_SIZE || + file->shstrndx == MELF_SHN_UNDEF || file->shstrndx >= file->shnum || + !cpu_melf_span( size, file->shoff, + (__mpu_uint64_t)file->shnum * MELF64_SHDR_SIZE ) ) + { + snprintf( err, errsz, "malformed section-header table" ); + return( -1 ); + } + + file->shdr = (mcpu_edit_shdr *)calloc( file->shnum, sizeof( *file->shdr ) ); + if( !file->shdr ) + { + snprintf( err, errsz, "out of memory" ); + return( -1 ); + } + + for( i = 0; i < file->shnum; ++i ) + { + get_shdr( data + (size_t)file->shoff + i * MELF64_SHDR_SIZE, + file->encoding, &file->shdr[i] ); + if( file->shdr[i].type != MELF_SHT_NOBITS && file->shdr[i].size && + !cpu_melf_span( size, file->shdr[i].offset, file->shdr[i].size ) ) + { + snprintf( err, errsz, "section %zu has an invalid file range", i ); + edit_file_free( file ); + return( -1 ); + } + } + + if( !shdr_is_zero( &file->shdr[0] ) || + file->shdr[file->shstrndx].type != MELF_SHT_STRTAB || + !file->shdr[file->shstrndx].size || + file->shdr[file->shstrndx].size > SIZE_MAX ) + { + snprintf( err, errsz, "malformed MELF section-name table" ); + edit_file_free( file ); + return( -1 ); + } + + file->shstr = data + (size_t)file->shdr[file->shstrndx].offset; + file->shstr_size = (size_t)file->shdr[file->shstrndx].size; + for( i = 1; i < file->shnum; ++i ) + if( !name_at( file, file->shdr[i].name ) ) + { + snprintf( err, errsz, "section %zu has an invalid name", i ); + edit_file_free( file ); + return( -1 ); + } + + return( 0 ); + +} /* End of edit_file_open() */ + + +static int +copy_original( const unsigned char *data, size_t size, + unsigned char **out, size_t *out_size, + char *err, size_t errsz ) +{ + unsigned char *p = (unsigned char *)malloc( size ? size : 1 ); + + if( !p ) + { + snprintf( err, errsz, "out of memory" ); + return( -1 ); + } + if( size ) + memcpy( p, data, size ); + *out = p; + *out_size = size; + return( 0 ); + +} /* End of copy_original() */ + + +static void +sections_free( mcpu_edit_section *sections, size_t count ) +{ + size_t i; + + if( !sections ) + return; + for( i = 0; i < count; ++i ) + { + free( sections[i].name ); + free( sections[i].owned_data ); + } + free( sections ); + +} /* End of sections_free() */ + + +static int +sections_from_file( const mcpu_edit_file *file, + mcpu_edit_section **result, size_t *count, + char *err, size_t errsz ) +{ + mcpu_edit_section *sections; + size_t i; + + sections = (mcpu_edit_section *)calloc( file->shnum, sizeof( *sections ) ); + if( !sections ) + { + snprintf( err, errsz, "out of memory" ); + return( -1 ); + } + + for( i = 0; i < file->shnum; ++i ) + { + const char *name = i ? name_at( file, file->shdr[i].name ) : ""; + + sections[i].shdr = file->shdr[i]; + sections[i].name = strdup( name ? name : "" ); + sections[i].old_index = (__mpu_uint16_t)i; + if( !sections[i].name ) + { + snprintf( err, errsz, "out of memory" ); + sections_free( sections, file->shnum ); + return( -1 ); + } + if( file->shdr[i].type != MELF_SHT_NOBITS && file->shdr[i].size ) + { + sections[i].data = file->data + (size_t)file->shdr[i].offset; + sections[i].data_size = (size_t)file->shdr[i].size; + } + } + + *result = sections; + *count = file->shnum; + return( 0 ); + +} /* End of sections_from_file() */ + + +static size_t +find_section( const mcpu_edit_section *sections, size_t count, + const char *name ) +{ + size_t i; + + for( i = 1; i < count; ++i ) + if( !sections[i].removed && !strcmp( sections[i].name, name ) ) + return( i ); + return( SIZE_MAX ); + +} /* End of find_section() */ + + +static int +name_selected( const char *name, const char *const *only, size_t count ) +{ + size_t i; + + for( i = 0; i < count; ++i ) + if( !strcmp( name, only[i] ) ) + return( 1 ); + return( 0 ); + +} /* End of name_selected() */ + + + +static int +reserved_section_name( const char *name ) +{ + static const char *const reserved[] = + { + MELF_SEC_INIT, MELF_SEC_TEXT, MELF_SEC_FINI, + MELF_SEC_DATA, MELF_SEC_BSS, MELF_SEC_CONTROL, + MELF_SEC_INPUT, MELF_SEC_OUTPUT, MELF_SEC_TRACE, + MELF_SEC_SYMTAB, MELF_SEC_STRTAB, MELF_SEC_SHSTRTAB, + MELF_SEC_DYNSYM, MELF_SEC_DYNSTR, MELF_SEC_DYNAMIC, + MELF_SEC_MCPU_TARGET, MELF_SEC_MCPU_LIFECYCLE, + MELF_SEC_RELA_INIT, MELF_SEC_RELA_TEXT, MELF_SEC_RELA_FINI, + MELF_SEC_RELA_DATA, MELF_SEC_RELA_CONTROL, MELF_SEC_RELA_INPUT, + MELF_SEC_RELA_OUTPUT, MELF_SEC_RELA_TRACE + }; + size_t i; + + if( !name || !*name ) + return( 1 ); + if( !strncmp( name, ".rela.", 6 ) ) + return( 1 ); + for( i = 0; i < sizeof( reserved ) / sizeof( reserved[0] ); ++i ) + if( !strcmp( name, reserved[i] ) ) + return( 1 ); + return( 0 ); + +} /* End of reserved_section_name() */ + +static int +section_runtime_critical( const mcpu_edit_file *file, + const mcpu_edit_section *section ) +{ + const mcpu_edit_shdr *sh = §ion->shdr; + + if( sh->flags & MELF_SHF_ALLOC ) + return( 1 ); + if( section->old_index == file->shstrndx ) + return( 1 ); + if( sh->type == MELF_SHT_DYNAMIC || sh->type == MELF_SHT_DYNSYM || + sh->type == MELF_SHT_RELA || sh->type == MELF_SHT_MCPU_TARGET || + sh->type == MELF_SHT_MCPU_LIFECYCLE ) + return( 1 ); + if( file->type == MELF_ET_REL && + ( sh->type == MELF_SHT_SYMTAB || sh->type == MELF_SHT_STRTAB ) ) + return( 1 ); + return( 0 ); + +} /* End of section_runtime_critical() */ + + +static int +section_user_editable( const mcpu_edit_file *file, + const mcpu_edit_section *section ) +{ + if( section_runtime_critical( file, section ) ) + return( 0 ); + return( section->shdr.type == MELF_SHT_PROGBITS && + !( section->shdr.flags & MELF_SHF_ALLOC ) ); + +} /* End of section_user_editable() */ + + +static int +section_link_references( const mcpu_edit_section *sections, size_t count, + size_t target ) +{ + size_t i; + + for( i = 1; i < count; ++i ) + { + if( sections[i].removed || sections[i].is_new ) + continue; + if( sections[i].shdr.link == target ) + return( 1 ); + if( sections[i].shdr.type == MELF_SHT_RELA && + sections[i].shdr.info == target ) + return( 1 ); + } + return( 0 ); + +} /* End of section_link_references() */ + + +static int +safe_remove_section( const mcpu_edit_file *file, + mcpu_edit_section *sections, size_t count, + size_t index, const char *reason, + char *err, size_t errsz ) +{ + if( index == 0 || index >= count || sections[index].removed ) + return( 0 ); + if( section_runtime_critical( file, §ions[index] ) ) + { + snprintf( err, errsz, "%s cannot remove section %s", + reason, sections[index].name ); + return( -1 ); + } + if( section_link_references( sections, count, index ) ) + { + snprintf( err, errsz, "%s cannot remove referenced section %s", + reason, sections[index].name ); + return( -1 ); + } + sections[index].removed = 1; + return( 0 ); + +} /* End of safe_remove_section() */ + + +static int +replace_section_data( mcpu_edit_section *section, + const unsigned char *data, size_t size, + char *err, size_t errsz ) +{ + unsigned char *p = NULL; + + if( size ) + { + p = (unsigned char *)malloc( size ); + if( !p ) + { + snprintf( err, errsz, "out of memory" ); + return( -1 ); + } + memcpy( p, data, size ); + } + free( section->owned_data ); + section->owned_data = p; + section->data = p; + section->data_size = size; + section->shdr.size = size; + return( 0 ); + +} /* End of replace_section_data() */ + + +static int +add_section( mcpu_edit_section **psections, size_t *pcount, + const char *name, const unsigned char *data, size_t size, + char *err, size_t errsz ) +{ + mcpu_edit_section *sections = *psections; + size_t count = *pcount; + mcpu_edit_section *q; + mcpu_edit_section *s; + + if( !name || !*name ) + { + snprintf( err, errsz, "section name must not be empty" ); + return( -1 ); + } + if( reserved_section_name( name ) ) + { + snprintf( err, errsz, "cannot add reserved section name %s", name ); + return( -1 ); + } + if( count == UINT16_MAX ) + { + snprintf( err, errsz, "too many sections" ); + return( -1 ); + } + q = (mcpu_edit_section *)realloc( sections, + (count + 1) * sizeof( *q ) ); + if( !q ) + { + snprintf( err, errsz, "out of memory" ); + return( -1 ); + } + sections = q; + memset( §ions[count], 0, sizeof( sections[count] ) ); + s = §ions[count]; + s->name = strdup( name ); + if( !s->name ) + { + snprintf( err, errsz, "out of memory" ); + *psections = sections; + return( -1 ); + } + s->is_new = 1; + s->old_index = MELF_SHN_UNDEF; + s->shdr.type = MELF_SHT_PROGBITS; + s->shdr.align = 1; + if( replace_section_data( s, data, size, err, errsz ) ) + { + free( s->name ); + memset( s, 0, sizeof( *s ) ); + *psections = sections; + return( -1 ); + } + + *psections = sections; + *pcount = count + 1; + return( 0 ); + +} /* End of add_section() */ + + +static int +apply_options( const mcpu_edit_file *file, + mcpu_edit_section **psections, size_t *pcount, + const mcpu_object_edit_options *options, + int *changed, char *err, size_t errsz ) +{ + mcpu_edit_section *sections = *psections; + size_t count = *pcount; + size_t i; + + if( options->only_count ) + { + int any = 0; + + /* Mark the whole filter first so dependencies between sections that are + both being discarded do not make the result depend on section order. */ + for( i = 1; i < count; ++i ) + if( !name_selected( sections[i].name, options->only_sections, + options->only_count ) ) + { + if( section_runtime_critical( file, §ions[i] ) ) + { + snprintf( err, errsz, "--only-section cannot remove section %s", + sections[i].name ); + return( -1 ); + } + sections[i].removed = 1; + any = 1; + } + + for( i = 1; i < count; ++i ) + if( sections[i].removed && section_link_references( sections, count, i ) ) + { + snprintf( err, errsz, + "--only-section cannot remove referenced section %s", + sections[i].name ); + return( -1 ); + } + if( any ) + *changed = 1; + } + + for( i = 0; i < options->action_count; ++i ) + { + const mcpu_object_edit_action *a = &options->actions[i]; + size_t index; + + if( a->type == MCPU_OBJECT_EDIT_ADD ) + { + if( find_section( sections, count, a->name ) != SIZE_MAX ) + { + snprintf( err, errsz, "section %s already exists", a->name ); + return( -1 ); + } + if( add_section( §ions, &count, a->name, a->data, a->size, + err, errsz ) ) + return( -1 ); + *psections = sections; + *pcount = count; + *changed = 1; + continue; + } + + index = find_section( sections, count, a->name ); + if( index == SIZE_MAX ) + { + snprintf( err, errsz, "section %s does not exist", a->name ); + return( -1 ); + } + + if( a->type == MCPU_OBJECT_EDIT_REMOVE ) + { + if( safe_remove_section( file, sections, count, index, + "--remove-section", err, errsz ) ) + return( -1 ); + *changed = 1; + } + else if( a->type == MCPU_OBJECT_EDIT_RENAME ) + { + char *p; + + if( !section_user_editable( file, §ions[index] ) ) + { + snprintf( err, errsz, "--rename-section cannot rename section %s", + a->name ); + return( -1 ); + } + if( !a->new_name || !*a->new_name ) + { + snprintf( err, errsz, "new section name must not be empty" ); + return( -1 ); + } + if( reserved_section_name( a->new_name ) ) + { + snprintf( err, errsz, "cannot rename metadata to reserved name %s", + a->new_name ); + return( -1 ); + } + if( find_section( sections, count, a->new_name ) != SIZE_MAX ) + { + snprintf( err, errsz, "section %s already exists", a->new_name ); + return( -1 ); + } + p = strdup( a->new_name ); + if( !p ) + { + snprintf( err, errsz, "out of memory" ); + return( -1 ); + } + free( sections[index].name ); + sections[index].name = p; + *changed = 1; + } + else if( a->type == MCPU_OBJECT_EDIT_UPDATE ) + { + if( !section_user_editable( file, §ions[index] ) ) + { + snprintf( err, errsz, "--update-section cannot update section %s", + a->name ); + return( -1 ); + } + if( replace_section_data( §ions[index], a->data, a->size, + err, errsz ) ) + return( -1 ); + *changed = 1; + } + else + { + snprintf( err, errsz, "internal error: invalid edit action" ); + return( -1 ); + } + } + + *psections = sections; + *pcount = count; + return( 0 ); + +} /* End of apply_options() */ + + +static int +build_output( const mcpu_edit_file *file, + mcpu_edit_section *sections, size_t count, + unsigned char **out, size_t *out_size, + char *err, size_t errsz ) +{ + __mpu_uint16_t *map = NULL; + mcpu_edit_shdr *nsh = NULL; + size_t *source_index = NULL; + unsigned char *result = NULL; + unsigned char *newshstr = NULL; + size_t newshstr_size = 1; + size_t newshstr_cap = 64; + __mpu_uint16_t newnum = 0; + __mpu_uint16_t newshstrndx = MELF_SHN_UNDEF; + __mpu_uint64_t fixed_end = MELF64_EHDR_SIZE; + __mpu_uint64_t cur; + __mpu_uint64_t shoff; + __mpu_uint64_t total; + int overflow = 0; + size_t i, j; + int rc = -1; + + map = (__mpu_uint16_t *)malloc( (size_t)file->shnum * sizeof( *map ) ); + if( !map ) + { + snprintf( err, errsz, "out of memory" ); + goto out; + } + for( i = 0; i < file->shnum; ++i ) + map[i] = MELF_SHN_UNDEF; + + for( i = 0; i < count; ++i ) + if( !sections[i].removed ) + { + if( newnum == UINT16_MAX ) + { + snprintf( err, errsz, "too many retained sections" ); + goto out; + } + if( !sections[i].is_new ) + map[sections[i].old_index] = newnum; + if( !sections[i].is_new && sections[i].old_index == file->shstrndx ) + newshstrndx = newnum; + ++newnum; + } + + if( newshstrndx == MELF_SHN_UNDEF ) + { + snprintf( err, errsz, "internal error: .shstrtab was removed" ); + goto out; + } + + nsh = (mcpu_edit_shdr *)calloc( newnum, sizeof( *nsh ) ); + source_index = (size_t *)calloc( newnum, sizeof( *source_index ) ); + newshstr = (unsigned char *)malloc( newshstr_cap ); + if( !nsh || !source_index || !newshstr ) + { + snprintf( err, errsz, "out of memory" ); + goto out; + } + newshstr[0] = 0; + + for( i = 0, j = 0; i < count; ++i ) + if( !sections[i].removed ) + { + size_t n = strlen( sections[i].name ) + 1; + mcpu_edit_shdr sh = sections[i].shdr; + + if( i == 0 ) + sh.name = 0; + else + { + if( n > SIZE_MAX - newshstr_size || + newshstr_size + n > UINT32_MAX ) + { + snprintf( err, errsz, "section-name table overflow" ); + goto out; + } + if( newshstr_size + n > newshstr_cap ) + { + size_t cap = newshstr_cap; + unsigned char *q; + + while( cap < newshstr_size + n ) + { + if( cap > SIZE_MAX / 2 ) + { + snprintf( err, errsz, "section-name table overflow" ); + goto out; + } + cap *= 2; + } + q = (unsigned char *)realloc( newshstr, cap ); + if( !q ) + { + snprintf( err, errsz, "out of memory" ); + goto out; + } + newshstr = q; + newshstr_cap = cap; + } + sh.name = (__mpu_uint32_t)newshstr_size; + memcpy( newshstr + newshstr_size, sections[i].name, n ); + newshstr_size += n; + } + + if( !sections[i].is_new ) + { + if( sh.link ) + { + if( sh.link >= file->shnum || map[sh.link] == MELF_SHN_UNDEF ) + { + snprintf( err, errsz, "retained section %s depends on removed section", + sections[i].name ); + goto out; + } + sh.link = map[sh.link]; + } + if( sh.type == MELF_SHT_RELA && sh.info ) + { + if( sh.info >= file->shnum || map[sh.info] == MELF_SHN_UNDEF ) + { + snprintf( err, errsz, "retained relocation section targets removed section" ); + goto out; + } + sh.info = map[sh.info]; + } + } + if( !sections[i].is_new && sections[i].old_index == file->shstrndx ) + sh.size = newshstr_size; + else + sh.size = sections[i].data_size; + nsh[j] = sh; + source_index[j] = i; + ++j; + } + + /* .shstrtab size is final only after all retained names were appended. */ + nsh[newshstrndx].size = newshstr_size; + + if( file->phnum ) + { + __mpu_uint64_t end = file->phoff + + (__mpu_uint64_t)file->phnum * MELF64_PHDR_SIZE; + + if( end > fixed_end ) + fixed_end = end; + for( i = 0; i < file->phnum; ++i ) + { + const unsigned char *p = file->data + (size_t)file->phoff + + i * MELF64_PHDR_SIZE; + __mpu_uint32_t type = cpu_melf_get32( p + MELF64_PHDR_P_TYPE_OFF, + file->encoding ); + __mpu_uint64_t off = cpu_melf_get64( p + MELF64_PHDR_P_OFFSET_OFF, + file->encoding ); + __mpu_uint64_t filesz = cpu_melf_get64( p + MELF64_PHDR_P_FILESZ_OFF, + file->encoding ); + + if( type == MELF_PT_LOAD && filesz ) + { + if( !cpu_melf_span( file->size, off, filesz ) ) + { + snprintf( err, errsz, "PT_LOAD has an invalid file range" ); + goto out; + } + if( off + filesz > fixed_end ) + fixed_end = off + filesz; + } + } + } + + for( i = 0; i < count; ++i ) + if( !sections[i].removed && !sections[i].is_new && + ( sections[i].shdr.flags & MELF_SHF_ALLOC ) && + sections[i].shdr.type != MELF_SHT_NOBITS && + sections[i].shdr.size ) + { + __mpu_uint64_t end = sections[i].shdr.offset + sections[i].shdr.size; + + if( end > fixed_end ) + fixed_end = end; + } + + if( fixed_end > file->size ) + { + snprintf( err, errsz, "fixed MELF image exceeds file size" ); + goto out; + } + + cur = fixed_end; + for( i = 0; i < newnum; ++i ) + { + size_t si = source_index[i]; + + if( nsh[i].flags & MELF_SHF_ALLOC ) + continue; + if( nsh[i].type == MELF_SHT_NOBITS || !nsh[i].size ) + continue; + cur = cpu_melf_align_up( cur, nsh[i].align ? nsh[i].align : 1, + &overflow ); + if( overflow ) + { + snprintf( err, errsz, "MELF layout overflow" ); + goto out; + } + nsh[i].offset = cur; + if( nsh[i].size > UINT64_MAX - cur ) + { + snprintf( err, errsz, "MELF layout overflow" ); + goto out; + } + cur += nsh[i].size; + (void)si; + } + + shoff = cpu_melf_align_up( cur, 8, &overflow ); + if( overflow || (__mpu_uint64_t)newnum > UINT64_MAX / MELF64_SHDR_SIZE || + shoff > UINT64_MAX - (__mpu_uint64_t)newnum * MELF64_SHDR_SIZE ) + { + snprintf( err, errsz, "section table overflow" ); + goto out; + } + total = shoff + (__mpu_uint64_t)newnum * MELF64_SHDR_SIZE; + if( total > SIZE_MAX ) + { + snprintf( err, errsz, "MELF file is too large for this host" ); + goto out; + } + + result = (unsigned char *)calloc( (size_t)total ? (size_t)total : 1, 1 ); + if( !result ) + { + snprintf( err, errsz, "out of memory" ); + goto out; + } + if( fixed_end ) + memcpy( result, file->data, (size_t)fixed_end ); + + for( i = 0; i < newnum; ++i ) + { + size_t si = source_index[i]; + const mcpu_edit_section *s = §ions[si]; + + if( nsh[i].type == MELF_SHT_NOBITS || !nsh[i].size ) + continue; + if( !cpu_melf_span( (size_t)total, nsh[i].offset, nsh[i].size ) ) + { + snprintf( err, errsz, "section copy exceeds output bounds" ); + goto out; + } + if( !s->is_new && s->old_index == file->shstrndx ) + memcpy( result + (size_t)nsh[i].offset, newshstr, newshstr_size ); + else if( s->data_size ) + memcpy( result + (size_t)nsh[i].offset, s->data, s->data_size ); + } + + /* Remap section indices stored inside retained symbol tables. */ + for( i = 0; i < newnum; ++i ) + if( nsh[i].type == MELF_SHT_SYMTAB || nsh[i].type == MELF_SHT_DYNSYM ) + { + size_t n; + + if( nsh[i].entsize != MELF64_SYM_SIZE || + nsh[i].size % MELF64_SYM_SIZE ) + { + snprintf( err, errsz, "retained symbol table is malformed" ); + goto out; + } + n = (size_t)( nsh[i].size / MELF64_SYM_SIZE ); + for( j = 0; j < n; ++j ) + { + unsigned char *p = result + (size_t)nsh[i].offset + + j * MELF64_SYM_SIZE; + __mpu_uint16_t shndx = cpu_melf_get16( + p + MELF64_SYM_ST_SHNDX_OFF, file->encoding ); + + if( shndx == MELF_SHN_UNDEF || shndx >= MELF_SHN_LORESERVE ) + continue; + if( shndx >= file->shnum || map[shndx] == MELF_SHN_UNDEF ) + { + snprintf( err, errsz, "retained symbol refers to removed section" ); + goto out; + } + cpu_melf_put16( p + MELF64_SYM_ST_SHNDX_OFF, map[shndx], + file->encoding ); + } + } + + cpu_melf_put64( result + MELF64_EHDR_E_SHOFF_OFF, shoff, file->encoding ); + cpu_melf_put16( result + MELF64_EHDR_E_SHNUM_OFF, newnum, file->encoding ); + cpu_melf_put16( result + MELF64_EHDR_E_SHSTRNDX_OFF, + newshstrndx, file->encoding ); + + for( i = 0; i < newnum; ++i ) + put_shdr( result + (size_t)shoff + i * MELF64_SHDR_SIZE, + file->encoding, &nsh[i] ); + + *out = result; + *out_size = (size_t)total; + result = NULL; + rc = 0; + +out: + free( result ); + free( newshstr ); + free( source_index ); + free( nsh ); + free( map ); + return( rc ); + +} /* End of build_output() */ + + +int +mcpu_object_edit_data( const unsigned char *data, size_t size, + const mcpu_object_edit_options *options, + unsigned char **out, size_t *out_size, + int *changed, char *err, size_t errsz ) +{ + mcpu_edit_file file; + mcpu_edit_section *sections = NULL; + size_t count = 0; + int local_changed = 0; + int rc = -1; + + if( !data || !options || !out || !out_size || !changed ) + return( -1 ); + *out = NULL; + *out_size = 0; + *changed = 0; + memset( &file, 0, sizeof( file ) ); + + if( edit_file_open( data, size, &file, err, errsz ) || + sections_from_file( &file, §ions, &count, err, errsz ) ) + goto out; + + if( apply_options( &file, §ions, &count, options, &local_changed, + err, errsz ) ) + goto out; + + if( !local_changed ) + { + rc = copy_original( data, size, out, out_size, err, errsz ); + goto out; + } + if( build_output( &file, sections, count, out, out_size, err, errsz ) ) + goto out; + + *changed = 1; + rc = 0; + +out: + sections_free( sections, count ); + edit_file_free( &file ); + return( rc ); + +} /* End of mcpu_object_edit_data() */ + + +int +mcpu_object_strip_data( const unsigned char *data, size_t size, + unsigned char **out, size_t *out_size, + int *changed, char *err, size_t errsz ) +{ + mcpu_edit_file file; + mcpu_edit_section *sections = NULL; + size_t count = 0; + size_t i; + int any = 0; + int rc = -1; + + if( !data || !out || !out_size || !changed ) + return( -1 ); + *out = NULL; + *out_size = 0; + *changed = 0; + memset( &file, 0, sizeof( file ) ); + + if( edit_file_open( data, size, &file, err, errsz ) || + sections_from_file( &file, §ions, &count, err, errsz ) ) + goto out; + + if( file.type != MELF_ET_REL ) + { + for( i = 1; i < count; ++i ) + if( sections[i].shdr.type == MELF_SHT_SYMTAB && + !( sections[i].shdr.flags & MELF_SHF_ALLOC ) && + !section_link_references( sections, count, i ) ) + { + __mpu_uint32_t link = sections[i].shdr.link; + + sections[i].removed = 1; + any = 1; + if( link && link < file.shnum && link != file.shstrndx && + sections[link].shdr.type == MELF_SHT_STRTAB ) + { + size_t k; + int referenced = 0; + + for( k = 1; k < count; ++k ) + if( !sections[k].removed && !sections[k].is_new && + sections[k].shdr.link == link ) + referenced = 1; + if( !referenced ) + sections[link].removed = 1; + } + } + } + + if( !any ) + { + rc = copy_original( data, size, out, out_size, err, errsz ); + goto out; + } + if( build_output( &file, sections, count, out, out_size, err, errsz ) ) + goto out; + + *changed = 1; + rc = 0; + +out: + sections_free( sections, count ); + edit_file_free( &file ); + return( rc ); + +} /* End of mcpu_object_strip_data() */ + + +int +mcpu_object_dump_section( const unsigned char *data, size_t size, + const char *name, + unsigned char **out, size_t *out_size, + char *err, size_t errsz ) +{ + mcpu_edit_file file; + size_t i; + int rc = -1; + + if( !data || !name || !out || !out_size ) + return( -1 ); + *out = NULL; + *out_size = 0; + memset( &file, 0, sizeof( file ) ); + + if( edit_file_open( data, size, &file, err, errsz ) ) + return( -1 ); + + for( i = 1; i < file.shnum; ++i ) + { + const char *s = name_at( &file, file.shdr[i].name ); + + if( s && !strcmp( s, name ) ) + { + if( file.shdr[i].type == MELF_SHT_NOBITS ) + { + snprintf( err, errsz, "section %s has no file contents", name ); + goto out; + } + rc = copy_original( file.data + (size_t)file.shdr[i].offset, + (size_t)file.shdr[i].size, + out, out_size, err, errsz ); + goto out; + } + } + snprintf( err, errsz, "section %s does not exist", name ); + +out: + edit_file_free( &file ); + return( rc ); + +} /* End of mcpu_object_dump_section() */ |
