#include #include #include #include #include #include #include #include #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() */