diff options
Diffstat (limited to 'utils/mcpu-dyn.c')
| -rw-r--r-- | utils/mcpu-dyn.c | 2242 |
1 files changed, 2242 insertions, 0 deletions
diff --git a/utils/mcpu-dyn.c b/utils/mcpu-dyn.c new file mode 100644 index 0000000..4d0ab2e --- /dev/null +++ b/utils/mcpu-dyn.c @@ -0,0 +1,2242 @@ +#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-dyn.h" +#include "mcpu-melf.h" + +typedef struct blob +{ + unsigned char *p; + size_t n, cap; +} blob; +typedef struct xph +{ + __mpu_uint32_t type, flags; + __mpu_uint64_t off, vaddr, paddr, filesz, memsz, maxsz, align; +} xph; +typedef struct xsec +{ + const char *name; + __mpu_uint32_t type; + __mpu_uint64_t flags, addr, off, size, align, entsize; + __mpu_uint32_t link, info, nameoff; + unsigned logical; + const unsigned char *payload; + unsigned char *owned; +} xsec; +typedef struct rsh +{ + __mpu_uint32_t name, type; + __mpu_uint64_t flags, addr, off, size; + __mpu_uint32_t link, info; + __mpu_uint64_t align, entsize; +} rsh; +typedef xph rph; + +typedef struct dsym +{ + char *name; + unsigned binding, type; + __mpu_uint16_t shndx; + __mpu_uint64_t value, size; +} dsym; +typedef struct drela +{ + __mpu_uint16_t target_shndx; + __mpu_uint64_t offset; + size_t sym; + unsigned type; + __mpu_int64_t addend; +} drela; +typedef struct dmod +{ + char *path; + unsigned char *file; + size_t file_size; + int data; + int abi; + __mpu_uint16_t etype; + rph *ph; + size_t phnum; + rsh *sh; + size_t shnum; + const unsigned char *shstr; + size_t shstrn; + dsym *sym; + size_t nsym; + drela *rela; + size_t nrela; + __mpu_uint64_t base[8]; + int load_index[8]; + int stacki, heapi; + int lifecycle_seen; + __mpu_uint64_t entry, init, life, fini; + __mpu_uint64_t data_section_begin, data_section_size; + int has_data_section; + char *soname; + char **needed; + size_t nneeded; +} dmod; + +typedef struct winner +{ + const char *name; + size_t mod, sym; + unsigned binding; +} winner; + +static int +grow( blob *b, size_t add ) +{ + size_t need, c; + unsigned char *q; + if( add > SIZE_MAX - b->n ) return( -1 ); + need = b->n + add; + if( need <= b->cap ) return( 0 ); + c = b->cap ? b->cap : 64; + while( c < need ) + { + if( c > SIZE_MAX / 2 ) return( -1 ); + c *= 2; + } + q = (unsigned char *)realloc( b->p, c ); + if( !q ) return( -1 ); + b->p = q; + b->cap = c; + return( 0 ); +} + +static int +bput( blob *b, const void *p, size_t n ) +{ + if( grow( b, n ) ) return( -1 ); + if( n ) memcpy( b->p + b->n, p, n ); + b->n += n; + return( 0 ); +} + +static int +bstr( blob *b, const char *s, __mpu_uint32_t *off ) +{ + size_t n = strlen( s ) + 1; + if( b->n > UINT32_MAX || n > UINT32_MAX - b->n ) return( -1 ); + *off = (__mpu_uint32_t)b->n; + return( bput( b, s, n ) ); +} + +static __mpu_uint64_t +nzalign( __mpu_uint64_t a ) +{ + return( a ? a : 1 ); +} + +static __mpu_uint64_t +maxu( __mpu_uint64_t a, __mpu_uint64_t b ) +{ + return( a > b ? a : b ); +} + +static int +addu64( __mpu_uint64_t a, __mpu_uint64_t b, __mpu_uint64_t *r ) +{ + if( a > UINT64_MAX - b ) return( -1 ); + *r = a + b; + return( 0 ); +} + +static int +addsigned( __mpu_uint64_t b, __mpu_int64_t a, __mpu_uint64_t *out ) +{ + if( a >= 0 ) + { + __mpu_uint64_t u = (__mpu_uint64_t)a; + if( b > UINT64_MAX - u ) return( -1 ); + *out = b + u; + } + else + { + __mpu_uint64_t u = (__mpu_uint64_t)( - ( a + 1 ) ) + 1; + if( b < u ) return( -1 ); + *out = b - u; + } + return( 0 ); +} + +static int +span( size_t n, __mpu_uint64_t off, __mpu_uint64_t sz ) +{ + return( off <= n && sz <= n - off ); +} + +static const char * +sec_name( unsigned s ) +{ + switch( s ) + { + 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( 0 ); + } +} + +static const char * +rela_name( unsigned s ) +{ + switch( s ) + { + 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( 0 ); + } +} + +static __mpu_uint64_t +sec_flags( unsigned s ) +{ + switch( s ) + { + 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 unsigned +sec_seg( unsigned s ) +{ + return( cpu_obj_sec_segment( s ) ); +} + +static int +image_sec( const cpu_asm_image *im, unsigned s, const unsigned char **p, __mpu_uint64_t *off, __mpu_uint64_t *sz, __mpu_uint64_t *al, int *present, int *nobits ) +{ + *p = 0; + *off = *sz = 0; + *al = 1; + *present = 0; + *nobits = 0; + switch( s ) + { + case CPU_OBJ_SEC_INIT: *p = im->code; + *off = im->init_offset; + *sz = im->init_size; + *al = im->init_align; + *present = im->init_present; + break; + case CPU_OBJ_SEC_TEXT: *p = im->code; + *off = im->text_offset; + *sz = im->text_size; + *al = im->text_align; + *present = im->text_present; + break; + case CPU_OBJ_SEC_FINI: *p = im->code; + *off = im->fini_offset; + *sz = im->fini_size; + *al = im->fini_align; + *present = im->fini_present; + break; + case CPU_OBJ_SEC_DATA: *p = im->data; + *off = im->data_offset; + *sz = im->data_section_size; + *al = im->data_align; + *present = im->data_present; + break; + case CPU_OBJ_SEC_BSS: *off = im->bss_offset; + *sz = im->bss_size; + *al = im->bss_align; + *present = im->bss_present; + *nobits = 1; + break; + case CPU_OBJ_SEC_CONTROL: *p = im->control; + *off = im->control_offset; + *sz = im->control_section_size; + *al = im->control_align; + *present = im->control_present; + break; + case CPU_OBJ_SEC_INPUT: *p = im->input; + *off = im->input_offset; + *sz = im->input_section_size; + *al = im->input_align; + *present = im->input_present; + break; + case CPU_OBJ_SEC_OUTPUT: *p = im->output; + *off = im->output_offset; + *sz = im->output_section_size; + *al = im->output_align; + *present = im->output_present; + break; + case CPU_OBJ_SEC_TRACE: *p = im->trace; + *off = im->trace_offset; + *sz = im->trace_section_size; + *al = im->trace_align; + *present = im->trace_present; + break; + default: return( -1 ); + } + if( *p ) * p += *off; + return( 0 ); +} + +static void +put_ph( unsigned char *p, const xph *x, int data ) +{ + memset( p, 0, MELF64_PHDR_SIZE ); + cpu_melf_put32( p + MELF64_PHDR_P_TYPE_OFF, x->type, data ); + cpu_melf_put32( p + MELF64_PHDR_P_FLAGS_OFF, x->flags, data ); + cpu_melf_put64( p + MELF64_PHDR_P_OFFSET_OFF, x->off, data ); + cpu_melf_put64( p + MELF64_PHDR_P_VADDR_OFF, x->vaddr, data ); + cpu_melf_put64( p + MELF64_PHDR_P_PADDR_OFF, x->paddr, data ); + cpu_melf_put64( p + MELF64_PHDR_P_FILESZ_OFF, x->filesz, data ); + cpu_melf_put64( p + MELF64_PHDR_P_MEMSZ_OFF, x->memsz, data ); + cpu_melf_put64( p + MELF64_PHDR_P_MAXSZ_OFF, x->maxsz, data ); + cpu_melf_put64( p + MELF64_PHDR_P_ALIGN_OFF, x->align, data ); +} + +static void +put_sh( unsigned char *p, const xsec *x, int data ) +{ + memset( p, 0, MELF64_SHDR_SIZE ); + cpu_melf_put32( p + MELF64_SHDR_SH_NAME_OFF, x->nameoff, data ); + cpu_melf_put32( p + MELF64_SHDR_SH_TYPE_OFF, x->type, data ); + cpu_melf_put64( p + MELF64_SHDR_SH_FLAGS_OFF, x->flags, data ); + cpu_melf_put64( p + MELF64_SHDR_SH_ADDR_OFF, x->addr, data ); + cpu_melf_put64( p + MELF64_SHDR_SH_OFFSET_OFF, x->off, data ); + cpu_melf_put64( p + MELF64_SHDR_SH_SIZE_OFF, x->size, data ); + cpu_melf_put32( p + MELF64_SHDR_SH_LINK_OFF, x->link, data ); + cpu_melf_put32( p + MELF64_SHDR_SH_INFO_OFF, x->info, data ); + cpu_melf_put64( p + MELF64_SHDR_SH_ADDRALIGN_OFF, x->align, data ); + cpu_melf_put64( p + MELF64_SHDR_SH_ENTSIZE_OFF, x->entsize, data ); +} + +static void +get_ph( const unsigned char *p, int data, rph *x ) +{ + x->type = cpu_melf_get32( p + MELF64_PHDR_P_TYPE_OFF, data ); + x->flags = cpu_melf_get32( p + MELF64_PHDR_P_FLAGS_OFF, data ); + x->off = cpu_melf_get64( p + MELF64_PHDR_P_OFFSET_OFF, data ); + x->vaddr = cpu_melf_get64( p + MELF64_PHDR_P_VADDR_OFF, data ); + x->paddr = cpu_melf_get64( p + MELF64_PHDR_P_PADDR_OFF, data ); + x->filesz = cpu_melf_get64( p + MELF64_PHDR_P_FILESZ_OFF, data ); + x->memsz = cpu_melf_get64( p + MELF64_PHDR_P_MEMSZ_OFF, data ); + x->maxsz = cpu_melf_get64( p + MELF64_PHDR_P_MAXSZ_OFF, data ); + x->align = cpu_melf_get64( p + MELF64_PHDR_P_ALIGN_OFF, data ); +} + +static void +get_sh( const unsigned char *p, int data, rsh *x ) +{ + x->name = cpu_melf_get32( p + MELF64_SHDR_SH_NAME_OFF, data ); + x->type = cpu_melf_get32( p + MELF64_SHDR_SH_TYPE_OFF, data ); + x->flags = cpu_melf_get64( p + MELF64_SHDR_SH_FLAGS_OFF, data ); + x->addr = cpu_melf_get64( p + MELF64_SHDR_SH_ADDR_OFF, data ); + x->off = cpu_melf_get64( p + MELF64_SHDR_SH_OFFSET_OFF, data ); + x->size = cpu_melf_get64( p + MELF64_SHDR_SH_SIZE_OFF, data ); + x->link = cpu_melf_get32( p + MELF64_SHDR_SH_LINK_OFF, data ); + x->info = cpu_melf_get32( p + MELF64_SHDR_SH_INFO_OFF, data ); + x->align = cpu_melf_get64( p + MELF64_SHDR_SH_ADDRALIGN_OFF, data ); + x->entsize = cpu_melf_get64( p + MELF64_SHDR_SH_ENTSIZE_OFF, data ); +} + +static int +add_load( xph *ph, size_t *np, __mpu_uint64_t *cur, unsigned seg, __mpu_uint64_t filesz, __mpu_uint64_t memsz, __mpu_uint32_t flags, __mpu_uint64_t align, char *err, size_t es ) +{ + int ov = 0; + align = nzalign( align ); + if( !cpu_melf_is_pow2( align ) || filesz > memsz || memsz > CPU_PTR_OFFSET_MASK || filesz > SIZE_MAX ) + { + snprintf( err, es, "invalid dynamic load descriptor" ); + return( -1 ); + } + *cur = cpu_melf_align_up( *cur, align, &ov ); + if( ov ) + { + snprintf( err, es, "dynamic file layout overflow" ); + return( -1 ); + } + ph[*np] = ( xph ) + { + MELF_PT_LOAD, flags, *cur, MELF_MCPU_PTR64( seg, 0 ), 0, filesz, memsz, memsz, align + }; + ++ *np; + if( addu64( *cur, filesz, cur ) ) + { + snprintf( err, es, "dynamic file layout overflow" ); + return( -1 ); + } + return( 0 ); +} + +static int +cpu_dyn_write_version( const char *path, const cpu_asm_image *im, + const cpu_exec_options *op, int shared, int abi, + const cpu_dyn_metadata *metadata, + char *err, size_t es ) +{ + static const unsigned logicals[] = + { + 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 + }; + xph ph[8]; + size_t np = 0; + int phidx[8] = + { + -1, -1, -1, -1, -1, -1, -1, -1 + }; + xsec sec[40]; + size_t ns = 1, i, j, nlocal = 0; + __mpu_uint16_t secidx[0x24]; + blob shstr = + { + 0 + } + , dynstr = + { + 0 + } + , dynsym = + { + 0 + } + , dynamic = + { + 0 + } + , relab[9] = + { + { + 0 + } + }; + int data = cpu_melf_data_encoding(), ov = 0; + __mpu_uint64_t cur = MELF64_EHDR_SIZE, shoff, total, data_mem; + unsigned char *file = 0, tbuf[MELF_MCPU_TARGET_SIZE], lbuf[MELF_MCPU_LIFECYCLE_SIZE]; + MElf_McpuTarget mt; + MElf_McpuLifecycle ml; + __mpu_uint16_t dynstridx, dynsymidx, dynamicidx = 0, shstridx; + int have_data; + memset( sec, 0, sizeof( sec ) ); + memset( secidx, 0, sizeof( secidx ) ); + if( !path || !im || ( !shared && !op ) ) + { + snprintf( err, es, "bad dynamic writer arguments" ); + goto fail; + } + + if( abi != MELF_ABIVERSION_DYNAMIC_EXPLICIT && + abi != MELF_ABIVERSION_DYNAMIC_NEEDED ) + { + snprintf( err, es, "unsupported dynamic ABI version" ); + goto fail; + } + + if( abi == MELF_ABIVERSION_DYNAMIC_EXPLICIT ) + { + if( metadata != NULL ) + { + snprintf( err, es, "ABI-v2 writer does not accept dependency metadata" ); + goto fail; + } + } + else + { + size_t k; + + if( shared ) + { + if( metadata == NULL || metadata->soname == NULL || + metadata->soname[0] == 0 || strchr( metadata->soname, '/' ) != NULL ) + { + snprintf( err, es, "ABI-v3 ET_DYN requires a logical SONAME" ); + goto fail; + } + } + else if( metadata != NULL && metadata->soname != NULL ) + { + snprintf( err, es, "ABI-v3 ET_EXEC must not define SONAME" ); + goto fail; + } + + if( metadata != NULL ) + for( k = 0; k < metadata->needed_count; ++k ) + { + if( metadata->needed == NULL || metadata->needed[k] == NULL || + metadata->needed[k][0] == 0 || + strchr( metadata->needed[k], '/' ) != NULL ) + { + snprintf( err, es, "ABI-v3 NEEDED name must be logical" ); + goto fail; + } + } + } + + if( shared ) + { + if( im->has_start || im->has_life || im->init_present || im->fini_present || im->control_present || im->input_present || im->output_present || im->trace_present ) + { + snprintf( err, es, "ET_DYN contains forbidden executable/model sections" ); + goto fail; + } + if( !im->text_present && !im->data_present && !im->bss_present ) + { + snprintf( err, es, "empty shared object" ); + goto fail; + } + } + else + { + if( !im->has_start || !im->has_life || !im->text_present || !im->code_size ) + { + snprintf( err, es, "incomplete dynamic executable" ); + goto fail; + } + if( !op->stack_size || op->stack_size % MCPU_STARTUP_ALIGN || op->stack_size > CPU_PTR_OFFSET_MASK || op->stack_size > SIZE_MAX ) + { + snprintf( err, es, "invalid STACK size" ); + goto fail; + } + if( op->have_heap && ( op->heap_size < CPU_HEAP_BEGIN || op->heap_limit < op->heap_size || op->heap_limit > CPU_PTR_OFFSET_MASK || op->heap_size > SIZE_MAX ) ) + { + snprintf( err, es, "invalid HEAP size/limit" ); + goto fail; + } + } + data_mem = im->data_mem_size ? im->data_mem_size : im->data_size; + have_data = ( data_mem || im->data_present || im->bss_present ); + /* count program headers then lay file images */ + np = ( im->code_size ? 1 : 0 ) + ( have_data ? 1 : 0 ) + ( shared ? 0 : ( ( im->control_present || im->control_size ) + ( im->input_present || im->input_size ) + ( im->output_present || im->output_size ) + ( im->trace_present || im->trace_size ) + 1 + ( op->have_heap ? 1 : 0 ) ) ); + if( np > 8 || cur > UINT64_MAX - np *MELF64_PHDR_SIZE ) + { + snprintf( err, es, "too many dynamic program headers" ); + goto fail; + } + cur += np *MELF64_PHDR_SIZE; + np = 0; + if( im->code_size ) + { + __mpu_uint64_t a = maxu( maxu( nzalign( im->init_align ), nzalign( im->text_align ) ), nzalign( im->fini_align ) ); + phidx[MELF_MCPU_SEG_PROGRAM] = (int)np; + if( add_load( ph, &np, &cur, MELF_MCPU_SEG_PROGRAM, im->code_size, im->code_size, MELF_PFLAGS_PROGRAM, a, err, es ) ) goto fail; + } + if( have_data ) + { + phidx[MELF_MCPU_SEG_DATA] = (int)np; + if( add_load( ph, &np, &cur, MELF_MCPU_SEG_DATA, im->data_size, data_mem, MELF_PFLAGS_DATA, maxu( 1, maxu( nzalign( im->data_align ), nzalign( im->bss_align ) ) ), err, es ) ) goto fail; + } + if( !shared ) + { + if( im->control_present || im->control_size ) + { + phidx[MELF_MCPU_SEG_CONTROL] = (int)np; + if( add_load( ph, &np, &cur, MELF_MCPU_SEG_CONTROL, im->control_size, im->control_size, MELF_PFLAGS_CONTROL, nzalign( im->control_align ), err, es ) ) goto fail; + } + if( im->input_present || im->input_size ) + { + phidx[MELF_MCPU_SEG_INPUT] = (int)np; + if( add_load( ph, &np, &cur, MELF_MCPU_SEG_INPUT, im->input_size, im->input_size, MELF_PFLAGS_INPUT, nzalign( im->input_align ), err, es ) ) goto fail; + } + if( im->output_present || im->output_size ) + { + phidx[MELF_MCPU_SEG_OUTPUT] = (int)np; + if( add_load( ph, &np, &cur, MELF_MCPU_SEG_OUTPUT, im->output_size, im->output_size, MELF_PFLAGS_OUTPUT, nzalign( im->output_align ), err, es ) ) goto fail; + } + if( im->trace_present || im->trace_size ) + { + phidx[MELF_MCPU_SEG_TRACE] = (int)np; + if( add_load( ph, &np, &cur, MELF_MCPU_SEG_TRACE, im->trace_size, im->trace_size, MELF_PFLAGS_TRACE, nzalign( im->trace_align ), err, es ) ) goto fail; + } + phidx[MELF_MCPU_SEG_STACK] = (int)np; + ph[np++] = ( xph ) + { + MELF_PT_MCPU_STACK, MELF_PFLAGS_STACK, 0, MELF_MCPU_VADDR_STACK, 0, 0, op->stack_size, op->stack_size, MCPU_STARTUP_ALIGN + }; + if( op->have_heap ) + { + phidx[MELF_MCPU_SEG_HEAP] = (int)np; + ph[np++] = ( xph ) + { + MELF_PT_MCPU_HEAP, MELF_PFLAGS_HEAP, 0, MELF_MCPU_VADDR_HEAP, 0, 0, op->heap_size, op->heap_limit, 16 + }; + } + } + /* allocated sections */ + for( i = 0; i < sizeof( logicals ) / sizeof( logicals[0] ); ++i ) + { + const unsigned char *p; + __mpu_uint64_t so, sz, al; + int pres, nb; + unsigned lg = logicals[i], seg; + if( image_sec( im, lg, &p, &so, &sz, &al, &pres, &nb ) || !pres ) continue; + if( shared && ( lg != CPU_OBJ_SEC_TEXT && lg != CPU_OBJ_SEC_DATA && lg != CPU_OBJ_SEC_BSS ) ) + { + snprintf( err, es, "forbidden ET_DYN section %s", sec_name( lg ) ); + goto fail; + } + seg = sec_seg( lg ); + if( phidx[seg] < 0 ) + { + snprintf( err, es, "section has no load segment" ); + goto fail; + } + secidx[lg] = (__mpu_uint16_t)ns; + sec[ns] = ( xsec ) + { + sec_name( lg ), nb ? MELF_SHT_NOBITS : MELF_SHT_PROGBITS, sec_flags( lg ), MELF_MCPU_PTR64( seg, so ), nb ? ph[phidx[seg]].off + so : ph[phidx[seg]].off + so, sz, nzalign( al ), 0, 0, 0, 0, lg, p, 0 + }; + ++ns; + } + cpu_melf_fill_target( &mt ); + cpu_melf_encode_target( tbuf, &mt, data ); + sec[ns++] = ( xsec ) + { + MELF_SEC_MCPU_TARGET, MELF_SHT_MCPU_TARGET, 0, 0, 0, MELF_MCPU_TARGET_SIZE, 8, MELF_MCPU_TARGET_SIZE, 0, 0, 0, 0, tbuf, 0 + }; + if( !shared ) + { + cpu_melf_fill_lifecycle( &ml ); + cpu_melf_encode_lifecycle( lbuf, &ml, data ); + sec[ns++] = ( xsec ) + { + MELF_SEC_MCPU_LIFECYCLE, MELF_SHT_MCPU_LIFECYCLE, 0, 0, 0, MELF_MCPU_LIFECYCLE_SIZE, 8, MELF_MCPU_LIFECYCLE_SIZE, 0, 0, 0, 0, lbuf, 0 + }; + } + if( bput( &dynstr, "", 1 ) || bput( &dynsym, ( unsigned char[MELF64_SYM_SIZE] ) + { + 0 + } + , MELF64_SYM_SIZE)) + { + snprintf( err, es, "out of memory" ); + goto fail; + } + for( i = 0; i < im->dyn_symbol_count; ++i ) if( im->dyn_symbols[i].binding == MELF_STB_LOCAL )++nlocal; + for( i = 0; i < im->dyn_symbol_count; ++i ) + { + unsigned char sb[MELF64_SYM_SIZE] = + { + 0 + }; + __mpu_uint32_t no; + const cpu_dyn_symbol *d = &im->dyn_symbols[i]; + __mpu_uint16_t shn = MELF_SHN_UNDEF; + if( bstr( &dynstr, d->name, &no ) ) + { + snprintf( err, es, "dynamic string table overflow" ); + goto fail; + } + if( d->section != CPU_OBJ_SEC_UNDEF ) + { + if( d->section >= sizeof( secidx ) / sizeof( secidx[0] ) || ! ( shn = secidx[d->section] ) ) + { + snprintf( err, es, "dynamic symbol belongs to absent output section: %s", d->name ); + goto fail; + } + } + cpu_melf_put32( sb + MELF64_SYM_ST_NAME_OFF, no, data ); + sb[MELF64_SYM_ST_INFO_OFF] = MELF64_ST_INFO( d->binding, d->type ); + sb[MELF64_SYM_ST_OTHER_OFF] = MELF_STV_DEFAULT; + cpu_melf_put16( sb + MELF64_SYM_ST_SHNDX_OFF, shn, data ); + cpu_melf_put64( sb + MELF64_SYM_ST_VALUE_OFF, d->value, data ); + cpu_melf_put64( sb + MELF64_SYM_ST_SIZE_OFF, d->size, data ); + if( bput( &dynsym, sb, sizeof( sb ) ) ) + { + snprintf( err, es, "out of memory" ); + goto fail; + } + } + if( abi == MELF_ABIVERSION_DYNAMIC_NEEDED ) + { + size_t k; + + if( metadata != NULL ) + { + if( metadata->soname != NULL ) + { + unsigned char db[MELF64_DYN_SIZE] = { 0 }; + __mpu_uint32_t no; + + if( bstr( &dynstr, metadata->soname, &no ) ) + { + snprintf( err, es, "dynamic string table overflow" ); + goto fail; + } + cpu_melf_put64( db + MELF64_DYN_D_TAG_OFF, + (__mpu_uint64_t)MELF_DT_SONAME, data ); + cpu_melf_put64( db + MELF64_DYN_D_VAL_OFF, no, data ); + if( bput( &dynamic, db, sizeof( db ) ) ) + { + snprintf( err, es, "out of memory" ); + goto fail; + } + } + + for( k = 0; k < metadata->needed_count; ++k ) + { + unsigned char db[MELF64_DYN_SIZE] = { 0 }; + __mpu_uint32_t no; + + if( bstr( &dynstr, metadata->needed[k], &no ) ) + { + snprintf( err, es, "dynamic string table overflow" ); + goto fail; + } + cpu_melf_put64( db + MELF64_DYN_D_TAG_OFF, + (__mpu_uint64_t)MELF_DT_NEEDED, data ); + cpu_melf_put64( db + MELF64_DYN_D_VAL_OFF, no, data ); + if( bput( &dynamic, db, sizeof( db ) ) ) + { + snprintf( err, es, "out of memory" ); + goto fail; + } + } + } + + { + unsigned char db[MELF64_DYN_SIZE] = { 0 }; + if( bput( &dynamic, db, sizeof( db ) ) ) + { + snprintf( err, es, "out of memory" ); + goto fail; + } + } + } + + dynstridx = (__mpu_uint16_t)ns; + sec[ns++] = ( xsec ) + { + MELF_SEC_DYNSTR, MELF_SHT_STRTAB, 0, 0, 0, dynstr.n, 1, 0, 0, 0, 0, 0, dynstr.p, 0 + }; + dynsymidx = (__mpu_uint16_t)ns; + sec[ns++] = ( xsec ) + { + MELF_SEC_DYNSYM, MELF_SHT_DYNSYM, 0, 0, 0, dynsym.n, 8, MELF64_SYM_SIZE, dynstridx, (__mpu_uint32_t)( nlocal + 1 ), 0, 0, dynsym.p, 0 + }; + if( abi == MELF_ABIVERSION_DYNAMIC_NEEDED ) + { + dynamicidx = (__mpu_uint16_t)ns; + sec[ns++] = ( xsec ) + { + MELF_SEC_DYNAMIC, MELF_SHT_DYNAMIC, 0, 0, 0, dynamic.n, 8, + MELF64_DYN_SIZE, dynstridx, 0, 0, 0, dynamic.p, 0 + }; + } + for( i = 0; i < im->dyn_reloc_count; ++i ) + { + const cpu_dyn_reloc *r = &im->dyn_relocs[i]; + int li = -1; + for( j = 0; j < sizeof( logicals ) / sizeof( logicals[0] ); ++j ) if( logicals[j] == r->section ) + { + li = (int)j; + break; + } + if( li < 0 || !secidx[r->section] || r->symbol_index >= im->dyn_symbol_count ) + { + snprintf( err, es, "bad retained dynamic relocation" ); + goto fail; + } + unsigned char rb[MELF64_RELA_SIZE] = + { + 0 + }; + cpu_melf_put64( rb + MELF64_RELA_R_OFFSET_OFF, r->offset, data ); + cpu_melf_put64( rb + MELF64_RELA_R_INFO_OFF, MELF64_R_INFO( r->symbol_index + 1, r->type ), data ); + cpu_melf_put64( rb + MELF64_RELA_R_ADDEND_OFF, (__mpu_uint64_t)r->addend, data ); + if( bput( &relab[li], rb, sizeof( rb ) ) ) + { + snprintf( err, es, "out of memory" ); + goto fail; + } + } + for( i = 0; i < sizeof( logicals ) / sizeof( logicals[0] ); ++i ) if( relab[i].n ) + { + const char *rn = rela_name( logicals[i] ); + sec[ns++] = ( xsec ) + { + rn, MELF_SHT_RELA, 0, 0, 0, relab[i].n, 8, MELF64_RELA_SIZE, dynsymidx, secidx[logicals[i]], 0, 0, relab[i].p, 0 + }; + } + /* section names */ + if( bput( &shstr, "", 1 ) ) + { + snprintf( err, es, "out of memory" ); + goto fail; + } + shstridx = (__mpu_uint16_t)ns; + sec[ns++] = ( xsec ) + { + MELF_SEC_SHSTRTAB, MELF_SHT_STRTAB, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 + }; + for( i = 1; i < ns; ++i ) if( bstr( &shstr, sec[i].name, &sec[i].nameoff ) ) + { + snprintf( err, es, "section string table overflow" ); + goto fail; + } + sec[shstridx].payload = shstr.p; + sec[shstridx].size = shstr.n; + /* non-ALLOC payload layout */ + for( i = 1; i < ns; ++i ) + { + if( sec[i].flags & MELF_SHF_ALLOC ) continue; + cur = cpu_melf_align_up( cur, nzalign( sec[i].align ), &ov ); + if( ov ) + { + snprintf( err, es, "dynamic file layout overflow" ); + goto fail; + } + sec[i].off = cur; + if( addu64( cur, sec[i].size, &cur ) ) + { + snprintf( err, es, "dynamic file layout overflow" ); + goto fail; + } + } + cur = cpu_melf_align_up( cur, 8, &ov ); + if( ov || cur > UINT64_MAX - ns *MELF64_SHDR_SIZE ) + { + snprintf( err, es, "dynamic file layout overflow" ); + goto fail; + } + shoff = cur; + total = shoff + ns *MELF64_SHDR_SIZE; + if( total > SIZE_MAX ) + { + snprintf( err, es, "dynamic file too large" ); + goto fail; + } + file = (unsigned char *)calloc( (size_t)total, 1 ); + if( !file ) + { + snprintf( err, es, "out of memory" ); + goto fail; + } + /* ehdr */ + file[MELF_EI_MAG0] = MELFMAG0; + file[MELF_EI_MAG1] = MELFMAG1; + file[MELF_EI_MAG2] = MELFMAG2; + file[MELF_EI_MAG3] = MELFMAG3; + file[MELF_EI_CLASS] = MELF_CLASS_64; + file[MELF_EI_DATA] = (unsigned char)data; + file[MELF_EI_VERSION] = MELF_EV_CURRENT; + file[MELF_EI_OSABI] = MELF_OSABI_MCPU; + file[MELF_EI_ABIVERSION] = (unsigned char)abi; + cpu_melf_put16( file + MELF64_EHDR_E_TYPE_OFF, shared ? MELF_ET_DYN : MELF_ET_EXEC, data ); + cpu_melf_put16( file + MELF64_EHDR_E_MACHINE_OFF, MELF_EM_MCPU, data ); + cpu_melf_put32( file + MELF64_EHDR_E_VERSION_OFF, MELF_EV_CURRENT, data ); + if( !shared ) + { + cpu_melf_put64( file + MELF64_EHDR_E_ENTRY_OFF, MELF_MCPU_PTR64( MELF_MCPU_SEG_PROGRAM, im->start_entry ), data ); + cpu_melf_put64( file + MELF64_EHDR_E_INIT_ENTRY_OFF, im->has_init ? MELF_MCPU_PTR64( MELF_MCPU_SEG_PROGRAM, im->init_entry ) : 0, data ); + cpu_melf_put64( file + MELF64_EHDR_E_LIFE_ENTRY_OFF, MELF_MCPU_PTR64( MELF_MCPU_SEG_PROGRAM, im->life_entry ), data ); + cpu_melf_put64( file + MELF64_EHDR_E_FINI_ENTRY_OFF, im->has_fini ? MELF_MCPU_PTR64( MELF_MCPU_SEG_PROGRAM, im->fini_entry ) : 0, data ); + } + cpu_melf_put64( file + MELF64_EHDR_E_PHOFF_OFF, MELF64_EHDR_SIZE, data ); + cpu_melf_put64( file + MELF64_EHDR_E_SHOFF_OFF, shoff, data ); + cpu_melf_put32( file + MELF64_EHDR_E_FLAGS_OFF, MELF_EF_MCPU_NONE, data ); + cpu_melf_put16( file + MELF64_EHDR_E_EHSIZE_OFF, MELF64_EHDR_SIZE, data ); + cpu_melf_put16( file + MELF64_EHDR_E_PHENTSIZE_OFF, MELF64_PHDR_SIZE, data ); + cpu_melf_put16( file + MELF64_EHDR_E_PHNUM_OFF, (__mpu_uint16_t)np, data ); + cpu_melf_put16( file + MELF64_EHDR_E_SHENTSIZE_OFF, MELF64_SHDR_SIZE, data ); + cpu_melf_put16( file + MELF64_EHDR_E_SHNUM_OFF, (__mpu_uint16_t)ns, data ); + cpu_melf_put16( file + MELF64_EHDR_E_SHSTRNDX_OFF, shstridx, data ); + for( i = 0; i < np; ++i ) put_ph( file + MELF64_EHDR_SIZE + i *MELF64_PHDR_SIZE, &ph[i], data ); + /* load payloads */ + for( i = 0; i < np; ++i ) if( ph[i].type == MELF_PT_LOAD && ph[i].filesz ) + { + unsigned seg = MELF_MCPU_PTR64_SEG( ph[i].vaddr ); + const unsigned char *p = 0; + if( seg == MELF_MCPU_SEG_PROGRAM ) p = im->code; + else if( seg == MELF_MCPU_SEG_DATA ) p = im->data; + else if( seg == MELF_MCPU_SEG_CONTROL ) p = im->control; + else if( seg == MELF_MCPU_SEG_INPUT ) p = im->input; + else if( seg == MELF_MCPU_SEG_OUTPUT ) p = im->output; + else if( seg == MELF_MCPU_SEG_TRACE ) p = im->trace; + if( !p ) + { + snprintf( err, es, "missing load payload" ); + goto fail; + } + memcpy( file + (size_t)ph[i].off, p, (size_t)ph[i].filesz ); + } + for( i = 1; i < ns; ++i ) if( ! ( sec[i].flags & MELF_SHF_ALLOC ) && sec[i].type != MELF_SHT_NOBITS && sec[i].size && sec[i].payload ) memcpy( file + (size_t)sec[i].off, sec[i].payload, (size_t)sec[i].size ); + for( i = 0; i < ns; ++i ) put_sh( file + (size_t)shoff + i *MELF64_SHDR_SIZE, &sec[i], data ); + if( cpu_melf_writeall( path, file, (size_t)total, err, es ) ) goto fail; + free( file ); + free( shstr.p ); + free( dynstr.p ); + free( dynsym.p ); + free( dynamic.p ); + for( i = 0; i < 9; ++i ) free( relab[i].p ); + (void)dynamicidx; + return( 0 ); + +fail: + free( file ); + free( shstr.p ); + free( dynstr.p ); + free( dynsym.p ); + free( dynamic.p ); + for( i = 0; i < 9; ++i ) free( relab[i].p ); + return( -1 ); +} + +int +cpu_dyn_write( const char *path, const cpu_asm_image *im, + const cpu_exec_options *op, int shared, + char *err, size_t es ) +{ + return( cpu_dyn_write_version(path, im, op, shared, + MELF_ABIVERSION_DYNAMIC_EXPLICIT, NULL, + err, es) ); +} + +int +cpu_dyn_write_abi3( const char *path, const cpu_asm_image *im, + const cpu_exec_options *op, int shared, + const cpu_dyn_metadata *metadata, + char *err, size_t es ) +{ + return( cpu_dyn_write_version(path, im, op, shared, + MELF_ABIVERSION_DYNAMIC_NEEDED, metadata, + err, es) ); +} + +static int +name_at( const unsigned char *t, size_t n, __mpu_uint32_t off, const char **out ) +{ + if( off >= n ) return( -1 ); + *out = (const char *)t + off; + return( memchr( *out, 0, n - off ) ? 0 : -1 ); +} + +static int +expected_pflags( unsigned seg ) +{ + switch( seg ) + { + case MELF_MCPU_SEG_PROGRAM: return( MELF_PFLAGS_PROGRAM ); + case MELF_MCPU_SEG_DATA: return( MELF_PFLAGS_DATA ); + case MELF_MCPU_SEG_CONTROL: return( MELF_PFLAGS_CONTROL ); + case MELF_MCPU_SEG_INPUT: return( MELF_PFLAGS_INPUT ); + case MELF_MCPU_SEG_OUTPUT: return( MELF_PFLAGS_OUTPUT ); + case MELF_MCPU_SEG_TRACE: return( MELF_PFLAGS_TRACE ); + case MELF_MCPU_SEG_STACK: return( MELF_PFLAGS_STACK ); + case MELF_MCPU_SEG_HEAP: return( MELF_PFLAGS_HEAP ); + default: return( -1 ); + } +} + +static __mpu_uint64_t +expected_shflags( unsigned seg ) +{ + switch( seg ) + { + case MELF_MCPU_SEG_PROGRAM: return( MELF_SHFLAGS_TEXT ); + case MELF_MCPU_SEG_DATA: return( MELF_SHFLAGS_DATA ); + case MELF_MCPU_SEG_CONTROL: return( MELF_SHFLAGS_CONTROL ); + case MELF_MCPU_SEG_INPUT: return( MELF_SHFLAGS_INPUT ); + case MELF_MCPU_SEG_OUTPUT: return( MELF_SHFLAGS_OUTPUT ); + case MELF_MCPU_SEG_TRACE: return( MELF_SHFLAGS_TRACE ); + default: return( 0 ); + } +} + +static int +image_seg( unsigned seg ) +{ + return( seg <= MELF_MCPU_SEG_TRACE && seg != MELF_MCPU_SEG_STACK ); +} + +static void +free_mod( dmod *m ) +{ + size_t i; + if( !m ) return; + free( m->path ); + free( m->file ); + free( m->ph ); + free( m->sh ); + for( i = 0; i < m->nsym; ++i ) free( m->sym[i].name ); + free( m->sym ); + free( m->rela ); + free( m->soname ); + for( i = 0; i < m->nneeded; ++i ) free( m->needed[i] ); + free( m->needed ); + memset( m, 0, sizeof( *m ) ); +} + +static int +parse_module( const char *path, int want_exec, int reject_needed, dmod *m, char *err, size_t es ) +{ + __mpu_uint64_t phoff, shoff; + __mpu_uint16_t phnum, shnum, shstridx; + size_t i; + int nt = 0, nl = 0, dynsymidx = -1, dynstridx = -1, dynamicidx = -1; + MElf_McpuTarget mt; + MElf_McpuLifecycle ml; + memset( m, 0, sizeof( *m ) ); + for( i = 0; i < 8; ++i ) m->load_index[i] = -1; + m->stacki = m->heapi = -1; + m->path = strdup( path ); + if( !m->path ) + { + snprintf( err, es, "out of memory" ); + return( -1 ); + } + if( cpu_melf_readall( path, &m->file, &m->file_size, err, es ) ) return( -1 ); + if( m->file_size < MELF64_EHDR_SIZE ) + { + snprintf( err, es, "%s: truncated MELF", path ); + return( -1 ); + } + unsigned char *f = m->file; + if( f[MELF_EI_MAG0] != MELFMAG0 || f[MELF_EI_MAG1] != MELFMAG1 || + f[MELF_EI_MAG2] != MELFMAG2 || f[MELF_EI_MAG3] != MELFMAG3 || + f[MELF_EI_CLASS] != MELF_CLASS_64 || + f[MELF_EI_VERSION] != MELF_EV_CURRENT || + f[MELF_EI_OSABI] != MELF_OSABI_MCPU ) + { + snprintf( err, es, "%s: not a supported dynamic MELF file", path ); + return( -1 ); + } + m->abi = f[MELF_EI_ABIVERSION]; + if( m->abi != MELF_ABIVERSION_DYNAMIC_EXPLICIT && + m->abi != MELF_ABIVERSION_DYNAMIC_NEEDED ) + { + snprintf( err, es, "%s: unsupported dynamic MELF ABI %d", path, m->abi ); + return( -1 ); + } + m->data = f[MELF_EI_DATA]; + if( m->data != MELF_DATA_LSB && m->data != MELF_DATA_MSB ) + { + snprintf( err, es, "%s: unsupported EI_DATA", path ); + return( -1 ); + } + m->etype = cpu_melf_get16( f + MELF64_EHDR_E_TYPE_OFF, m->data ); + if( ( want_exec && m->etype != MELF_ET_EXEC ) || ( !want_exec && m->etype != MELF_ET_DYN ) || cpu_melf_get16( f + MELF64_EHDR_E_MACHINE_OFF, m->data ) != MELF_EM_MCPU || cpu_melf_get32( f + MELF64_EHDR_E_FLAGS_OFF, m->data ) != 0 || cpu_melf_get16( f + MELF64_EHDR_E_EHSIZE_OFF, m->data ) != MELF64_EHDR_SIZE || cpu_melf_get16( f + MELF64_EHDR_E_PHENTSIZE_OFF, m->data ) != MELF64_PHDR_SIZE || cpu_melf_get16( f + MELF64_EHDR_E_SHENTSIZE_OFF, m->data ) != MELF64_SHDR_SIZE ) + { + snprintf( err, es, "%s: invalid dynamic MELF header", path ); + return( -1 ); + } + m->entry = cpu_melf_get64( f + MELF64_EHDR_E_ENTRY_OFF, m->data ); + m->init = cpu_melf_get64( f + MELF64_EHDR_E_INIT_ENTRY_OFF, m->data ); + m->life = cpu_melf_get64( f + MELF64_EHDR_E_LIFE_ENTRY_OFF, m->data ); + m->fini = cpu_melf_get64( f + MELF64_EHDR_E_FINI_ENTRY_OFF, m->data ); + phoff = cpu_melf_get64( f + MELF64_EHDR_E_PHOFF_OFF, m->data ); + shoff = cpu_melf_get64( f + MELF64_EHDR_E_SHOFF_OFF, m->data ); + phnum = cpu_melf_get16( f + MELF64_EHDR_E_PHNUM_OFF, m->data ); + shnum = cpu_melf_get16( f + MELF64_EHDR_E_SHNUM_OFF, m->data ); + shstridx = cpu_melf_get16( f + MELF64_EHDR_E_SHSTRNDX_OFF, m->data ); + if( !phnum || !shnum || shstridx >= shnum || !span( m->file_size, phoff, (__mpu_uint64_t)phnum *MELF64_PHDR_SIZE ) || !span( m->file_size, shoff, (__mpu_uint64_t)shnum *MELF64_SHDR_SIZE ) ) + { + snprintf( err, es, "%s: invalid MELF tables", path ); + return( -1 ); + } + m->ph = ( rph * ) calloc( phnum, sizeof( *m->ph ) ); + m->sh = ( rsh * ) calloc( shnum, sizeof( *m->sh ) ); + if( !m->ph || !m->sh ) + { + snprintf( err, es, "out of memory" ); + return( -1 ); + } + m->phnum = phnum; + m->shnum = shnum; + for( i = 0; i < phnum; ++i ) get_ph( f + (size_t)phoff + i *MELF64_PHDR_SIZE, m->data, &m->ph[i] ); + for( i = 0; i < shnum; ++i ) get_sh( f + (size_t)shoff + i *MELF64_SHDR_SIZE, m->data, &m->sh[i] ); + if( m->sh[shstridx].type != MELF_SHT_STRTAB || !span( m->file_size, m->sh[shstridx].off, m->sh[shstridx].size ) || m->sh[shstridx].size > SIZE_MAX ) + { + snprintf( err, es, "%s: invalid .shstrtab", path ); + return( -1 ); + } + m->shstr = f + (size_t)m->sh[shstridx].off; + m->shstrn = (size_t)m->sh[shstridx].size; + for( i = 0; i < phnum; ++i ) + { + rph *p = &m->ph[i]; + if( p->paddr || ( p->align > 1 && !cpu_melf_is_pow2( p->align ) ) ) + { + snprintf( err, es, "%s: invalid program header", path ); + return( -1 ); + } + if( p->type == MELF_PT_LOAD ) + { + unsigned seg = MELF_MCPU_PTR64_SEG( p->vaddr ); + if( !image_seg( seg ) || MELF_MCPU_PTR64_OFFSET( p->vaddr ) != 0 || m->load_index[seg] >= 0 || p->flags != (unsigned)expected_pflags( seg ) || p->filesz > p->memsz || p->maxsz != p->memsz || !span( m->file_size, p->off, p->filesz ) || p->memsz > CPU_PTR_OFFSET_MASK ) + { + snprintf( err, es, "%s: invalid PT_LOAD", path ); + return( -1 ); + } + if( !want_exec && seg != MELF_MCPU_SEG_PROGRAM && seg != MELF_MCPU_SEG_DATA ) + { + snprintf( err, es, "%s: ET_DYN has forbidden load segment", path ); + return( -1 ); + } + m->load_index[seg] = (int)i; + } + else if( p->type == MELF_PT_MCPU_STACK ) + { + if( !want_exec || m->stacki >= 0 || p->vaddr != MELF_MCPU_VADDR_STACK || p->flags != MELF_PFLAGS_STACK || p->off || p->filesz || !p->memsz || p->maxsz != p->memsz || p->memsz % MCPU_STARTUP_ALIGN ) + { + snprintf( err, es, "%s: invalid STACK descriptor", path ); + return( -1 ); + } + m->stacki = (int)i; + } + else if( p->type == MELF_PT_MCPU_HEAP ) + { + if( !want_exec || m->heapi >= 0 || p->vaddr != MELF_MCPU_VADDR_HEAP || p->flags != MELF_PFLAGS_HEAP || p->off || p->filesz || p->memsz < CPU_HEAP_BEGIN || p->maxsz < p->memsz ) + { + snprintf( err, es, "%s: invalid HEAP descriptor", path ); + return( -1 ); + } + m->heapi = (int)i; + } + else + { + snprintf( err, es, "%s: unsupported program header", path ); + return( -1 ); + } + } + if( want_exec && ( m->load_index[MELF_MCPU_SEG_PROGRAM] < 0 || m->stacki < 0 ) ) + { + snprintf( err, es, "%s: dynamic executable lacks PROGRAM/STACK", path ); + return( -1 ); + } + if( !want_exec && ( m->stacki >= 0 || m->heapi >= 0 || m->entry || m->init || m->life || m->fini ) ) + { + snprintf( err, es, "%s: ET_DYN contains process lifecycle state", path ); + return( -1 ); + } + if( want_exec && m->load_index[MELF_MCPU_SEG_DATA] >= 0 ) + { + rph *dp = &m->ph[m->load_index[MELF_MCPU_SEG_DATA]]; + if( dp->filesz < CPU_DATA_NULL_GUARD_SIZE || dp->memsz < CPU_DATA_NULL_GUARD_SIZE ) + { + snprintf( err, es, "%s: DATA segment lacks process NULL guard", path ); + return( -1 ); + } + for( i = 0; i < CPU_DATA_NULL_GUARD_SIZE; ++i ) if( m->file[(size_t)dp->off + i] ) + { + snprintf( err, es, "%s: DATA NULL guard is not zero", path ); + return( -1 ); + } + } + for( i = 1; i < shnum; ++i ) + { + const char *name; + if( name_at( m->shstr, m->shstrn, m->sh[i].name, &name ) ) + { + snprintf( err, es, "%s: bad section name", path ); + return( -1 ); + } + if( m->sh[i].type == MELF_SHT_MCPU_TARGET ) + { + if( nt++ || m->sh[i].size != MELF_MCPU_TARGET_SIZE || !span( m->file_size, m->sh[i].off, m->sh[i].size ) || cpu_melf_decode_target( f + (size_t)m->sh[i].off, &mt, m->data ) || cpu_melf_validate_target( &mt, m->data, err, es ) ) return( -1 ); + } + else if( m->sh[i].type == MELF_SHT_MCPU_LIFECYCLE ) + { + if( !want_exec || nl++ || m->sh[i].size != MELF_MCPU_LIFECYCLE_SIZE || !span( m->file_size, m->sh[i].off, m->sh[i].size ) || cpu_melf_decode_lifecycle( f + (size_t)m->sh[i].off, &ml, m->data ) || cpu_melf_validate_lifecycle( &ml, err, es ) ) return( -1 ); + } + else if( m->sh[i].type == MELF_SHT_DYNSYM ) + { + if( dynsymidx >= 0 ) + { + snprintf( err, es, "%s: duplicate .dynsym", path ); + return( -1 ); + } + dynsymidx = (int)i; + } + else if( m->sh[i].type == MELF_SHT_DYNAMIC ) + { + if( dynamicidx >= 0 || strcmp( name, MELF_SEC_DYNAMIC ) ) + { + snprintf( err, es, "%s: invalid or duplicate .dynamic", path ); + return( -1 ); + } + dynamicidx = (int)i; + } + else if( m->sh[i].type == MELF_SHT_STRTAB && !strcmp( name, MELF_SEC_DYNSTR ) ) + { + if( dynstridx >= 0 ) + { + snprintf( err, es, "%s: duplicate .dynstr", path ); + return( -1 ); + } + dynstridx = (int)i; + } + if( m->sh[i].flags & MELF_SHF_ALLOC ) + { + unsigned seg = MELF_MCPU_PTR64_SEG( m->sh[i].addr ), flagseg = MELF_SHF_MCPU_GET_SEG( m->sh[i].flags ); + if( ( m->sh[i].flags & MELF_SHF_MCPU_SEG_VALID ) == 0 || m->load_index[seg] < 0 || seg != flagseg || m->sh[i].flags != expected_shflags( seg ) || m->sh[i].align == 0 || !cpu_melf_is_pow2( m->sh[i].align ) ) + { + snprintf( err, es, "%s: invalid allocated section %s", path, name ); + return( -1 ); + } + if( !want_exec && strcmp( name, MELF_SEC_TEXT ) && strcmp( name, MELF_SEC_DATA ) && strcmp( name, MELF_SEC_BSS ) ) + { + snprintf( err, es, "%s: ET_DYN has forbidden allocated section %s", path, name ); + return( -1 ); + } + __mpu_uint64_t off = MELF_MCPU_PTR64_OFFSET( m->sh[i].addr ); + rph *p = &m->ph[m->load_index[seg]]; + if( off > p->memsz || m->sh[i].size > p->memsz - off ) + { + snprintf( err, es, "%s: section outside PT_LOAD", path ); + return( -1 ); + } + if( m->sh[i].type == MELF_SHT_PROGBITS && ( m->sh[i].off < p->off || m->sh[i].off - p->off != off || m->sh[i].size > p->filesz - off || !span( m->file_size, m->sh[i].off, m->sh[i].size ) ) ) + { + snprintf( err, es, "%s: section/file mapping mismatch", path ); + return( -1 ); + } + if( seg == MELF_MCPU_SEG_DATA && !strcmp( name, MELF_SEC_DATA ) ) + { + m->has_data_section = 1; + m->data_section_begin = off; + m->data_section_size = m->sh[i].size; + } + } + } + if( nt != 1 || nl != ( want_exec ? 1 : 0 ) || + dynsymidx < 0 || dynstridx < 0 ) + { + snprintf( err, es, "%s: required dynamic metadata missing", path ); + return( -1 ); + } + if( m->abi == MELF_ABIVERSION_DYNAMIC_EXPLICIT && dynamicidx >= 0 ) + { + snprintf( err, es, "%s: ABI-v2 file must not contain .dynamic", path ); + return( -1 ); + } + if( m->abi == MELF_ABIVERSION_DYNAMIC_NEEDED && dynamicidx < 0 ) + { + snprintf( err, es, "%s: ABI-v3 file lacks .dynamic", path ); + return( -1 ); + } + if( want_exec ) + { + rph *pp = &m->ph[m->load_index[MELF_MCPU_SEG_PROGRAM]]; + if( !m->entry || !m->life || MELF_MCPU_PTR64_SEG( m->entry ) != MELF_MCPU_SEG_PROGRAM || MELF_MCPU_PTR64_SEG( m->life ) != MELF_MCPU_SEG_PROGRAM || MELF_MCPU_PTR64_OFFSET( m->entry ) >= pp->memsz || MELF_MCPU_PTR64_OFFSET( m->life ) >= pp->memsz || ( m->init && m->init != m->entry ) || ( m->init && MELF_MCPU_PTR64_SEG( m->init ) != MELF_MCPU_SEG_PROGRAM ) || ( m->fini && MELF_MCPU_PTR64_SEG( m->fini ) != MELF_MCPU_SEG_PROGRAM ) ) + { + snprintf( err, es, "%s: invalid lifecycle entries", path ); + return( -1 ); + } + } + rsh *dy = &m->sh[dynsymidx], *ds = &m->sh[dynstridx]; + if( dy->link != (unsigned)dynstridx || dy->entsize != MELF64_SYM_SIZE || dy->size % MELF64_SYM_SIZE || !span( m->file_size, dy->off, dy->size ) || !span( m->file_size, ds->off, ds->size ) || ds->size > SIZE_MAX ) + { + snprintf( err, es, "%s: invalid dynamic symbol tables", path ); + return( -1 ); + } + const unsigned char *str = f + (size_t)ds->off; + size_t strn = (size_t)ds->size; + + if( m->abi == MELF_ABIVERSION_DYNAMIC_NEEDED ) + { + rsh *dd = &m->sh[dynamicidx]; + size_t ndyn, k; + int seen_null = 0; + + if( dd->flags != 0 || dd->addr != 0 || + dd->link != (unsigned)dynstridx || dd->info != 0 || + dd->align != 8 || dd->entsize != MELF64_DYN_SIZE || + dd->size < MELF64_DYN_SIZE || dd->size % MELF64_DYN_SIZE || + !span( m->file_size, dd->off, dd->size ) ) + { + snprintf( err, es, "%s: invalid .dynamic section", path ); + return( -1 ); + } + + ndyn = (size_t)( dd->size / MELF64_DYN_SIZE ); + for( k = 0; k < ndyn; ++k ) + { + const unsigned char *dp = + f + (size_t)dd->off + k * MELF64_DYN_SIZE; + __mpu_int64_t tag = + (__mpu_int64_t)cpu_melf_get64( dp + MELF64_DYN_D_TAG_OFF, m->data ); + __mpu_uint64_t val = + cpu_melf_get64( dp + MELF64_DYN_D_VAL_OFF, m->data ); + const char *nm; + + if( tag == MELF_DT_NULL ) + { + if( val != 0 || k + 1 != ndyn ) + { + snprintf( err, es, "%s: non-canonical DT_NULL", path ); + return( -1 ); + } + seen_null = 1; + continue; + } + + if( seen_null ) + { + snprintf( err, es, "%s: dynamic entry follows DT_NULL", path ); + return( -1 ); + } + + if( tag == MELF_DT_SONAME ) + { + if( want_exec || m->soname != NULL || val > UINT32_MAX || + name_at( str, strn, (__mpu_uint32_t)val, &nm ) || + nm[0] == 0 || strchr( nm, '/' ) != NULL ) + { + snprintf( err, es, "%s: invalid DT_SONAME", path ); + return( -1 ); + } + m->soname = strdup( nm ); + if( m->soname == NULL ) + { + snprintf( err, es, "out of memory" ); + return( -1 ); + } + } + else if( tag == MELF_DT_NEEDED ) + { + char **q; + + if( val > UINT32_MAX || + name_at( str, strn, (__mpu_uint32_t)val, &nm ) || + nm[0] == 0 || strchr( nm, '/' ) != NULL ) + { + snprintf( err, es, "%s: invalid DT_NEEDED", path ); + return( -1 ); + } + if( m->nneeded == SIZE_MAX / sizeof( *q ) ) + { + snprintf( err, es, "too many DT_NEEDED entries" ); + return( -1 ); + } + q = (char **)realloc( m->needed, + (m->nneeded + 1) * sizeof( *q ) ); + if( q == NULL ) + { + snprintf( err, es, "out of memory" ); + return( -1 ); + } + m->needed = q; + m->needed[m->nneeded] = strdup( nm ); + if( m->needed[m->nneeded] == NULL ) + { + snprintf( err, es, "out of memory" ); + return( -1 ); + } + ++m->nneeded; + } + else + { + snprintf( err, es, "%s: unsupported dynamic tag %lld", + path, (long long)tag ); + return( -1 ); + } + } + + if( !seen_null ) + { + snprintf( err, es, "%s: .dynamic lacks DT_NULL", path ); + return( -1 ); + } + if( !want_exec && m->soname == NULL ) + { + snprintf( err, es, "%s: ABI-v3 ET_DYN lacks SONAME", path ); + return( -1 ); + } + if( reject_needed && m->nneeded != 0 ) + { + snprintf( err, es, + "%s: ABI-v3 DT_NEEDED loading is not implemented yet", path ); + return( -1 ); + } + } + + m->nsym = (size_t)( dy->size / MELF64_SYM_SIZE ); + m->sym = ( dsym * ) calloc( m->nsym, sizeof( *m->sym ) ); + if( !m->sym ) + { + snprintf( err, es, "out of memory" ); + return( -1 ); + } + for( i = 0; i < m->nsym; ++i ) + { + const unsigned char *sp = f + (size_t)dy->off + i *MELF64_SYM_SIZE; + __mpu_uint32_t no = cpu_melf_get32( sp + MELF64_SYM_ST_NAME_OFF, m->data ); + m->sym[i].binding = MELF64_ST_BIND( sp[MELF64_SYM_ST_INFO_OFF] ); + m->sym[i].type = MELF64_ST_TYPE( sp[MELF64_SYM_ST_INFO_OFF] ); + m->sym[i].shndx = cpu_melf_get16( sp + MELF64_SYM_ST_SHNDX_OFF, m->data ); + m->sym[i].value = cpu_melf_get64( sp + MELF64_SYM_ST_VALUE_OFF, m->data ); + m->sym[i].size = cpu_melf_get64( sp + MELF64_SYM_ST_SIZE_OFF, m->data ); + if( i == 0 ) continue; + const char *nm; + if( name_at( str, strn, no, &nm ) || !no || ( m->sym[i].binding != MELF_STB_LOCAL && m->sym[i].binding != MELF_STB_GLOBAL && m->sym[i].binding != MELF_STB_WEAK ) || sp[MELF64_SYM_ST_OTHER_OFF] != MELF_STV_DEFAULT ) + { + snprintf( err, es, "%s: invalid .dynsym entry", path ); + return( -1 ); + } + m->sym[i].name = strdup( nm ); + if( !m->sym[i].name ) + { + snprintf( err, es, "out of memory" ); + return( -1 ); + } + if( m->sym[i].shndx != MELF_SHN_UNDEF ) + { + if( m->sym[i].shndx >= shnum || ( m->sh[m->sym[i].shndx].flags & MELF_SHF_ALLOC ) == 0 || m->sym[i].value > m->sh[m->sym[i].shndx].size || m->sym[i].size > m->sh[m->sym[i].shndx].size - m->sym[i].value ) + { + snprintf( err, es, "%s: dynamic symbol outside section", path ); + return( -1 ); + } + } + } + for( i = 1; i < shnum; ++i ) if( m->sh[i].type == MELF_SHT_RELA ) + { + rsh *rs = &m->sh[i]; + if( rs->link != (unsigned)dynsymidx || rs->info >= shnum || rs->entsize != MELF64_RELA_SIZE || rs->size % MELF64_RELA_SIZE || !span( m->file_size, rs->off, rs->size ) || ( m->sh[rs->info].flags & MELF_SHF_ALLOC ) == 0 || m->sh[rs->info].type == MELF_SHT_NOBITS ) + { + snprintf( err, es, "%s: invalid dynamic RELA section", path ); + return( -1 ); + } + size_t nr = (size_t)( rs->size / MELF64_RELA_SIZE ), k; + drela *q = ( drela * ) realloc( m->rela, ( m->nrela + nr ) * sizeof( *q ) ); + if( !q ) + { + snprintf( err, es, "out of memory" ); + return( -1 ); + } + m->rela = q; + for( k = 0; k < nr; ++k ) + { + const unsigned char *rp = f + (size_t)rs->off + k *MELF64_RELA_SIZE; + __mpu_uint64_t info = cpu_melf_get64( rp + MELF64_RELA_R_INFO_OFF, m->data ); + size_t sy = (size_t)MELF64_R_SYM( info ); + unsigned rt = MELF64_R_TYPE( info ); + __mpu_uint64_t ro = cpu_melf_get64( rp + MELF64_RELA_R_OFFSET_OFF, m->data ); + if( !sy || sy >= m->nsym || rt < CPU_RELOC_CODE64 || rt > CPU_RELOC_CPTR64_DATA || ro > m->sh[rs->info].size || 8 > m->sh[rs->info].size - ro ) + { + snprintf( err, es, "%s: bad runtime relocation", path ); + return( -1 ); + } + m->rela[m->nrela++] = ( drela ) + { + (__mpu_uint16_t)rs->info, ro, sy, rt, (__mpu_int64_t)cpu_melf_get64( rp + MELF64_RELA_R_ADDEND_OFF, m->data ) + }; + } + } + return( 0 ); +} + +void +cpu_dyn_library_free( cpu_dyn_library_info *info ) +{ + size_t i; + + if( info == NULL ) + return; + + free( info->soname ); + info->soname = NULL; + for( i = 0; i < info->symbol_count; ++i ) + free( info->symbols[i].name ); + free( info->symbols ); + info->symbols = NULL; + info->symbol_count = 0; + +} /* End of cpu_dyn_library_free() */ + + +int +cpu_dyn_library_read( const char *path, cpu_dyn_library_info *info, + char *err, size_t es ) +{ + dmod m; + size_t i, n = 0; + int rc = -1; + + if( path == NULL || info == NULL ) + { + if( err != NULL && es != 0 ) + snprintf( err, es, "invalid shared-library inspection arguments" ); + return( -1 ); + } + + memset( info, 0, sizeof( *info ) ); + memset( &m, 0, sizeof( m ) ); + + if( parse_module(path, 0, 0, &m, err, es) != 0 ) + goto done; + + if( m.abi != MELF_ABIVERSION_DYNAMIC_NEEDED ) + { + snprintf( err, es, "%s: shared-library dependency requires MELF ABI-v3", + path ); + goto done; + } + if( m.soname == NULL ) + { + snprintf( err, es, "%s: ABI-v3 ET_DYN lacks SONAME", path ); + goto done; + } + + info->soname = strdup( m.soname ); + if( info->soname == NULL ) + { + snprintf( err, es, "out of memory" ); + goto done; + } + + for( i = 1; i < m.nsym; ++i ) + if( m.sym[i].shndx != MELF_SHN_UNDEF && + ( m.sym[i].binding == MELF_STB_GLOBAL || + m.sym[i].binding == MELF_STB_WEAK ) ) + ++n; + + if( n != 0 ) + { + info->symbols = (cpu_dyn_library_symbol *)calloc( n, + sizeof(*info->symbols) ); + if( info->symbols == NULL ) + { + snprintf( err, es, "out of memory" ); + goto done; + } + + for( i = 1; i < m.nsym; ++i ) + if( m.sym[i].shndx != MELF_SHN_UNDEF && + ( m.sym[i].binding == MELF_STB_GLOBAL || + m.sym[i].binding == MELF_STB_WEAK ) ) + { + cpu_dyn_library_symbol *s = &info->symbols[info->symbol_count]; + + s->name = strdup( m.sym[i].name ); + if( s->name == NULL ) + { + snprintf( err, es, "out of memory" ); + goto done; + } + s->binding = m.sym[i].binding; + s->type = m.sym[i].type; + ++info->symbol_count; + } + } + + rc = 0; + +done: + free_mod( &m ); + if( rc != 0 ) + cpu_dyn_library_free( info ); + return( rc ); + +} /* End of cpu_dyn_library_read() */ + + +int +cpu_dyn_library_find_symbol( const cpu_dyn_library_info *info, + const char *name, + const cpu_dyn_library_symbol **symbol ) +{ + size_t i; + + if( symbol != NULL ) + *symbol = NULL; + if( info == NULL || name == NULL ) + return( -1 ); + + for( i = 0; i < info->symbol_count; ++i ) + if( strcmp(info->symbols[i].name, name) == 0 ) + { + if( symbol != NULL ) + *symbol = &info->symbols[i]; + return( 0 ); + } + + return( -1 ); + +} /* End of cpu_dyn_library_find_symbol() */ + + +void +cpu_dyn_dependency_free( cpu_dyn_dependency_info *info ) +{ + size_t i; + + if( info == NULL ) + return; + + free( info->soname ); + info->soname = NULL; + for( i = 0; i < info->needed_count; ++i ) + free( info->needed[i] ); + free( info->needed ); + info->needed = NULL; + info->needed_count = 0; + info->abi = 0; + +} /* End of cpu_dyn_dependency_free() */ + + +int +cpu_dyn_dependency_read( const char *path, int want_exec, + cpu_dyn_dependency_info *info, + char *err, size_t es ) +{ + dmod m; + size_t i; + int rc = -1; + + if( path == NULL || info == NULL ) + { + if( err != NULL && es != 0 ) + snprintf( err, es, "invalid dynamic dependency inspection arguments" ); + return( -1 ); + } + + memset( info, 0, sizeof(*info) ); + memset( &m, 0, sizeof(m) ); + + if( parse_module(path, want_exec, 0, &m, err, es) != 0 ) + goto done; + + info->abi = m.abi; + if( m.soname != NULL ) + { + info->soname = strdup( m.soname ); + if( info->soname == NULL ) + { + snprintf( err, es, "out of memory" ); + goto done; + } + } + + if( m.nneeded != 0 ) + { + info->needed = (char **)calloc( m.nneeded, sizeof(*info->needed) ); + if( info->needed == NULL ) + { + snprintf( err, es, "out of memory" ); + goto done; + } + for( i = 0; i < m.nneeded; ++i ) + { + info->needed[i] = strdup( m.needed[i] ); + if( info->needed[i] == NULL ) + { + snprintf( err, es, "out of memory" ); + goto done; + } + ++info->needed_count; + } + } + + rc = 0; + +done: + free_mod( &m ); + if( rc != 0 ) + cpu_dyn_dependency_free( info ); + return( rc ); + +} /* End of cpu_dyn_dependency_read() */ + + +static int +seg_alloc( unsigned seg, __mpu_uint64_t n, __mpu_uint64_t max, __mpu_UNIT_t **dst ) +{ + int r = -1; + *dst = 0; + switch( seg ) + { + case MELF_MCPU_SEG_PROGRAM: r = __cpu_program_alloc( n ); + *dst = __cpu_program_ptr(); + break; + case MELF_MCPU_SEG_DATA: r = __cpu_data_alloc( n ); + *dst = __cpu_data_ptr(); + break; + case MELF_MCPU_SEG_CONTROL: r = __cpu_control_alloc( n ); + *dst = __cpu_control_ptr(); + break; + case MELF_MCPU_SEG_INPUT: r = __cpu_input_alloc( n ); + *dst = __cpu_input_ptr(); + break; + case MELF_MCPU_SEG_OUTPUT: r = __cpu_output_alloc( n ); + *dst = __cpu_output_ptr(); + break; + case MELF_MCPU_SEG_TRACE: r = __cpu_trace_alloc( n ); + *dst = __cpu_trace_ptr(); + break; + case MELF_MCPU_SEG_STACK: r = __cpu_stack_alloc( n ); + *dst = __cpu_stack_ptr(); + break; + case MELF_MCPU_SEG_HEAP: r = __cpu_heap_alloc( n, max ); + *dst = __cpu_heap_ptr(); + break; + } + return( r ); +} + +static __mpu_UNIT_t * +seg_ptr( unsigned seg ) +{ + switch( seg ) + { + case MELF_MCPU_SEG_PROGRAM: return( __cpu_program_ptr() ); + case MELF_MCPU_SEG_DATA: return( __cpu_data_ptr() ); + case MELF_MCPU_SEG_CONTROL: return( __cpu_control_ptr() ); + case MELF_MCPU_SEG_INPUT: return( __cpu_input_ptr() ); + case MELF_MCPU_SEG_OUTPUT: return( __cpu_output_ptr() ); + case MELF_MCPU_SEG_TRACE: return( __cpu_trace_ptr() ); + default: return( 0 ); + } +} + +static int +put_startup( int argc, char **argv, __mpu_uint64_t stack_size, __mpu_uint64_t *base, char *err, size_t es ) +{ + __mpu_uint64_t count, arr, strings = 0, raw, b; + size_t i; + __mpu_UNIT_t *sp = __cpu_stack_ptr(); + if( argc < 0 || ( argc && !argv ) ) + { + snprintf( err, es, "bad argc/argv" ); + return( -1 ); + } + count = (__mpu_uint64_t)(unsigned)argc; + if( count == UINT64_MAX || count + 1 > UINT64_MAX / MCPU_STARTUP_ARGV_SIZE ) + { + snprintf( err, es, "startup argv overflow" ); + return( -1 ); + } + arr = ( count + 1 ) * MCPU_STARTUP_ARGV_SIZE; + for( i = 0; i < (size_t)argc; ++i ) + { + size_t sl = strlen( argv[i] ); + __mpu_uint64_t z = (__mpu_uint64_t)sl + 1; + if( !z || strings > UINT64_MAX - z ) + { + snprintf( err, es, "startup strings overflow" ); + return( -1 ); + } + strings += z; + } + if( MCPU_STARTUP_SIZE > UINT64_MAX - arr || MCPU_STARTUP_SIZE + arr > UINT64_MAX - strings ) + { + snprintf( err, es, "startup size overflow" ); + return( -1 ); + } + raw = MCPU_STARTUP_SIZE + arr + strings; + if( raw > stack_size ) + { + snprintf( err, es, "command line does not fit executable STACK" ); + return( -1 ); + } + b = ( stack_size - raw ) & ~ ( (__mpu_uint64_t)MCPU_STARTUP_ALIGN - 1 ); + if( raw > stack_size - b ) + { + snprintf( err, es, "command line does not fit aligned executable STACK" ); + return( -1 ); + } + memset( sp + (size_t)b, 0, (size_t)( stack_size - b ) ); + __cpu_data_put_u32( sp + (size_t)b + MCPU_STARTUP_VERSION_OFF, MCPU_STARTUP_VERSION_CURRENT ); + __cpu_data_put_u32( sp + (size_t)b + MCPU_STARTUP_SIZE_OFF, MCPU_STARTUP_SIZE ); + __cpu_data_put_u64( sp + (size_t)b + MCPU_STARTUP_ARGC_OFF, count ); + __cpu_data_put_u64( sp + (size_t)b + MCPU_STARTUP_ARGV_OFF, MELF_MCPU_PTR64( MELF_MCPU_SEG_STACK, b + MCPU_STARTUP_SIZE ) ); + __cpu_data_put_u64( sp + (size_t)b + MCPU_STARTUP_RESERVED_OFF, 0 ); + { + __mpu_uint64_t pos = b + MCPU_STARTUP_SIZE + arr; + for( i = 0; i < (size_t)argc; ++i ) + { + size_t z = strlen( argv[i] ) + 1; + __cpu_data_put_u64( sp + (size_t)( b + MCPU_STARTUP_SIZE + i * 8 ), MELF_MCPU_PTR64( MELF_MCPU_SEG_STACK, pos ) ); + memcpy( sp + (size_t)pos, argv[i], z ); + pos += z; + } + __cpu_data_put_u64( sp + (size_t)( b + MCPU_STARTUP_SIZE + (size_t)argc * 8 ), 0 ); + } + *base = b; + return( 0 ); +} + +static int +split_preloads( const char *s, char ***out, size_t *n, char *err, size_t es ) +{ + char *copy = 0, *p, *q; + char **v = 0; + size_t nv = 0, cap = 0; + *out = 0; + *n = 0; + if( !s || !*s ) return( 0 ); + copy = strdup( s ); + if( !copy ) + { + snprintf( err, es, "out of memory" ); + return( -1 ); + } + p = copy; + for( ; ; ) + { + q = strchr( p, ':' ); + if( q ) * q = 0; + if( !*p ) + { + snprintf( err, es, "empty path in --ldpreload list" ); + goto bad; + } + if( nv == cap ) + { + size_t nc = cap ? cap * 2 : 4; + char **z = (char **)realloc( v, nc *sizeof( *z ) ); + if( !z ) + { + snprintf( err, es, "out of memory" ); + goto bad; + } + v = z; + cap = nc; + } + v[nv] = strdup( p ); + if( !v[nv] ) + { + snprintf( err, es, "out of memory" ); + goto bad; + } + ++nv; + if( !q ) break; + p = q + 1; + } + free( copy ); + *out = v; + *n = nv; + return( 0 ); + bad: free( copy ); + while( nv ) free( v[--nv] ); + free( v ); + return( -1 ); +} + +static void +free_paths( char **v, size_t n ) +{ + while( n ) free( v[--n] ); + free( v ); +} + +static int +is_runtime_hook_name( const char *name ) +{ + if( name == NULL ) + return( 0 ); + + return( !strcmp(name, "__mcpu_libc_init") || + !strcmp(name, "__mcpu_init_array") || + !strcmp(name, "__mcpu_fini_array") || + !strcmp(name, "__mcpu_libc_fini") ); + +} /* End of is_runtime_hook_name() */ + + +static int +find_winner( winner *w, size_t nw, const char *name, size_t *idx ) +{ + size_t i; + for( i = 0; i < nw; ++i ) if( !strcmp( w[i].name, name ) ) + { + if( idx ) * idx = i; + return( 0 ); + } + return( -1 ); +} + +static int +scope_class( size_t mod, size_t preload_start ) +{ + if( mod >= preload_start ) + return( 3 ); + if( mod == 0 ) + return( 2 ); + return( 1 ); + +} /* End of scope_class() */ + +static int +scoped_candidate_better( unsigned binding, size_t mod, + unsigned old_binding, size_t old_mod, + size_t preload_start ) +{ + int cls, old_cls; + + if( binding != old_binding ) + return( binding == MELF_STB_GLOBAL ); + + cls = scope_class( mod, preload_start ); + old_cls = scope_class( old_mod, preload_start ); + if( cls != old_cls ) + return( cls > old_cls ); + + if( cls == 3 ) + return( mod > old_mod ); + if( cls == 1 ) + return( mod < old_mod ); + + return( 0 ); + +} /* End of scoped_candidate_better() */ + +static int +build_winners( dmod *m, size_t nm, size_t preload_start, int scoped, + winner **wp, size_t *np, char *err, size_t es ) +{ + winner *w = 0; + size_t nw = 0, cap = 0, mi, si, x; + for( mi = 0; mi < nm; ++mi ) for( si = 1; si < m[mi].nsym; ++si ) + { + dsym *s = &m[mi].sym[si]; + if( s->binding == MELF_STB_LOCAL || s->shndx == MELF_SHN_UNDEF ) continue; + if( !strcmp( s->name, "_start" ) || !strcmp( s->name, "_life" ) || !strcmp( s->name, "_fini" ) ) continue; + + /* + * HLL runtime hooks may be supplied by the normal MCPU Libc dependency, + * but explicit preloads are never allowed to interpose them. Main- + * executable definitions still win naturally through scoped resolution. + */ + if( scoped && mi >= preload_start && is_runtime_hook_name(s->name) ) + continue; + if( !find_winner( w, nw, s->name, &x ) ) + { + winner *o = &w[x]; + int replace; + + if( scoped ) + replace = scoped_candidate_better(s->binding, mi, o->binding, o->mod, + preload_start); + else + replace = ( s->binding == MELF_STB_GLOBAL || + o->binding == MELF_STB_WEAK ); + + if( replace ) + { + o->mod = mi; + o->sym = si; + o->binding = s->binding; + } + continue; + } + if( nw == cap ) + { + size_t nc = cap ? cap * 2 : 32; + winner *q = ( winner * ) realloc( w, nc *sizeof( *q ) ); + if( !q ) + { + free( w ); + snprintf( err, es, "out of memory" ); + return( -1 ); + } + w = q; + cap = nc; + } + w[nw++] = ( winner ) + { + s->name, mi, si, s->binding + }; + } + *wp = w; + *np = nw; + return( 0 ); +} + +static int +symbol_addr( dmod *m, size_t mi, size_t si, __mpu_uint64_t *ptr, unsigned *seg, __mpu_uint64_t *off, char *err, size_t es ) +{ + dsym *s = &m[mi].sym[si]; + if( s->shndx == MELF_SHN_UNDEF || s->shndx >= m[mi].shnum ) + { + snprintf( err, es, "undefined dynamic symbol" ); + return( -1 ); + } + rsh *sh = &m[mi].sh[s->shndx]; + unsigned sg = MELF_MCPU_PTR64_SEG( sh->addr ); + __mpu_uint64_t rel = MELF_MCPU_PTR64_OFFSET( sh->addr ), o; + if( addu64( m[mi].base[sg], rel, &o ) || addu64( o, s->value, &o ) || o > CPU_PTR_OFFSET_MASK || __cpu_ptr_make( ptr, sg, o ) ) + { + snprintf( err, es, "dynamic symbol address overflow: %s", s->name ); + return( -1 ); + } + *seg = sg; + *off = o; + return( 0 ); +} + +static int +cpu_dyn_load_internal( const char *path, const char *preload_list, + int allow_needed, size_t preload_count, + int scoped_resolution, int argc, char **argv, + cpu_exec_loaded *ld, char *err, size_t es ) +{ + char **paths = 0; + size_t np = 0, nm = 0, preload_start = 0, i, j; + dmod *mods = 0; + winner *w = 0; + size_t nw = 0; + __mpu_uint64_t cursor[8] = + { + 0 + } + , segsize[8] = + { + 0 + }; + int rc = -1; + __mpu_UNIT_t *dst; + memset( ld, 0, sizeof( *ld ) ); + if( split_preloads( preload_list, &paths, &np, err, es ) ) return( -1 ); + if( scoped_resolution && preload_count > np ) + { + snprintf( err, es, "invalid resolved preload count" ); + free_paths( paths, np ); + return( -1 ); + } + nm = np + 1; + preload_start = scoped_resolution ? nm - preload_count : nm; + mods = ( dmod * ) calloc( nm, sizeof( *mods ) ); + if( !mods ) + { + snprintf( err, es, "out of memory" ); + goto out; + } + if( parse_module( path, 1, allow_needed ? 0 : 1, &mods[0], err, es ) ) + goto out; + for( i = 0; i < np; ++i ) + if( parse_module(paths[i], 0, allow_needed ? 0 : 1, + &mods[i + 1], err, es) ) + goto out; + /* process layout: main bases are zero; shared objects append per segment */ + for( i = 0; i < 8; ++i ) + { + if( mods[0].load_index[i] >= 0 ) cursor[i] = mods[0].ph[mods[0].load_index[i]].memsz; + } + for( i = 1; i < nm; ++i ) for( j = 0; j < 8; ++j ) if( mods[i].load_index[j] >= 0 ) + { + rph *p = &mods[i].ph[mods[i].load_index[j]]; + int ov = 0; + if( j == MELF_MCPU_SEG_DATA && cursor[j] == 0 ) cursor[j] = CPU_DATA_NULL_GUARD_SIZE; + cursor[j] = cpu_melf_align_up( cursor[j], nzalign( p->align ), &ov ); + if( ov || cursor[j] > CPU_PTR_OFFSET_MASK - p->memsz ) + { + snprintf( err, es, "dynamic segment layout overflow" ); + goto out; + } + mods[i].base[j] = cursor[j]; + cursor[j] += p->memsz; + } + for( i = 0; i < 8; ++i ) segsize[i] = cursor[i]; + /* allocate/copy image-backed process segments */ + for( i = 0; i <= MELF_MCPU_SEG_TRACE; ++i ) if( segsize[i] ) + { + if( seg_alloc( (unsigned)i, segsize[i], segsize[i], &dst ) ) + { + snprintf( err, es, "cannot allocate process segment %zu", i ); + goto out; + } + memset( dst, 0, (size_t)segsize[i] ); + } + for( i = 0; i < nm; ++i ) for( j = 0; j < 8; ++j ) if( mods[i].load_index[j] >= 0 ) + { + rph *p = &mods[i].ph[mods[i].load_index[j]]; + dst = seg_ptr( (unsigned)j ); + if( p->filesz ) memcpy( dst + (size_t)mods[i].base[j], mods[i].file + (size_t)p->off, (size_t)p->filesz ); + } + if( build_winners(mods, nm, preload_start, scoped_resolution, + &w, &nw, err, es) ) goto out; + /* apply every retained startup relocation */ + for( i = 0; i < nm; ++i ) for( j = 0; j < mods[i].nrela; ++j ) + { + drela *r = &mods[i].rela[j]; + dsym *ref = &mods[i].sym[r->sym]; + size_t wi = 0; + __mpu_uint64_t tptr = 0, toff = 0, final = 0, field; + unsigned tseg = 0, sseg; + int unresolved = 0; + int local = ( ref->binding == MELF_STB_LOCAL ); + rsh *ssh = &mods[i].sh[r->target_shndx]; + sseg = MELF_MCPU_PTR64_SEG( ssh->addr ); + if( local ) + { + if( ref->shndx == MELF_SHN_UNDEF ) + { + snprintf( err, es, "undefined LOCAL dynamic symbol" ); + goto out; + } + if( symbol_addr( mods, i, r->sym, &tptr, &tseg, &toff, err, es ) ) goto out; + } + else unresolved = find_winner( w, nw, ref->name, &wi ); + if( addu64( mods[i].base[sseg], MELF_MCPU_PTR64_OFFSET( ssh->addr ), &field ) || addu64( field, r->offset, &field ) || field > segsize[sseg] || 8 > segsize[sseg] - field ) + { + snprintf( err, es, "runtime relocation field outside process image" ); + goto out; + } + if( unresolved ) + { + if( ref->binding == MELF_STB_WEAK && ( r->type == CPU_RELOC_PTR64_CODE || r->type == CPU_RELOC_PTR64_DATA || r->type == CPU_RELOC_CPTR64_DATA ) && r->addend == 0 ) + { + final = 0; + } + else + { + snprintf( err, es, "unresolved dynamic symbol: %s", ref->name ); + goto out; + } + } + else + { + if( !local && symbol_addr( mods, w[wi].mod, w[wi].sym, &tptr, &tseg, &toff, err, es ) ) goto out; + if( addsigned( toff, r->addend, &final ) ) + { + snprintf( err, es, "dynamic relocation overflow: %s", ref->name ); + goto out; + } + } + unsigned char *fp = seg_ptr( sseg ) + (size_t)field; + switch( r->type ) + { + case CPU_RELOC_CODE64: if( unresolved || tseg != MELF_MCPU_SEG_PROGRAM || final >= segsize[MELF_MCPU_SEG_PROGRAM] ) + { + snprintf( err, es, "CODE64 dynamic target is not PROGRAM: %s", ref->name ); + goto out; + } + __cpu_code_put_u64( fp, final ); + break; + case CPU_RELOC_DATA64: if( unresolved || tseg != MELF_MCPU_SEG_DATA || final > segsize[MELF_MCPU_SEG_DATA] ) + { + snprintf( err, es, "DATA64 dynamic target is not DATA: %s", ref->name ); + goto out; + } + __cpu_code_put_u64( fp, final ); + break; + case CPU_RELOC_PTR64_CODE: if( unresolved ) __cpu_code_put_u64( fp, 0 ); + else + { + if( final > CPU_PTR_OFFSET_MASK || __cpu_ptr_make( &tptr, tseg, final ) ) + { + snprintf( err, es, "PTR64 dynamic target overflow" ); + goto out; + } + __cpu_code_put_u64( fp, tptr ); + } break; + case CPU_RELOC_PTR64_DATA: if( unresolved ) __cpu_data_put_u64( fp, 0 ); + else + { + if( final > CPU_PTR_OFFSET_MASK || __cpu_ptr_make( &tptr, tseg, final ) ) + { + snprintf( err, es, "PTR64 dynamic target overflow" ); + goto out; + } + __cpu_data_put_u64( fp, tptr ); + } break; + case CPU_RELOC_CPTR64_DATA: if( unresolved ) __cpu_data_put_u64( fp, 0 ); + else + { + if( tseg != MELF_MCPU_SEG_PROGRAM || final > CPU_PTR_OFFSET_MASK || __cpu_ptr_make( &tptr, MELF_MCPU_SEG_PROGRAM, final ) ) + { + snprintf( err, es, "CPTR64 dynamic target is not PROGRAM" ); + goto out; + } + __cpu_data_put_u64( fp, tptr ); + } break; + default: snprintf( err, es, "unsupported runtime relocation" ); + goto out; + } + } + /* instantiate process-owned STACK/HEAP only after all startup linking succeeds */ + { + rph *sp = &mods[0].ph[mods[0].stacki]; + if( seg_alloc( MELF_MCPU_SEG_STACK, sp->memsz, sp->memsz, &dst ) ) + { + snprintf( err, es, "cannot allocate STACK" ); + goto out; + } + ld->stack_size = sp->memsz; + if( mods[0].heapi >= 0 ) + { + rph *hp = &mods[0].ph[mods[0].heapi]; + if( seg_alloc( MELF_MCPU_SEG_HEAP, hp->memsz, hp->maxsz, &dst ) ) + { + snprintf( err, es, "cannot allocate HEAP" ); + goto out; + } + } + } + if( put_startup( argc, argv, ld->stack_size, &ld->startup_base, err, es ) ) + goto out; + if( __cpu_program_protect() ) + { + snprintf( err, es, "cannot protect PROGRAM" ); + goto out; + } + ld->start_entry = MELF_MCPU_PTR64_OFFSET( mods[0].entry ); + ld->life_entry = MELF_MCPU_PTR64_OFFSET( mods[0].life ); + ld->has_init = mods[0].init != 0; + ld->init_entry = ld->has_init ? MELF_MCPU_PTR64_OFFSET( mods[0].init ) : 0; + ld->has_fini = mods[0].fini != 0; + ld->fini_entry = ld->has_fini ? MELF_MCPU_PTR64_OFFSET( mods[0].fini ) : 0; + ld->has_data_section = mods[0].has_data_section; + ld->data_section_begin = mods[0].data_section_begin; + ld->data_section_size = mods[0].data_section_size; + __cpu_pc = ld->start_entry; + __cpu_sp = ld->startup_base; + __cpu_fp = ld->stack_size; + rc = 0; +out: free( w ); +if( mods ) +{ + for( i = 0; i < nm; ++i ) free_mod( &mods[i] ); + free( mods ); +} +free_paths( paths, np ); +return( rc ); +} + + +int +cpu_dyn_load( const char *path, const char *preload_list, + int argc, char **argv, cpu_exec_loaded *ld, + char *err, size_t es ) +{ + return( cpu_dyn_load_internal(path, preload_list, 0, 0, 0, + argc, argv, ld, err, es) ); + +} /* End of cpu_dyn_load() */ + + +int +cpu_dyn_load_resolved( const char *path, const char *module_list, + size_t preload_count, + int argc, char **argv, cpu_exec_loaded *ld, + char *err, size_t es ) +{ + return( cpu_dyn_load_internal(path, module_list, 1, preload_count, 1, + argc, argv, ld, err, es) ); + +} /* End of cpu_dyn_load_resolved() */ |
