diff options
Diffstat (limited to 'utils/mcpu-exec.c')
| -rw-r--r-- | utils/mcpu-exec.c | 1483 |
1 files changed, 1483 insertions, 0 deletions
diff --git a/utils/mcpu-exec.c b/utils/mcpu-exec.c new file mode 100644 index 0000000..09b5256 --- /dev/null +++ b/utils/mcpu-exec.c @@ -0,0 +1,1483 @@ +#include <errno.h> +#include <limits.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <libmcpu.h> +#include <mcpu-elf.h> + +#include "mcpu-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 || ns > UINT64_MAX / MELF64_SHDR_SIZE || + 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 ) ); +} |
