#ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #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 || shoff > UINT64_MAX - (__mpu_uint64_t)newnum * (__mpu_uint64_t)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() */