summaryrefslogtreecommitdiff
path: root/utils/mcpu-exec.c
diff options
context:
space:
mode:
authorkx <kx@radix-linux.su>2026-10-09 18:00:15 +0300
committerkx <kx@radix-linux.su>2026-10-09 18:00:15 +0300
commit065da3509019b15dd0e678072e6be629e5330bef (patch)
tree69c934c9e786ac048b4fbe8f4c4c5571661521f4 /utils/mcpu-exec.c
parentd130fe27fa753be02ed76fd9b277aa3c3d928eb9 (diff)
downloadlibmcpu-0.4.0.tar.xz
Version 0.4.00.4.0
Diffstat (limited to 'utils/mcpu-exec.c')
-rw-r--r--utils/mcpu-exec.c1483
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 ) );
+}