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