#include #include #include #include #include #include #include #include #include "mcpu-exec.h" #include "mcpu-melf.h" #include "mcpu-dyn.h" typedef struct blob { unsigned char *p; size_t n; size_t cap; } blob; typedef struct xsec { const char *name; __mpu_uint32_t type; __mpu_uint64_t flags; __mpu_uint64_t addr; __mpu_uint64_t off; __mpu_uint64_t size; __mpu_uint64_t align; __mpu_uint64_t entsize; __mpu_uint32_t link; __mpu_uint32_t info; __mpu_uint32_t nameoff; } xsec; typedef struct xph { __mpu_uint32_t type; __mpu_uint32_t flags; __mpu_uint64_t off; __mpu_uint64_t vaddr; __mpu_uint64_t paddr; __mpu_uint64_t filesz; __mpu_uint64_t memsz; __mpu_uint64_t maxsz; __mpu_uint64_t align; } xph; typedef struct rsh { __mpu_uint32_t name; __mpu_uint32_t type; __mpu_uint64_t flags; __mpu_uint64_t addr; __mpu_uint64_t off; __mpu_uint64_t size; __mpu_uint32_t link; __mpu_uint32_t info; __mpu_uint64_t align; __mpu_uint64_t entsize; } rsh; typedef xph rph; typedef struct exec_section_ranges { int has_init; int has_text; int has_fini; __mpu_uint64_t init_begin, init_end; __mpu_uint64_t text_begin, text_end; __mpu_uint64_t fini_begin, fini_end; } exec_section_ranges; 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; c = b->cap ? b->cap : 64; while( c < need ) { if( c > SIZE_MAX / 2 ) return( -1 ); c *= 2; } if( c == b->cap ) return( 0 ); 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 maxu( __mpu_uint64_t a, __mpu_uint64_t b ) { return( a > b ? a : b ); } static __mpu_uint64_t nzalign( __mpu_uint64_t a ) { return( a ? a : 1 ); } static int add_u64( __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 void put_ehdr( unsigned char *p, int data, const cpu_asm_image *im, __mpu_uint64_t phoff, __mpu_uint16_t phnum, __mpu_uint64_t shoff, __mpu_uint16_t shnum, __mpu_uint16_t shstr) { memset( p, 0, MELF64_EHDR_SIZE ); p[MELF_EI_MAG0] = MELFMAG0; p[MELF_EI_MAG1] = MELFMAG1; p[MELF_EI_MAG2] = MELFMAG2; p[MELF_EI_MAG3] = MELFMAG3; p[MELF_EI_CLASS] = MELF_CLASS_64; p[MELF_EI_DATA] = (unsigned char)data; p[MELF_EI_VERSION] = MELF_EV_CURRENT; p[MELF_EI_OSABI] = MELF_OSABI_MCPU; p[MELF_EI_ABIVERSION] = MELF_ABIVERSION_CURRENT; cpu_melf_put16( p + MELF64_EHDR_E_TYPE_OFF, MELF_ET_EXEC, data ); cpu_melf_put16( p + MELF64_EHDR_E_MACHINE_OFF, MELF_EM_MCPU, data ); cpu_melf_put32( p + MELF64_EHDR_E_VERSION_OFF, MELF_EV_CURRENT, data ); cpu_melf_put64( p + MELF64_EHDR_E_ENTRY_OFF, MELF_MCPU_PTR64( MELF_MCPU_SEG_PROGRAM, im->start_entry ), data); cpu_melf_put64( p + MELF64_EHDR_E_INIT_ENTRY_OFF, im->has_init ? MELF_MCPU_PTR64( MELF_MCPU_SEG_PROGRAM, im->init_entry ) : 0, data); cpu_melf_put64( p + MELF64_EHDR_E_LIFE_ENTRY_OFF, MELF_MCPU_PTR64( MELF_MCPU_SEG_PROGRAM, im->life_entry ), data); cpu_melf_put64( p + MELF64_EHDR_E_FINI_ENTRY_OFF, im->has_fini ? MELF_MCPU_PTR64( MELF_MCPU_SEG_PROGRAM, im->fini_entry ) : 0, data); cpu_melf_put64( p + MELF64_EHDR_E_PHOFF_OFF, phoff, data ); cpu_melf_put64( p + MELF64_EHDR_E_SHOFF_OFF, shoff, data ); cpu_melf_put32( p + MELF64_EHDR_E_FLAGS_OFF, MELF_EF_MCPU_NONE, data ); cpu_melf_put16( p + MELF64_EHDR_E_EHSIZE_OFF, MELF64_EHDR_SIZE, data ); cpu_melf_put16( p + MELF64_EHDR_E_PHENTSIZE_OFF, MELF64_PHDR_SIZE, data ); cpu_melf_put16( p + MELF64_EHDR_E_PHNUM_OFF, phnum, data ); cpu_melf_put16( p + MELF64_EHDR_E_SHENTSIZE_OFF, MELF64_SHDR_SIZE, data ); cpu_melf_put16( p + MELF64_EHDR_E_SHNUM_OFF, shnum, data ); cpu_melf_put16( p + MELF64_EHDR_E_SHSTRNDX_OFF, shstr, data ); } static void put_phdr( 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_shdr( 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_phdr( 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_shdr( 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 ) ) { snprintf( err, es, "invalid load alignment" ); return( -1 ); } if( filesz > memsz || memsz > CPU_PTR_OFFSET_MASK || filesz > SIZE_MAX ) { snprintf( err, es, "invalid load size" ); return( -1 ); } *cur = cpu_melf_align_up( *cur, align, &ov ); if( ov ) { snprintf( err, es, "executable layout overflow" ); return( -1 ); } ph[*np] = ( xph ) { MELF_PT_LOAD, flags, *cur, MELF_MCPU_PTR64( seg, 0 ), 0, filesz, memsz, memsz, align }; ++*np; if( add_u64( *cur, filesz, cur ) ) { snprintf( err, es, "executable layout overflow" ); return( -1 ); } return( 0 ); } static int image_range( __mpu_uint64_t off, __mpu_uint64_t size, __mpu_uint64_t limit ) { return( off <= limit && size <= limit - off ); } int cpu_exec_write( const char *path, const cpu_asm_image *im, const cpu_exec_options *op, char *err, size_t es) { xph ph[8]; size_t np = 0; xsec s[20]; size_t ns = 1; blob shstr = {0}; int data = cpu_melf_data_encoding(), ov = 0; __mpu_uint64_t phoff = MELF64_EHDR_SIZE, cur, shoff, total; __mpu_uint64_t data_mem_size; unsigned char *file = 0; unsigned char tbuf[MELF_MCPU_TARGET_SIZE], lbuf[MELF_MCPU_LIFECYCLE_SIZE]; MElf_McpuTarget mt; MElf_McpuLifecycle ml; __mpu_uint16_t shstridx, targetidx, lifeidx; size_t i; int phidx[8] = { -1, -1, -1, -1, -1, -1, -1, -1 }; int have_data; __mpu_uint64_t palign; if( !path || !im || !op || !im->has_start || !im->has_life || !im->code_size ) { snprintf( err, es, "incomplete executable image" ); return( -1 ); } if( im->start_entry >= im->code_size || im->life_entry >= im->code_size || (im->has_init && im->init_entry >= im->code_size) || (im->has_fini && im->fini_entry >= im->code_size)) { snprintf( err, es, "lifecycle entry is outside PROGRAM" ); return( -1 ); } if( im->has_init && im->start_entry != im->init_entry ) { snprintf( err, es, "INIT entry must equal _start" ); return( -1 ); } if( !im->has_init && im->start_entry != im->life_entry ) { snprintf( err, es, "without INIT, _start must equal _life" ); return( -1 ); } if( !im->text_present || !image_range( im->text_offset, im->text_size, im->code_size ) || im->life_entry < im->text_offset || im->life_entry - im->text_offset >= im->text_size) { snprintf( err, es, "_life entry must be inside .text" ); return( -1 ); } if( im->has_init && (!im->init_present || !image_range( im->init_offset, im->init_size, im->code_size ) || im->start_entry < im->init_offset || im->start_entry - im->init_offset >= im->init_size)) { snprintf( err, es, "_start entry must be inside .init" ); return( -1 ); } if( im->has_fini && (!im->fini_present || !image_range( im->fini_offset, im->fini_size, im->code_size ) || im->fini_entry < im->fini_offset || im->fini_entry - im->fini_offset >= im->fini_size)) { snprintf( err, es, "_fini entry must be inside .fini" ); return( -1 ); } 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, "STACK size must be a positive 16-byte-aligned PTR64 offset" ); return( -1 ); } 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" ); return( -1 ); } data_mem_size = im->data_mem_size ? im->data_mem_size : im->data_size; if( data_mem_size < im->data_size ) { snprintf( err, es, "DATA memory size is smaller than DATA file image" ); return( -1 ); } have_data = data_mem_size || im->data_present || im->bss_present; if( have_data && ( data_mem_size < CPU_DATA_NULL_GUARD_SIZE || im->data_size < CPU_DATA_NULL_GUARD_SIZE)) { snprintf( err, es, "DATA image lacks the mandatory 16-byte NULL guard" ); return( -1 ); } palign = maxu( maxu( nzalign( im->init_align ), nzalign( im->text_align ) ), nzalign( im->fini_align )); /* Count PHDRs first because file payload begins after the whole table. */ np = 1 + (size_t)have_data + (im->control_size || im->control_present) + (im->input_size || im->input_present) + (im->output_size || im->output_present) + (im->trace_size || im->trace_present) + 1 + (op->have_heap ? 1 : 0); if( np > 8 || phoff > UINT64_MAX - np *MELF64_PHDR_SIZE ) { snprintf( err, es, "too many program headers" ); return( -1 ); } cur = phoff + np * MELF64_PHDR_SIZE; np = 0; 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, palign, 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_size, MELF_PFLAGS_DATA, maxu( 16, maxu( nzalign( im->data_align ), nzalign( im->bss_align ) ) ), err, es)) goto fail; } if( im->control_size || im->control_present ) { 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_size || im->input_present ) { 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_size || im->output_present ) { 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_size || im->trace_present ) { 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 }; } memset( s, 0, sizeof( s ) ); #define ADDSEC(N,T,F,A,O,Z,AL,E) do { \ if(ns >= sizeof(s)/sizeof(s[0])) { snprintf(err,es,"too many sections"); goto fail; } \ s[ns] = (xsec){(N),(T),(F),(A),(O),(Z),(AL),(E),0,0,0}; \ ++ns; \ } while(0) if( im->init_present ) { if( !image_range( im->init_offset, im->init_size, im->code_size ) ) { snprintf( err, es, ".init range outside PROGRAM" ); goto fail; } ADDSEC( MELF_SEC_INIT, MELF_SHT_PROGBITS, MELF_SHFLAGS_INIT, MELF_MCPU_PTR64( MELF_MCPU_SEG_PROGRAM, im->init_offset ), ph[phidx[MELF_MCPU_SEG_PROGRAM]].off + im->init_offset, im->init_size, nzalign( im->init_align ), 0); } if( im->text_present ) { if( !image_range( im->text_offset, im->text_size, im->code_size ) ) { snprintf( err, es, ".text range outside PROGRAM" ); goto fail; } ADDSEC( MELF_SEC_TEXT, MELF_SHT_PROGBITS, MELF_SHFLAGS_TEXT, MELF_MCPU_PTR64( MELF_MCPU_SEG_PROGRAM, im->text_offset ), ph[phidx[MELF_MCPU_SEG_PROGRAM]].off + im->text_offset, im->text_size, nzalign( im->text_align ), 0); } if( im->fini_present ) { if( !image_range( im->fini_offset, im->fini_size, im->code_size ) ) { snprintf( err, es, ".fini range outside PROGRAM" ); goto fail; } ADDSEC( MELF_SEC_FINI, MELF_SHT_PROGBITS, MELF_SHFLAGS_FINI, MELF_MCPU_PTR64( MELF_MCPU_SEG_PROGRAM, im->fini_offset ), ph[phidx[MELF_MCPU_SEG_PROGRAM]].off + im->fini_offset, im->fini_size, nzalign( im->fini_align ), 0); } if( im->data_present ) { if( phidx[MELF_MCPU_SEG_DATA] < 0 || !image_range( im->data_offset, im->data_section_size, im->data_size )) { snprintf( err, es, ".data range outside DATA file image" ); goto fail; } ADDSEC( MELF_SEC_DATA, MELF_SHT_PROGBITS, MELF_SHFLAGS_DATA, MELF_MCPU_PTR64( MELF_MCPU_SEG_DATA, im->data_offset ), ph[phidx[MELF_MCPU_SEG_DATA]].off + im->data_offset, im->data_section_size, nzalign( im->data_align ), 0); } if( im->bss_present ) { if( phidx[MELF_MCPU_SEG_DATA] < 0 || !image_range( im->bss_offset, im->bss_size, data_mem_size )) { snprintf( err, es, ".bss range outside DATA memory image" ); goto fail; } ADDSEC( MELF_SEC_BSS, MELF_SHT_NOBITS, MELF_SHFLAGS_BSS, MELF_MCPU_PTR64( MELF_MCPU_SEG_DATA, im->bss_offset ), ph[phidx[MELF_MCPU_SEG_DATA]].off + im->bss_offset, im->bss_size, nzalign( im->bss_align ), 0); } #define ADD_DATA_LIKE(PRESENT,SEG,NAME,FLAGS,OFFSET,SIZE,ALIGN) do { \ if((PRESENT)) { \ if(phidx[(SEG)] < 0 || !image_range((OFFSET),(SIZE),ph[phidx[(SEG)]].memsz)) { \ snprintf(err,es,"%s range outside segment",(NAME)); goto fail; \ } \ ADDSEC((NAME),MELF_SHT_PROGBITS,(FLAGS),MELF_MCPU_PTR64((SEG),(OFFSET)), \ ph[phidx[(SEG)]].off+(OFFSET),(SIZE),nzalign((ALIGN)),0); \ } \ } while(0) ADD_DATA_LIKE( im->control_present, MELF_MCPU_SEG_CONTROL, MELF_SEC_CONTROL, MELF_SHFLAGS_CONTROL, im->control_offset, im->control_section_size, im->control_align); ADD_DATA_LIKE( im->input_present, MELF_MCPU_SEG_INPUT, MELF_SEC_INPUT, MELF_SHFLAGS_INPUT, im->input_offset, im->input_section_size, im->input_align); ADD_DATA_LIKE( im->output_present, MELF_MCPU_SEG_OUTPUT, MELF_SEC_OUTPUT, MELF_SHFLAGS_OUTPUT, im->output_offset, im->output_section_size, im->output_align); ADD_DATA_LIKE( im->trace_present, MELF_MCPU_SEG_TRACE, MELF_SEC_TRACE, MELF_SHFLAGS_TRACE, im->trace_offset, im->trace_section_size, im->trace_align); #undef ADD_DATA_LIKE cpu_melf_fill_target( &mt ); cpu_melf_encode_target( tbuf, &mt, data ); cur = cpu_melf_align_up( cur, 8, &ov ); if( ov ) goto overflow; targetidx = (__mpu_uint16_t)ns; ADDSEC( MELF_SEC_MCPU_TARGET, MELF_SHT_MCPU_TARGET, 0, 0, cur, MELF_MCPU_TARGET_SIZE, 8, MELF_MCPU_TARGET_SIZE); if( add_u64( cur, MELF_MCPU_TARGET_SIZE, &cur ) ) goto overflow; cpu_melf_fill_lifecycle( &ml ); cpu_melf_encode_lifecycle( lbuf, &ml, data ); cur = cpu_melf_align_up( cur, 8, &ov ); if( ov ) goto overflow; lifeidx = (__mpu_uint16_t)ns; ADDSEC( MELF_SEC_MCPU_LIFECYCLE, MELF_SHT_MCPU_LIFECYCLE, 0, 0, cur, MELF_MCPU_LIFECYCLE_SIZE, 8, MELF_MCPU_LIFECYCLE_SIZE); if( add_u64( cur, MELF_MCPU_LIFECYCLE_SIZE, &cur ) ) goto overflow; if( bput( &shstr, "", 1 ) ) { snprintf( err, es, "out of memory" ); goto fail; } shstridx = (__mpu_uint16_t)ns; ADDSEC( MELF_SEC_SHSTRTAB, MELF_SHT_STRTAB, 0, 0, 0, 0, 1, 0 ); for( i = 1; i < ns; ++i ) if( bstr( &shstr, s[i].name, &s[i].nameoff ) ) { snprintf( err, es, "section string table too large" ); goto fail; } s[shstridx].off = cur; s[shstridx].size = shstr.n; if( add_u64( cur, shstr.n, &cur ) ) goto overflow; #undef ADDSEC shoff = cpu_melf_align_up( cur, 8, &ov ); if( ov || shoff > UINT64_MAX - ns * MELF64_SHDR_SIZE ) goto overflow; total = shoff + ns * MELF64_SHDR_SIZE; if( total > SIZE_MAX ) { snprintf( err, es, "executable too large for this host" ); goto fail; } file = (unsigned char *)calloc( (size_t)total, 1 ); if( !file ) { snprintf( err, es, "out of memory" ); goto fail; } put_ehdr( file, data, im, phoff, (__mpu_uint16_t)np, shoff, (__mpu_uint16_t)ns, shstridx); for( i = 0; i < np; ++i ) put_phdr( file + (size_t)phoff + i *MELF64_PHDR_SIZE, &ph[i], data ); for( i = 0; i < np; ++i ) if( ph[i].type == MELF_PT_LOAD && ph[i].filesz ) { const unsigned char *payload = 0; unsigned seg = MELF_MCPU_PTR64_SEG( ph[i].vaddr ); switch( seg ) { case MELF_MCPU_SEG_PROGRAM: payload = im->code; break; case MELF_MCPU_SEG_DATA: payload = im->data; break; case MELF_MCPU_SEG_CONTROL: payload = im->control; break; case MELF_MCPU_SEG_INPUT: payload = im->input; break; case MELF_MCPU_SEG_OUTPUT: payload = im->output; break; case MELF_MCPU_SEG_TRACE: payload = im->trace; break; default: break; } if( !payload ) { snprintf( err, es, "missing PT_LOAD payload" ); goto fail; } memcpy( file + (size_t)ph[i].off, payload, (size_t)ph[i].filesz ); } memcpy( file + (size_t)s[targetidx].off, tbuf, MELF_MCPU_TARGET_SIZE ); memcpy( file + (size_t)s[lifeidx].off, lbuf, MELF_MCPU_LIFECYCLE_SIZE ); memcpy( file + (size_t)s[shstridx].off, shstr.p, shstr.n ); for( i = 0; i < ns; ++i ) put_shdr( file + (size_t)shoff + i *MELF64_SHDR_SIZE, &s[i], data ); if( cpu_melf_writeall( path, file, (size_t)total, err, es ) ) goto fail; free( file ); free( shstr.p ); return( 0 ); overflow: snprintf( err, es, "executable layout overflow" ); fail: free( file ); free( shstr.p ); return( -1 ); } static int validate_ident( const unsigned char *f, size_t fn, int *data, char *err, size_t es) { if( fn < MELF64_EHDR_SIZE || f[MELF_EI_MAG0] != MELFMAG0 || f[MELF_EI_MAG1] != MELFMAG1 || f[MELF_EI_MAG2] != MELFMAG2 || f[MELF_EI_MAG3] != MELFMAG3) { snprintf( err, es, "not a MELF executable" ); return( -1 ); } if( f[MELF_EI_CLASS] != MELF_CLASS_64 ) { snprintf( err, es, "unsupported MELF class" ); return( -1 ); } *data = f[MELF_EI_DATA]; if( *data != MELF_DATA_LSB && *data != MELF_DATA_MSB ) { snprintf( err, es, "unsupported EI_DATA" ); return( -1 ); } if( f[MELF_EI_VERSION] != MELF_EV_CURRENT || f[MELF_EI_OSABI] != MELF_OSABI_MCPU || f[MELF_EI_ABIVERSION] != MELF_ABIVERSION_CURRENT || f[9] || f[10] || f[11] || f[12] || f[13] || f[14] || f[15] || cpu_melf_get16( f + MELF64_EHDR_E_TYPE_OFF, *data ) != MELF_ET_EXEC || cpu_melf_get16( f + MELF64_EHDR_E_MACHINE_OFF, *data ) != MELF_EM_MCPU || cpu_melf_get32( f + MELF64_EHDR_E_VERSION_OFF, *data ) != MELF_EV_CURRENT || cpu_melf_get32( f + MELF64_EHDR_E_FLAGS_OFF, *data ) != MELF_EF_MCPU_NONE || cpu_melf_get16( f + MELF64_EHDR_E_EHSIZE_OFF, *data ) != MELF64_EHDR_SIZE || cpu_melf_get16( f + MELF64_EHDR_E_PHENTSIZE_OFF, *data ) != MELF64_PHDR_SIZE || cpu_melf_get16( f + MELF64_EHDR_E_SHENTSIZE_OFF, *data ) != MELF64_SHDR_SIZE) { snprintf( err, es, "unsupported MELF executable header" ); return( -1 ); } return( 0 ); } 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; default: break; } return( r ); } 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_backed_seg( unsigned seg ) { return seg == MELF_MCPU_SEG_PROGRAM || seg == MELF_MCPU_SEG_DATA || seg == MELF_MCPU_SEG_CONTROL || seg == MELF_MCPU_SEG_INPUT || seg == MELF_MCPU_SEG_OUTPUT || seg == MELF_MCPU_SEG_TRACE; } static int name_at( const unsigned char *tab, size_t n, __mpu_uint32_t off, const char **name ) { if( off >= n ) return( -1 ); *name = (const char *)tab + off; return( memchr( *name, 0, n - off ) ? 0 : -1 ); } static int range_contains( __mpu_uint64_t begin, __mpu_uint64_t end, __mpu_uint64_t value ) { return( value >= begin && value < end ); } static int record_lifecycle_section( exec_section_ranges *r, const char *name, __mpu_uint64_t begin, __mpu_uint64_t size, char *err, size_t es) { __mpu_uint64_t end; if( add_u64( begin, size, &end ) ) { snprintf( err, es, "section address overflow" ); return( -1 ); } if( !strcmp( name, MELF_SEC_INIT ) ) { if( r->has_init++ ) { snprintf( err, es, "duplicate .init section" ); return( -1 ); } r->init_begin = begin; r->init_end = end; } else if( !strcmp( name, MELF_SEC_TEXT ) ) { if( r->has_text++ ) { snprintf( err, es, "duplicate .text section" ); return( -1 ); } r->text_begin = begin; r->text_end = end; } else if( !strcmp( name, MELF_SEC_FINI ) ) { if( r->has_fini++ ) { snprintf( err, es, "duplicate .fini section" ); return( -1 ); } r->fini_begin = begin; r->fini_end = end; } 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 == 0 || 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( b > stack_size || 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 cpu_exec_load_v1( const char *path, int argc, char **argv, cpu_exec_loaded *ld, char *err, size_t es) { unsigned char *f = 0; size_t fn = 0; int data, rc = -1; __mpu_uint64_t phoff, shoff, entry, init, life, fini; __mpu_uint16_t phnum, shnum, shstrndx; size_t i; rph ph[8]; int seen[8] = {0}; int stacki = -1, heapi = -1, progi = -1, datai = -1; rsh *sh = 0; int nt = 0, nl = 0; MElf_McpuTarget mt; MElf_McpuLifecycle ml; const unsigned char *shstr; size_t shstrn; exec_section_ranges ranges = {0}; int data_section_seen = 0; __mpu_uint64_t data_section_begin = 0, data_section_size = 0; if( !path || !ld ) return( -1 ); memset( ld, 0, sizeof( *ld ) ); if( cpu_melf_readall( path, &f, &fn, err, es ) ) return( -1 ); if( validate_ident( f, fn, &data, err, es ) ) goto out; phoff = cpu_melf_get64( f + MELF64_EHDR_E_PHOFF_OFF, data ); shoff = cpu_melf_get64( f + MELF64_EHDR_E_SHOFF_OFF, data ); phnum = cpu_melf_get16( f + MELF64_EHDR_E_PHNUM_OFF, data ); shnum = cpu_melf_get16( f + MELF64_EHDR_E_SHNUM_OFF, data ); shstrndx = cpu_melf_get16( f + MELF64_EHDR_E_SHSTRNDX_OFF, data ); entry = cpu_melf_get64( f + MELF64_EHDR_E_ENTRY_OFF, data ); init = cpu_melf_get64( f + MELF64_EHDR_E_INIT_ENTRY_OFF, data ); life = cpu_melf_get64( f + MELF64_EHDR_E_LIFE_ENTRY_OFF, data ); fini = cpu_melf_get64( f + MELF64_EHDR_E_FINI_ENTRY_OFF, data ); if( !phnum || phnum > 8 || !cpu_melf_span( fn, phoff, (__mpu_uint64_t)phnum *MELF64_PHDR_SIZE ) || !shnum || shstrndx == MELF_SHN_UNDEF || shstrndx >= shnum || !cpu_melf_span( fn, shoff, (__mpu_uint64_t)shnum *MELF64_SHDR_SIZE )) { snprintf( err, es, "bad MELF table ranges" ); goto out; } sh = ( rsh * ) calloc( shnum, sizeof( *sh ) ); if( !sh ) { snprintf( err, es, "out of memory" ); goto out; } for( i = 0; i < shnum; ++i ) get_shdr( f + (size_t)shoff + i *MELF64_SHDR_SIZE, data, &sh[i] ); /* Section zero is the all-zero null entry. */ if( sh[0].name || sh[0].type || sh[0].flags || sh[0].addr || sh[0].off || sh[0].size || sh[0].link || sh[0].info || sh[0].align || sh[0].entsize) { snprintf( err, es, "section zero is not MELF_SHT_NULL/all-zero" ); goto out; } if( sh[shstrndx].type != MELF_SHT_STRTAB || sh[shstrndx].flags || sh[shstrndx].addr || sh[shstrndx].link || sh[shstrndx].info || sh[shstrndx].align != 1 || sh[shstrndx].entsize || sh[shstrndx].size > SIZE_MAX || !cpu_melf_span( fn, sh[shstrndx].off, sh[shstrndx].size )) { snprintf( err, es, "bad .shstrtab" ); goto out; } shstr = f + (size_t)sh[shstrndx].off; shstrn = (size_t)sh[shstrndx].size; if( !shstrn || shstr[0] != 0 ) { snprintf( err, es, "bad .shstrtab contents" ); goto out; } { const char *shstrname; if( name_at( shstr, shstrn, sh[shstrndx].name, &shstrname ) || strcmp( shstrname, MELF_SEC_SHSTRTAB )) { snprintf( err, es, "section-name table is not canonical .shstrtab" ); goto out; } } for( i = 1; i < shnum; ++i ) { const char *name; if( name_at( shstr, shstrn, sh[i].name, &name ) ) { snprintf( err, es, "bad section name offset" ); goto out; } if( sh[i].type == MELF_SHT_RELA ) { snprintf( err, es, "ET_EXEC must not contain SHT_RELA sections" ); goto out; } if( sh[i].type == MELF_SHT_MCPU_TARGET ) { if( nt++ || strcmp( name, MELF_SEC_MCPU_TARGET ) || sh[i].flags || sh[i].addr || sh[i].size != MELF_MCPU_TARGET_SIZE || sh[i].link || sh[i].info || sh[i].align != 8 || sh[i].entsize != MELF_MCPU_TARGET_SIZE || !cpu_melf_span( fn, sh[i].off, sh[i].size ) || cpu_melf_decode_target( f + (size_t)sh[i].off, &mt, data ) || cpu_melf_validate_target( &mt, data, err, es )) goto out; } else if( sh[i].type == MELF_SHT_MCPU_LIFECYCLE ) { if( nl++ || strcmp( name, MELF_SEC_MCPU_LIFECYCLE ) || sh[i].flags || sh[i].addr || sh[i].size != MELF_MCPU_LIFECYCLE_SIZE || sh[i].link || sh[i].info || sh[i].align != 8 || sh[i].entsize != MELF_MCPU_LIFECYCLE_SIZE || !cpu_melf_span( fn, sh[i].off, sh[i].size ) || cpu_melf_decode_lifecycle( f + (size_t)sh[i].off, &ml, data ) || cpu_melf_validate_lifecycle( &ml, err, es )) goto out; } } if( nt != 1 || nl != 1 ) { snprintf( err, es, "MELF executable requires exactly one target and lifecycle metadata section"); goto out; } /* Validate Program Headers without allocating guest memory. */ for( i = 0; i < phnum; ++i ) { unsigned seg; get_phdr( f + (size_t)phoff + i *MELF64_PHDR_SIZE, data, &ph[i] ); if( ph[i].paddr ) { snprintf( err, es, "p_paddr must be zero" ); goto out; } if( ph[i].align > 1 && !cpu_melf_is_pow2( ph[i].align ) ) { snprintf( err, es, "bad p_align" ); goto out; } if( ph[i].type == MELF_PT_LOAD ) { seg = MELF_MCPU_PTR64_SEG( ph[i].vaddr ); if( !image_backed_seg( seg ) || MELF_MCPU_PTR64_OFFSET( ph[i].vaddr ) != 0 || seen[seg]++ || ph[i].flags != (unsigned)expected_pflags( seg ) || ph[i].filesz > ph[i].memsz || ph[i].maxsz != ph[i].memsz || !cpu_melf_span( fn, ph[i].off, ph[i].filesz ) || ph[i].memsz > CPU_PTR_OFFSET_MASK || ph[i].memsz > SIZE_MAX || (ph[i].align > 1 && ph[i].off % ph[i].align != 0)) { snprintf( err, es, "invalid PT_LOAD descriptor" ); goto out; } if( seg == MELF_MCPU_SEG_PROGRAM ) progi = (int)i; if( seg == MELF_MCPU_SEG_DATA ) datai = (int)i; } else if( ph[i].type == MELF_PT_MCPU_STACK ) { seg = MELF_MCPU_SEG_STACK; if( seen[seg]++ || ph[i].vaddr != MELF_MCPU_VADDR_STACK || ph[i].flags != MELF_PFLAGS_STACK || ph[i].off || ph[i].filesz || !ph[i].memsz || ph[i].maxsz != ph[i].memsz || ph[i].memsz % MCPU_STARTUP_ALIGN || ph[i].align < MCPU_STARTUP_ALIGN || !cpu_melf_is_pow2( ph[i].align ) || ph[i].memsz > CPU_PTR_OFFSET_MASK || ph[i].memsz > SIZE_MAX) { snprintf( err, es, "invalid PT_MCPU_STACK descriptor" ); goto out; } stacki = (int)i; } else if( ph[i].type == MELF_PT_MCPU_HEAP ) { seg = MELF_MCPU_SEG_HEAP; if( seen[seg]++ || ph[i].vaddr != MELF_MCPU_VADDR_HEAP || ph[i].flags != MELF_PFLAGS_HEAP || ph[i].off || ph[i].filesz || ph[i].memsz < CPU_HEAP_BEGIN || ph[i].maxsz < ph[i].memsz || ph[i].align < 16 || !cpu_melf_is_pow2( ph[i].align ) || ph[i].maxsz > CPU_PTR_OFFSET_MASK || ph[i].memsz > SIZE_MAX) { snprintf( err, es, "invalid PT_MCPU_HEAP descriptor" ); goto out; } heapi = (int)i; } else if( ph[i].type != MELF_PT_NULL ) { snprintf( err, es, "unsupported program-header type" ); goto out; } } if( progi < 0 || stacki < 0 ) { snprintf( err, es, "MELF executable requires PROGRAM and STACK descriptors" ); goto out; } if( datai >= 0 ) { if( ph[datai].memsz < CPU_DATA_NULL_GUARD_SIZE || ph[datai].filesz < CPU_DATA_NULL_GUARD_SIZE) { snprintf( err, es, "DATA segment is too small for NULL guard" ); goto out; } for( i = 0; i < CPU_DATA_NULL_GUARD_SIZE; ++i ) if( f[(size_t)ph[datai].off + i] ) { snprintf( err, es, "DATA NULL guard is not zero" ); goto out; } } /* Section-to-segment validation. */ for( i = 1; i < shnum; ++i ) { const char *name; if( name_at( shstr, shstrn, sh[i].name, &name ) ) { snprintf( err, es, "bad section name offset" ); goto out; } if( sh[i].flags & MELF_SHF_ALLOC ) { unsigned seg, flagseg; size_t k; int found = 0; __mpu_uint64_t off; if( sh[i].type != MELF_SHT_PROGBITS && sh[i].type != MELF_SHT_NOBITS ) { snprintf( err, es, "allocated section has unsupported type: %s", name ); goto out; } if( ( sh[i].flags & MELF_SHF_MCPU_SEG_VALID ) == 0 || !sh[i].align || !cpu_melf_is_pow2( sh[i].align )) { snprintf( err, es, "invalid allocated section attributes: %s", name ); goto out; } seg = MELF_MCPU_PTR64_SEG( sh[i].addr ); flagseg = MELF_SHF_MCPU_GET_SEG( sh[i].flags ); if( !image_backed_seg( seg ) || flagseg != seg || sh[i].flags != expected_shflags( seg )) { snprintf( err, es, "allocated section segment/flags disagree: %s", name ); goto out; } if( sh[i].type == MELF_SHT_NOBITS && ( seg != MELF_MCPU_SEG_DATA || strcmp( name, MELF_SEC_BSS ) )) { snprintf( err, es, "ABI-v1 SHT_NOBITS is only canonical .bss in DATA" ); goto out; } off = MELF_MCPU_PTR64_OFFSET( sh[i].addr ); for( k = 0; k < phnum; ++k ) if( ph[k].type == MELF_PT_LOAD && MELF_MCPU_PTR64_SEG( ph[k].vaddr ) == seg) { if( off > ph[k].memsz || sh[i].size > ph[k].memsz - off || sh[i].align > nzalign( ph[k].align )) { snprintf( err, es, "allocated section outside/incompatible with PT_LOAD" ); goto out; } if( sh[i].type == MELF_SHT_PROGBITS ) { if( sh[i].off < ph[k].off || off > ph[k].filesz || sh[i].off - ph[k].off != off || sh[i].size > ph[k].filesz - off || !cpu_melf_span( fn, sh[i].off, sh[i].size )) { snprintf( err, es, "section/file mapping disagrees with PT_LOAD" ); goto out; } } found = 1; break; } if( !found ) { snprintf( err, es, "allocated section has no PT_LOAD" ); goto out; } if( seg == MELF_MCPU_SEG_DATA && !strcmp( name, MELF_SEC_DATA ) ) { if( sh[i].type != MELF_SHT_PROGBITS || data_section_seen++ ) { snprintf( err, es, "invalid or duplicate .data section" ); goto out; } data_section_begin = off; data_section_size = sh[i].size; } if( seg == MELF_MCPU_SEG_PROGRAM && record_lifecycle_section( &ranges, name, off, sh[i].size, err, es )) goto out; } else if( sh[i].type == MELF_SHT_MCPU_TARGET || sh[i].type == MELF_SHT_MCPU_LIFECYCLE) { if( sh[i].addr ) { snprintf( err, es, "metadata section must not have guest address" ); goto out; } } } /* Lifecycle entries are numeric Ehdr authority; sections validate their role. */ if( !entry || !life || MELF_MCPU_PTR64_SEG( entry ) != MELF_MCPU_SEG_PROGRAM || MELF_MCPU_PTR64_SEG( life ) != MELF_MCPU_SEG_PROGRAM || MELF_MCPU_PTR64_OFFSET( entry ) >= ph[progi].memsz || MELF_MCPU_PTR64_OFFSET( life ) >= ph[progi].memsz) { snprintf( err, es, "invalid mandatory lifecycle entry" ); goto out; } if( !ranges.has_text || !range_contains( ranges.text_begin, ranges.text_end, MELF_MCPU_PTR64_OFFSET( life ))) { snprintf( err, es, "e_life_entry is not inside .text" ); goto out; } if( init ) { if( MELF_MCPU_PTR64_SEG( init ) != MELF_MCPU_SEG_PROGRAM || MELF_MCPU_PTR64_OFFSET( init ) >= ph[progi].memsz || init != entry || !ranges.has_init || !range_contains( ranges.init_begin, ranges.init_end, MELF_MCPU_PTR64_OFFSET( entry ))) { snprintf( err, es, "invalid INIT entry" ); goto out; } } else { if( entry != life || ranges.has_init ) { snprintf( err, es, "without INIT, e_entry must equal e_life_entry and .init must be absent" ); goto out; } } if( fini ) { if( MELF_MCPU_PTR64_SEG( fini ) != MELF_MCPU_SEG_PROGRAM || MELF_MCPU_PTR64_OFFSET( fini ) >= ph[progi].memsz || !ranges.has_fini || !range_contains( ranges.fini_begin, ranges.fini_end, MELF_MCPU_PTR64_OFFSET( fini ))) { snprintf( err, es, "invalid FINI entry" ); goto out; } } else if( ranges.has_fini ) { snprintf( err, es, ".fini exists but e_fini_entry is zero" ); goto out; } /* Validation complete: only now instantiate guest memory. */ for( i = 0; i < phnum; ++i ) if( ph[i].type == MELF_PT_LOAD ) { unsigned seg = MELF_MCPU_PTR64_SEG( ph[i].vaddr ); __mpu_UNIT_t *dst; if( seg_alloc( seg, ph[i].memsz, ph[i].maxsz, &dst ) ) { snprintf( err, es, "cannot allocate guest segment %u", seg ); goto out; } if( ph[i].memsz ) memset( dst, 0, (size_t)ph[i].memsz ); if( ph[i].filesz ) memcpy( dst, f + (size_t)ph[i].off, (size_t)ph[i].filesz ); } { __mpu_UNIT_t *dst; if( seg_alloc( MELF_MCPU_SEG_STACK, ph[stacki].memsz, ph[stacki].memsz, &dst ) ) { snprintf( err, es, "cannot allocate STACK" ); goto out; } (void)dst; if( heapi >= 0 && seg_alloc( MELF_MCPU_SEG_HEAP, ph[heapi].memsz, ph[heapi].maxsz, &dst )) { snprintf( err, es, "cannot allocate HEAP" ); goto out; } } if( put_startup( argc, argv, ph[stacki].memsz, &ld->startup_base, err, es ) ) goto out; if( __cpu_program_protect() ) { snprintf( err, es, "cannot protect PROGRAM" ); goto out; } ld->stack_size = ph[stacki].memsz; ld->has_data_section = data_section_seen; ld->data_section_begin = data_section_begin; ld->data_section_size = data_section_size; ld->start_entry = MELF_MCPU_PTR64_OFFSET( entry ); ld->life_entry = MELF_MCPU_PTR64_OFFSET( life ); ld->has_init = init != 0; ld->init_entry = init ? MELF_MCPU_PTR64_OFFSET( init ) : 0; ld->has_fini = fini != 0; ld->fini_entry = fini ? MELF_MCPU_PTR64_OFFSET( fini ) : 0; __cpu_pc = ld->start_entry; __cpu_sp = ld->startup_base; __cpu_fp = ld->stack_size; rc = 0; out: free( f ); free( sh ); return( rc ); } int cpu_exec_abi_version( const char *path, unsigned *abi, char *err, size_t es ) { FILE *fp; unsigned char h[MELF_EI_NIDENT]; size_t n; fp = fopen( path, "rb" ); if( !fp ) { snprintf( err, es, "%s: %s", path, strerror( errno ) ); return( -1 ); } n = fread( h, 1, sizeof( h ), fp ); if( fclose( fp ) != 0 && n == sizeof( h ) ) { snprintf( err, es, "%s: %s", path, strerror( errno ) ); return( -1 ); } if( n != sizeof( h ) || h[MELF_EI_MAG0] != MELFMAG0 || h[MELF_EI_MAG1] != MELFMAG1 || h[MELF_EI_MAG2] != MELFMAG2 || h[MELF_EI_MAG3] != MELFMAG3) { snprintf( err, es, "%s: not a MELF image", path ); return( -1 ); } *abi = h[MELF_EI_ABIVERSION]; return( 0 ); } int cpu_exec_load_preload( const char *path, const char *preload_list, int argc, char **argv, cpu_exec_loaded *ld, char *err, size_t es) { unsigned abi; if( cpu_exec_abi_version( path, &abi, err, es ) ) return( -1 ); if( abi == MELF_ABIVERSION_MCPU_1 ) { if( preload_list && *preload_list ) { snprintf( err, es, "--ldpreload requires an ABI-v2/ABI-v3 executable linked with mcpu-ld --dynamic"); return( -1 ); } return( cpu_exec_load_v1( path, argc, argv, ld, err, es ) ); } if( abi == MELF_ABIVERSION_MCPU_2 || abi == MELF_ABIVERSION_MCPU_3 ) return( cpu_dyn_load( path, preload_list, argc, argv, ld, err, es ) ); snprintf( err, es, "unsupported MELF ABI version %u", abi ); return( -1 ); } int cpu_exec_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 ) { unsigned abi; if( cpu_exec_abi_version(path, &abi, err, es) ) return( -1 ); if( abi == MELF_ABIVERSION_MCPU_1 ) { if( module_list != NULL && *module_list != 0 ) { snprintf( err, es, "dynamic modules require an ABI-v2/ABI-v3 executable linked with mcpu-ld --dynamic" ); return( -1 ); } return( cpu_exec_load_v1(path, argc, argv, ld, err, es) ); } if( abi == MELF_ABIVERSION_MCPU_2 || abi == MELF_ABIVERSION_MCPU_3 ) return( cpu_dyn_load_resolved(path, module_list, preload_count, argc, argv, ld, err, es) ); snprintf( err, es, "unsupported MELF ABI version %u", abi ); return( -1 ); } /* End of cpu_exec_load_resolved() */ int cpu_exec_load( const char *path, int argc, char **argv, cpu_exec_loaded *ld, char *err, size_t es) { return( cpu_exec_load_preload( path, NULL, argc, argv, ld, err, es ) ); }