diff options
Diffstat (limited to 'utils/mcpu-object.c')
| -rw-r--r-- | utils/mcpu-object.c | 1489 |
1 files changed, 1489 insertions, 0 deletions
diff --git a/utils/mcpu-object.c b/utils/mcpu-object.c new file mode 100644 index 0000000..0b36788 --- /dev/null +++ b/utils/mcpu-object.c @@ -0,0 +1,1489 @@ +#include <errno.h> +#include <limits.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <libmcpu.h> +#include <mcpu-elf.h> + +#include "mcpu-object.h" +#include "mcpu-melf.h" + +typedef struct blob +{ + unsigned char *p; + size_t n; + size_t cap; +} blob; + +typedef struct wsec +{ + const char *name; + __mpu_uint32_t type; + __mpu_uint64_t flags; + __mpu_uint64_t align; + __mpu_uint64_t entsize; + __mpu_uint32_t link; + __mpu_uint32_t info; + unsigned char *owned; + const unsigned char *payload; + size_t size; + __mpu_uint64_t off; + __mpu_uint32_t name_off; + unsigned logical; +} wsec; + +typedef struct rsh +{ + __mpu_uint32_t name; + __mpu_uint32_t type; + __mpu_uint64_t flags; + __mpu_uint64_t addr; + __mpu_uint64_t off; + __mpu_uint64_t size; + __mpu_uint32_t link; + __mpu_uint32_t info; + __mpu_uint64_t align; + __mpu_uint64_t entsize; +} rsh; + +static const unsigned object_sections[] = +{ + CPU_OBJ_SEC_INIT, + CPU_OBJ_SEC_TEXT, + CPU_OBJ_SEC_FINI, + CPU_OBJ_SEC_DATA, + CPU_OBJ_SEC_BSS, + CPU_OBJ_SEC_CONTROL, + CPU_OBJ_SEC_INPUT, + CPU_OBJ_SEC_OUTPUT, + CPU_OBJ_SEC_TRACE +}; + +#define OBJECT_SECTION_COUNT \ + (sizeof(object_sections) / sizeof(object_sections[0])) + +static int +object_add_symbol( cpu_asm_object *o, const char *name, unsigned section, + unsigned binding, unsigned type, __mpu_uint64_t value, + char *err, size_t es) +{ + cpu_asm_symbol *q; + + if (!o || !name) + return( -1 ); + + q = (cpu_asm_symbol *)realloc( o->symbols, + (o->symbol_count + 1) * sizeof(*q)); + if (!q) + { + snprintf( err, es, "out of memory" ); + return( -1 ); + } + + o->symbols = q; + memset( &o->symbols[o->symbol_count], 0, + sizeof(o->symbols[o->symbol_count])); + o->symbols[o->symbol_count].name = strdup( name ); + if (!o->symbols[o->symbol_count].name) + { + snprintf( err, es, "out of memory" ); + return( -1 ); + } + + o->symbols[o->symbol_count].section = section; + o->symbols[o->symbol_count].binding = binding; + o->symbols[o->symbol_count].type = type; + o->symbols[o->symbol_count].value = value; + ++o->symbol_count; + return( 0 ); +} + +static int +object_add_reloc( cpu_asm_object *o, unsigned section, unsigned type, + size_t symbol_index, __mpu_uint64_t offset, + __mpu_int64_t addend, char *err, size_t es) +{ + cpu_asm_reloc *q; + + q = (cpu_asm_reloc *)realloc( o->relocs, + (o->reloc_count + 1) * sizeof(*q)); + if (!q) + { + snprintf( err, es, "out of memory" ); + return( -1 ); + } + + o->relocs = q; + o->relocs[o->reloc_count].section = section; + o->relocs[o->reloc_count].type = type; + o->relocs[o->reloc_count].symbol_index = symbol_index; + o->relocs[o->reloc_count].offset = offset; + o->relocs[o->reloc_count].addend = addend; + ++o->reloc_count; + return( 0 ); +} + +static int +blob_grow( blob *b, size_t add ) +{ + size_t need; + size_t cap; + unsigned char *q; + + if (add > SIZE_MAX - b->n) + return( -1 ); + need = b->n + add; + if (need <= b->cap) + return( 0 ); + + cap = b->cap ? b->cap : 64; + while (cap < need) + { + if (cap > SIZE_MAX / 2) + return( -1 ); + cap *= 2; + } + + q = (unsigned char *)realloc( b->p, cap ); + if (!q) + return( -1 ); + b->p = q; + b->cap = cap; + return( 0 ); +} + +static int +blob_put( blob *b, const void *p, size_t n ) +{ + if( blob_grow( b, n ) ) + return( -1 ); + if (n) + memcpy( b->p + b->n, p, n ); + b->n += n; + return( 0 ); +} + +static int +blob_string( blob *b, const char *s, __mpu_uint32_t *off ) +{ + size_t n = strlen( s ) + 1; + + if (b->n > UINT32_MAX || n > (size_t)UINT32_MAX - b->n) + return( -1 ); + *off = (__mpu_uint32_t)b->n; + return( blob_put( b, s, n ) ); +} + +static void +free_wsecs( wsec *s, size_t n ) +{ + size_t i; + for( i = 0; i < n; ++i ) + free( s[i].owned ); +} + +static void +put_ehdr64( unsigned char *p, int data, __mpu_uint16_t type, + __mpu_uint64_t shoff, __mpu_uint16_t shnum, + __mpu_uint16_t shstr) +{ + memset( p, 0, MELF64_EHDR_SIZE ); + p[MELF_EI_MAG0] = MELFMAG0; + p[MELF_EI_MAG1] = MELFMAG1; + p[MELF_EI_MAG2] = MELFMAG2; + p[MELF_EI_MAG3] = MELFMAG3; + p[MELF_EI_CLASS] = MELF_CLASS_64; + p[MELF_EI_DATA] = (unsigned char)data; + p[MELF_EI_VERSION] = MELF_EV_CURRENT; + p[MELF_EI_OSABI] = MELF_OSABI_MCPU; + p[MELF_EI_ABIVERSION] = MELF_ABIVERSION_CURRENT; + + cpu_melf_put16( p + MELF64_EHDR_E_TYPE_OFF, type, data ); + cpu_melf_put16( p + MELF64_EHDR_E_MACHINE_OFF, MELF_EM_MCPU, data ); + cpu_melf_put32( p + MELF64_EHDR_E_VERSION_OFF, MELF_EV_CURRENT, data ); + cpu_melf_put64( p + MELF64_EHDR_E_ENTRY_OFF, 0, data ); + cpu_melf_put64( p + MELF64_EHDR_E_INIT_ENTRY_OFF, 0, data ); + cpu_melf_put64( p + MELF64_EHDR_E_LIFE_ENTRY_OFF, 0, data ); + cpu_melf_put64( p + MELF64_EHDR_E_FINI_ENTRY_OFF, 0, data ); + cpu_melf_put64( p + MELF64_EHDR_E_PHOFF_OFF, 0, data ); + cpu_melf_put64( p + MELF64_EHDR_E_SHOFF_OFF, shoff, data ); + cpu_melf_put32( p + MELF64_EHDR_E_FLAGS_OFF, MELF_EF_MCPU_NONE, data ); + cpu_melf_put16( p + MELF64_EHDR_E_EHSIZE_OFF, MELF64_EHDR_SIZE, data ); + cpu_melf_put16( p + MELF64_EHDR_E_PHENTSIZE_OFF, 0, data ); + cpu_melf_put16( p + MELF64_EHDR_E_PHNUM_OFF, 0, data ); + cpu_melf_put16( p + MELF64_EHDR_E_SHENTSIZE_OFF, MELF64_SHDR_SIZE, data ); + cpu_melf_put16( p + MELF64_EHDR_E_SHNUM_OFF, shnum, data ); + cpu_melf_put16( p + MELF64_EHDR_E_SHSTRNDX_OFF, shstr, data ); +} + +static void +put_shdr64( unsigned char *p, const wsec *s, int data ) +{ + memset( p, 0, MELF64_SHDR_SIZE ); + cpu_melf_put32( p + MELF64_SHDR_SH_NAME_OFF, s->name_off, data ); + cpu_melf_put32( p + MELF64_SHDR_SH_TYPE_OFF, s->type, data ); + cpu_melf_put64( p + MELF64_SHDR_SH_FLAGS_OFF, s->flags, data ); + cpu_melf_put64( p + MELF64_SHDR_SH_ADDR_OFF, 0, data ); + cpu_melf_put64( p + MELF64_SHDR_SH_OFFSET_OFF, s->off, data ); + cpu_melf_put64( p + MELF64_SHDR_SH_SIZE_OFF, s->size, data ); + cpu_melf_put32( p + MELF64_SHDR_SH_LINK_OFF, s->link, data ); + cpu_melf_put32( p + MELF64_SHDR_SH_INFO_OFF, s->info, data ); + cpu_melf_put64( p + MELF64_SHDR_SH_ADDRALIGN_OFF, s->align, data ); + cpu_melf_put64( p + MELF64_SHDR_SH_ENTSIZE_OFF, s->entsize, data ); +} + +static void +get_shdr64( const unsigned char *p, int data, rsh *s ) +{ + memset( s, 0, sizeof( *s ) ); + s->name = cpu_melf_get32( p + MELF64_SHDR_SH_NAME_OFF, data ); + s->type = cpu_melf_get32( p + MELF64_SHDR_SH_TYPE_OFF, data ); + s->flags = cpu_melf_get64( p + MELF64_SHDR_SH_FLAGS_OFF, data ); + s->addr = cpu_melf_get64( p + MELF64_SHDR_SH_ADDR_OFF, data ); + s->off = cpu_melf_get64( p + MELF64_SHDR_SH_OFFSET_OFF, data ); + s->size = cpu_melf_get64( p + MELF64_SHDR_SH_SIZE_OFF, data ); + s->link = cpu_melf_get32( p + MELF64_SHDR_SH_LINK_OFF, data ); + s->info = cpu_melf_get32( p + MELF64_SHDR_SH_INFO_OFF, data ); + s->align = cpu_melf_get64( p + MELF64_SHDR_SH_ADDRALIGN_OFF, data ); + s->entsize = cpu_melf_get64( p + MELF64_SHDR_SH_ENTSIZE_OFF, data ); +} + +static int +shdr_is_zero( const rsh *s ) +{ + return s->name == 0 && s->type == 0 && s->flags == 0 && s->addr == 0 && + s->off == 0 && s->size == 0 && s->link == 0 && s->info == 0 && + s->align == 0 && s->entsize == 0; +} + +static __mpu_uint64_t +sec_flags( unsigned logical ) +{ + switch (logical) + { + case CPU_OBJ_SEC_INIT: return( MELF_SHFLAGS_INIT ); + case CPU_OBJ_SEC_TEXT: return( MELF_SHFLAGS_TEXT ); + case CPU_OBJ_SEC_FINI: return( MELF_SHFLAGS_FINI ); + case CPU_OBJ_SEC_DATA: return( MELF_SHFLAGS_DATA ); + case CPU_OBJ_SEC_BSS: return( MELF_SHFLAGS_BSS ); + case CPU_OBJ_SEC_CONTROL: return( MELF_SHFLAGS_CONTROL ); + case CPU_OBJ_SEC_INPUT: return( MELF_SHFLAGS_INPUT ); + case CPU_OBJ_SEC_OUTPUT: return( MELF_SHFLAGS_OUTPUT ); + case CPU_OBJ_SEC_TRACE: return( MELF_SHFLAGS_TRACE ); + default: return( 0 ); + } +} + +static const char * +sec_name( unsigned logical ) +{ + switch (logical) + { + case CPU_OBJ_SEC_INIT: return( MELF_SEC_INIT ); + case CPU_OBJ_SEC_TEXT: return( MELF_SEC_TEXT ); + case CPU_OBJ_SEC_FINI: return( MELF_SEC_FINI ); + case CPU_OBJ_SEC_DATA: return( MELF_SEC_DATA ); + case CPU_OBJ_SEC_BSS: return( MELF_SEC_BSS ); + case CPU_OBJ_SEC_CONTROL: return( MELF_SEC_CONTROL ); + case CPU_OBJ_SEC_INPUT: return( MELF_SEC_INPUT ); + case CPU_OBJ_SEC_OUTPUT: return( MELF_SEC_OUTPUT ); + case CPU_OBJ_SEC_TRACE: return( MELF_SEC_TRACE ); + default: return( "" ); + } +} + +static const char * +rela_name( unsigned logical ) +{ + switch (logical) + { + case CPU_OBJ_SEC_INIT: return( MELF_SEC_RELA_INIT ); + case CPU_OBJ_SEC_TEXT: return( MELF_SEC_RELA_TEXT ); + case CPU_OBJ_SEC_FINI: return( MELF_SEC_RELA_FINI ); + case CPU_OBJ_SEC_DATA: return( MELF_SEC_RELA_DATA ); + case CPU_OBJ_SEC_CONTROL: return( MELF_SEC_RELA_CONTROL ); + case CPU_OBJ_SEC_INPUT: return( MELF_SEC_RELA_INPUT ); + case CPU_OBJ_SEC_OUTPUT: return( MELF_SEC_RELA_OUTPUT ); + case CPU_OBJ_SEC_TRACE: return( MELF_SEC_RELA_TRACE ); + default: return( NULL ); + } +} + +static int +obj_sec( const cpu_asm_object *o, unsigned logical, const unsigned char **p, + size_t *n, __mpu_uint64_t *a, int *present) +{ + switch (logical) + { + case CPU_OBJ_SEC_INIT: + *p = o->init; + *n = o->init_size; + *a = o->init_align; + *present = o->has_init; + return( 0 ); + case CPU_OBJ_SEC_TEXT: + *p = o->text; + *n = o->text_size; + *a = o->text_align; + *present = o->has_text; + return( 0 ); + case CPU_OBJ_SEC_FINI: + *p = o->fini; + *n = o->fini_size; + *a = o->fini_align; + *present = o->has_fini; + return( 0 ); + case CPU_OBJ_SEC_DATA: + *p = o->data; + *n = o->data_size; + *a = o->data_align; + *present = o->has_data || o->data_size != 0; + return( 0 ); + case CPU_OBJ_SEC_BSS: + *p = NULL; + *n = o->bss_size; + *a = o->bss_align; + *present = o->has_bss || o->bss_size != 0; + return( 0 ); + case CPU_OBJ_SEC_CONTROL: + *p = o->control; + *n = o->control_size; + *a = o->control_align; + *present = o->has_control || o->control_size != 0; + return( 0 ); + case CPU_OBJ_SEC_INPUT: + *p = o->input; + *n = o->input_size; + *a = o->input_align; + *present = o->has_input || o->input_size != 0; + return( 0 ); + case CPU_OBJ_SEC_OUTPUT: + *p = o->output; + *n = o->output_size; + *a = o->output_align; + *present = o->has_output || o->output_size != 0; + return( 0 ); + case CPU_OBJ_SEC_TRACE: + *p = o->trace; + *n = o->trace_size; + *a = o->trace_align; + *present = o->has_trace || o->trace_size != 0; + return( 0 ); + default: + return( -1 ); + } +} + +static int +logical_index( const __mpu_uint16_t idx[OBJECT_SECTION_COUNT], unsigned logical ) +{ + size_t i; + for( i = 0; i < OBJECT_SECTION_COUNT; ++i ) + if (object_sections[i] == logical) + return( idx[i] ); + return( 0 ); +} + +static int +validate_object_for_write( const cpu_asm_object *o, char *err, size_t es ) +{ + size_t i; + + for( i = 0; i < OBJECT_SECTION_COUNT; ++i ) + { + const unsigned char *p; + size_t n; + __mpu_uint64_t a; + int present; + + if( obj_sec( o, object_sections[i], &p, &n, &a, &present ) ) + return( -1 ); + if (!present) + continue; + if( a == 0 || !cpu_melf_is_pow2( a ) ) + { + snprintf( err, es, "invalid alignment for %s", sec_name( object_sections[i] ) ); + return( -1 ); + } + if (object_sections[i] != CPU_OBJ_SEC_BSS && n && !p) + { + snprintf( err, es, "missing payload for %s", sec_name( object_sections[i] ) ); + return( -1 ); + } + } + + for( i = 0; i < o->symbol_count; ++i ) + { + const cpu_asm_symbol *s = &o->symbols[i]; + const unsigned char *p; + size_t n; + __mpu_uint64_t a; + int present; + + if (!s->name || !*s->name) + { + snprintf( err, es, "empty symbol name" ); + return( -1 ); + } + if (s->binding > CPU_OBJ_BIND_WEAK || s->type > CPU_OBJ_TYPE_FUNC) + { + snprintf( err, es, "invalid symbol attributes: %s", s->name ); + return( -1 ); + } + if (s->section == CPU_OBJ_SEC_UNDEF) + { + if (s->binding == CPU_OBJ_BIND_LOCAL || s->value != 0 || s->size != 0) + { + snprintf( err, es, "malformed undefined symbol: %s", s->name ); + return( -1 ); + } continue; + } + if( obj_sec( o, s->section, &p, &n, &a, &present ) || !present ) + { + snprintf( err, es, "symbol %s belongs to absent section", s->name ); + return( -1 ); + } + if (s->value > n || s->size > (__mpu_uint64_t)n - s->value) + { + snprintf( err, es, "symbol range outside section: %s", s->name ); + return( -1 ); + } + } + + for( i = 0; i < o->reloc_count; ++i ) + { + const cpu_asm_reloc *r = &o->relocs[i]; + const unsigned char *p; + size_t n; + __mpu_uint64_t a; + int present; + + if (r->type < CPU_RELOC_CODE64 || r->type > CPU_RELOC_CPTR64_DATA) + { + snprintf( err, es, "invalid relocation type" ); + return( -1 ); + } + if (r->symbol_index >= o->symbol_count) + { + snprintf( err, es, "bad relocation symbol index" ); + return( -1 ); + } + if (r->section == CPU_OBJ_SEC_BSS) + { + snprintf( err, es, "relocations cannot patch SHT_NOBITS .bss" ); + return( -1 ); + } + if( obj_sec( o, r->section, &p, &n, &a, &present ) || !present ) + { + snprintf( err, es, "relocation refers to absent source section" ); + return( -1 ); + } + if (r->offset > n || CPU_PTR64_SIZE > n - (size_t)r->offset) + { + snprintf( err, es, "relocation field outside source section" ); + return( -1 ); + } + } + + return( 0 ); +} + +int +cpu_object_write( const char *path, const cpu_asm_object *o, + char *err, size_t es) +{ + wsec s[32]; + size_t ns = 1; + size_t i, j; + __mpu_uint16_t secidx[OBJECT_SECTION_COUNT] = {0}; + __mpu_uint16_t relaidx[OBJECT_SECTION_COUNT] = {0}; + int data = cpu_melf_data_encoding(); + int ov = 0; + blob str = {0}; + blob shstr = {0}; + __mpu_uint32_t *nameoff = NULL; + __mpu_uint32_t *symmap = NULL; + size_t nlocal = 0, nonlocal = 0, symcount; + unsigned char *sympay = NULL; + unsigned char *file = NULL; + size_t symsz; + __mpu_uint16_t symsec, strsec, targetsec, shstrsec; + MElf_McpuTarget mt; + unsigned char mtbuf[MELF_MCPU_TARGET_SIZE]; + __mpu_uint64_t cur, shoff, total; + + if (!path || !o) + return( -1 ); + if( validate_object_for_write( o, err, es ) ) + return( -1 ); + + memset( s, 0, sizeof( s ) ); + if( blob_put( &str, "", 1 ) || blob_put( &shstr, "", 1 ) ) + { + snprintf( err, es, "out of memory" ); + goto fail; + } + + for( i = 0; i < OBJECT_SECTION_COUNT; ++i ) + { + const unsigned char *p; + size_t n; + __mpu_uint64_t a; + int present; + unsigned logical = object_sections[i]; + + if( obj_sec( o, logical, &p, &n, &a, &present ) ) + goto fail; + if (!present) + continue; + if (ns >= sizeof(s) / sizeof(s[0])) + { + snprintf( err, es, "too many MELF sections" ); + goto fail; + } + + secidx[i] = (__mpu_uint16_t)ns; + s[ns].name = sec_name( logical ); + s[ns].type = logical == CPU_OBJ_SEC_BSS ? MELF_SHT_NOBITS + : MELF_SHT_PROGBITS; + s[ns].flags = sec_flags( logical ); + s[ns].align = a; + s[ns].payload = p; + s[ns].size = n; + s[ns].logical = logical; + ++ns; + } + + for( i = 0; i < OBJECT_SECTION_COUNT; ++i ) + { + size_t cnt = 0; + const char *rn; + + for( j = 0; j < o->reloc_count; ++j ) + if (o->relocs[j].section == object_sections[i]) + ++cnt; + if (!cnt) + continue; + + rn = rela_name( object_sections[i] ); + if (!rn || !secidx[i]) + { + snprintf( err, es, "invalid relocation source section" ); + goto fail; + } + if (ns >= sizeof(s) / sizeof(s[0])) + { + snprintf( err, es, "too many MELF sections" ); + goto fail; + } + + relaidx[i] = (__mpu_uint16_t)ns; + s[ns].name = rn; + s[ns].type = MELF_SHT_RELA; + s[ns].align = 8; + s[ns].entsize = MELF64_RELA_SIZE; + s[ns].info = secidx[i]; + s[ns].size = cnt * MELF64_RELA_SIZE; + s[ns].logical = object_sections[i]; + ++ns; + } + + symsec = (__mpu_uint16_t)ns; + s[ns].name = MELF_SEC_SYMTAB; + s[ns].type = MELF_SHT_SYMTAB; + s[ns].align = 8; + s[ns].entsize = MELF64_SYM_SIZE; + ++ns; + + strsec = (__mpu_uint16_t)ns; + s[ns].name = MELF_SEC_STRTAB; + s[ns].type = MELF_SHT_STRTAB; + s[ns].align = 1; + ++ns; + + targetsec = (__mpu_uint16_t)ns; + s[ns].name = MELF_SEC_MCPU_TARGET; + s[ns].type = MELF_SHT_MCPU_TARGET; + s[ns].align = 8; + s[ns].entsize = MELF_MCPU_TARGET_SIZE; + s[ns].size = MELF_MCPU_TARGET_SIZE; + ++ns; + + shstrsec = (__mpu_uint16_t)ns; + s[ns].name = MELF_SEC_SHSTRTAB; + s[ns].type = MELF_SHT_STRTAB; + s[ns].align = 1; + ++ns; + + nameoff = (__mpu_uint32_t *)calloc( o->symbol_count ? o->symbol_count : 1, + sizeof(*nameoff)); + symmap = (__mpu_uint32_t *)calloc( o->symbol_count ? o->symbol_count : 1, + sizeof(*symmap)); + if (!nameoff || !symmap) + { + snprintf( err, es, "out of memory" ); + goto fail; + } + + for( i = 0; i < o->symbol_count; ++i ) + { + if( blob_string( &str, o->symbols[i].name, &nameoff[i] ) ) + { + snprintf( err, es, "symbol string table too large" ); + goto fail; + } + if (o->symbols[i].binding == CPU_OBJ_BIND_LOCAL) + ++nlocal; + else + ++nonlocal; + } + + if (nlocal > UINT32_MAX - 1 || nonlocal > UINT32_MAX - 1 - nlocal) + { + snprintf( err, es, "too many symbols" ); + goto fail; + } + symcount = 1 + nlocal + nonlocal; + if (symcount > SIZE_MAX / MELF64_SYM_SIZE) + { + snprintf( err, es, "symbol table too large" ); + goto fail; + } + symsz = symcount * MELF64_SYM_SIZE; + sympay = (unsigned char *)calloc( symsz ? symsz : 1, 1 ); + if (!sympay) + { + snprintf( err, es, "out of memory" ); + goto fail; + } + + { + size_t li = 1; + size_t gi = 1 + nlocal; + + for( i = 0; i < o->symbol_count; ++i ) + { + const cpu_asm_symbol *x = &o->symbols[i]; + size_t mi = x->binding == CPU_OBJ_BIND_LOCAL ? li++ : gi++; + unsigned bind; + unsigned type; + __mpu_uint16_t shn = MELF_SHN_UNDEF; + unsigned char *q = sympay + mi * MELF64_SYM_SIZE; + + if (x->binding == CPU_OBJ_BIND_LOCAL) + bind = MELF_STB_LOCAL; + else if (x->binding == CPU_OBJ_BIND_WEAK) + bind = MELF_STB_WEAK; + else + bind = MELF_STB_GLOBAL; + + if (x->type == CPU_OBJ_TYPE_FUNC) + type = MELF_STT_FUNC; + else if (x->type == CPU_OBJ_TYPE_OBJECT) + type = MELF_STT_OBJECT; + else + type = MELF_STT_NOTYPE; + + if (x->section != CPU_OBJ_SEC_UNDEF) + { + int idx = logical_index( secidx, x->section ); + if (!idx) + { + snprintf( err, es, "symbol %s belongs to absent section", x->name ); + goto fail; + } + shn = (__mpu_uint16_t)idx; + } + + symmap[i] = (__mpu_uint32_t)mi; + cpu_melf_put32( q + MELF64_SYM_ST_NAME_OFF, nameoff[i], data ); + q[MELF64_SYM_ST_INFO_OFF] = MELF64_ST_INFO( bind, type ); + q[MELF64_SYM_ST_OTHER_OFF] = MELF_STV_DEFAULT; + cpu_melf_put16( q + MELF64_SYM_ST_SHNDX_OFF, shn, data ); + cpu_melf_put64( q + MELF64_SYM_ST_VALUE_OFF, x->value, data ); + cpu_melf_put64( q + MELF64_SYM_ST_SIZE_OFF, x->size, data ); + } + } + + s[symsec].owned = sympay; + s[symsec].payload = sympay; + s[symsec].size = symsz; + s[symsec].link = strsec; + s[symsec].info = (__mpu_uint32_t)(1 + nlocal); + sympay = NULL; + + s[strsec].owned = str.p; + s[strsec].payload = str.p; + s[strsec].size = str.n; + str.p = NULL; + + cpu_melf_fill_target( &mt ); + cpu_melf_encode_target( mtbuf, &mt, data ); + s[targetsec].owned = (unsigned char *)malloc( MELF_MCPU_TARGET_SIZE ); + if (!s[targetsec].owned) + { + snprintf( err, es, "out of memory" ); + goto fail; + } + memcpy( s[targetsec].owned, mtbuf, MELF_MCPU_TARGET_SIZE ); + s[targetsec].payload = s[targetsec].owned; + + for( i = 0; i < OBJECT_SECTION_COUNT; ++i ) + { + if (relaidx[i]) + { + size_t cnt = 0; + wsec *r = &s[relaidx[i]]; + + r->link = symsec; + r->owned = (unsigned char *)calloc( r->size ? r->size : 1, 1 ); + if (!r->owned) + { + snprintf( err, es, "out of memory" ); + goto fail; + } + r->payload = r->owned; + + for( j = 0; j < o->reloc_count; ++j ) + { + const cpu_asm_reloc *x; + unsigned char *q; + + if (o->relocs[j].section != object_sections[i]) + continue; + x = &o->relocs[j]; + q = r->owned + cnt * MELF64_RELA_SIZE; + cpu_melf_put64( q + MELF64_RELA_R_OFFSET_OFF, x->offset, data ); + cpu_melf_put64( q + MELF64_RELA_R_INFO_OFF, + MELF64_R_INFO( symmap[x->symbol_index], x->type ), data); + cpu_melf_put64( q + MELF64_RELA_R_ADDEND_OFF, + (__mpu_uint64_t)x->addend, data); + ++cnt; + } + } + } + + for( i = 1; i < ns; ++i ) + if( blob_string( &shstr, s[i].name, &s[i].name_off ) ) + { + snprintf( err, es, "section-name string table too large" ); + goto fail; + } + + s[shstrsec].owned = shstr.p; + s[shstrsec].payload = shstr.p; + s[shstrsec].size = shstr.n; + shstr.p = NULL; + + cur = MELF64_EHDR_SIZE; + for( i = 1; i < ns; ++i ) + { + cur = cpu_melf_align_up( cur, s[i].align ? s[i].align : 1, &ov ); + if (ov || cur > SIZE_MAX) + { + snprintf( err, es, "MELF file layout overflow" ); + goto fail; + } + s[i].off = cur; + if (s[i].type != MELF_SHT_NOBITS) + { + if ((__mpu_uint64_t)s[i].size > UINT64_MAX - cur) + { + snprintf( err, es, "MELF file layout overflow" ); + goto fail; + } + cur += s[i].size; + } + } + + shoff = cpu_melf_align_up( cur, 8, &ov ); + if (ov || ns > UINT64_MAX / MELF64_SHDR_SIZE || + shoff > UINT64_MAX - ns * MELF64_SHDR_SIZE) + { + snprintf( err, es, "MELF section table overflow" ); + goto fail; + } + total = shoff + ns * MELF64_SHDR_SIZE; + if (total > SIZE_MAX) + { + snprintf( err, es, "MELF file too large for this host" ); + goto fail; + } + + file = (unsigned char *)calloc( (size_t)total ? (size_t)total : 1, 1 ); + if (!file) + { + snprintf( err, es, "out of memory" ); + goto fail; + } + + put_ehdr64( file, data, MELF_ET_REL, shoff, (__mpu_uint16_t)ns, shstrsec ); + for( i = 1; i < ns; ++i ) + if (s[i].type != MELF_SHT_NOBITS && s[i].size) + memcpy( file + (size_t)s[i].off, s[i].payload, s[i].size ); + for( i = 0; i < ns; ++i ) + put_shdr64( file + (size_t)shoff + i *MELF64_SHDR_SIZE, &s[i], data ); + + if( cpu_melf_writeall( path, file, (size_t)total, err, es ) ) + goto fail; + + free( file ); + free( nameoff ); + free( symmap ); + free_wsecs( s, ns ); + return( 0 ); + +fail: + free( file ); + free( nameoff ); + free( symmap ); + free( sympay ); + free( str.p ); + free( shstr.p ); + free_wsecs( s, ns ); + return( -1 ); +} + +static int +validate_ident( const unsigned char *p, size_t n, __mpu_uint16_t want, + int *data, char *err, size_t es) +{ + size_t i; + + if (n < MELF64_EHDR_SIZE || p[MELF_EI_MAG0] != MELFMAG0 || + p[MELF_EI_MAG1] != MELFMAG1 || p[MELF_EI_MAG2] != MELFMAG2 || + p[MELF_EI_MAG3] != MELFMAG3) + { + snprintf( err, es, "not a MELF file" ); + return( -1 ); + } + if (p[MELF_EI_CLASS] != MELF_CLASS_64) + { + snprintf( err, es, "unsupported MELF class" ); + return( -1 ); + } + + *data = p[MELF_EI_DATA]; + if (*data != MELF_DATA_LSB && *data != MELF_DATA_MSB) + { + snprintf( err, es, "unsupported MELF data encoding" ); + return( -1 ); + } + if (p[MELF_EI_VERSION] != MELF_EV_CURRENT || + p[MELF_EI_OSABI] != MELF_OSABI_MCPU || + p[MELF_EI_ABIVERSION] != MELF_ABIVERSION_CURRENT || + cpu_melf_get16( p + MELF64_EHDR_E_TYPE_OFF, *data ) != want || + cpu_melf_get16( p + MELF64_EHDR_E_MACHINE_OFF, *data ) != MELF_EM_MCPU || + cpu_melf_get32( p + MELF64_EHDR_E_VERSION_OFF, *data ) != MELF_EV_CURRENT || + cpu_melf_get32( p + MELF64_EHDR_E_FLAGS_OFF, *data ) != MELF_EF_MCPU_NONE || + cpu_melf_get16( p + MELF64_EHDR_E_EHSIZE_OFF, *data ) != MELF64_EHDR_SIZE) + { + snprintf( err, es, "unsupported MELF header" ); + return( -1 ); + } + + for( i = MELF_EI_PAD; i < MELF_EI_NIDENT; ++i ) + if (p[i] != 0) + { + snprintf( err, es, "nonzero reserved e_ident byte" ); + return( -1 ); + } + return( 0 ); +} + +static int +name_at( const unsigned char *tab, size_t n, __mpu_uint32_t off, + const char **s) +{ + if (off >= n) + return( -1 ); + *s = (const char *)tab + off; + return( memchr( *s, 0, n - off ) ? 0 : -1 ); +} + +static int +map_name_logical( const char *n, unsigned *logical ) +{ + size_t i; + for( i = 0; i < OBJECT_SECTION_COUNT; ++i ) + if( !strcmp( n, sec_name( object_sections[i] ) ) ) + { + *logical = object_sections[i]; + return( 0 ); + } + return( -1 ); +} + +static int +load_payload( unsigned char **dst, size_t *dn, const unsigned char *f, + size_t fn, const rsh *s, char *err, size_t es) +{ + if (s->type == MELF_SHT_NOBITS) + { + snprintf( err, es, "attempt to load SHT_NOBITS payload" ); + return( -1 ); + } + if( s->size > SIZE_MAX || !cpu_melf_span( fn, s->off, s->size ) ) + { + snprintf( err, es, "bad section file range" ); + return( -1 ); + } + if (s->size) + { + *dst = (unsigned char *)malloc( (size_t)s->size ); + if (!*dst) + { + snprintf( err, es, "out of memory" ); + return( -1 ); + } + memcpy( *dst, f + (size_t)s->off, (size_t)s->size ); + } + *dn = (size_t)s->size; + return( 0 ); +} + +static int +u64_to_s64( __mpu_uint64_t u, __mpu_int64_t *out ) +{ + if (u <= (__mpu_uint64_t)INT64_MAX) + { + *out = (__mpu_int64_t)u; + return( 0 ); + } + *out = -1 - (__mpu_int64_t)(UINT64_MAX - u); + return( 0 ); +} + +static int +load_allocated_section( cpu_asm_object *o, unsigned l, const rsh *s, + const unsigned char *f, size_t fn, + char *err, size_t es) +{ + switch (l) + { + case CPU_OBJ_SEC_INIT: + if (o->has_init) goto duplicate; + if( load_payload( (unsigned char **)&o->init, &o->init_size, + f, fn, s, err, es)) return( -1 ); + o->init_align = s->align; + o->has_init = 1; + return( 0 ); + case CPU_OBJ_SEC_TEXT: + if (o->has_text) goto duplicate; + if( load_payload( (unsigned char **)&o->text, &o->text_size, + f, fn, s, err, es)) return( -1 ); + o->text_align = s->align; + o->has_text = 1; + return( 0 ); + case CPU_OBJ_SEC_FINI: + if (o->has_fini) goto duplicate; + if( load_payload( (unsigned char **)&o->fini, &o->fini_size, + f, fn, s, err, es)) return( -1 ); + o->fini_align = s->align; + o->has_fini = 1; + return( 0 ); + case CPU_OBJ_SEC_DATA: + if (o->has_data) goto duplicate; + if( load_payload( (unsigned char **)&o->data, &o->data_size, + f, fn, s, err, es)) return( -1 ); + o->data_align = s->align; + o->has_data = 1; + return( 0 ); + case CPU_OBJ_SEC_BSS: + if (o->has_bss) goto duplicate; + if (s->size > SIZE_MAX) + { + snprintf( err, es, ".bss too large for this host" ); + return( -1 ); + } + o->bss_size = (size_t)s->size; + o->bss_align = s->align; + o->has_bss = 1; + return( 0 ); + case CPU_OBJ_SEC_CONTROL: + if (o->has_control) goto duplicate; + if( load_payload( (unsigned char **)&o->control, &o->control_size, + f, fn, s, err, es)) return( -1 ); + o->control_align = s->align; + o->has_control = 1; + return( 0 ); + case CPU_OBJ_SEC_INPUT: + if (o->has_input) goto duplicate; + if( load_payload( (unsigned char **)&o->input, &o->input_size, + f, fn, s, err, es)) return( -1 ); + o->input_align = s->align; + o->has_input = 1; + return( 0 ); + case CPU_OBJ_SEC_OUTPUT: + if (o->has_output) goto duplicate; + if( load_payload( (unsigned char **)&o->output, &o->output_size, + f, fn, s, err, es)) return( -1 ); + o->output_align = s->align; + o->has_output = 1; + return( 0 ); + case CPU_OBJ_SEC_TRACE: + if (o->has_trace) goto duplicate; + if( load_payload( (unsigned char **)&o->trace, &o->trace_size, + f, fn, s, err, es)) return( -1 ); + o->trace_align = s->align; + o->has_trace = 1; + return( 0 ); + default: + snprintf( err, es, "unsupported allocated section" ); + return( -1 ); + } + +duplicate: + snprintf( err, es, "duplicate section %s", sec_name( l ) ); + return( -1 ); +} + +static int +cpu_object_read_data_internal( const unsigned char *f, size_t fn, + cpu_asm_object *o, char *err, size_t es ) +{ + int data; + __mpu_uint64_t shoff; + __mpu_uint16_t shnum, shentsz, shstrndx; + size_t i, j; + rsh *sh = NULL; + const unsigned char *shstr = NULL; + size_t shstrn = 0; + unsigned *logical = NULL; + int target_found = 0; + __mpu_uint32_t symsec = 0; + MElf_McpuTarget mt; + __mpu_uint32_t *smap = NULL; + size_t symcount = 0; + int rc = -1; + + if( !f || !o ) + return( -1 ); + memset( o, 0, sizeof( *o ) ); + + if( validate_ident( f, fn, MELF_ET_REL, &data, err, es ) ) + goto out; + + if( cpu_melf_get64( f + MELF64_EHDR_E_ENTRY_OFF, data ) != 0 || + cpu_melf_get64( f + MELF64_EHDR_E_INIT_ENTRY_OFF, data ) != 0 || + cpu_melf_get64( f + MELF64_EHDR_E_LIFE_ENTRY_OFF, data ) != 0 || + cpu_melf_get64( f + MELF64_EHDR_E_FINI_ENTRY_OFF, data ) != 0 || + cpu_melf_get64( f + MELF64_EHDR_E_PHOFF_OFF, data ) != 0 || + cpu_melf_get16( f + MELF64_EHDR_E_PHENTSIZE_OFF, data ) != 0 || + cpu_melf_get16( f + MELF64_EHDR_E_PHNUM_OFF, data ) != 0) + { + snprintf( err, es, "ET_REL contains executable/program-header state" ); + goto out; + } + + shoff = cpu_melf_get64( f + MELF64_EHDR_E_SHOFF_OFF, data ); + shentsz = cpu_melf_get16( f + MELF64_EHDR_E_SHENTSIZE_OFF, data ); + shnum = cpu_melf_get16( f + MELF64_EHDR_E_SHNUM_OFF, data ); + shstrndx = cpu_melf_get16( f + MELF64_EHDR_E_SHSTRNDX_OFF, data ); + if (shentsz != MELF64_SHDR_SIZE || !shnum || shstrndx == MELF_SHN_UNDEF || + shstrndx >= shnum || + !cpu_melf_span( fn, shoff, (__mpu_uint64_t)shnum *MELF64_SHDR_SIZE )) + { + snprintf( err, es, "bad MELF section table" ); + goto out; + } + + sh = ( rsh * ) calloc( shnum, sizeof( *sh ) ); + logical = (unsigned *)malloc( shnum *sizeof( *logical ) ); + if (!sh || !logical) + { + snprintf( err, es, "out of memory" ); + goto out; + } + for( i = 0; i < shnum; ++i ) + { + get_shdr64( f + (size_t)shoff + i *MELF64_SHDR_SIZE, data, &sh[i] ); + logical[i] = CPU_OBJ_SEC_UNDEF; + } + if( !shdr_is_zero( &sh[0] ) ) + { + snprintf( err, es, "section header zero is not all-zero" ); + goto out; + } + + if (sh[shstrndx].type != MELF_SHT_STRTAB || sh[shstrndx].flags != 0 || + sh[shstrndx].addr != 0 || sh[shstrndx].link != 0 || + sh[shstrndx].info != 0 || sh[shstrndx].align != 1 || + sh[shstrndx].entsize != 0 || sh[shstrndx].size == 0 || + sh[shstrndx].size > SIZE_MAX || + !cpu_melf_span( fn, sh[shstrndx].off, sh[shstrndx].size )) + { + snprintf( err, es, "bad .shstrtab" ); + goto out; + } + shstr = f + (size_t)sh[shstrndx].off; + shstrn = (size_t)sh[shstrndx].size; + if (shstr[0] != 0) + { + snprintf( err, es, ".shstrtab does not start with NUL" ); + goto out; + } + + for( i = 1; i < shnum; ++i ) + { + const char *name; + + if( name_at( shstr, shstrn, sh[i].name, &name ) ) + { + snprintf( err, es, "bad section name offset" ); + goto out; + } + + if( i == shstrndx && strcmp( name, MELF_SEC_SHSTRTAB ) ) + { + snprintf( err, es, "section-name table has noncanonical name" ); + goto out; + } + + if ((sh[i].type == MELF_SHT_PROGBITS || sh[i].type == MELF_SHT_NOBITS) && + (sh[i].flags & MELF_SHF_ALLOC)) + { + unsigned l; + + if( map_name_logical( name, &l ) ) + { + snprintf( err, es, "unsupported allocated MELF section: %s", name ); + goto out; + } + logical[i] = l; + if ((l == CPU_OBJ_SEC_BSS) != (sh[i].type == MELF_SHT_NOBITS)) + { + snprintf( err, es, "section type disagrees with section role: %s", name ); + goto out; + } + if( sh[i].flags != sec_flags( l ) || sh[i].addr != 0 || + sh[i].link != 0 || sh[i].info != 0 || sh[i].entsize != 0 || + sh[i].align == 0 || !cpu_melf_is_pow2( sh[i].align )) + { + snprintf( err, es, "invalid ET_REL section attributes: %s", name ); + goto out; + } + if (sh[i].type != MELF_SHT_NOBITS && + !cpu_melf_span( fn, sh[i].off, sh[i].size )) + { + snprintf( err, es, "bad section file range: %s", name ); + goto out; + } + if( load_allocated_section( o, l, &sh[i], f, fn, err, es ) ) + goto out; + } + else if (sh[i].type == MELF_SHT_MCPU_TARGET) + { + if (target_found++) + { + snprintf( err, es, "duplicate .mcpu.target" ); + goto out; + } + if( strcmp( name, MELF_SEC_MCPU_TARGET ) || sh[i].flags != 0 || + sh[i].addr != 0 || sh[i].size != MELF_MCPU_TARGET_SIZE || + sh[i].link != 0 || sh[i].info != 0 || sh[i].align != 8 || + sh[i].entsize != MELF_MCPU_TARGET_SIZE || + !cpu_melf_span( fn, sh[i].off, sh[i].size ) || + cpu_melf_decode_target( f + (size_t)sh[i].off, &mt, data ) || + cpu_melf_validate_target( &mt, data, err, es )) + goto out; + } + else if (sh[i].type == MELF_SHT_SYMTAB) + { + if( strcmp( name, MELF_SEC_SYMTAB ) || symsec ) + { + snprintf( err, es, "multiple/noncanonical symbol table" ); + goto out; + } + symsec = (__mpu_uint32_t)i; + } + } + + if (!target_found) + { + snprintf( err, es, "missing .mcpu.target" ); + goto out; + } + if (!symsec) + { + snprintf( err, es, "missing .symtab" ); + goto out; + } + + { + rsh *sy = &sh[symsec]; + const unsigned char *str; + size_t strn; + const char *strname; + + if (sy->flags != 0 || sy->addr != 0 || sy->align != 8 || + sy->entsize != MELF64_SYM_SIZE || sy->size % MELF64_SYM_SIZE || + sy->link == 0 || sy->link >= shnum || sy->info == 0 || + sy->info > sy->size / MELF64_SYM_SIZE || + sh[sy->link].type != MELF_SHT_STRTAB || + sh[sy->link].flags != 0 || sh[sy->link].addr != 0 || + sh[sy->link].link != 0 || sh[sy->link].info != 0 || + sh[sy->link].align != 1 || sh[sy->link].entsize != 0 || + !cpu_melf_span( fn, sy->off, sy->size ) || + !cpu_melf_span( fn, sh[sy->link].off, sh[sy->link].size ) || + sh[sy->link].size == 0 || sh[sy->link].size > SIZE_MAX) + { + snprintf( err, es, "bad MELF symbol table" ); + goto out; + } + if( name_at( shstr, shstrn, sh[sy->link].name, &strname ) || + strcmp( strname, MELF_SEC_STRTAB )) + { + snprintf( err, es, "noncanonical symbol string table" ); + goto out; + } + + str = f + (size_t)sh[sy->link].off; + strn = (size_t)sh[sy->link].size; + if (str[0] != 0) + { + snprintf( err, es, ".strtab does not start with NUL" ); + goto out; + } + + symcount = (size_t)(sy->size / MELF64_SYM_SIZE); + if (symcount == 0) + { + snprintf( err, es, "empty MELF symbol table" ); + goto out; + } + { + const unsigned char *q0 = f + (size_t)sy->off; + size_t k; + for( k = 0; k < MELF64_SYM_SIZE; ++k ) + if (q0[k] != 0) + { + snprintf( err, es, "symbol table entry zero is not all-zero" ); + goto out; + } + } + + smap = (__mpu_uint32_t *)malloc( symcount *sizeof( *smap ) ); + if (!smap) + { + snprintf( err, es, "out of memory" ); + goto out; + } + for( i = 0; i < symcount; ++i ) + smap[i] = UINT32_MAX; + + for( i = 1; i < symcount; ++i ) + { + const unsigned char *q = f + (size_t)sy->off + i * MELF64_SYM_SIZE; + __mpu_uint32_t no = cpu_melf_get32( q + MELF64_SYM_ST_NAME_OFF, data ); + __mpu_uint16_t shn = cpu_melf_get16( q + MELF64_SYM_ST_SHNDX_OFF, data ); + __mpu_uint64_t value = cpu_melf_get64( q + MELF64_SYM_ST_VALUE_OFF, data ); + __mpu_uint64_t size = cpu_melf_get64( q + MELF64_SYM_ST_SIZE_OFF, data ); + unsigned bind = MELF64_ST_BIND( q[MELF64_SYM_ST_INFO_OFF] ); + unsigned type = MELF64_ST_TYPE( q[MELF64_SYM_ST_INFO_OFF] ); + unsigned b, t; + const char *n; + + if (i < sy->info) + { + if (bind != MELF_STB_LOCAL) + { + snprintf( err, es, "non-local symbol precedes sh_info boundary" ); + goto out; + } + } + else if (bind == MELF_STB_LOCAL) + { + snprintf( err, es, "local symbol follows sh_info boundary" ); + goto out; + } + if (q[MELF64_SYM_ST_OTHER_OFF] != MELF_STV_DEFAULT) + { + snprintf( err, es, "unsupported symbol visibility" ); + goto out; + } + if( name_at( str, strn, no, &n ) || !*n ) + { + snprintf( err, es, "bad/empty symbol name" ); + goto out; + } + + if (bind == MELF_STB_LOCAL) + b = CPU_OBJ_BIND_LOCAL; + else if (bind == MELF_STB_GLOBAL) + b = CPU_OBJ_BIND_GLOBAL; + else if (bind == MELF_STB_WEAK) + b = CPU_OBJ_BIND_WEAK; + else + { + snprintf( err, es, "unsupported symbol binding" ); + goto out; + } + + if (type == MELF_STT_NOTYPE) + t = CPU_OBJ_TYPE_NOTYPE; + else if (type == MELF_STT_OBJECT) + t = CPU_OBJ_TYPE_OBJECT; + else if (type == MELF_STT_FUNC) + t = CPU_OBJ_TYPE_FUNC; + else + { + snprintf( err, es, "unsupported symbol type" ); + goto out; + } + + if (shn == MELF_SHN_UNDEF) + { + if (b == CPU_OBJ_BIND_LOCAL || value != 0 || size != 0) + { + snprintf( err, es, "malformed undefined symbol: %s", n ); + goto out; + } + if( object_add_symbol( o, n, CPU_OBJ_SEC_UNDEF, b, t, 0, err, es ) ) + goto out; + } + else + { + if (shn >= MELF_SHN_LORESERVE || shn >= shnum || + logical[shn] == CPU_OBJ_SEC_UNDEF) + { + snprintf( err, es, "symbol refers to unsupported section" ); + goto out; + } + if (value > sh[shn].size || size > sh[shn].size - value) + { + snprintf( err, es, "symbol range outside section: %s", n ); + goto out; + } + if( object_add_symbol( o, n, logical[shn], b, t, value, err, es ) ) + goto out; + o->symbols[o->symbol_count - 1].size = size; + } + smap[i] = (__mpu_uint32_t)(o->symbol_count - 1); + } + } + + for( i = 1; i < shnum; ++i ) + { + if (sh[i].type == MELF_SHT_RELA) + { + const char *name; + unsigned target_logical; + const char *expected_name; + + if (sh[i].link != symsec || sh[i].info == 0 || sh[i].info >= shnum || + logical[sh[i].info] == CPU_OBJ_SEC_UNDEF || + sh[sh[i].info].type == MELF_SHT_NOBITS || + sh[i].flags != 0 || sh[i].addr != 0 || sh[i].align != 8 || + sh[i].entsize != MELF64_RELA_SIZE || + sh[i].size % MELF64_RELA_SIZE || + !cpu_melf_span( fn, sh[i].off, sh[i].size )) + { + snprintf( err, es, "bad RELA section" ); + goto out; + } + + target_logical = logical[sh[i].info]; + expected_name = rela_name( target_logical ); + if( !expected_name || name_at( shstr, shstrn, sh[i].name, &name ) || + strcmp( name, expected_name )) + { + snprintf( err, es, "noncanonical RELA section name" ); + goto out; + } + + for( j = 0; j < (size_t)( sh[i].size / MELF64_RELA_SIZE ); ++j ) + { + const unsigned char *q = f + (size_t)sh[i].off + j * MELF64_RELA_SIZE; + __mpu_uint64_t roff = cpu_melf_get64( q + MELF64_RELA_R_OFFSET_OFF, data ); + __mpu_uint64_t info = cpu_melf_get64( q + MELF64_RELA_R_INFO_OFF, data ); + __mpu_uint32_t ms = MELF64_R_SYM( info ); + __mpu_uint32_t rt = MELF64_R_TYPE( info ); + __mpu_int64_t addend; + + if (ms == MELF_STN_UNDEF || ms >= symcount || smap[ms] == UINT32_MAX || + rt < CPU_RELOC_CODE64 || rt > CPU_RELOC_CPTR64_DATA) + { + snprintf( err, es, "bad RELA symbol/type" ); + goto out; + } + if (roff > sh[sh[i].info].size || + CPU_PTR64_SIZE > sh[sh[i].info].size - roff) + { + snprintf( err, es, "RELA field outside section" ); + goto out; + } + u64_to_s64( cpu_melf_get64( q + MELF64_RELA_R_ADDEND_OFF, data ), &addend ); + if( object_add_reloc( o, target_logical, rt, smap[ms], roff, + addend, err, es)) + goto out; + } + } + } + + if (o->init_size > SIZE_MAX - o->text_size || + o->init_size + o->text_size > SIZE_MAX - o->fini_size) + { + snprintf( err, es, "PROGRAM compatibility image too large" ); + goto out; + } + o->code_size = o->init_size + o->text_size + o->fini_size; + o->code_align = 1; + if (o->code_size) + { + o->code = (__mpu_UNIT_t *)malloc( o->code_size ); + if (!o->code) + { + snprintf( err, es, "out of memory" ); + goto out; + } + memcpy( o->code, o->init, o->init_size ); + memcpy( o->code + o->init_size, o->text, o->text_size ); + memcpy( o->code + o->init_size + o->text_size, o->fini, o->fini_size ); + } + + rc = 0; +out: + free( sh ); + free( logical ); + free( smap ); + if (rc) + cpu_asm_object_free( o ); + return( rc ); +} + +int +cpu_object_read_data( const void *data, size_t size, cpu_asm_object *o, + char *err, size_t es ) +{ + if( data == NULL || size == 0 || o == NULL ) + return( -1 ); + + return( cpu_object_read_data_internal((const unsigned char *)data, size, + o, err, es) ); + +} /* End of cpu_object_read_data() */ + + +int +cpu_object_read( const char *path, cpu_asm_object *o, char *err, size_t es ) +{ + unsigned char *data = NULL; + size_t size = 0; + int rc; + + if( path == NULL || o == NULL ) + return( -1 ); + + if( cpu_melf_readall(path, &data, &size, err, es) ) + return( -1 ); + + rc = cpu_object_read_data_internal( data, size, o, err, es ); + free( data ); + return( rc ); + +} /* End of cpu_object_read() */ + |
