summaryrefslogtreecommitdiff
path: root/utils/mcpu-object.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-object.c
parentd130fe27fa753be02ed76fd9b277aa3c3d928eb9 (diff)
downloadlibmcpu-0.4.0.tar.xz
Version 0.4.00.4.0
Diffstat (limited to 'utils/mcpu-object.c')
-rw-r--r--utils/mcpu-object.c1489
1 files changed, 1489 insertions, 0 deletions
diff --git a/utils/mcpu-object.c b/utils/mcpu-object.c
new file mode 100644
index 0000000..0b36788
--- /dev/null
+++ b/utils/mcpu-object.c
@@ -0,0 +1,1489 @@
+#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-object.h"
+#include "mcpu-melf.h"
+
+typedef struct blob
+{
+ unsigned char *p;
+ size_t n;
+ size_t cap;
+} blob;
+
+typedef struct wsec
+{
+ const char *name;
+ __mpu_uint32_t type;
+ __mpu_uint64_t flags;
+ __mpu_uint64_t align;
+ __mpu_uint64_t entsize;
+ __mpu_uint32_t link;
+ __mpu_uint32_t info;
+ unsigned char *owned;
+ const unsigned char *payload;
+ size_t size;
+ __mpu_uint64_t off;
+ __mpu_uint32_t name_off;
+ unsigned logical;
+} wsec;
+
+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;
+
+static const unsigned object_sections[] =
+{
+ 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
+};
+
+#define OBJECT_SECTION_COUNT \
+ (sizeof(object_sections) / sizeof(object_sections[0]))
+
+static int
+object_add_symbol( cpu_asm_object *o, const char *name, unsigned section,
+ unsigned binding, unsigned type, __mpu_uint64_t value,
+ char *err, size_t es)
+{
+ cpu_asm_symbol *q;
+
+ if (!o || !name)
+ return( -1 );
+
+ q = (cpu_asm_symbol *)realloc( o->symbols,
+ (o->symbol_count + 1) * sizeof(*q));
+ if (!q)
+ {
+ snprintf( err, es, "out of memory" );
+ return( -1 );
+ }
+
+ o->symbols = q;
+ memset( &o->symbols[o->symbol_count], 0,
+ sizeof(o->symbols[o->symbol_count]));
+ o->symbols[o->symbol_count].name = strdup( name );
+ if (!o->symbols[o->symbol_count].name)
+ {
+ snprintf( err, es, "out of memory" );
+ return( -1 );
+ }
+
+ o->symbols[o->symbol_count].section = section;
+ o->symbols[o->symbol_count].binding = binding;
+ o->symbols[o->symbol_count].type = type;
+ o->symbols[o->symbol_count].value = value;
+ ++o->symbol_count;
+ return( 0 );
+}
+
+static int
+object_add_reloc( cpu_asm_object *o, unsigned section, unsigned type,
+ size_t symbol_index, __mpu_uint64_t offset,
+ __mpu_int64_t addend, char *err, size_t es)
+{
+ cpu_asm_reloc *q;
+
+ q = (cpu_asm_reloc *)realloc( o->relocs,
+ (o->reloc_count + 1) * sizeof(*q));
+ if (!q)
+ {
+ snprintf( err, es, "out of memory" );
+ return( -1 );
+ }
+
+ o->relocs = q;
+ o->relocs[o->reloc_count].section = section;
+ o->relocs[o->reloc_count].type = type;
+ o->relocs[o->reloc_count].symbol_index = symbol_index;
+ o->relocs[o->reloc_count].offset = offset;
+ o->relocs[o->reloc_count].addend = addend;
+ ++o->reloc_count;
+ return( 0 );
+}
+
+static int
+blob_grow( blob *b, size_t add )
+{
+ size_t need;
+ size_t cap;
+ unsigned char *q;
+
+ if (add > SIZE_MAX - b->n)
+ return( -1 );
+ need = b->n + add;
+ if (need <= b->cap)
+ return( 0 );
+
+ cap = b->cap ? b->cap : 64;
+ while (cap < need)
+ {
+ if (cap > SIZE_MAX / 2)
+ return( -1 );
+ cap *= 2;
+ }
+
+ q = (unsigned char *)realloc( b->p, cap );
+ if (!q)
+ return( -1 );
+ b->p = q;
+ b->cap = cap;
+ return( 0 );
+}
+
+static int
+blob_put( blob *b, const void *p, size_t n )
+{
+ if( blob_grow( b, n ) )
+ return( -1 );
+ if (n)
+ memcpy( b->p + b->n, p, n );
+ b->n += n;
+ return( 0 );
+}
+
+static int
+blob_string( blob *b, const char *s, __mpu_uint32_t *off )
+{
+ size_t n = strlen( s ) + 1;
+
+ if (b->n > UINT32_MAX || n > (size_t)UINT32_MAX - b->n)
+ return( -1 );
+ *off = (__mpu_uint32_t)b->n;
+ return( blob_put( b, s, n ) );
+}
+
+static void
+free_wsecs( wsec *s, size_t n )
+{
+ size_t i;
+ for( i = 0; i < n; ++i )
+ free( s[i].owned );
+}
+
+static void
+put_ehdr64( unsigned char *p, int data, __mpu_uint16_t type,
+ __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, type, 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, 0, data );
+ cpu_melf_put64( p + MELF64_EHDR_E_INIT_ENTRY_OFF, 0, data );
+ cpu_melf_put64( p + MELF64_EHDR_E_LIFE_ENTRY_OFF, 0, data );
+ cpu_melf_put64( p + MELF64_EHDR_E_FINI_ENTRY_OFF, 0, data );
+ cpu_melf_put64( p + MELF64_EHDR_E_PHOFF_OFF, 0, 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, 0, data );
+ cpu_melf_put16( p + MELF64_EHDR_E_PHNUM_OFF, 0, 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_shdr64( unsigned char *p, const wsec *s, int data )
+{
+ memset( p, 0, MELF64_SHDR_SIZE );
+ cpu_melf_put32( p + MELF64_SHDR_SH_NAME_OFF, s->name_off, data );
+ cpu_melf_put32( p + MELF64_SHDR_SH_TYPE_OFF, s->type, data );
+ cpu_melf_put64( p + MELF64_SHDR_SH_FLAGS_OFF, s->flags, data );
+ cpu_melf_put64( p + MELF64_SHDR_SH_ADDR_OFF, 0, data );
+ cpu_melf_put64( p + MELF64_SHDR_SH_OFFSET_OFF, s->off, data );
+ cpu_melf_put64( p + MELF64_SHDR_SH_SIZE_OFF, s->size, data );
+ cpu_melf_put32( p + MELF64_SHDR_SH_LINK_OFF, s->link, data );
+ cpu_melf_put32( p + MELF64_SHDR_SH_INFO_OFF, s->info, data );
+ cpu_melf_put64( p + MELF64_SHDR_SH_ADDRALIGN_OFF, s->align, data );
+ cpu_melf_put64( p + MELF64_SHDR_SH_ENTSIZE_OFF, s->entsize, data );
+}
+
+static void
+get_shdr64( const unsigned char *p, int data, rsh *s )
+{
+ memset( s, 0, sizeof( *s ) );
+ s->name = cpu_melf_get32( p + MELF64_SHDR_SH_NAME_OFF, data );
+ s->type = cpu_melf_get32( p + MELF64_SHDR_SH_TYPE_OFF, data );
+ s->flags = cpu_melf_get64( p + MELF64_SHDR_SH_FLAGS_OFF, data );
+ s->addr = cpu_melf_get64( p + MELF64_SHDR_SH_ADDR_OFF, data );
+ s->off = cpu_melf_get64( p + MELF64_SHDR_SH_OFFSET_OFF, data );
+ s->size = cpu_melf_get64( p + MELF64_SHDR_SH_SIZE_OFF, data );
+ s->link = cpu_melf_get32( p + MELF64_SHDR_SH_LINK_OFF, data );
+ s->info = cpu_melf_get32( p + MELF64_SHDR_SH_INFO_OFF, data );
+ s->align = cpu_melf_get64( p + MELF64_SHDR_SH_ADDRALIGN_OFF, data );
+ s->entsize = cpu_melf_get64( p + MELF64_SHDR_SH_ENTSIZE_OFF, data );
+}
+
+static int
+shdr_is_zero( const rsh *s )
+{
+ return s->name == 0 && s->type == 0 && s->flags == 0 && s->addr == 0 &&
+ s->off == 0 && s->size == 0 && s->link == 0 && s->info == 0 &&
+ s->align == 0 && s->entsize == 0;
+}
+
+static __mpu_uint64_t
+sec_flags( unsigned logical )
+{
+ switch (logical)
+ {
+ 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 const char *
+sec_name( unsigned logical )
+{
+ switch (logical)
+ {
+ 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( "" );
+ }
+}
+
+static const char *
+rela_name( unsigned logical )
+{
+ switch (logical)
+ {
+ 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( NULL );
+ }
+}
+
+static int
+obj_sec( const cpu_asm_object *o, unsigned logical, const unsigned char **p,
+ size_t *n, __mpu_uint64_t *a, int *present)
+{
+ switch (logical)
+ {
+ case CPU_OBJ_SEC_INIT:
+ *p = o->init;
+ *n = o->init_size;
+ *a = o->init_align;
+ *present = o->has_init;
+ return( 0 );
+ case CPU_OBJ_SEC_TEXT:
+ *p = o->text;
+ *n = o->text_size;
+ *a = o->text_align;
+ *present = o->has_text;
+ return( 0 );
+ case CPU_OBJ_SEC_FINI:
+ *p = o->fini;
+ *n = o->fini_size;
+ *a = o->fini_align;
+ *present = o->has_fini;
+ return( 0 );
+ case CPU_OBJ_SEC_DATA:
+ *p = o->data;
+ *n = o->data_size;
+ *a = o->data_align;
+ *present = o->has_data || o->data_size != 0;
+ return( 0 );
+ case CPU_OBJ_SEC_BSS:
+ *p = NULL;
+ *n = o->bss_size;
+ *a = o->bss_align;
+ *present = o->has_bss || o->bss_size != 0;
+ return( 0 );
+ case CPU_OBJ_SEC_CONTROL:
+ *p = o->control;
+ *n = o->control_size;
+ *a = o->control_align;
+ *present = o->has_control || o->control_size != 0;
+ return( 0 );
+ case CPU_OBJ_SEC_INPUT:
+ *p = o->input;
+ *n = o->input_size;
+ *a = o->input_align;
+ *present = o->has_input || o->input_size != 0;
+ return( 0 );
+ case CPU_OBJ_SEC_OUTPUT:
+ *p = o->output;
+ *n = o->output_size;
+ *a = o->output_align;
+ *present = o->has_output || o->output_size != 0;
+ return( 0 );
+ case CPU_OBJ_SEC_TRACE:
+ *p = o->trace;
+ *n = o->trace_size;
+ *a = o->trace_align;
+ *present = o->has_trace || o->trace_size != 0;
+ return( 0 );
+ default:
+ return( -1 );
+ }
+}
+
+static int
+logical_index( const __mpu_uint16_t idx[OBJECT_SECTION_COUNT], unsigned logical )
+{
+ size_t i;
+ for( i = 0; i < OBJECT_SECTION_COUNT; ++i )
+ if (object_sections[i] == logical)
+ return( idx[i] );
+ return( 0 );
+}
+
+static int
+validate_object_for_write( const cpu_asm_object *o, char *err, size_t es )
+{
+ size_t i;
+
+ for( i = 0; i < OBJECT_SECTION_COUNT; ++i )
+ {
+ const unsigned char *p;
+ size_t n;
+ __mpu_uint64_t a;
+ int present;
+
+ if( obj_sec( o, object_sections[i], &p, &n, &a, &present ) )
+ return( -1 );
+ if (!present)
+ continue;
+ if( a == 0 || !cpu_melf_is_pow2( a ) )
+ {
+ snprintf( err, es, "invalid alignment for %s", sec_name( object_sections[i] ) );
+ return( -1 );
+ }
+ if (object_sections[i] != CPU_OBJ_SEC_BSS && n && !p)
+ {
+ snprintf( err, es, "missing payload for %s", sec_name( object_sections[i] ) );
+ return( -1 );
+ }
+ }
+
+ for( i = 0; i < o->symbol_count; ++i )
+ {
+ const cpu_asm_symbol *s = &o->symbols[i];
+ const unsigned char *p;
+ size_t n;
+ __mpu_uint64_t a;
+ int present;
+
+ if (!s->name || !*s->name)
+ {
+ snprintf( err, es, "empty symbol name" );
+ return( -1 );
+ }
+ if (s->binding > CPU_OBJ_BIND_WEAK || s->type > CPU_OBJ_TYPE_FUNC)
+ {
+ snprintf( err, es, "invalid symbol attributes: %s", s->name );
+ return( -1 );
+ }
+ if (s->section == CPU_OBJ_SEC_UNDEF)
+ {
+ if (s->binding == CPU_OBJ_BIND_LOCAL || s->value != 0 || s->size != 0)
+ {
+ snprintf( err, es, "malformed undefined symbol: %s", s->name );
+ return( -1 );
+ } continue;
+ }
+ if( obj_sec( o, s->section, &p, &n, &a, &present ) || !present )
+ {
+ snprintf( err, es, "symbol %s belongs to absent section", s->name );
+ return( -1 );
+ }
+ if (s->value > n || s->size > (__mpu_uint64_t)n - s->value)
+ {
+ snprintf( err, es, "symbol range outside section: %s", s->name );
+ return( -1 );
+ }
+ }
+
+ for( i = 0; i < o->reloc_count; ++i )
+ {
+ const cpu_asm_reloc *r = &o->relocs[i];
+ const unsigned char *p;
+ size_t n;
+ __mpu_uint64_t a;
+ int present;
+
+ if (r->type < CPU_RELOC_CODE64 || r->type > CPU_RELOC_CPTR64_DATA)
+ {
+ snprintf( err, es, "invalid relocation type" );
+ return( -1 );
+ }
+ if (r->symbol_index >= o->symbol_count)
+ {
+ snprintf( err, es, "bad relocation symbol index" );
+ return( -1 );
+ }
+ if (r->section == CPU_OBJ_SEC_BSS)
+ {
+ snprintf( err, es, "relocations cannot patch SHT_NOBITS .bss" );
+ return( -1 );
+ }
+ if( obj_sec( o, r->section, &p, &n, &a, &present ) || !present )
+ {
+ snprintf( err, es, "relocation refers to absent source section" );
+ return( -1 );
+ }
+ if (r->offset > n || CPU_PTR64_SIZE > n - (size_t)r->offset)
+ {
+ snprintf( err, es, "relocation field outside source section" );
+ return( -1 );
+ }
+ }
+
+ return( 0 );
+}
+
+int
+cpu_object_write( const char *path, const cpu_asm_object *o,
+ char *err, size_t es)
+{
+ wsec s[32];
+ size_t ns = 1;
+ size_t i, j;
+ __mpu_uint16_t secidx[OBJECT_SECTION_COUNT] = {0};
+ __mpu_uint16_t relaidx[OBJECT_SECTION_COUNT] = {0};
+ int data = cpu_melf_data_encoding();
+ int ov = 0;
+ blob str = {0};
+ blob shstr = {0};
+ __mpu_uint32_t *nameoff = NULL;
+ __mpu_uint32_t *symmap = NULL;
+ size_t nlocal = 0, nonlocal = 0, symcount;
+ unsigned char *sympay = NULL;
+ unsigned char *file = NULL;
+ size_t symsz;
+ __mpu_uint16_t symsec, strsec, targetsec, shstrsec;
+ MElf_McpuTarget mt;
+ unsigned char mtbuf[MELF_MCPU_TARGET_SIZE];
+ __mpu_uint64_t cur, shoff, total;
+
+ if (!path || !o)
+ return( -1 );
+ if( validate_object_for_write( o, err, es ) )
+ return( -1 );
+
+ memset( s, 0, sizeof( s ) );
+ if( blob_put( &str, "", 1 ) || blob_put( &shstr, "", 1 ) )
+ {
+ snprintf( err, es, "out of memory" );
+ goto fail;
+ }
+
+ for( i = 0; i < OBJECT_SECTION_COUNT; ++i )
+ {
+ const unsigned char *p;
+ size_t n;
+ __mpu_uint64_t a;
+ int present;
+ unsigned logical = object_sections[i];
+
+ if( obj_sec( o, logical, &p, &n, &a, &present ) )
+ goto fail;
+ if (!present)
+ continue;
+ if (ns >= sizeof(s) / sizeof(s[0]))
+ {
+ snprintf( err, es, "too many MELF sections" );
+ goto fail;
+ }
+
+ secidx[i] = (__mpu_uint16_t)ns;
+ s[ns].name = sec_name( logical );
+ s[ns].type = logical == CPU_OBJ_SEC_BSS ? MELF_SHT_NOBITS
+ : MELF_SHT_PROGBITS;
+ s[ns].flags = sec_flags( logical );
+ s[ns].align = a;
+ s[ns].payload = p;
+ s[ns].size = n;
+ s[ns].logical = logical;
+ ++ns;
+ }
+
+ for( i = 0; i < OBJECT_SECTION_COUNT; ++i )
+ {
+ size_t cnt = 0;
+ const char *rn;
+
+ for( j = 0; j < o->reloc_count; ++j )
+ if (o->relocs[j].section == object_sections[i])
+ ++cnt;
+ if (!cnt)
+ continue;
+
+ rn = rela_name( object_sections[i] );
+ if (!rn || !secidx[i])
+ {
+ snprintf( err, es, "invalid relocation source section" );
+ goto fail;
+ }
+ if (ns >= sizeof(s) / sizeof(s[0]))
+ {
+ snprintf( err, es, "too many MELF sections" );
+ goto fail;
+ }
+
+ relaidx[i] = (__mpu_uint16_t)ns;
+ s[ns].name = rn;
+ s[ns].type = MELF_SHT_RELA;
+ s[ns].align = 8;
+ s[ns].entsize = MELF64_RELA_SIZE;
+ s[ns].info = secidx[i];
+ s[ns].size = cnt * MELF64_RELA_SIZE;
+ s[ns].logical = object_sections[i];
+ ++ns;
+ }
+
+ symsec = (__mpu_uint16_t)ns;
+ s[ns].name = MELF_SEC_SYMTAB;
+ s[ns].type = MELF_SHT_SYMTAB;
+ s[ns].align = 8;
+ s[ns].entsize = MELF64_SYM_SIZE;
+ ++ns;
+
+ strsec = (__mpu_uint16_t)ns;
+ s[ns].name = MELF_SEC_STRTAB;
+ s[ns].type = MELF_SHT_STRTAB;
+ s[ns].align = 1;
+ ++ns;
+
+ targetsec = (__mpu_uint16_t)ns;
+ s[ns].name = MELF_SEC_MCPU_TARGET;
+ s[ns].type = MELF_SHT_MCPU_TARGET;
+ s[ns].align = 8;
+ s[ns].entsize = MELF_MCPU_TARGET_SIZE;
+ s[ns].size = MELF_MCPU_TARGET_SIZE;
+ ++ns;
+
+ shstrsec = (__mpu_uint16_t)ns;
+ s[ns].name = MELF_SEC_SHSTRTAB;
+ s[ns].type = MELF_SHT_STRTAB;
+ s[ns].align = 1;
+ ++ns;
+
+ nameoff = (__mpu_uint32_t *)calloc( o->symbol_count ? o->symbol_count : 1,
+ sizeof(*nameoff));
+ symmap = (__mpu_uint32_t *)calloc( o->symbol_count ? o->symbol_count : 1,
+ sizeof(*symmap));
+ if (!nameoff || !symmap)
+ {
+ snprintf( err, es, "out of memory" );
+ goto fail;
+ }
+
+ for( i = 0; i < o->symbol_count; ++i )
+ {
+ if( blob_string( &str, o->symbols[i].name, &nameoff[i] ) )
+ {
+ snprintf( err, es, "symbol string table too large" );
+ goto fail;
+ }
+ if (o->symbols[i].binding == CPU_OBJ_BIND_LOCAL)
+ ++nlocal;
+ else
+ ++nonlocal;
+ }
+
+ if (nlocal > UINT32_MAX - 1 || nonlocal > UINT32_MAX - 1 - nlocal)
+ {
+ snprintf( err, es, "too many symbols" );
+ goto fail;
+ }
+ symcount = 1 + nlocal + nonlocal;
+ if (symcount > SIZE_MAX / MELF64_SYM_SIZE)
+ {
+ snprintf( err, es, "symbol table too large" );
+ goto fail;
+ }
+ symsz = symcount * MELF64_SYM_SIZE;
+ sympay = (unsigned char *)calloc( symsz ? symsz : 1, 1 );
+ if (!sympay)
+ {
+ snprintf( err, es, "out of memory" );
+ goto fail;
+ }
+
+ {
+ size_t li = 1;
+ size_t gi = 1 + nlocal;
+
+ for( i = 0; i < o->symbol_count; ++i )
+ {
+ const cpu_asm_symbol *x = &o->symbols[i];
+ size_t mi = x->binding == CPU_OBJ_BIND_LOCAL ? li++ : gi++;
+ unsigned bind;
+ unsigned type;
+ __mpu_uint16_t shn = MELF_SHN_UNDEF;
+ unsigned char *q = sympay + mi * MELF64_SYM_SIZE;
+
+ if (x->binding == CPU_OBJ_BIND_LOCAL)
+ bind = MELF_STB_LOCAL;
+ else if (x->binding == CPU_OBJ_BIND_WEAK)
+ bind = MELF_STB_WEAK;
+ else
+ bind = MELF_STB_GLOBAL;
+
+ if (x->type == CPU_OBJ_TYPE_FUNC)
+ type = MELF_STT_FUNC;
+ else if (x->type == CPU_OBJ_TYPE_OBJECT)
+ type = MELF_STT_OBJECT;
+ else
+ type = MELF_STT_NOTYPE;
+
+ if (x->section != CPU_OBJ_SEC_UNDEF)
+ {
+ int idx = logical_index( secidx, x->section );
+ if (!idx)
+ {
+ snprintf( err, es, "symbol %s belongs to absent section", x->name );
+ goto fail;
+ }
+ shn = (__mpu_uint16_t)idx;
+ }
+
+ symmap[i] = (__mpu_uint32_t)mi;
+ cpu_melf_put32( q + MELF64_SYM_ST_NAME_OFF, nameoff[i], data );
+ q[MELF64_SYM_ST_INFO_OFF] = MELF64_ST_INFO( bind, type );
+ q[MELF64_SYM_ST_OTHER_OFF] = MELF_STV_DEFAULT;
+ cpu_melf_put16( q + MELF64_SYM_ST_SHNDX_OFF, shn, data );
+ cpu_melf_put64( q + MELF64_SYM_ST_VALUE_OFF, x->value, data );
+ cpu_melf_put64( q + MELF64_SYM_ST_SIZE_OFF, x->size, data );
+ }
+ }
+
+ s[symsec].owned = sympay;
+ s[symsec].payload = sympay;
+ s[symsec].size = symsz;
+ s[symsec].link = strsec;
+ s[symsec].info = (__mpu_uint32_t)(1 + nlocal);
+ sympay = NULL;
+
+ s[strsec].owned = str.p;
+ s[strsec].payload = str.p;
+ s[strsec].size = str.n;
+ str.p = NULL;
+
+ cpu_melf_fill_target( &mt );
+ cpu_melf_encode_target( mtbuf, &mt, data );
+ s[targetsec].owned = (unsigned char *)malloc( MELF_MCPU_TARGET_SIZE );
+ if (!s[targetsec].owned)
+ {
+ snprintf( err, es, "out of memory" );
+ goto fail;
+ }
+ memcpy( s[targetsec].owned, mtbuf, MELF_MCPU_TARGET_SIZE );
+ s[targetsec].payload = s[targetsec].owned;
+
+ for( i = 0; i < OBJECT_SECTION_COUNT; ++i )
+ {
+ if (relaidx[i])
+ {
+ size_t cnt = 0;
+ wsec *r = &s[relaidx[i]];
+
+ r->link = symsec;
+ r->owned = (unsigned char *)calloc( r->size ? r->size : 1, 1 );
+ if (!r->owned)
+ {
+ snprintf( err, es, "out of memory" );
+ goto fail;
+ }
+ r->payload = r->owned;
+
+ for( j = 0; j < o->reloc_count; ++j )
+ {
+ const cpu_asm_reloc *x;
+ unsigned char *q;
+
+ if (o->relocs[j].section != object_sections[i])
+ continue;
+ x = &o->relocs[j];
+ q = r->owned + cnt * MELF64_RELA_SIZE;
+ cpu_melf_put64( q + MELF64_RELA_R_OFFSET_OFF, x->offset, data );
+ cpu_melf_put64( q + MELF64_RELA_R_INFO_OFF,
+ MELF64_R_INFO( symmap[x->symbol_index], x->type ), data);
+ cpu_melf_put64( q + MELF64_RELA_R_ADDEND_OFF,
+ (__mpu_uint64_t)x->addend, data);
+ ++cnt;
+ }
+ }
+ }
+
+ for( i = 1; i < ns; ++i )
+ if( blob_string( &shstr, s[i].name, &s[i].name_off ) )
+ {
+ snprintf( err, es, "section-name string table too large" );
+ goto fail;
+ }
+
+ s[shstrsec].owned = shstr.p;
+ s[shstrsec].payload = shstr.p;
+ s[shstrsec].size = shstr.n;
+ shstr.p = NULL;
+
+ cur = MELF64_EHDR_SIZE;
+ for( i = 1; i < ns; ++i )
+ {
+ cur = cpu_melf_align_up( cur, s[i].align ? s[i].align : 1, &ov );
+ if (ov || cur > SIZE_MAX)
+ {
+ snprintf( err, es, "MELF file layout overflow" );
+ goto fail;
+ }
+ s[i].off = cur;
+ if (s[i].type != MELF_SHT_NOBITS)
+ {
+ if ((__mpu_uint64_t)s[i].size > UINT64_MAX - cur)
+ {
+ snprintf( err, es, "MELF file layout overflow" );
+ goto fail;
+ }
+ cur += s[i].size;
+ }
+ }
+
+ shoff = cpu_melf_align_up( cur, 8, &ov );
+ if (ov || ns > UINT64_MAX / MELF64_SHDR_SIZE ||
+ shoff > UINT64_MAX - ns * MELF64_SHDR_SIZE)
+ {
+ snprintf( err, es, "MELF section table overflow" );
+ goto fail;
+ }
+ total = shoff + ns * MELF64_SHDR_SIZE;
+ if (total > SIZE_MAX)
+ {
+ snprintf( err, es, "MELF file too large for this host" );
+ goto fail;
+ }
+
+ file = (unsigned char *)calloc( (size_t)total ? (size_t)total : 1, 1 );
+ if (!file)
+ {
+ snprintf( err, es, "out of memory" );
+ goto fail;
+ }
+
+ put_ehdr64( file, data, MELF_ET_REL, shoff, (__mpu_uint16_t)ns, shstrsec );
+ for( i = 1; i < ns; ++i )
+ if (s[i].type != MELF_SHT_NOBITS && s[i].size)
+ memcpy( file + (size_t)s[i].off, s[i].payload, s[i].size );
+ for( i = 0; i < ns; ++i )
+ put_shdr64( 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( nameoff );
+ free( symmap );
+ free_wsecs( s, ns );
+ return( 0 );
+
+fail:
+ free( file );
+ free( nameoff );
+ free( symmap );
+ free( sympay );
+ free( str.p );
+ free( shstr.p );
+ free_wsecs( s, ns );
+ return( -1 );
+}
+
+static int
+validate_ident( const unsigned char *p, size_t n, __mpu_uint16_t want,
+ int *data, char *err, size_t es)
+{
+ size_t i;
+
+ if (n < MELF64_EHDR_SIZE || p[MELF_EI_MAG0] != MELFMAG0 ||
+ p[MELF_EI_MAG1] != MELFMAG1 || p[MELF_EI_MAG2] != MELFMAG2 ||
+ p[MELF_EI_MAG3] != MELFMAG3)
+ {
+ snprintf( err, es, "not a MELF file" );
+ return( -1 );
+ }
+ if (p[MELF_EI_CLASS] != MELF_CLASS_64)
+ {
+ snprintf( err, es, "unsupported MELF class" );
+ return( -1 );
+ }
+
+ *data = p[MELF_EI_DATA];
+ if (*data != MELF_DATA_LSB && *data != MELF_DATA_MSB)
+ {
+ snprintf( err, es, "unsupported MELF data encoding" );
+ return( -1 );
+ }
+ if (p[MELF_EI_VERSION] != MELF_EV_CURRENT ||
+ p[MELF_EI_OSABI] != MELF_OSABI_MCPU ||
+ p[MELF_EI_ABIVERSION] != MELF_ABIVERSION_CURRENT ||
+ cpu_melf_get16( p + MELF64_EHDR_E_TYPE_OFF, *data ) != want ||
+ cpu_melf_get16( p + MELF64_EHDR_E_MACHINE_OFF, *data ) != MELF_EM_MCPU ||
+ cpu_melf_get32( p + MELF64_EHDR_E_VERSION_OFF, *data ) != MELF_EV_CURRENT ||
+ cpu_melf_get32( p + MELF64_EHDR_E_FLAGS_OFF, *data ) != MELF_EF_MCPU_NONE ||
+ cpu_melf_get16( p + MELF64_EHDR_E_EHSIZE_OFF, *data ) != MELF64_EHDR_SIZE)
+ {
+ snprintf( err, es, "unsupported MELF header" );
+ return( -1 );
+ }
+
+ for( i = MELF_EI_PAD; i < MELF_EI_NIDENT; ++i )
+ if (p[i] != 0)
+ {
+ snprintf( err, es, "nonzero reserved e_ident byte" );
+ return( -1 );
+ }
+ return( 0 );
+}
+
+static int
+name_at( const unsigned char *tab, size_t n, __mpu_uint32_t off,
+ const char **s)
+{
+ if (off >= n)
+ return( -1 );
+ *s = (const char *)tab + off;
+ return( memchr( *s, 0, n - off ) ? 0 : -1 );
+}
+
+static int
+map_name_logical( const char *n, unsigned *logical )
+{
+ size_t i;
+ for( i = 0; i < OBJECT_SECTION_COUNT; ++i )
+ if( !strcmp( n, sec_name( object_sections[i] ) ) )
+ {
+ *logical = object_sections[i];
+ return( 0 );
+ }
+ return( -1 );
+}
+
+static int
+load_payload( unsigned char **dst, size_t *dn, const unsigned char *f,
+ size_t fn, const rsh *s, char *err, size_t es)
+{
+ if (s->type == MELF_SHT_NOBITS)
+ {
+ snprintf( err, es, "attempt to load SHT_NOBITS payload" );
+ return( -1 );
+ }
+ if( s->size > SIZE_MAX || !cpu_melf_span( fn, s->off, s->size ) )
+ {
+ snprintf( err, es, "bad section file range" );
+ return( -1 );
+ }
+ if (s->size)
+ {
+ *dst = (unsigned char *)malloc( (size_t)s->size );
+ if (!*dst)
+ {
+ snprintf( err, es, "out of memory" );
+ return( -1 );
+ }
+ memcpy( *dst, f + (size_t)s->off, (size_t)s->size );
+ }
+ *dn = (size_t)s->size;
+ return( 0 );
+}
+
+static int
+u64_to_s64( __mpu_uint64_t u, __mpu_int64_t *out )
+{
+ if (u <= (__mpu_uint64_t)INT64_MAX)
+ {
+ *out = (__mpu_int64_t)u;
+ return( 0 );
+ }
+ *out = -1 - (__mpu_int64_t)(UINT64_MAX - u);
+ return( 0 );
+}
+
+static int
+load_allocated_section( cpu_asm_object *o, unsigned l, const rsh *s,
+ const unsigned char *f, size_t fn,
+ char *err, size_t es)
+{
+ switch (l)
+ {
+ case CPU_OBJ_SEC_INIT:
+ if (o->has_init) goto duplicate;
+ if( load_payload( (unsigned char **)&o->init, &o->init_size,
+ f, fn, s, err, es)) return( -1 );
+ o->init_align = s->align;
+ o->has_init = 1;
+ return( 0 );
+ case CPU_OBJ_SEC_TEXT:
+ if (o->has_text) goto duplicate;
+ if( load_payload( (unsigned char **)&o->text, &o->text_size,
+ f, fn, s, err, es)) return( -1 );
+ o->text_align = s->align;
+ o->has_text = 1;
+ return( 0 );
+ case CPU_OBJ_SEC_FINI:
+ if (o->has_fini) goto duplicate;
+ if( load_payload( (unsigned char **)&o->fini, &o->fini_size,
+ f, fn, s, err, es)) return( -1 );
+ o->fini_align = s->align;
+ o->has_fini = 1;
+ return( 0 );
+ case CPU_OBJ_SEC_DATA:
+ if (o->has_data) goto duplicate;
+ if( load_payload( (unsigned char **)&o->data, &o->data_size,
+ f, fn, s, err, es)) return( -1 );
+ o->data_align = s->align;
+ o->has_data = 1;
+ return( 0 );
+ case CPU_OBJ_SEC_BSS:
+ if (o->has_bss) goto duplicate;
+ if (s->size > SIZE_MAX)
+ {
+ snprintf( err, es, ".bss too large for this host" );
+ return( -1 );
+ }
+ o->bss_size = (size_t)s->size;
+ o->bss_align = s->align;
+ o->has_bss = 1;
+ return( 0 );
+ case CPU_OBJ_SEC_CONTROL:
+ if (o->has_control) goto duplicate;
+ if( load_payload( (unsigned char **)&o->control, &o->control_size,
+ f, fn, s, err, es)) return( -1 );
+ o->control_align = s->align;
+ o->has_control = 1;
+ return( 0 );
+ case CPU_OBJ_SEC_INPUT:
+ if (o->has_input) goto duplicate;
+ if( load_payload( (unsigned char **)&o->input, &o->input_size,
+ f, fn, s, err, es)) return( -1 );
+ o->input_align = s->align;
+ o->has_input = 1;
+ return( 0 );
+ case CPU_OBJ_SEC_OUTPUT:
+ if (o->has_output) goto duplicate;
+ if( load_payload( (unsigned char **)&o->output, &o->output_size,
+ f, fn, s, err, es)) return( -1 );
+ o->output_align = s->align;
+ o->has_output = 1;
+ return( 0 );
+ case CPU_OBJ_SEC_TRACE:
+ if (o->has_trace) goto duplicate;
+ if( load_payload( (unsigned char **)&o->trace, &o->trace_size,
+ f, fn, s, err, es)) return( -1 );
+ o->trace_align = s->align;
+ o->has_trace = 1;
+ return( 0 );
+ default:
+ snprintf( err, es, "unsupported allocated section" );
+ return( -1 );
+ }
+
+duplicate:
+ snprintf( err, es, "duplicate section %s", sec_name( l ) );
+ return( -1 );
+}
+
+static int
+cpu_object_read_data_internal( const unsigned char *f, size_t fn,
+ cpu_asm_object *o, char *err, size_t es )
+{
+ int data;
+ __mpu_uint64_t shoff;
+ __mpu_uint16_t shnum, shentsz, shstrndx;
+ size_t i, j;
+ rsh *sh = NULL;
+ const unsigned char *shstr = NULL;
+ size_t shstrn = 0;
+ unsigned *logical = NULL;
+ int target_found = 0;
+ __mpu_uint32_t symsec = 0;
+ MElf_McpuTarget mt;
+ __mpu_uint32_t *smap = NULL;
+ size_t symcount = 0;
+ int rc = -1;
+
+ if( !f || !o )
+ return( -1 );
+ memset( o, 0, sizeof( *o ) );
+
+ if( validate_ident( f, fn, MELF_ET_REL, &data, err, es ) )
+ goto out;
+
+ if( cpu_melf_get64( f + MELF64_EHDR_E_ENTRY_OFF, data ) != 0 ||
+ cpu_melf_get64( f + MELF64_EHDR_E_INIT_ENTRY_OFF, data ) != 0 ||
+ cpu_melf_get64( f + MELF64_EHDR_E_LIFE_ENTRY_OFF, data ) != 0 ||
+ cpu_melf_get64( f + MELF64_EHDR_E_FINI_ENTRY_OFF, data ) != 0 ||
+ cpu_melf_get64( f + MELF64_EHDR_E_PHOFF_OFF, data ) != 0 ||
+ cpu_melf_get16( f + MELF64_EHDR_E_PHENTSIZE_OFF, data ) != 0 ||
+ cpu_melf_get16( f + MELF64_EHDR_E_PHNUM_OFF, data ) != 0)
+ {
+ snprintf( err, es, "ET_REL contains executable/program-header state" );
+ goto out;
+ }
+
+ shoff = cpu_melf_get64( f + MELF64_EHDR_E_SHOFF_OFF, data );
+ shentsz = cpu_melf_get16( f + MELF64_EHDR_E_SHENTSIZE_OFF, data );
+ shnum = cpu_melf_get16( f + MELF64_EHDR_E_SHNUM_OFF, data );
+ shstrndx = cpu_melf_get16( f + MELF64_EHDR_E_SHSTRNDX_OFF, data );
+ if (shentsz != MELF64_SHDR_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 section table" );
+ goto out;
+ }
+
+ sh = ( rsh * ) calloc( shnum, sizeof( *sh ) );
+ logical = (unsigned *)malloc( shnum *sizeof( *logical ) );
+ if (!sh || !logical)
+ {
+ snprintf( err, es, "out of memory" );
+ goto out;
+ }
+ for( i = 0; i < shnum; ++i )
+ {
+ get_shdr64( f + (size_t)shoff + i *MELF64_SHDR_SIZE, data, &sh[i] );
+ logical[i] = CPU_OBJ_SEC_UNDEF;
+ }
+ if( !shdr_is_zero( &sh[0] ) )
+ {
+ snprintf( err, es, "section header zero is not all-zero" );
+ goto out;
+ }
+
+ if (sh[shstrndx].type != MELF_SHT_STRTAB || sh[shstrndx].flags != 0 ||
+ sh[shstrndx].addr != 0 || sh[shstrndx].link != 0 ||
+ sh[shstrndx].info != 0 || sh[shstrndx].align != 1 ||
+ sh[shstrndx].entsize != 0 || sh[shstrndx].size == 0 ||
+ 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 (shstr[0] != 0)
+ {
+ snprintf( err, es, ".shstrtab does not start with NUL" );
+ 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( i == shstrndx && strcmp( name, MELF_SEC_SHSTRTAB ) )
+ {
+ snprintf( err, es, "section-name table has noncanonical name" );
+ goto out;
+ }
+
+ if ((sh[i].type == MELF_SHT_PROGBITS || sh[i].type == MELF_SHT_NOBITS) &&
+ (sh[i].flags & MELF_SHF_ALLOC))
+ {
+ unsigned l;
+
+ if( map_name_logical( name, &l ) )
+ {
+ snprintf( err, es, "unsupported allocated MELF section: %s", name );
+ goto out;
+ }
+ logical[i] = l;
+ if ((l == CPU_OBJ_SEC_BSS) != (sh[i].type == MELF_SHT_NOBITS))
+ {
+ snprintf( err, es, "section type disagrees with section role: %s", name );
+ goto out;
+ }
+ if( sh[i].flags != sec_flags( l ) || sh[i].addr != 0 ||
+ sh[i].link != 0 || sh[i].info != 0 || sh[i].entsize != 0 ||
+ sh[i].align == 0 || !cpu_melf_is_pow2( sh[i].align ))
+ {
+ snprintf( err, es, "invalid ET_REL section attributes: %s", name );
+ goto out;
+ }
+ if (sh[i].type != MELF_SHT_NOBITS &&
+ !cpu_melf_span( fn, sh[i].off, sh[i].size ))
+ {
+ snprintf( err, es, "bad section file range: %s", name );
+ goto out;
+ }
+ if( load_allocated_section( o, l, &sh[i], f, fn, err, es ) )
+ goto out;
+ }
+ else if (sh[i].type == MELF_SHT_MCPU_TARGET)
+ {
+ if (target_found++)
+ {
+ snprintf( err, es, "duplicate .mcpu.target" );
+ goto out;
+ }
+ if( strcmp( name, MELF_SEC_MCPU_TARGET ) || sh[i].flags != 0 ||
+ sh[i].addr != 0 || sh[i].size != MELF_MCPU_TARGET_SIZE ||
+ sh[i].link != 0 || sh[i].info != 0 || 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_SYMTAB)
+ {
+ if( strcmp( name, MELF_SEC_SYMTAB ) || symsec )
+ {
+ snprintf( err, es, "multiple/noncanonical symbol table" );
+ goto out;
+ }
+ symsec = (__mpu_uint32_t)i;
+ }
+ }
+
+ if (!target_found)
+ {
+ snprintf( err, es, "missing .mcpu.target" );
+ goto out;
+ }
+ if (!symsec)
+ {
+ snprintf( err, es, "missing .symtab" );
+ goto out;
+ }
+
+ {
+ rsh *sy = &sh[symsec];
+ const unsigned char *str;
+ size_t strn;
+ const char *strname;
+
+ if (sy->flags != 0 || sy->addr != 0 || sy->align != 8 ||
+ sy->entsize != MELF64_SYM_SIZE || sy->size % MELF64_SYM_SIZE ||
+ sy->link == 0 || sy->link >= shnum || sy->info == 0 ||
+ sy->info > sy->size / MELF64_SYM_SIZE ||
+ sh[sy->link].type != MELF_SHT_STRTAB ||
+ sh[sy->link].flags != 0 || sh[sy->link].addr != 0 ||
+ sh[sy->link].link != 0 || sh[sy->link].info != 0 ||
+ sh[sy->link].align != 1 || sh[sy->link].entsize != 0 ||
+ !cpu_melf_span( fn, sy->off, sy->size ) ||
+ !cpu_melf_span( fn, sh[sy->link].off, sh[sy->link].size ) ||
+ sh[sy->link].size == 0 || sh[sy->link].size > SIZE_MAX)
+ {
+ snprintf( err, es, "bad MELF symbol table" );
+ goto out;
+ }
+ if( name_at( shstr, shstrn, sh[sy->link].name, &strname ) ||
+ strcmp( strname, MELF_SEC_STRTAB ))
+ {
+ snprintf( err, es, "noncanonical symbol string table" );
+ goto out;
+ }
+
+ str = f + (size_t)sh[sy->link].off;
+ strn = (size_t)sh[sy->link].size;
+ if (str[0] != 0)
+ {
+ snprintf( err, es, ".strtab does not start with NUL" );
+ goto out;
+ }
+
+ symcount = (size_t)(sy->size / MELF64_SYM_SIZE);
+ if (symcount == 0)
+ {
+ snprintf( err, es, "empty MELF symbol table" );
+ goto out;
+ }
+ {
+ const unsigned char *q0 = f + (size_t)sy->off;
+ size_t k;
+ for( k = 0; k < MELF64_SYM_SIZE; ++k )
+ if (q0[k] != 0)
+ {
+ snprintf( err, es, "symbol table entry zero is not all-zero" );
+ goto out;
+ }
+ }
+
+ smap = (__mpu_uint32_t *)malloc( symcount *sizeof( *smap ) );
+ if (!smap)
+ {
+ snprintf( err, es, "out of memory" );
+ goto out;
+ }
+ for( i = 0; i < symcount; ++i )
+ smap[i] = UINT32_MAX;
+
+ for( i = 1; i < symcount; ++i )
+ {
+ const unsigned char *q = f + (size_t)sy->off + i * MELF64_SYM_SIZE;
+ __mpu_uint32_t no = cpu_melf_get32( q + MELF64_SYM_ST_NAME_OFF, data );
+ __mpu_uint16_t shn = cpu_melf_get16( q + MELF64_SYM_ST_SHNDX_OFF, data );
+ __mpu_uint64_t value = cpu_melf_get64( q + MELF64_SYM_ST_VALUE_OFF, data );
+ __mpu_uint64_t size = cpu_melf_get64( q + MELF64_SYM_ST_SIZE_OFF, data );
+ unsigned bind = MELF64_ST_BIND( q[MELF64_SYM_ST_INFO_OFF] );
+ unsigned type = MELF64_ST_TYPE( q[MELF64_SYM_ST_INFO_OFF] );
+ unsigned b, t;
+ const char *n;
+
+ if (i < sy->info)
+ {
+ if (bind != MELF_STB_LOCAL)
+ {
+ snprintf( err, es, "non-local symbol precedes sh_info boundary" );
+ goto out;
+ }
+ }
+ else if (bind == MELF_STB_LOCAL)
+ {
+ snprintf( err, es, "local symbol follows sh_info boundary" );
+ goto out;
+ }
+ if (q[MELF64_SYM_ST_OTHER_OFF] != MELF_STV_DEFAULT)
+ {
+ snprintf( err, es, "unsupported symbol visibility" );
+ goto out;
+ }
+ if( name_at( str, strn, no, &n ) || !*n )
+ {
+ snprintf( err, es, "bad/empty symbol name" );
+ goto out;
+ }
+
+ if (bind == MELF_STB_LOCAL)
+ b = CPU_OBJ_BIND_LOCAL;
+ else if (bind == MELF_STB_GLOBAL)
+ b = CPU_OBJ_BIND_GLOBAL;
+ else if (bind == MELF_STB_WEAK)
+ b = CPU_OBJ_BIND_WEAK;
+ else
+ {
+ snprintf( err, es, "unsupported symbol binding" );
+ goto out;
+ }
+
+ if (type == MELF_STT_NOTYPE)
+ t = CPU_OBJ_TYPE_NOTYPE;
+ else if (type == MELF_STT_OBJECT)
+ t = CPU_OBJ_TYPE_OBJECT;
+ else if (type == MELF_STT_FUNC)
+ t = CPU_OBJ_TYPE_FUNC;
+ else
+ {
+ snprintf( err, es, "unsupported symbol type" );
+ goto out;
+ }
+
+ if (shn == MELF_SHN_UNDEF)
+ {
+ if (b == CPU_OBJ_BIND_LOCAL || value != 0 || size != 0)
+ {
+ snprintf( err, es, "malformed undefined symbol: %s", n );
+ goto out;
+ }
+ if( object_add_symbol( o, n, CPU_OBJ_SEC_UNDEF, b, t, 0, err, es ) )
+ goto out;
+ }
+ else
+ {
+ if (shn >= MELF_SHN_LORESERVE || shn >= shnum ||
+ logical[shn] == CPU_OBJ_SEC_UNDEF)
+ {
+ snprintf( err, es, "symbol refers to unsupported section" );
+ goto out;
+ }
+ if (value > sh[shn].size || size > sh[shn].size - value)
+ {
+ snprintf( err, es, "symbol range outside section: %s", n );
+ goto out;
+ }
+ if( object_add_symbol( o, n, logical[shn], b, t, value, err, es ) )
+ goto out;
+ o->symbols[o->symbol_count - 1].size = size;
+ }
+ smap[i] = (__mpu_uint32_t)(o->symbol_count - 1);
+ }
+ }
+
+ for( i = 1; i < shnum; ++i )
+ {
+ if (sh[i].type == MELF_SHT_RELA)
+ {
+ const char *name;
+ unsigned target_logical;
+ const char *expected_name;
+
+ if (sh[i].link != symsec || sh[i].info == 0 || sh[i].info >= shnum ||
+ logical[sh[i].info] == CPU_OBJ_SEC_UNDEF ||
+ sh[sh[i].info].type == MELF_SHT_NOBITS ||
+ sh[i].flags != 0 || sh[i].addr != 0 || sh[i].align != 8 ||
+ sh[i].entsize != MELF64_RELA_SIZE ||
+ sh[i].size % MELF64_RELA_SIZE ||
+ !cpu_melf_span( fn, sh[i].off, sh[i].size ))
+ {
+ snprintf( err, es, "bad RELA section" );
+ goto out;
+ }
+
+ target_logical = logical[sh[i].info];
+ expected_name = rela_name( target_logical );
+ if( !expected_name || name_at( shstr, shstrn, sh[i].name, &name ) ||
+ strcmp( name, expected_name ))
+ {
+ snprintf( err, es, "noncanonical RELA section name" );
+ goto out;
+ }
+
+ for( j = 0; j < (size_t)( sh[i].size / MELF64_RELA_SIZE ); ++j )
+ {
+ const unsigned char *q = f + (size_t)sh[i].off + j * MELF64_RELA_SIZE;
+ __mpu_uint64_t roff = cpu_melf_get64( q + MELF64_RELA_R_OFFSET_OFF, data );
+ __mpu_uint64_t info = cpu_melf_get64( q + MELF64_RELA_R_INFO_OFF, data );
+ __mpu_uint32_t ms = MELF64_R_SYM( info );
+ __mpu_uint32_t rt = MELF64_R_TYPE( info );
+ __mpu_int64_t addend;
+
+ if (ms == MELF_STN_UNDEF || ms >= symcount || smap[ms] == UINT32_MAX ||
+ rt < CPU_RELOC_CODE64 || rt > CPU_RELOC_CPTR64_DATA)
+ {
+ snprintf( err, es, "bad RELA symbol/type" );
+ goto out;
+ }
+ if (roff > sh[sh[i].info].size ||
+ CPU_PTR64_SIZE > sh[sh[i].info].size - roff)
+ {
+ snprintf( err, es, "RELA field outside section" );
+ goto out;
+ }
+ u64_to_s64( cpu_melf_get64( q + MELF64_RELA_R_ADDEND_OFF, data ), &addend );
+ if( object_add_reloc( o, target_logical, rt, smap[ms], roff,
+ addend, err, es))
+ goto out;
+ }
+ }
+ }
+
+ if (o->init_size > SIZE_MAX - o->text_size ||
+ o->init_size + o->text_size > SIZE_MAX - o->fini_size)
+ {
+ snprintf( err, es, "PROGRAM compatibility image too large" );
+ goto out;
+ }
+ o->code_size = o->init_size + o->text_size + o->fini_size;
+ o->code_align = 1;
+ if (o->code_size)
+ {
+ o->code = (__mpu_UNIT_t *)malloc( o->code_size );
+ if (!o->code)
+ {
+ snprintf( err, es, "out of memory" );
+ goto out;
+ }
+ memcpy( o->code, o->init, o->init_size );
+ memcpy( o->code + o->init_size, o->text, o->text_size );
+ memcpy( o->code + o->init_size + o->text_size, o->fini, o->fini_size );
+ }
+
+ rc = 0;
+out:
+ free( sh );
+ free( logical );
+ free( smap );
+ if (rc)
+ cpu_asm_object_free( o );
+ return( rc );
+}
+
+int
+cpu_object_read_data( const void *data, size_t size, cpu_asm_object *o,
+ char *err, size_t es )
+{
+ if( data == NULL || size == 0 || o == NULL )
+ return( -1 );
+
+ return( cpu_object_read_data_internal((const unsigned char *)data, size,
+ o, err, es) );
+
+} /* End of cpu_object_read_data() */
+
+
+int
+cpu_object_read( const char *path, cpu_asm_object *o, char *err, size_t es )
+{
+ unsigned char *data = NULL;
+ size_t size = 0;
+ int rc;
+
+ if( path == NULL || o == NULL )
+ return( -1 );
+
+ if( cpu_melf_readall(path, &data, &size, err, es) )
+ return( -1 );
+
+ rc = cpu_object_read_data_internal( data, size, o, err, es );
+ free( data );
+ return( rc );
+
+} /* End of cpu_object_read() */
+