#include #include #include #include #include #include #include #include #include #include #include "mcpu-asm.h" /* Signed 128-bit expression arithmetic, including ILP32 hosts without __int128. */ #if defined(__SIZEOF_INT128__) typedef __int128 asm_i128; #define I128(v) ((asm_i128)(v)) #define i128_u64(v) ((asm_i128)(v)) #define i128_add(a,b,r) __builtin_add_overflow((a),(b),(r)) #define i128_sub(a,b,r) __builtin_sub_overflow((a),(b),(r)) #define i128_mul(a,b,r) __builtin_mul_overflow((a),(b),(r)) #define i128_div(a,b) ((a)/(b)) #define i128_mod(a,b) ((a)%(b)) #define i128_iszero(a) ((a)==0) #define i128_eqsmall(a,b) ((a)==I128(b)) #define i128_ltzero(a) ((a)<0) #define i128_signed_ok(a) ((a)>=I128(INT64_MIN) && (a)<=I128(INT64_MAX)) #define i128_unsigned_ok(a) ((a)>=0 && (a)<=I128(UINT64_MAX)) #define i128_signed(a) ((__mpu_int64_t)(a)) #define i128_unsigned(a) ((__mpu_uint64_t)(a)) static void i128_decimal( asm_i128 v, char out[64] ) { char tmp[64]; int neg = v < 0, n = 0, i = 0; unsigned __int128 u; if( neg ) u = (unsigned __int128)( -( v + 1 ) ) + 1; else u = (unsigned __int128)v; do { tmp[n++] = (char)( '0' + u % 10 ); u /= 10; } while( u ); if( neg ) out[i++] = '-'; while( n ) out[i++] = tmp[--n]; out[i] = 0; } #else typedef struct { uint64_t lo, hi; } asm_i128; static asm_i128 I128( int64_t v ) { asm_i128 r = { (uint64_t)v, v < 0 ? UINT64_MAX : 0 }; return( r ); } static asm_i128 i128_u64( uint64_t v ) { asm_i128 r = { v, 0 }; return( r ); } static int i128_ltzero( asm_i128 a ) { return( (int)( a.hi >> 63 ) ); } static int i128_iszero( asm_i128 a ) { return( !a.hi && !a.lo ); } static int i128_eqsmall( asm_i128 a, int64_t b ) { asm_i128 v = I128( b ); return( a.hi == v.hi && a.lo == v.lo ); } static asm_i128 i128_neg( asm_i128 a ) { asm_i128 r = { ~a.lo + 1, ~a.hi + ( a.lo == 0 ) }; return( r ); } static int i128_add( asm_i128 a, asm_i128 b, asm_i128 *r ) { r->lo = a.lo + b.lo; r->hi = a.hi + b.hi + ( r->lo < a.lo ); return( ( ( ~( a.hi ^ b.hi ) & ( a.hi ^ r->hi ) ) >> 63 ) != 0 ); } static int i128_sub( asm_i128 a, asm_i128 b, asm_i128 *r ) { r->lo = a.lo - b.lo; r->hi = a.hi - b.hi - ( a.lo < b.lo ); return( ( ( ( a.hi ^ b.hi ) & ( a.hi ^ r->hi ) ) >> 63 ) != 0 ); } static int i128_ucmp( asm_i128 a, asm_i128 b ) { if( a.hi != b.hi ) return( a.hi > b.hi ? 1 : -1 ); if( a.lo != b.lo ) return( a.lo > b.lo ? 1 : -1 ); return( 0 ); } static asm_i128 i128_shl1( asm_i128 a ) { asm_i128 r = { a.lo << 1, ( a.hi << 1 ) | ( a.lo >> 63 ) }; return( r ); } /* Unsigned 128x128 multiplication, with explicit high-half overflow. */ static int i128_umul( asm_i128 a, asm_i128 b, asm_i128 *out ) { asm_i128 r = { 0, 0 }; int k, overflow = 0; for( k = 0; k < 128; ++k ) { if( b.lo & 1 ) { asm_i128 t; uint64_t old = r.hi; t.lo = r.lo + a.lo; t.hi = r.hi + a.hi + ( t.lo < r.lo ); if( t.hi < old || ( t.hi == old && ( a.hi || t.lo < r.lo ) ) ) overflow = 1; r = t; } b.lo = ( b.lo >> 1 ) | ( b.hi << 63 ); b.hi >>= 1; if( k != 127 ) { if( ( a.hi >> 63 ) && ( b.lo || b.hi ) ) overflow = 1; a = i128_shl1( a ); } } *out = r; return( overflow ); } static int i128_mul( asm_i128 a, asm_i128 b, asm_i128 *r ) { int na = i128_ltzero( a ), nb = i128_ltzero( b ); int bad; if( na ) a = i128_neg( a ); if( nb ) b = i128_neg( b ); bad = i128_umul( a, b, r ); if( bad || ( na ^ nb ? ( r->hi >> 63 && !( r->hi == UINT64_C(0x8000000000000000) && !r->lo ) ) : ( r->hi >> 63 ) ) ) return( 1 ); if( na ^ nb ) *r = i128_neg( *r ); return( 0 ); } static void i128_udivmod( asm_i128 a, asm_i128 b, asm_i128 *q, asm_i128 *rem ) { asm_i128 quotient = { 0, 0 }, rest = { 0, 0 }; int k; for( k = 127; k >= 0; --k ) { rest = i128_shl1( rest ); rest.lo |= k >= 64 ? ( a.hi >> ( k - 64 ) ) & 1 : ( a.lo >> k ) & 1; if( i128_ucmp( rest, b ) >= 0 ) { asm_i128 t; (void)i128_sub( rest, b, &t ); rest = t; if( k >= 64 ) quotient.hi |= UINT64_C(1) << ( k - 64 ); else quotient.lo |= UINT64_C(1) << k; } } *q = quotient; *rem = rest; } static asm_i128 i128_divmod( asm_i128 a, asm_i128 b, int mod ) { asm_i128 q, r; int na = i128_ltzero( a ), nb = i128_ltzero( b ); if( na ) a = i128_neg( a ); if( nb ) b = i128_neg( b ); i128_udivmod( a, b, &q, &r ); if( mod ) return( na ? i128_neg( r ) : r ); return( na ^ nb ? i128_neg( q ) : q ); } #define i128_div(a,b) i128_divmod((a),(b),0) #define i128_mod(a,b) i128_divmod((a),(b),1) static int i128_signed_ok( asm_i128 a ) { return( ( a.hi == 0 && a.lo <= INT64_MAX ) || ( a.hi == UINT64_MAX && a.lo >= UINT64_C(0x8000000000000000) ) ); } static int i128_unsigned_ok( asm_i128 a ) { return( a.hi == 0 ); } static int64_t i128_signed( asm_i128 a ) { return( (int64_t)a.lo ); } static uint64_t i128_unsigned( asm_i128 a ) { return( a.lo ); } static void i128_decimal( asm_i128 v, char out[64] ) { char tmp[64]; int neg = i128_ltzero( v ), n = 0, i = 0; asm_i128 ten = { 10, 0 }, q, rem; if( neg ) v = i128_neg( v ); do { i128_udivmod( v, ten, &q, &rem ); tmp[n++] = (char)( '0' + rem.lo ); v = q; } while( !i128_iszero( v ) ); if( neg ) out[i++] = '-'; while( n ) out[i++] = tmp[--n]; out[i] = 0; } #endif #define ASM_NAME_MAX 64 #define ASM_EXPR_MAX 256 typedef struct { char name[ASM_NAME_MAX]; __mpu_uint64_t addr; } label_t; typedef struct { size_t pos; char expr[ASM_EXPR_MAX]; } fixup_t; typedef struct { char name[ASM_NAME_MAX]; char expr[ASM_EXPR_MAX]; int line; int state; /* 0=not evaluated, 1=evaluating, 2=done */ asm_i128 value; } equ_t; typedef struct { __mpu_UNIT_t *p; size_t n, cap; } buf_t; typedef struct { label_t *p; size_t n, cap; } labels_t; typedef struct { fixup_t *p; size_t n, cap; } fixups_t; typedef struct { equ_t *p; size_t n, cap; } equs_t; typedef struct { char name[ASM_NAME_MAX]; int is_global; int is_extern; int is_weak; } symdecl_t; typedef struct { symdecl_t *p; size_t n, cap; } symdecls_t; static int grow( void **p, size_t *cap, size_t need, size_t unit ) { size_t c = *cap ? *cap : 64; void *q; while( c < need ) c *= 2; q = realloc( *p, c *unit ); if( !q ) return( -1 ); *p = q; *cap = c; return( 0 ); } static int put( buf_t *b, const void *p, size_t n ) { if( grow( (void **)&b->p, &b->cap, b->n + n, 1 ) ) return( -1 ); memcpy( b->p + b->n, p, n ); b->n += n; return( 0 ); } static int put8( buf_t *b, unsigned v ) { __mpu_UNIT_t x = (__mpu_UNIT_t)v; return( put( b, &x, 1 ) ); } static int put16( buf_t *b, unsigned v ) { __mpu_UNIT_t x[2]; __cpu_code_put_u16( x, (__mpu_uint16_t)v ); return( put( b, x, 2 ) ); } static int put64( buf_t *b, __mpu_uint64_t v ) { __mpu_UNIT_t x[8]; __cpu_code_put_u64( x, v ); return( put( b, x, 8 ) ); } static char * trim( char *s ) { char *e; while( isspace( (unsigned char)*s ) ) s++; e = s + strlen( s ); while( e > s && isspace( (unsigned char)e[-1] ) ) *--e = 0; return( s ); } static int streqi( const char *a, const char *b ) { for( ; *a && *b; a++, b++ ) if( toupper( (unsigned char)*a ) != toupper( (unsigned char)*b ) ) return( 0 ); return( !*a && !*b ); } /* ';' starts a comment only outside a quoted assembler string. */ static void strip_comment( char *s ) { int quote = 0, esc = 0; for( ; *s; s++ ) { if( quote ) { if( esc ) { esc = 0; continue; } if( *s == '\\' ) { esc = 1; continue; } if( *s == '"' ) { quote = 0; continue; } } else { if( *s == '"' ) { quote = 1; continue; } if( *s == ';' ) { *s = 0; return; } } } } /* A label colon is recognized only in the first token, never inside operands. */ static char * label_colon( char *s ) { char *p = s; while( *p && !isspace( (unsigned char)*p ) ) { if( *p == ':' ) return( p ); p++; } return( 0 ); } static int addlabel( labels_t *l, const char *n, __mpu_uint64_t a ) { size_t i; for( i = 0; i < l->n; i++ ) if( !strcmp( l->p[i].name, n ) ) return( -1 ); if( strlen( n ) >= ASM_NAME_MAX ) return( -1 ); if( grow( (void **)&l->p, &l->cap, l->n + 1, sizeof( *l->p ) ) ) return( -1 ); strcpy( l->p[l->n].name, n ); l->p[l->n++].addr = a; return( 0 ); } static int findlabel( const labels_t *l, const char *n, __mpu_uint64_t *a ) { size_t i; for( i = 0; i < l->n; i++ ) if( !strcmp( l->p[i].name, n ) ) { *a = l->p[i].addr; return( 0 ); } return( -1 ); } static int addfix( fixups_t *f, size_t pos, const char *expr ) { if( strlen( expr ) >= ASM_EXPR_MAX ) return( -1 ); if( grow( (void **)&f->p, &f->cap, f->n + 1, sizeof( *f->p ) ) ) return( -1 ); f->p[f->n].pos = pos; strcpy( f->p[f->n].expr, expr ); f->n++; return( 0 ); } static int findequ( const equs_t *e, const char *n, size_t *idx ) { size_t i; for( i = 0; i < e->n; i++ ) if( !strcmp( e->p[i].name, n ) ) { if( idx ) * idx = i; return( 0 ); } return( -1 ); } static int addequ( equs_t *e, const char *n, const char *expr, int line, char *err, size_t es ) { size_t dummy; if( !*n || strlen( n ) >= ASM_NAME_MAX || strlen( expr ) >= ASM_EXPR_MAX ) { snprintf( err, es, "bad .equ name/expression" ); return( -1 ); } if( !findequ( e, n, &dummy ) ) { snprintf( err, es, "duplicate .equ symbol: %s", n ); return( -1 ); } if( grow( (void **)&e->p, &e->cap, e->n + 1, sizeof( *e->p ) ) ) { snprintf( err, es, "out of memory" ); return( -1 ); } memset( &e->p[e->n], 0, sizeof( e->p[e->n] ) ); strcpy( e->p[e->n].name, n ); strcpy( e->p[e->n].expr, expr ); e->p[e->n].line = line; e->n++; return( 0 ); } /* Split comma-separated arguments while respecting parentheses and quoted strings. */ static int splitargs( char *s, char **a, int max ) { int n = 0, depth = 0, quote = 0, esc = 0; char *p = s, *start = s; for( ; ; p++ ) { int end = ( *p == 0 ); if( !end ) { if( quote ) { if( esc ) esc = 0; else if( *p == '\\' ) esc = 1; else if( *p == '"' ) quote = 0; } else { if( *p == '"' ) quote = 1; else if( *p == '(' ) depth++; else if( *p == ')' ) depth--; } } if( ( !quote && depth == 0 && *p == ',' ) || end ) { if( n >= max ) return( -1 ); if( !end ) * p = 0; a[n++] = trim( start ); if( end ) break; start = p + 1; } } if( quote || depth != 0 ) return( -1 ); return( n ); } /* ------------------------- integer expressions ------------------------- */ typedef struct { const char *p; equs_t *eqs; const labels_t *labels; /* optional section-local label namespace */ char *err; size_t es; } expr_parser_t; static int eval_expression( const char *, equs_t *, const labels_t *, asm_i128 *, char *, size_t ); static void expr_skip( expr_parser_t *x ) { while( isspace( (unsigned char)*x->p ) ) x->p++; } static int symfirst( int c ) { return( isalpha( (unsigned char)c ) || c == '_' || c == '.' || c == '$' ); } static int symrest( int c ) { return( isalnum( (unsigned char)c ) || c == '_' || c == '.' || c == '$' ); } static int equ_value( equs_t *eqs, size_t i, asm_i128 *v, char *err, size_t es ) { equ_t *e = &eqs->p[i]; if( e->state == 2 ) { *v = e->value; return( 0 ); } if( e->state == 1 ) { snprintf( err, es, "cyclic .equ definition involving %s", e->name ); return( -1 ); } e->state = 1; if( eval_expression( e->expr, eqs, 0, &e->value, err, es ) ) { e->state = 0; return( -1 ); } e->state = 2; *v = e->value; return( 0 ); } static int expr_symbol( expr_parser_t *x, const char *name, asm_i128 *v ) { size_t i; __mpu_uint64_t a; if( !findequ( x->eqs, name, &i ) ) return( equ_value( x->eqs, i, v, x->err, x->es ) ); if( x->labels && !findlabel( x->labels, name, &a ) ) { *v = i128_u64( a ); return( 0 ); } snprintf( x->err, x->es, "unknown symbol in expression: %s", name ); return( -1 ); } static int expr_primary( expr_parser_t *x, asm_i128 *v ) { char name[ASM_NAME_MAX], *end; size_t n = 0; unsigned long long u; expr_skip( x ); if( symfirst( (unsigned char)*x->p ) ) { while( symrest( (unsigned char)*x->p ) ) { if( n + 1 >= sizeof( name ) ) { snprintf( x->err, x->es, "symbol name too long" ); return( -1 ); } name[n++] = *x->p++; } name[n] = 0; return( expr_symbol( x, name, v ) ); } errno = 0; u = strtoull( x->p, &end, 0 ); if( end == x->p ) { snprintf( x->err, x->es, "expected integer or symbol in expression" ); return( -1 ); } if( errno == ERANGE ) { snprintf( x->err, x->es, "integer literal out of 64-bit range in expression" ); return( -1 ); } x->p = end; *v = i128_u64( u ); return( 0 ); } /* Forward declarations for recursive-descent parentheses. */ static int expr_add( expr_parser_t *, asm_i128 * ); static int expr_atom( expr_parser_t *x, asm_i128 *v ) { expr_skip( x ); if( *x->p == '(' ) { x->p++; if( expr_add( x, v ) ) return( -1 ); expr_skip( x ); if( *x->p != ')' ) { snprintf( x->err, x->es, "missing ')' in expression" ); return( -1 ); } x->p++; return( 0 ); } return( expr_primary( x, v ) ); } static int expr_unary( expr_parser_t *x, asm_i128 *v ) { expr_skip( x ); if( *x->p == '+' ) { x->p++; return( expr_unary( x, v ) ); } if( *x->p == '-' ) { asm_i128 t; x->p++; if( expr_unary( x, &t ) ) return( -1 ); if( i128_sub( I128( 0 ), t, v ) ) { snprintf( x->err, x->es, "expression overflow" ); return( -1 ); } return( 0 ); } return( expr_atom( x, v ) ); } static int expr_mul( expr_parser_t *x, asm_i128 *v ) { asm_i128 a, b, r; char op; if( expr_unary( x, &a ) ) return( -1 ); for( ; ; ) { expr_skip( x ); op = *x->p; if( op != '*' && op != '/' && op != '%' ) break; x->p++; if( expr_unary( x, &b ) ) return( -1 ); if( op == '*' ) { if( i128_mul( a, b, &r ) ) { snprintf( x->err, x->es, "expression overflow" ); return( -1 ); } } else { if( i128_iszero( b ) ) { snprintf( x->err, x->es, "division by zero in expression" ); return( -1 ); } if( op == '/' ) r = i128_div( a, b ); else r = i128_mod( a, b ); } a = r; } *v = a; return( 0 ); } static int expr_add( expr_parser_t *x, asm_i128 *v ) { asm_i128 a, b, r; char op; if( expr_mul( x, &a ) ) return( -1 ); for( ; ; ) { expr_skip( x ); op = *x->p; if( op != '+' && op != '-' ) break; x->p++; if( expr_mul( x, &b ) ) return( -1 ); if( op == '+' ) { if( i128_add( a, b, &r ) ) { snprintf( x->err, x->es, "expression overflow" ); return( -1 ); } } else { if( i128_sub( a, b, &r ) ) { snprintf( x->err, x->es, "expression overflow" ); return( -1 ); } } a = r; } *v = a; return( 0 ); } static int eval_expression( const char *s, equs_t *eqs, const labels_t *labels, asm_i128 *v, char *err, size_t es ) { expr_parser_t x = { s, eqs, labels, err, es }; if( expr_add( &x, v ) ) return( -1 ); expr_skip( &x ); if( *x.p ) { snprintf( err, es, "unexpected token in expression near: %s", x.p ); return( -1 ); } return( 0 ); } static int eval_i64( const char *s, equs_t *eqs, const labels_t *l, __mpu_int64_t *v, char *err, size_t es ) { asm_i128 x; if( eval_expression( s, eqs, l, &x, err, es ) ) return( -1 ); if( !i128_signed_ok( x ) ) return( 1 ); *v = i128_signed( x ); return( 0 ); } static int eval_u64( const char *s, equs_t *eqs, const labels_t *l, __mpu_uint64_t *v, char *err, size_t es ) { asm_i128 x; if( eval_expression( s, eqs, l, &x, err, es ) ) return( -1 ); if( !i128_unsigned_ok( x ) ) return( 1 ); *v = i128_unsigned( x ); return( 0 ); } static int plain_integer_literal( const char *s ) { const unsigned char *p = (const unsigned char *)s; int base = 10, nd = 0; while( isspace( *p ) ) p++; if( *p == '+' || *p == '-' ) p++; if( !*p ) return( 0 ); if( p[0] == '0' && ( p[1] == 'x' || p[1] == 'X' ) ) { base = 16; p += 2; } else if( p[0] == '0' && ( p[1] == 'b' || p[1] == 'B' ) ) { base = 2; p += 2; } else if( p[0] == '0' && isdigit( p[1] ) ) { base = 8; p++; } while( *p && !isspace( *p ) ) { int ok = ( base == 16 ) ? isxdigit( *p ) : ( base == 10 ) ? isdigit( *p ) : ( base == 8 ) ? ( *p >= '0' && *p <= '7' ) : ( *p == '0' || *p == '1' ); if( !ok ) return( 0 ); p++; nd++; } while( isspace( *p ) ) p++; return( nd && !*p ); } /* --------------------------- register syntax --------------------------- */ static int regparse( const char *s, cpu_register *r, int *isc, equs_t *eqs, char *err, size_t es ) { const char *p = s, *at; char *e; unsigned long bits; __mpu_uint64_t a; while( isspace( (unsigned char)*p ) ) p++; if( *p != 'r' && *p != 'R' && *p != 'c' && *p != 'C' ) return( -1 ); *isc = ( *p == 'c' || *p == 'C' ); p++; errno = 0; bits = strtoul( p, &e, 10 ); if( errno || e == p || *e != '@' ) return( -1 ); at = e + 1; if( !*at ) return( -1 ); if( eval_u64( at, eqs, 0, &a, err, es ) != 0 || a > UINT32_MAX ) return( -1 ); r->bits = (unsigned int)bits; r->address = (__mpu_uint32_t)a; if( !__cpu_reg_valid( r->bits, r->address ) ) return( -1 ); if( *isc && r->bits < 64 ) return( -1 ); return( 0 ); } static int preg( buf_t *b, const char *s, int require_c, int require_r, equs_t *eqs, char *err, size_t es ) { cpu_register r; int c; __mpu_UNIT_t x[3]; if( regparse( trim( (char *)s ), &r, &c, eqs, err, es ) ) { snprintf( err, es, "bad register: %s", s ); return( -1 ); } if( require_c && !c ) { snprintf( err, es, "complex instruction requires c@addr: %s", s ); return( -1 ); } if( require_r && c ) { snprintf( err, es, "instruction requires r@addr: %s", s ); return( -1 ); } if( __cpu_encode_reg24( x, &r ) ) return( -1 ); return( put( b, x, 3 ) ); } static int preg_r64( buf_t *b, const char *s, const char *what, equs_t *eqs, char *err, size_t es ) { cpu_register r; int c; __mpu_UNIT_t x[3]; if( regparse( trim( (char *)s ), &r, &c, eqs, err, es ) || c || r.bits != 64 ) { snprintf( err, es, "%s requires r64@addr: %s", what, s ); return( -1 ); } if( __cpu_encode_reg24( x, &r ) ) return( -1 ); return( put( b, x, 3 ) ); } static int preg_ptr( buf_t *b, const char *s, equs_t *eqs, char *err, size_t es ) { return( preg_r64( b, s, "PTR64 operand", eqs, err, es ) ); } /* ---------------------------- string literals ---------------------------- */ static int hex_digit( int c ) { if( c >= '0' && c <= '9' ) return( c - '0' ); if( c >= 'a' && c <= 'f' ) return( c - 'a' + 10 ); if( c >= 'A' && c <= 'F' ) return( c - 'A' + 10 ); return( -1 ); } static int put_utf8_cp( buf_t *b, unsigned long cp, char *err, size_t es ) { unsigned char x[4]; size_t n; if( cp == 0 ) { snprintf( err, es, "embedded NUL is not supported in .utf8/.ucs2; use .byte/.integer" ); return( -1 ); } if( cp <= 0x7f ) { x[0] = (unsigned char)cp; n = 1; } else if( cp <= 0x7ff ) { x[0] = 0xc0 | ( cp >> 6 ); x[1] = 0x80 | ( cp & 0x3f ); n = 2; } else if( cp <= 0xffff ) { if( cp >= 0xd800 && cp <= 0xdfff ) { snprintf( err, es, "Unicode surrogate is not a valid scalar value" ); return( -1 ); } x[0] = 0xe0 | ( cp >> 12 ); x[1] = 0x80 | ( ( cp >> 6 ) & 0x3f ); x[2] = 0x80 | ( cp & 0x3f ); n = 3; } else if( cp <= 0x10ffff ) { x[0] = 0xf0 | ( cp >> 18 ); x[1] = 0x80 | ( ( cp >> 12 ) & 0x3f ); x[2] = 0x80 | ( ( cp >> 6 ) & 0x3f ); x[3] = 0x80 | ( cp & 0x3f ); n = 4; } else { snprintf( err, es, "Unicode code point out of range" ); return( -1 ); } if( put( b, x, n ) ) { snprintf( err, es, "out of memory" ); return( -1 ); } return( 0 ); } static int parse_utf8_string( const char *s, buf_t *out, char *err, size_t es ) { const unsigned char *p = (const unsigned char *)s; int closed = 0; memset( out, 0, sizeof( *out ) ); while( isspace( *p ) ) p++; if( *p != '"' ) { snprintf( err, es, "expected quoted string" ); return( -1 ); } p++; while( *p ) { if( *p == '"' ) { p++; closed = 1; break; } if( *p != '\\' ) { if( *p == 0 ) { break; } if( put8( out, *p++ ) ) { snprintf( err, es, "out of memory" ); goto fail; } continue; } p++; if( !*p ) { snprintf( err, es, "unfinished escape sequence" ); goto fail; } switch( *p++ ) { case '\\': if( put8( out, '\\' ) ) goto oom; break; case '"': if( put8( out, '"' ) ) goto oom; break; case 'n': if( put8( out, '\n' ) ) goto oom; break; case 'r': if( put8( out, '\r' ) ) goto oom; break; case 't': if( put8( out, '\t' ) ) goto oom; break; case 'b': if( put8( out, '\b' ) ) goto oom; break; case 'f': if( put8( out, '\f' ) ) goto oom; break; case 'v': if( put8( out, '\v' ) ) goto oom; break; case 'a': if( put8( out, '\a' ) ) goto oom; break; case '0': snprintf( err, es, "embedded NUL is not supported in .utf8/.ucs2; use .byte/.integer" ); goto fail; case 'x': { int h1, h2; unsigned v; if( !p[0] || !p[1] ) { snprintf( err, es, "\\x escape requires exactly two hex digits" ); goto fail; } h1 = hex_digit( p[0] ); h2 = hex_digit( p[1] ); if( h1 < 0 || h2 < 0 ) { snprintf( err, es, "\\x escape requires exactly two hex digits" ); goto fail; } v = (unsigned)( ( h1 << 4 ) | h2 ); p += 2; if( v == 0 ) { snprintf( err, es, "embedded NUL is not supported in .utf8/.ucs2; use .byte/.integer" ); goto fail; } if( put8( out, v ) ) goto oom; break; } case 'u': case 'U': { int nd = p[-1] == 'u' ? 4 : 8, i, h; unsigned long cp = 0; for( i = 0; i < nd; i++ ) { if( !p[i] ) { snprintf( err, es, "Unicode escape requires %d hex digits", nd ); goto fail; } h = hex_digit( p[i] ); if( h < 0 ) { snprintf( err, es, "Unicode escape requires %d hex digits", nd ); goto fail; } cp = ( cp << 4 ) | (unsigned)h; } p += nd; if( put_utf8_cp( out, cp, err, es ) ) goto fail; break; } default: snprintf( err, es, "unknown string escape sequence" ); goto fail; } } if( !closed ) { snprintf( err, es, "unterminated string literal" ); goto fail; } while( isspace( *p ) ) p++; if( *p ) { snprintf( err, es, "unexpected text after string literal" ); goto fail; } if( put8( out, 0 ) ) goto oom; if( !mpu_utf8valid( (const __mpu_char8_t *)out->p ) ) { snprintf( err, es, "string literal is not valid UTF-8" ); goto fail; } return( 0 ); oom: snprintf( err, es, "out of memory" ); fail: free( out->p ); memset( out, 0, sizeof( *out ) ); return( -1 ); } static int real_literal_exact_value( const char *s, int nb, mpu_real *out ) { mpu_wide_real_t src, narrow, back; memset( src, 0, sizeof( src ) ); memset( narrow, 0, sizeof( narrow ) ); memset( back, 0, sizeof( back ) ); if( ascii_to_real( ( mpu_real * ) src, (__mpu_char8_t *)s, NB_R65536 ) < 0 ) return( -1 ); r_cvt( ( mpu_real * ) narrow, ( mpu_real * ) src, nb, NB_R65536 ); r_cvt( ( mpu_real * ) back, ( mpu_real * ) narrow, NB_R65536, nb ); if( r_cmp( ( mpu_real * ) src, ( mpu_real * ) back, NB_R65536 ) != 0 ) return( 0 ); if( out ) memcpy( out, narrow, (size_t)nb ); return( 1 ); } static int cimm_exact( const char *s, float *r, float *j, char *err, size_t es ) { const char *rp = strstr( s, "r0" ), *jp; char *rs = 0, *js = 0; size_t rn, jn; int ex; if( !rp || rp == s ) return( -1 ); jp = strstr( rp + 2, "j0" ); if( !jp || jp == rp + 2 || jp[2] ) return( -1 ); rn = (size_t)( rp - s ); jn = (size_t)( jp - ( rp + 2 ) ); rs = (char *)malloc( rn + 1 ); js = (char *)malloc( jn + 1 ); if( !rs || !js ) { free( rs ); free( js ); snprintf( err, es, "out of memory" ); return( -2 ); } memcpy( rs, s, rn ); rs[rn] = 0; memcpy( js, rp + 2, jn ); js[jn] = 0; { mpu_real32_t xr, xj; ex = real_literal_exact_value( rs, NB_R32, ( mpu_real * ) xr ); if( ex != 1 ) { free( rs ); free( js ); if( ex == 0 ) snprintf( err, es, "LDC immediate component is not exactly representable as real32;\nuse .complex , real, imag in DATA and LOAD" ); return( ex == 0 ? -2 : -1 ); } ex = real_literal_exact_value( js, NB_R32, ( mpu_real * ) xj ); if( ex != 1 ) { free( rs ); free( js ); if( ex == 0 ) snprintf( err, es, "LDC immediate component is not exactly representable as real32;\nuse .complex , real, imag in DATA and LOAD" ); return( ex == 0 ? -2 : -1 ); } memcpy( r, xr, NB_R32 ); memcpy( j, xj, NB_R32 ); } free( rs ); free( js ); return( 0 ); } static int constid( const char *s, unsigned *v ) { struct K { const char *n; unsigned v; } ks[] = { { "ZERO", CPU_RCONST_ZERO }, { "HALF", CPU_RCONST_HALF }, { "ONE", CPU_RCONST_ONE }, { "TWO", CPU_RCONST_TWO }, { "TEN", CPU_RCONST_TEN }, { "MTEN", CPU_RCONST_MTEN }, { "32", CPU_RCONST_32 }, { "PI", CPU_RCONST_PI }, { "E", CPU_RCONST_E }, { "1_LN2", CPU_RCONST_1_LN2 }, { "LN2", CPU_RCONST_LN2 }, { "1_LN10", CPU_RCONST_1_LN10 }, { "LN10", CPU_RCONST_LN10 }, { "1_LG2", CPU_RCONST_1_LG2 }, { "LG2", CPU_RCONST_LG2 }, { "PI_2", CPU_RCONST_PI_2 }, { "PI_3", CPU_RCONST_PI_3 }, { "PI_4", CPU_RCONST_PI_4 }, { "PI_5", CPU_RCONST_PI_5 }, { "PI_6", CPU_RCONST_PI_6 }, { "1_PI", CPU_RCONST_1_PI }, { "2_PI", CPU_RCONST_2_PI }, { "3_PI", CPU_RCONST_3_PI }, { "4_PI", CPU_RCONST_4_PI }, { "5_PI", CPU_RCONST_5_PI }, { "2PI", CPU_RCONST_2PI }, { "3PI", CPU_RCONST_3PI }, { "1_2PI", CPU_RCONST_1_2PI }, { "1_3PI", CPU_RCONST_1_3PI }, { "1_4PI", CPU_RCONST_1_4PI }, { "1_5PI", CPU_RCONST_1_5PI }, { "1_6PI", CPU_RCONST_1_6PI }, { "3PI_4", CPU_RCONST_3PI_4 }, { "SQRTPI", CPU_RCONST_SQRTPI }, { "1_SQRTPI", CPU_RCONST_1_SQRTPI }, { "2_SQRTPI", CPU_RCONST_2_SQRTPI }, { "SQRT2", CPU_RCONST_SQRT2 }, { "1_SQRT2", CPU_RCONST_1_SQRT2 }, { "SQRT3", CPU_RCONST_SQRT3 }, { "1_SQRT3", CPU_RCONST_1_SQRT3 }, { "DEGREE", CPU_RCONST_DEGREE }, { "1_DEGREE", CPU_RCONST_1_DEGREE }, { "GOLDENRATIO", CPU_RCONST_GOLDENRATIO }, { "EULERGAMMA", CPU_RCONST_EULERGAMMA }, { "CATALAN", CPU_RCONST_CATALAN }, { "EPSILON", CPU_RCONST_EPSILON }, { "IND", CPU_RCONST_IND }, { "PNAN", CPU_RCONST_PNAN }, { "NNAN", CPU_RCONST_NNAN }, { "PNANMAX", CPU_RCONST_PNANMAX }, { "NNANMAX", CPU_RCONST_NNANMAX }, { "PNANMIN", CPU_RCONST_PNANMIN }, { "NNANMIN", CPU_RCONST_NNANMIN }, { "PINF", CPU_RCONST_PINF }, { "NINF", CPU_RCONST_NINF }, { "PREALMIN", CPU_RCONST_PREALMIN }, { "NREALMIN", CPU_RCONST_NREALMIN }, { "PREALMAX", CPU_RCONST_PREALMAX }, { "NREALMAX", CPU_RCONST_NREALMAX }, { "PZERO", CPU_RCONST_PZERO }, { "NZERO", CPU_RCONST_NZERO } }; size_t i; for( i = 0; i < sizeof( ks ) / sizeof( ks[0] ); i++ ) if( streqi( s, ks[i].n ) ) { *v = ks[i].v; return( 0 ); } return( -1 ); } static int pswbitid( const char *s, unsigned *v ) { struct B { const char *n; unsigned v; } bs[] = { { "AF", CPU_PSWBIT_AF }, { "CF", CPU_PSWBIT_CF }, { "OF", CPU_PSWBIT_OF }, { "SF", CPU_PSWBIT_SF }, { "PF", CPU_PSWBIT_PF }, { "ZF", CPU_PSWBIT_ZF }, { "RF", CPU_PSWBIT_RF }, { "VF", CPU_PSWBIT_VF }, { "DOMF", CPU_PSWBIT_DOMF }, { "SNGF", CPU_PSWBIT_SNGF }, { "OVFF", CPU_PSWBIT_OVFF }, { "UDFF", CPU_PSWBIT_UDFF }, { "TLSF", CPU_PSWBIT_TLSF }, { "PLSF", CPU_PSWBIT_PLSF }, { "INDF", CPU_PSWBIT_INDF }, { "INXF", CPU_PSWBIT_INXF }, { "HF", CPU_PSWBIT_HF }, { "TF", CPU_PSWBIT_TF }, { "IF", CPU_PSWBIT_IF } }; size_t i; for( i = 0; i < sizeof( bs ) / sizeof( bs[0] ); i++ ) if( streqi( s, bs[i].n ) ) { *v = bs[i].v; return( 0 ); } return( -1 ); } typedef struct { const char *n; unsigned op; int nr; int c; int r; } optab_t; static const optab_t ops[] = { { "NOP", CPU_OP_NOP, 0, 0, 0 }, { "HALT", CPU_OP_HALT, 0, 0, 0 }, { "END", CPU_OP_END, 0, 0, 0 }, { "SYSCALL", CPU_OP_SYSCALL, 0, 0, 0 }, { "CLRCPUF", CPU_OP_CLRCPUF, 0, 0, 0 }, { "CLRMPUF", CPU_OP_CLRMPUF, 0, 0, 0 }, { "CLR", CPU_OP_CLR, 1, 0, 0 }, { "MOV", CPU_OP_MOV, 2, 0, 0 }, { "SWAP", CPU_OP_SWAP, 2, 0, 0 }, { "BSWAP", CPU_OP_BSWAP, 2, 0, 1 }, { "PUSH", CPU_OP_PUSH, 1, 0, 0 }, { "POP", CPU_OP_POP, 1, 0, 0 }, { "PUSHFP", CPU_OP_PUSHFP, 0, 0, 0 }, { "SETFP", CPU_OP_SETFP, 0, 0, 0 }, { "RESTSP", CPU_OP_RESTSP, 0, 0, 0 }, { "POPFP", CPU_OP_POPFP, 0, 0, 0 }, { "IADD", CPU_OP_IADD, 3, 0, 1 }, { "IADC", CPU_OP_IADC, 3, 0, 1 }, { "ISUB", CPU_OP_ISUB, 3, 0, 1 }, { "ISBB", CPU_OP_ISBB, 3, 0, 1 }, { "IAND", CPU_OP_IAND, 3, 0, 1 }, { "IOR", CPU_OP_IOR, 3, 0, 1 }, { "IXOR", CPU_OP_IXOR, 3, 0, 1 }, { "INOT", CPU_OP_INOT, 2, 0, 1 }, { "INEG", CPU_OP_INEG, 2, 0, 1 }, { "ICMP", CPU_OP_ICMP, 2, 0, 1 }, { "ITEST", CPU_OP_ITEST, 2, 0, 1 }, { "ISHL", CPU_OP_ISHL, 2, 0, 1 }, { "ISHR", CPU_OP_ISHR, 2, 0, 1 }, { "ISAL", CPU_OP_ISAL, 2, 0, 1 }, { "ISAR", CPU_OP_ISAR, 2, 0, 1 }, { "IROL", CPU_OP_IROL, 2, 0, 1 }, { "IROR", CPU_OP_IROR, 2, 0, 1 }, { "IRCL", CPU_OP_IRCL, 2, 0, 1 }, { "IRCR", CPU_OP_IRCR, 2, 0, 1 }, { "IINC", CPU_OP_IINC, 2, 0, 1 }, { "IDEC", CPU_OP_IDEC, 2, 0, 1 }, { "IXCHG", CPU_OP_IXCHG, 2, 0, 1 }, { "ICPY", CPU_OP_ICPY, 2, 0, 1 }, { "ICVT", CPU_OP_ICVT, 2, 0, 1 }, { "IMUL", CPU_OP_IMUL, 3, 0, 1 }, { "ISMUL", CPU_OP_ISMUL, 3, 0, 1 }, { "IDIV", CPU_OP_IDIV, 4, 0, 1 }, { "ISDIV", CPU_OP_ISDIV, 4, 0, 1 }, { "IBITSLEN", CPU_OP_IBITSLEN, 2, 0, 1 }, { "RADD", CPU_OP_RADD, 3, 0, 1 }, { "RSUB", CPU_OP_RSUB, 3, 0, 1 }, { "RMUL", CPU_OP_RMUL, 3, 0, 1 }, { "RDIV", CPU_OP_RDIV, 3, 0, 1 }, { "RSQRT", CPU_OP_RSQRT, 2, 0, 1 }, { "RSIN", CPU_OP_RSIN, 2, 0, 1 }, { "RCOS", CPU_OP_RCOS, 2, 0, 1 }, { "RTAN", CPU_OP_RTAN, 2, 0, 1 }, { "RASIN", CPU_OP_RASIN, 2, 0, 1 }, { "RACOS", CPU_OP_RACOS, 2, 0, 1 }, { "RATAN", CPU_OP_RATAN, 2, 0, 1 }, { "RLOG", CPU_OP_RLOG, 2, 0, 1 }, { "RLOG10", CPU_OP_RLOG10, 2, 0, 1 }, { "RLOG2", CPU_OP_RLOG2, 2, 0, 1 }, { "REXP", CPU_OP_REXP, 2, 0, 1 }, { "RSINH", CPU_OP_RSINH, 2, 0, 1 }, { "RCOSH", CPU_OP_RCOSH, 2, 0, 1 }, { "RTANH", CPU_OP_RTANH, 2, 0, 1 }, { "RPOW", CPU_OP_RPOW, 3, 0, 1 }, { "RHYPOT", CPU_OP_RHYPOT, 3, 0, 1 }, { "RATAN2", CPU_OP_RATAN2, 3, 0, 1 }, { "RNEG", CPU_OP_RNEG, 2, 0, 1 }, { "RABS", CPU_OP_RABS, 2, 0, 1 }, { "RCPY", CPU_OP_RCPY, 2, 0, 1 }, { "RCVT", CPU_OP_RCVT, 2, 0, 1 }, { "RFLOOR", CPU_OP_RFLOOR, 2, 0, 1 }, { "RCEIL", CPU_OP_RCEIL, 2, 0, 1 }, { "RROUND", CPU_OP_RROUND, 2, 0, 1 }, { "RFREXP", CPU_OP_RFREXP, 3, 0, 1 }, { "RLDEXP", CPU_OP_RLDEXP, 3, 0, 1 }, { "RLOGB", CPU_OP_RLOGB, 2, 0, 1 }, { "RMODF", CPU_OP_RMODF, 3, 0, 1 }, { "RREMAIN", CPU_OP_RREMAIN, 3, 0, 1 }, { "RLOG1P", CPU_OP_RLOG1P, 2, 0, 1 }, { "REXPM1", CPU_OP_REXPM1, 2, 0, 1 }, { "RASINH", CPU_OP_RASINH, 2, 0, 1 }, { "RACOSH", CPU_OP_RACOSH, 2, 0, 1 }, { "RATANH", CPU_OP_RATANH, 2, 0, 1 }, { "ITOR", CPU_OP_ITOR, 2, 0, 1 }, { "UITOR", CPU_OP_UITOR, 2, 0, 1 }, { "RTOI", CPU_OP_RTOI, 3, 0, 1 }, { "RTOUI", CPU_OP_RTOUI, 3, 0, 1 }, { "CADD", CPU_OP_CADD, 3, 1, 0 }, { "CSUB", CPU_OP_CSUB, 3, 1, 0 }, { "CMUL", CPU_OP_CMUL, 3, 1, 0 }, { "CDIV", CPU_OP_CDIV, 3, 1, 0 }, { "CEXP", CPU_OP_CEXP, 2, 1, 0 }, { "CSIN", CPU_OP_CSIN, 2, 1, 0 }, { "CCOS", CPU_OP_CCOS, 2, 1, 0 }, { "CSINH", CPU_OP_CSINH, 2, 1, 0 }, { "CCOSH", CPU_OP_CCOSH, 2, 1, 0 }, { "CLOG", CPU_OP_CLOG, 2, 1, 0 }, { "CSQRT", CPU_OP_CSQRT, 2, 1, 0 }, { "CPOW", CPU_OP_CPOW, 3, 1, 0 }, { "CCONJ", CPU_OP_CCONJ, 2, 1, 0 }, { "CCPY", CPU_OP_CCPY, 2, 1, 0 }, { "CCVT", CPU_OP_CCVT, 2, 1, 0 } }; static int emit_instruction( buf_t *c, fixups_t *df, fixups_t *cf, fixups_t *pf, equs_t *eqs, char *mn, char *rest, char *err, size_t es ) { char *av[4]; int ac = rest && *trim( rest ) ? splitargs( rest, av, 4 ) : 0; size_t i; unsigned k; for( i = 0; i < sizeof( ops ) / sizeof( ops[0] ); i++ ) if( streqi( mn, ops[i].n ) ) { buf_t tmp = { 0 }; if( ac != ops[i].nr ) { snprintf( err, es, "wrong operand count for %s", mn ); return( -1 ); } if( put16( &tmp, ops[i].op ) ) return( -1 ); for( k = 0; k < (unsigned)ac; k++ ) if( preg( &tmp, av[k], ops[i].c, ops[i].r, eqs, err, es ) ) { free( tmp.p ); return( -1 ); } if( put( c, tmp.p, tmp.n ) ) { free( tmp.p ); return( -1 ); } free( tmp.p ); return( 0 ); } if( streqi( mn, "SETF" ) || streqi( mn, "CLRF" ) ) { unsigned bit; if( ac != 1 || pswbitid( av[0], &bit ) || bit > CPU_PSWBIT_WRITE_MAX ) { snprintf( err, es, "%s requires one writable PSW flag in range AF..INXF", mn ); return( -1 ); } if( put16( c, streqi( mn, "SETF" ) ? CPU_OP_SETF : CPU_OP_CLRF ) || put8( c, bit ) ) return( -1 ); return( 0 ); } if( streqi( mn, "EXIT" ) ) { cpu_register r; int ic; if( ac != 1 || regparse( av[0], &r, &ic, eqs, err, es ) || ic || r.bits != 32 ) { snprintf( err, es, "EXIT requires one r32 status operand" ); return( -1 ); } if( put16( c, CPU_OP_EXIT ) || preg( c, av[0], 0, 1, eqs, err, es ) ) return( -1 ); return( 0 ); } if( streqi( mn, "LDR" ) ) { __mpu_real64_t v; mpu_real64_t xv; __mpu_uint64_t u; cpu_register r; int ic, ex, nb; if( ac != 2 || regparse( av[0], &r, &ic, eqs, err, es ) || ic ) { snprintf( err, es, "bad LDR destination" ); return( -1 ); } if( r.bits < 32 ) { snprintf( err, es, "LDR requires r32 or wider" ); return( -1 ); } nb = ( r.bits == 32 ) ? NB_R32 : NB_R64; if( nb == NB_R32 ) { mpu_real32_t x32; ex = real_literal_exact_value( av[1], NB_R32, ( mpu_real * ) x32 ); if( ex == 1 ) r_cvt( ( mpu_real * ) xv, ( mpu_real * ) x32, NB_R64, NB_R32 ); } else ex = real_literal_exact_value( av[1], NB_R64, ( mpu_real * ) xv ); if( ex < 0 ) { snprintf( err, es, "bad real immediate" ); return( -1 ); } if( !ex ) { snprintf( err, es, "LDR immediate is not exactly representable as real%d;\nuse .real , value in DATA and LOAD", r.bits == 32 ? 32 : 64 ); return( -1 ); } memcpy( &v, xv, NB_R64 ); if( put16( c, CPU_OP_LDR ) || preg( c, av[0], 0, 1, eqs, err, es ) ) return( -1 ); memcpy( &u, &v, 8 ); return( put64( c, u ) ); } if( streqi( mn, "LDC" ) ) { float r, j; __mpu_uint32_t ur, ui; __mpu_uint64_t u; cpu_register rg; int ic, cr; if( ac != 2 || regparse( av[0], &rg, &ic, eqs, err, es ) || !ic ) { snprintf( err, es, "bad LDC destination" ); return( -1 ); } cr = cimm_exact( av[1], &r, &j, err, es ); if( cr == -1 ) { snprintf( err, es, "bad complex immediate" ); return( -1 ); } if( cr == -2 ) return( -1 ); memcpy( &ur, &r, 4 ); memcpy( &ui, &j, 4 ); u = ( (__mpu_uint64_t)ur << 32 ) | ui; if( put16( c, CPU_OP_LDC ) || preg( c, av[0], 1, 0, eqs, err, es ) || put64( c, u ) ) return( -1 ); return( 0 ); } if( streqi( mn, "RCONST" ) ) { if( ac != 2 || constid( av[1], &k ) ) { snprintf( err, es, "bad RCONST" ); return( -1 ); } if( put16( c, CPU_OP_RCONST ) || preg( c, av[0], 0, 1, eqs, err, es ) || put8( c, k ) ) return( -1 ); return( 0 ); } if( streqi( mn, "ISHLN" ) || streqi( mn, "ISHRN" ) || streqi( mn, "ISALN" ) || streqi( mn, "ISARN" ) || streqi( mn, "IROLN" ) || streqi( mn, "IRORN" ) || streqi( mn, "IRCLN" ) || streqi( mn, "IRCRN" )) { unsigned op; if( ac != 3 ) { snprintf( err, es, "%s requires dst, src, r64 count", mn ); return( -1 ); } if( streqi( mn, "ISHLN" ) ) op = CPU_OP_ISHLN; else if( streqi( mn, "ISHRN" ) ) op = CPU_OP_ISHRN; else if( streqi( mn, "ISALN" ) ) op = CPU_OP_ISALN; else if( streqi( mn, "ISARN" ) ) op = CPU_OP_ISARN; else if( streqi( mn, "IROLN" ) ) op = CPU_OP_IROLN; else if( streqi( mn, "IRORN" ) ) op = CPU_OP_IRORN; else if( streqi( mn, "IRCLN" ) ) op = CPU_OP_IRCLN; else op = CPU_OP_IRCRN; if( put16( c, op ) || preg( c, av[0], 0, 1, eqs, err, es ) || preg( c, av[1], 0, 1, eqs, err, es ) || preg_r64( c, av[2], "shift count", eqs, err, es )) return( -1 ); return( 0 ); } if( streqi( mn, "RCMP" ) ) { if( ac != 3 ) { snprintf( err, es, "RCMP requires r64 result, real a, real b" ); return( -1 ); } if( put16( c, CPU_OP_RCMP ) || preg_r64( c, av[0], "RCMP result", eqs, err, es ) || preg( c, av[1], 0, 1, eqs, err, es ) || preg( c, av[2], 0, 1, eqs, err, es )) return( -1 ); return( 0 ); } if( streqi( mn, "RISNEG" ) || streqi( mn, "RSIGN" ) || streqi( mn, "RISIND" ) || streqi( mn, "RISNAN" ) || streqi( mn, "RISNANMAX" ) || streqi( mn, "RISNANMIN" ) || streqi( mn, "RISINF" ) || streqi( mn, "RISSIGNULL" )) { unsigned op; if( ac != 2 ) { snprintf( err, es, "%s requires r64 result, real source", mn ); return( -1 ); } if( streqi( mn, "RISNEG" ) ) op = CPU_OP_RISNEG; else if( streqi( mn, "RSIGN" ) ) op = CPU_OP_RSIGN; else if( streqi( mn, "RISIND" ) ) op = CPU_OP_RISIND; else if( streqi( mn, "RISNAN" ) ) op = CPU_OP_RISNAN; else if( streqi( mn, "RISNANMAX" ) ) op = CPU_OP_RISNANMAX; else if( streqi( mn, "RISNANMIN" ) ) op = CPU_OP_RISNANMIN; else if( streqi( mn, "RISINF" ) ) op = CPU_OP_RISINF; else op = CPU_OP_RISSIGNULL; if( put16( c, op ) || preg_r64( c, av[0], "real predicate result", eqs, err, es ) || preg( c, av[1], 0, 1, eqs, err, es )) return( -1 ); return( 0 ); } if( streqi( mn, "CREAL" ) || streqi( mn, "CIMAG" ) || streqi( mn, "CABS" ) || streqi( mn, "CNORM" ) || streqi( mn, "CARG" )) { unsigned op; if( ac != 2 ) { snprintf( err, es, "%s requires real destination, complex source", mn ); return( -1 ); } if( streqi( mn, "CREAL" ) ) op = CPU_OP_CREAL; else if( streqi( mn, "CIMAG" ) ) op = CPU_OP_CIMAG; else if( streqi( mn, "CABS" ) ) op = CPU_OP_CABS; else if( streqi( mn, "CNORM" ) ) op = CPU_OP_CNORM; else op = CPU_OP_CARG; if( put16( c, op ) || preg( c, av[0], 0, 1, eqs, err, es ) || preg( c, av[1], 1, 0, eqs, err, es ) ) return( -1 ); return( 0 ); } if( streqi( mn, "CRTOC" ) ) { if( ac != 2 ) { snprintf( err, es, "CRTOC requires complex destination, real source" ); return( -1 ); } if( put16( c, CPU_OP_CRTOC ) || preg( c, av[0], 1, 0, eqs, err, es ) || preg( c, av[1], 0, 1, eqs, err, es ) ) return( -1 ); return( 0 ); } if( streqi( mn, "CGEN" ) || streqi( mn, "CPOLAR" ) ) { unsigned op = streqi( mn, "CGEN" ) ? CPU_OP_CGEN : CPU_OP_CPOLAR; if( ac != 3 ) { snprintf( err, es, "%s requires complex destination, two real sources", mn ); return( -1 ); } if( put16( c, op ) || preg( c, av[0], 1, 0, eqs, err, es ) || preg( c, av[1], 0, 1, eqs, err, es ) || preg( c, av[2], 0, 1, eqs, err, es )) return( -1 ); return( 0 ); } if( streqi( mn, "CISEQ" ) || streqi( mn, "CISNE" ) ) { unsigned op = streqi( mn, "CISEQ" ) ? CPU_OP_CISEQ : CPU_OP_CISNE; if( ac != 3 ) { snprintf( err, es, "%s requires r64 result, complex a, complex b", mn ); return( -1 ); } if( put16( c, op ) || preg_r64( c, av[0], "complex predicate result", eqs, err, es ) || preg( c, av[1], 1, 0, eqs, err, es ) || preg( c, av[2], 1, 0, eqs, err, es )) return( -1 ); return( 0 ); } if( streqi( mn, "LOAD" ) || streqi( mn, "STORE" ) ) { int store = streqi( mn, "STORE" ); if( ac != 2 ) { snprintf( err, es, "wrong operand count" ); return( -1 ); } if( put16( c, store ? CPU_OP_STORE : CPU_OP_LOAD ) ) return( -1 ); if( store ) { if( addfix( df, c->n, av[0] ) || put64( c, 0 ) || preg( c, av[1], 0, 0, eqs, err, es ) ) return( -1 ); } else { if( preg( c, av[0], 0, 0, eqs, err, es ) || addfix( df, c->n, av[1] ) || put64( c, 0 ) ) return( -1 ); } return( 0 ); } if( streqi( mn, "LEA" ) ) { if( ac != 2 ) { snprintf( err, es, "LEA requires r64 destination and pointer expression" ); return( -1 ); } if( put16( c, CPU_OP_LEA ) || preg_ptr( c, av[0], eqs, err, es ) || addfix( pf, c->n, av[1] ) || put64( c, 0 ) ) return( -1 ); return( 0 ); } if( streqi( mn, "LEASP" ) ) { __mpu_int64_t off; if( ac != 2 ) { snprintf( err, es, "LEASP requires r64 destination and signed SP offset" ); return( -1 ); } { int er = eval_i64( av[1], eqs, 0, &off, err, es ); if( er < 0 ) return( -1 ); if( er > 0 ) { snprintf( err, es, "LEASP offset must fit signed 64 bits" ); return( -1 ); } } if( put16( c, CPU_OP_LEASP ) || preg_ptr( c, av[0], eqs, err, es ) || put64( c, (__mpu_uint64_t)off ) ) return( -1 ); return( 0 ); } if( streqi( mn, "LEAFP" ) ) { __mpu_int64_t off; if( ac != 2 ) { snprintf( err, es, "LEAFP requires r64 destination and signed FP offset" ); return( -1 ); } { int er = eval_i64( av[1], eqs, 0, &off, err, es ); if( er < 0 ) return( -1 ); if( er > 0 ) { snprintf( err, es, "LEAFP offset must fit signed 64 bits" ); return( -1 ); } } if( put16( c, CPU_OP_LEAFP ) || preg_ptr( c, av[0], eqs, err, es ) || put64( c, (__mpu_uint64_t)off ) ) return( -1 ); return( 0 ); } if( streqi( mn, "LOADP" ) || streqi( mn, "STOREP" ) ) { int store = streqi( mn, "STOREP" ); if( ac != 2 ) { snprintf( err, es, "%s requires two register operands", mn ); return( -1 ); } if( put16( c, store ? CPU_OP_STOREP : CPU_OP_LOADP ) ) return( -1 ); if( store ) { if( preg_ptr( c, av[0], eqs, err, es ) || preg( c, av[1], 0, 0, eqs, err, es ) ) return( -1 ); } else { if( preg( c, av[0], 0, 0, eqs, err, es ) || preg_ptr( c, av[1], eqs, err, es ) ) return( -1 ); } return( 0 ); } if( streqi( mn, "PADD" ) || streqi( mn, "PSUB" ) || streqi( mn, "PDIFF" ) ) { unsigned op = streqi( mn, "PADD" ) ? CPU_OP_PADD : ( streqi( mn, "PSUB" ) ? CPU_OP_PSUB : CPU_OP_PDIFF ); if( ac != 3 ) { snprintf( err, es, "%s requires three r64 operands", mn ); return( -1 ); } if( put16( c, op ) || preg_r64( c, av[0], mn, eqs, err, es ) || preg_r64( c, av[1], mn, eqs, err, es ) || preg_r64( c, av[2], mn, eqs, err, es )) return( -1 ); return( 0 ); } if( streqi( mn, "PADDI" ) || streqi( mn, "PSUBI" ) ) { __mpu_int64_t off; unsigned op = streqi( mn, "PADDI" ) ? CPU_OP_PADDI : CPU_OP_PSUBI; if( ac != 3 ) { snprintf( err, es, "%s requires r64 destination, r64 PTR64 source and signed OFF64", mn ); return( -1 ); } { int er = eval_i64( av[2], eqs, 0, &off, err, es ); if( er < 0 ) return( -1 ); if( er > 0 ) { snprintf( err, es, "%s offset must fit signed 64 bits", mn ); return( -1 ); } } if( put16( c, op ) || preg_r64( c, av[0], mn, eqs, err, es ) || preg_r64( c, av[1], mn, eqs, err, es ) || put64( c, (__mpu_uint64_t)off )) return( -1 ); return( 0 ); } if( streqi( mn, "BMOV" ) ) { if( ac != 3 ) { snprintf( err, es, "BMOV requires r64 destination PTR64, r64 source PTR64 and r64 byte count" ); return( -1 ); } if( put16( c, CPU_OP_BMOV ) || preg_ptr( c, av[0], eqs, err, es ) || preg_ptr( c, av[1], eqs, err, es ) || preg_r64( c, av[2], "BMOV byte count", eqs, err, es )) return( -1 ); return( 0 ); } if( streqi( mn, "BMOVI" ) ) { __mpu_uint64_t count; if( ac != 3 ) { snprintf( err, es, "BMOVI requires r64 destination PTR64, r64 source PTR64 and unsigned IMM64 byte count" ); return( -1 ); } { int er = eval_u64( av[2], eqs, 0, &count, err, es ); if( er < 0 ) return( -1 ); if( er > 0 ) { snprintf( err, es, "BMOVI byte count must fit unsigned 64 bits" ); return( -1 ); } } if( put16( c, CPU_OP_BMOVI ) || preg_ptr( c, av[0], eqs, err, es ) || preg_ptr( c, av[1], eqs, err, es ) || put64( c, count )) return( -1 ); return( 0 ); } if( streqi( mn, "JMPP" ) || streqi( mn, "CALLP" ) ) { unsigned op = streqi( mn, "CALLP" ) ? CPU_OP_CALLP : CPU_OP_JMPP; if( ac != 1 ) { snprintf( err, es, "%s requires one r64 PROGRAM PTR64 operand", mn ); return( -1 ); } if( put16( c, op ) || preg_ptr( c, av[0], eqs, err, es ) ) return( -1 ); return( 0 ); } if( streqi( mn, "JMP" ) || streqi( mn, "CALL" ) ) { int call = streqi( mn, "CALL" ); if( ac != 1 ) { snprintf( err, es, "%s requires one CODE label", mn ); return( -1 ); } if( put16( c, call ? CPU_OP_CALL : CPU_OP_JMP ) || addfix( cf, c->n, av[0] ) || put64( c, 0 ) ) return( -1 ); return( 0 ); } if( streqi( mn, "RET" ) ) { if( ac != 0 ) { snprintf( err, es, "RET takes no operands" ); return( -1 ); } return( put16( c, CPU_OP_RET ) ); } if( streqi( mn, "JSET" ) || streqi( mn, "JCLR" ) ) { unsigned bit; if( ac != 2 || pswbitid( av[0], &bit ) ) { snprintf( err, es, "bad PSW bit for %s", mn ); return( -1 ); } if( put16( c, streqi( mn, "JSET" ) ? CPU_OP_JSET : CPU_OP_JCLR ) || put8( c, bit ) || addfix( cf, c->n, av[1] ) || put64( c, 0 )) return( -1 ); return( 0 ); } { struct A { const char *n; unsigned op; unsigned bit; } as[] = { { "JZ", CPU_OP_JSET, CPU_PSWBIT_ZF }, { "JNZ", CPU_OP_JCLR, CPU_PSWBIT_ZF }, { "JC", CPU_OP_JSET, CPU_PSWBIT_CF }, { "JNC", CPU_OP_JCLR, CPU_PSWBIT_CF }, { "JO", CPU_OP_JSET, CPU_PSWBIT_OF }, { "JNO", CPU_OP_JCLR, CPU_PSWBIT_OF }, { "JS", CPU_OP_JSET, CPU_PSWBIT_SF }, { "JNS", CPU_OP_JCLR, CPU_PSWBIT_SF } }; size_t ai; for( ai = 0; ai < sizeof( as ) / sizeof( as[0] ); ai++ ) if( streqi( mn, as[ai].n ) ) { if( ac != 1 ) { snprintf( err, es, "%s requires one CODE label", mn ); return( -1 ); } if( put16( c, as[ai].op ) || put8( c, as[ai].bit ) || addfix( cf, c->n, av[0] ) || put64( c, 0 ) ) return( -1 ); return( 0 ); } } if( streqi( mn, "LDSP" ) || streqi( mn, "STSP" ) ) { __mpu_int64_t off; int store = streqi( mn, "STSP" ); if( ac != 2 ) { snprintf( err, es, "%s requires two operands", mn ); return( -1 ); } { int er = eval_i64( store ? av[0] : av[1], eqs, 0, &off, err, es ); if( er < 0 ) return( -1 ); if( er > 0 ) { snprintf( err, es, "%s offset must fit signed 64 bits", mn ); return( -1 ); } } if( put16( c, store ? CPU_OP_STSP : CPU_OP_LDSP ) ) return( -1 ); if( store ) { if( put64( c, (__mpu_uint64_t)off ) || preg( c, av[1], 0, 0, eqs, err, es ) ) return( -1 ); } else { if( preg( c, av[0], 0, 0, eqs, err, es ) || put64( c, (__mpu_uint64_t)off ) ) return( -1 ); } return( 0 ); } if( streqi( mn, "LDFP" ) || streqi( mn, "STFP" ) ) { __mpu_int64_t off; int store = streqi( mn, "STFP" ); if( ac != 2 ) { snprintf( err, es, "%s requires two operands", mn ); return( -1 ); } { int er = eval_i64( store ? av[0] : av[1], eqs, 0, &off, err, es ); if( er < 0 ) return( -1 ); if( er > 0 ) { snprintf( err, es, "%s offset must fit signed 64 bits", mn ); return( -1 ); } } if( put16( c, store ? CPU_OP_STFP : CPU_OP_LDFP ) ) return( -1 ); if( store ) { if( put64( c, (__mpu_uint64_t)off ) || preg( c, av[1], 0, 0, eqs, err, es ) ) return( -1 ); } else { if( preg( c, av[0], 0, 0, eqs, err, es ) || put64( c, (__mpu_uint64_t)off ) ) return( -1 ); } return( 0 ); } if( streqi( mn, "ADJSP" ) ) { __mpu_int64_t off; if( ac != 1 ) { snprintf( err, es, "ADJSP requires one signed byte offset" ); return( -1 ); } { int er = eval_i64( av[0], eqs, 0, &off, err, es ); if( er < 0 ) return( -1 ); if( er > 0 ) { snprintf( err, es, "ADJSP offset must fit signed 64 bits" ); return( -1 ); } } if( put16( c, CPU_OP_ADJSP ) || put64( c, (__mpu_uint64_t)off ) ) return( -1 ); return( 0 ); } if( streqi( mn, "SETSP" ) ) { if( ac != 1 ) { snprintf( err, es, "SETSP requires one r64 PTR64 operand" ); return( -1 ); } if( put16( c, CPU_OP_SETSP ) || preg_ptr( c, av[0], eqs, err, es ) ) return( -1 ); return( 0 ); } snprintf( err, es, "unknown mnemonic: %s", mn ); return( -1 ); } /* Special LDI emitter kept separate to make signed 64-bit parsing explicit. */ static int emit_ldi( buf_t *c, equs_t *eqs, char *rest, char *err, size_t es ) { char *av[3]; __mpu_int64_t v; asm_i128 xv; cpu_register r; int ic, ac = splitargs( rest, av, 3 ); if( ac != 2 ) { snprintf( err, es, "wrong operand count for LDI" ); return( -1 ); } if( regparse( av[0], &r, &ic, eqs, err, es ) || ic ) { snprintf( err, es, "bad LDI destination" ); return( -1 ); } if( eval_expression( av[1], eqs, 0, &xv, err, es ) ) { snprintf( err, es, "bad integer immediate: %s", av[1] ); return( -1 ); } if( !i128_signed_ok( xv ) ) { snprintf( err, es, "LDI immediate is not exactly representable as integer64;\nuse .integer , value in DATA and LOAD" ); return( -1 ); } v = i128_signed( xv ); if( r.bits < 64 ) { __mpu_int64_t lo = - ( (__mpu_int64_t)1 << ( r.bits - 1 ) ), hi = ( (__mpu_int64_t)1 << ( r.bits - 1 ) ) - 1; if( v < lo || v > hi ) { snprintf( err, es, "LDI immediate is not exactly representable as integer%u;\nuse .integer , value in DATA and LOAD", r.bits ); return( -1 ); } } if( put16( c, CPU_OP_LDI ) || preg( c, av[0], 0, 1, eqs, err, es ) || put64( c, (__mpu_uint64_t)v ) ) return( -1 ); return( 0 ); } static int datadir( buf_t *d, char *line, fixups_t *cptr_fix, fixups_t *ptr_fix, __mpu_uint64_t *section_align, equs_t *eqs, char *err, size_t es) { char *p = trim( line ), *mn = p, *rest, *av[256]; int ac, bits, nb; __mpu_uint64_t uv; while( *p && !isspace( (unsigned char)*p ) ) p++; if( *p ) { *p++ = 0; rest = trim( p ); } else rest = (char *)""; if( streqi( mn, ".align" ) ) { if( !*rest ) { snprintf( err, es, ".align requires a value" ); return( -1 ); } { int er = eval_u64( rest, eqs, 0, &uv, err, es ); if( er < 0 ) return( -1 ); if( er > 0 || !uv || uv > (__mpu_uint64_t)SIZE_MAX ) { snprintf( err, es, "bad .align" ); return( -1 ); } } if( section_align && uv > *section_align ) * section_align = uv; while( d->n % (size_t)uv ) if( put8( d, 0 ) ) { snprintf( err, es, "out of memory" ); return( -1 ); } return( 0 ); } if( streqi( mn, ".utf8" ) || streqi( mn, ".ucs2" ) ) { buf_t text = { 0 }; if( !*rest ) { snprintf( err, es, "%s requires a quoted UTF-8 string", mn ); return( -1 ); } if( parse_utf8_string( rest, &text, err, es ) ) return( -1 ); if( streqi( mn, ".utf8" ) ) { if( put( d, text.p, text.n ) ) { free( text.p ); snprintf( err, es, "out of memory" ); return( -1 ); } } else { __mpu_size_t nch = mpu_utf8_to_ucs2( 0, (const __mpu_char8_t *)text.p, 0 ), i; __mpu_char16_t *ucs; if( nch == (__mpu_size_t)-1 ) { free( text.p ); snprintf( err, es, ".ucs2 string is not representable as strict UCS-2" ); return( -1 ); } ucs = (__mpu_char16_t *)calloc( (size_t)nch + 1, sizeof( *ucs ) ); if( !ucs ) { free( text.p ); snprintf( err, es, "out of memory" ); return( -1 ); } if( mpu_utf8_to_ucs2( ucs, (const __mpu_char8_t *)text.p, nch + 1 ) != nch ) { free( ucs ); free( text.p ); snprintf( err, es, "UTF-8 to UCS-2 conversion failed" ); return( -1 ); } for( i = 0; i < nch; i++ ) { __mpu_UNIT_t x[2]; __cpu_data_put_u16( x, (__mpu_uint16_t)ucs[i] ); if( put( d, x, 2 ) ) { free( ucs ); free( text.p ); snprintf( err, es, "out of memory" ); return( -1 ); } } { __mpu_UNIT_t z[2]; __cpu_data_put_u16( z, 0 ); if( put( d, z, 2 ) ) { free( ucs ); free( text.p ); snprintf( err, es, "out of memory" ); return( -1 ); } } free( ucs ); } free( text.p ); return( 0 ); } if( !*rest ) { snprintf( err, es, "data directive requires operands" ); return( -1 ); } ac = splitargs( rest, av, 256 ); if( ac < 0 ) { snprintf( err, es, "too many or malformed directive arguments" ); return( -1 ); } if( streqi( mn, ".byte" ) ) { int i; if( ac < 1 ) { snprintf( err, es, ".byte requires one or more values" ); return( -1 ); } for( i = 0; i < ac; i++ ) { int er = eval_u64( av[i], eqs, 0, &uv, err, es ); if( er < 0 ) return( -1 ); if( er > 0 || uv > 255 ) { snprintf( err, es, ".byte value must fit 8 bits: %s", av[i] ); return( -1 ); } if( put8( d, (unsigned)uv ) ) { snprintf( err, es, "out of memory" ); return( -1 ); } } return( 0 ); } if( streqi( mn, ".integer" ) || streqi( mn, ".int" ) ) { int i; mpu_wide_int_t x; if( ac < 2 ) { snprintf( err, es, "%s requires: bits, value[, value...]", mn ); return( -1 ); } { int er = eval_u64( av[0], eqs, 0, &uv, err, es ); if( er < 0 ) return( -1 ); if( er > 0 || uv > 65536 ) { snprintf( err, es, "bad .integer width" ); return( -1 ); } } bits = (int)uv; nb = bits / 8; if( bits < 8 || ( bits & ( bits - 1 ) ) ) { snprintf( err, es, "bad .integer width" ); return( -1 ); } for( i = 1; i < ac; i++ ) { const char *value = av[i]; char dec[64]; asm_i128 ev; memset( x, 0, sizeof( x ) ); if( !plain_integer_literal( value ) ) { if( eval_expression( value, eqs, 0, &ev, err, es ) ) return( -1 ); i128_decimal( ev, dec ); value = dec; } iatoi( ( mpu_int * ) x, (__mpu_char8_t *)value, nb ); if( put( d, x, (size_t)nb ) ) { snprintf( err, es, "out of memory" ); return( -1 ); } } return( 0 ); } if( streqi( mn, ".real" ) ) { mpu_wide_real_t x; if( ac != 2 ) { snprintf( err, es, ".real requires: bits, value" ); return( -1 ); } { int er = eval_u64( av[0], eqs, 0, &uv, err, es ); if( er < 0 ) return( -1 ); if( er > 0 || uv > 65536 ) { snprintf( err, es, "bad .real width" ); return( -1 ); } } bits = (int)uv; nb = bits / 8; if( bits < 32 || ( bits & ( bits - 1 ) ) ) { snprintf( err, es, "bad .real width" ); return( -1 ); } memset( x, 0, sizeof( x ) ); if( ascii_to_real( ( mpu_real * ) x, (__mpu_char8_t *)av[1], nb ) < 0 ) { snprintf( err, es, "bad .real constant" ); return( -1 ); } if( put( d, x, (size_t)nb ) ) { snprintf( err, es, "out of memory" ); return( -1 ); } return( 0 ); } if( streqi( mn, ".complex" ) ) { mpu_wide_complex_t x; mpu_wide_real_t r, j; if( ac != 3 ) { snprintf( err, es, ".complex requires: physical-bits, real, imag" ); return( -1 ); } { int er = eval_u64( av[0], eqs, 0, &uv, err, es ); if( er < 0 ) return( -1 ); if( er > 0 || uv > 65536 ) { snprintf( err, es, "bad .complex physical width" ); return( -1 ); } } bits = (int)uv; nb = bits / 8; if( bits < 64 || ( bits & ( bits - 1 ) ) ) { snprintf( err, es, "bad .complex physical width" ); return( -1 ); } if( ascii_to_real( ( mpu_real * ) r, (__mpu_char8_t *)av[1], nb / 2 ) < 0 || ascii_to_real( ( mpu_real * ) j, (__mpu_char8_t *)av[2], nb / 2 ) < 0 ) { snprintf( err, es, "bad .complex component" ); return( -1 ); } c_gen_complex( ( mpu_complex * ) x, ( mpu_real * ) r, ( mpu_real * ) j, nb / 2 ); if( put( d, x, (size_t)nb ) ) { snprintf( err, es, "out of memory" ); return( -1 ); } return( 0 ); } if( streqi( mn, ".cptr" ) || streqi( mn, ".codeptr" ) ) { __mpu_uint64_t zero = 0; if( ac != 1 || !*av[0] ) { snprintf( err, es, ".cptr requires one CODE expression" ); return( -1 ); } if( addfix( cptr_fix, d->n, av[0] ) || put( d, &zero, CPU_PTR64_SIZE ) ) { snprintf( err, es, "out of memory" ); return( -1 ); } return( 0 ); } if( streqi( mn, ".ptr" ) ) { __mpu_uint64_t zero = 0; if( ac != 1 || !*av[0] ) { snprintf( err, es, ".ptr requires one pointer expression" ); return( -1 ); } if( addfix( ptr_fix, d->n, av[0] ) || put( d, &zero, CPU_PTR64_SIZE ) ) { snprintf( err, es, "out of memory" ); return( -1 ); } return( 0 ); } snprintf( err, es, "unknown data directive: %s", mn ); return( -1 ); } static int valid_symbol_name( const char *s ) { if( !s || !symfirst( (unsigned char)*s ) ) return( 0 ); for( s++; *s; s++ ) if( !symrest( (unsigned char)*s ) ) return( 0 ); return( 1 ); } static int finddecl( const symdecls_t *d, const char *name, size_t *idx ) { size_t i; for( i = 0; i < d->n; i++ ) if( !strcmp( d->p[i].name, name ) ) { if( idx ) * idx = i; return( 0 ); } return( -1 ); } static int adddecl( symdecls_t *d, const char *name, int is_global, int is_extern, int is_weak, char *err, size_t es) { size_t i; if( !valid_symbol_name( name ) ) { snprintf( err, es, "bad symbol name: %s", name ); return( -1 ); } if( !strcmp( name, "_start_life" ) ) { snprintf( err, es, "_start_life is an assembler pseudo-label and cannot be declared" ); return( -1 ); } if( is_weak && ( !strcmp( name, "_start" ) || !strcmp( name, "_life" ) || !strcmp( name, "_fini" ) ) ) { snprintf( err, es, "lifecycle symbol %s cannot be weak", name ); return( -1 ); } if( !finddecl( d, name, &i ) ) { if( is_global ) d->p[i].is_global = 1; if( is_extern ) d->p[i].is_extern = 1; if( is_weak ) d->p[i].is_weak = 1; return( 0 ); } if( grow( (void **)&d->p, &d->cap, d->n + 1, sizeof( *d->p ) ) ) { snprintf( err, es, "out of memory" ); return( -1 ); } memset( &d->p[d->n], 0, sizeof( *d->p ) ); strcpy( d->p[d->n].name, name ); d->p[d->n].is_global = is_global; d->p[d->n].is_extern = is_extern; d->p[d->n].is_weak = is_weak; d->n++; return( 0 ); } /* Return 1 for a declaration line, 0 otherwise, -1 on error. */ static int collect_decl_line( char *line, symdecls_t *decls, int line_no, char *err, size_t es ) { char *p = trim( line ), *mn = p, *rest, *av[256]; int ac, i, is_global = 0, is_extern = 0, is_weak = 0; while( *p && !isspace( (unsigned char)*p ) ) p++; if( *p ) { *p++ = 0; rest = trim( p ); } else rest = (char *)""; if( streqi( mn, ".global" ) || streqi( mn, ".globl" ) ) is_global = 1; else if( streqi( mn, ".extern" ) ) { is_global = 1; is_extern = 1; } else if( streqi( mn, ".weak" ) ) { is_weak = 1; } else return( 0 ); if( !*rest ) { snprintf( err, es, "line %d: %s requires one or more symbol names", line_no, mn ); return( -1 ); } ac = splitargs( rest, av, 256 ); if( ac < 1 ) { snprintf( err, es, "line %d: malformed %s", line_no, mn ); return( -1 ); } for( i = 0; i < ac; i++ ) if( adddecl( decls, av[i], is_global, is_extern, is_weak, err, es ) ) { char t[256]; snprintf( t, sizeof( t ), "line %d: %s", line_no, err ); snprintf( err, es, "%s", t ); return( -1 ); } return( 1 ); } /* .bss is SHT_NOBITS: only layout directives are legal and no payload bytes are emitted into the relocatable object. */ static int bssdir( size_t *bss_size, char *line, __mpu_uint64_t *section_align, equs_t *eqs, char *err, size_t es) { char *p = trim( line ), *mn = p, *rest; __mpu_uint64_t v; while( *p && !isspace( (unsigned char)*p ) ) p++; if( *p ) { *p++ = 0; rest = trim( p ); } else rest = (char *)""; if( streqi( mn, ".align" ) ) { __mpu_uint64_t rem, add; int er; if( !*rest ) { snprintf( err, es, ".align requires a value" ); return( -1 ); } er = eval_u64( rest, eqs, 0, &v, err, es ); if( er < 0 ) return( -1 ); if( er > 0 || !v || v > (__mpu_uint64_t)SIZE_MAX || ( v & ( v - 1 ) ) ) { snprintf( err, es, "bad .align" ); return( -1 ); } if( section_align && v > *section_align ) * section_align = v; rem = (__mpu_uint64_t)( *bss_size ) % v; add = rem ? v - rem : 0; if( add > (__mpu_uint64_t)SIZE_MAX - *bss_size ) { snprintf( err, es, ".bss size overflow" ); return( -1 ); } *bss_size+=(size_t)add; return( 0 ); } if( streqi( mn, ".zero" ) ) { int er; if( !*rest ) { snprintf( err, es, ".zero requires a byte count" ); return( -1 ); } er = eval_u64( rest, eqs, 0, &v, err, es ); if( er < 0 ) return( -1 ); if( er > 0 || v > (__mpu_uint64_t)SIZE_MAX - *bss_size ) { snprintf( err, es, "bad .zero byte count" ); return( -1 ); } *bss_size+=(size_t)v; return( 0 ); } snprintf( err, es, "only .align and .zero are valid in .bss" ); return( -1 ); } /* Return 1 when line is .equ, 0 when it is not, -1 on malformed .equ. */ static int collect_equ_line( char *line, equs_t *eqs, int line_no, char *err, size_t es ) { char *p = trim( line ), *mn = p, *rest, *av[2]; int ac; while( *p && !isspace( (unsigned char)*p ) ) p++; if( *p ) { *p++ = 0; rest = trim( p ); } else rest = (char *)""; if( !streqi( mn, ".equ" ) ) return( 0 ); if( !*rest ) { snprintf( err, es, "line %d: .equ requires NAME, expression", line_no ); return( -1 ); } ac = splitargs( rest, av, 2 ); if( ac != 2 || !valid_symbol_name( av[0] ) || !*av[1] ) { snprintf( err, es, "line %d: .equ requires NAME, expression", line_no ); return( -1 ); } if( addequ( eqs, av[0], av[1], line_no, err, es ) ) { char t[256]; snprintf( t, sizeof( t ), "line %d: %s", line_no, err ); snprintf( err, es, "%s", t ); return( -1 ); } return( 1 ); } enum asm_section { ASM_SEC_NONE = 0, ASM_SEC_TEXT, ASM_SEC_DATA, ASM_SEC_BSS, ASM_SEC_INIT, ASM_SEC_FINI, ASM_SEC_CONTROL, ASM_SEC_INPUT, ASM_SEC_OUTPUT, ASM_SEC_TRACE }; static int label_in_any( const char *name, const labels_t *a, const labels_t *b, const labels_t *c, const labels_t *d, const labels_t *e, const labels_t *f) { __mpu_uint64_t x; return( a && !findlabel( a, name, &x ) ) || ( b && !findlabel( b, name, &x ) ) || ( c && !findlabel( c, name, &x ) ) || ( d && !findlabel( d, name, &x ) ) || ( e && !findlabel( e, name, &x ) ) || ( f && !findlabel( f, name, &x ) ); } static int resolve_ptr_expression( const char *expr, equs_t *eqs, const labels_t *code, const labels_t *data, const labels_t *control, const labels_t *input, const labels_t *output, const labels_t *trace, const size_t sizes[8], __mpu_uint64_t *ptr, char *err, size_t es) { const labels_t *ls[6] = { data, control, input, output, trace, code }; const unsigned segs[6] = { CPU_PTR_SEG_DATA, CPU_PTR_SEG_CONTROL, CPU_PTR_SEG_INPUT, CPU_PTR_SEG_OUTPUT, CPU_PTR_SEG_TRACE, CPU_PTR_SEG_PROGRAM }; __mpu_uint64_t value = 0; int i, found = 0, er; unsigned found_seg = CPU_PTR_SEG_DATA; char tmp[256]; /* A pure absolute/.equ expression keeps historical LEA semantics: DATA. */ er = eval_u64( expr, eqs, 0, &value, tmp, sizeof( tmp ) ); if( er == 0 ) { if( value > sizes[CPU_PTR_SEG_DATA] ) { snprintf( err, es, "LEA DATA expression is outside DATA: %s", expr ); return( -1 ); } return( __cpu_ptr_make( ptr, CPU_PTR_SEG_DATA, value ) ); } for( i = 0; i < 6; i++ ) { __mpu_uint64_t v; if( eval_u64( expr, eqs, ls[i], &v, tmp, sizeof( tmp ) ) == 0 ) { found++; value = v; found_seg = segs[i]; } } if( found != 1 ) { snprintf( err, es, found ? "ambiguous pointer expression: %s" : "unknown symbol in pointer expression: %s", expr ); return( -1 ); } if( value > sizes[found_seg] ) { snprintf( err, es, "pointer expression is outside its segment: %s", expr ); return( -1 ); } if( found_seg == CPU_PTR_SEG_HEAP && value < CPU_HEAP_BEGIN ) { snprintf( err, es, "pointer expression enters reserved HEAP guard: %s", expr ); return( -1 ); } if( __cpu_ptr_make( ptr, found_seg, value ) ) { snprintf( err, es, "cannot encode PTR64 expression: %s", expr ); return( -1 ); } return( 0 ); } static int apply_cptr_fixups( buf_t *dst, const fixups_t *fx, equs_t *eqs, const labels_t *clabels, size_t code_size, char *err, size_t es) { size_t i; for( i = 0; i < fx->n; i++ ) { __mpu_uint64_t a, ptr; int er = eval_u64( fx->p[i].expr, eqs, clabels, &a, err, es ); if( er < 0 ) { char cause[256]; snprintf( cause, sizeof( cause ), "%s", err ); snprintf( err, es, ".cptr expression '%s': %s", fx->p[i].expr, cause ); return( -1 ); } if( er > 0 || a >= code_size ) { snprintf( err, es, ".cptr expression does not address PROGRAM: %s", fx->p[i].expr ); return( -1 ); } if( __cpu_ptr_make( &ptr, CPU_PTR_SEG_PROGRAM, a ) ) { snprintf( err, es, "cannot encode PROGRAM PTR64 for: %s", fx->p[i].expr ); return( -1 ); } __cpu_data_put_u64( dst->p + fx->p[i].pos, ptr ); } return( 0 ); } static int apply_ptr_data_fixups( buf_t *dst, const fixups_t *fx, equs_t *eqs, const labels_t *code, const labels_t *data, const labels_t *control, const labels_t *input, const labels_t *output, const labels_t *trace, const size_t sizes[8], char *err, size_t es) { size_t i; for( i = 0; i < fx->n; i++ ) { __mpu_uint64_t ptr; if( resolve_ptr_expression( fx->p[i].expr, eqs, code, data, control, input, output, trace, sizes, &ptr, err, es)) { char cause[256]; snprintf( cause, sizeof( cause ), "%s", err ); snprintf( err, es, ".ptr expression '%s': %s", fx->p[i].expr, cause ); return( -1 ); } __cpu_data_put_u64( dst->p + fx->p[i].pos, ptr ); } return( 0 ); } int cpu_assemble_text( const char *src, cpu_asm_image *im, char *err, size_t es ) { char *copy = 0, *scan = 0, *save, *line; buf_t code = { 0 } , data = { 0 } , control = { 0 } , input = { 0 } , output = { 0 } , trace = { 0 }; labels_t clabels = { 0 } , dlabels = { 0 } , ctlabels = { 0 } , ilabels = { 0 } , olabels = { 0 } , tlabels = { 0 }; fixups_t dfix = { 0 } , cfix = { 0 } , pfix = { 0 }; fixups_t dcptr = { 0 } , ccptr = { 0 } , icptr = { 0 } , ocptr = { 0 } , tcptr = { 0 }; fixups_t dptr = { 0 } , ctptr = { 0 } , iptr = { 0 } , optr = { 0 } , tptr = { 0 }; __mpu_uint64_t dalign = 1, ctalign = 1, ialign = 1, oalign = 1, talign = 1; equs_t eqs = { 0 }; int section = ASM_SEC_NONE, line_no = 0, rc = -1; int init_seen = 0, text_seen = 0, fini_seen = 0, start_seen = 0, life_seen = 0, fini_entry_seen = 0; __mpu_uint64_t first_text = 0, start_entry = 0, life_entry = 0, fini_entry = 0; if( !src || !im ) return( -1 ); memset( im, 0, sizeof( *im ) ); if( !mpu_utf8valid( (const __mpu_char8_t *)src ) ) { snprintf( err, es, "assembler source is not valid UTF-8" ); return( -1 ); } /* Resolve absolute .equ symbols first. Section labels intentionally do not participate in .equ, preserving the distinction between absolute symbols and relocatable/segment-relative labels. */ scan = strdup( src ); if( !scan ) return( -1 ); for( line = strtok_r( scan, "\n", &save ); line; line = strtok_r( NULL, "\n", &save ) ) { int er; line_no++; strip_comment( line ); if( !*trim( line ) ) continue; er = collect_equ_line( line, &eqs, line_no, err, es ); if( er < 0 ) goto out; } free( scan ); scan = 0; { size_t i; asm_i128 v; for( i = 0; i < eqs.n; i++ ) if( equ_value( &eqs, i, &v, err, es ) ) { char t[256]; snprintf( t, sizeof( t ), "line %d: %s", eqs.p[i].line, err ); snprintf( err, es, "%s", t ); goto out; } } /* DATA keeps its ABI NULL guard. Other image-backed data spaces begin at offset zero because their non-zero segment selector already distinguishes them from the all-zero NULL PTR64. */ while( data.n < CPU_DATA_NULL_GUARD_SIZE ) if( put8( &data, 0 ) ) { snprintf( err, es, "out of memory" ); goto out; } copy = strdup( src ); if( !copy ) goto out; line_no = 0; for( line = strtok_r( copy, "\n", &save ); line; line = strtok_r( NULL, "\n", &save ) ) { char *p, *colon, *mn, *rest, *q; buf_t *db = 0; labels_t *dl = 0; fixups_t *cpfx = 0, *pfx = 0; __mpu_uint64_t *salign = 0; line_no++; strip_comment( line ); p = trim( line ); if( !*p ) continue; q = p; while( *q && !isspace( (unsigned char)*q ) ) q++; if( (size_t)( q - p ) == 4 && !strncasecmp( p, ".equ", 4 ) ) continue; if( streqi( p, ".text" ) ) { if( !text_seen ) { first_text = (__mpu_uint64_t)code.n; text_seen = 1; } section = ASM_SEC_TEXT; continue; } if( streqi( p, ".init" ) ) { init_seen = 1; section = ASM_SEC_INIT; continue; } if( streqi( p, ".fini" ) ) { fini_seen = 1; section = ASM_SEC_FINI; continue; } if( streqi( p, ".data" ) ) { section = ASM_SEC_DATA; continue; } if( streqi( p, ".control" ) ) { section = ASM_SEC_CONTROL; continue; } if( streqi( p, ".input" ) ) { section = ASM_SEC_INPUT; continue; } if( streqi( p, ".output" ) ) { section = ASM_SEC_OUTPUT; continue; } if( streqi( p, ".trace" ) ) { section = ASM_SEC_TRACE; continue; } colon = label_colon( p ); if( colon ) { size_t ei; char *name; *colon = 0; name = trim( p ); if( !valid_symbol_name( name ) ) { snprintf( err, es, "line %d: bad label name", line_no ); goto out; } if( !findequ( &eqs, name, &ei ) ) { snprintf( err, es, "line %d: label conflicts with .equ symbol: %s", line_no, name ); goto out; } if( label_in_any( name, &clabels, &dlabels, &ctlabels, &ilabels, &olabels, &tlabels ) ) { snprintf( err, es, "line %d: duplicate label: %s", line_no, name ); goto out; } if( section == ASM_SEC_TEXT || section == ASM_SEC_INIT || section == ASM_SEC_FINI ) { if( addlabel( &clabels, name, code.n ) ) { snprintf( err, es, "line %d: bad/duplicate CODE label", line_no ); goto out; } if( !strcmp( name, "_start" ) ) { if( section != ASM_SEC_INIT ) { snprintf( err, es, "line %d: _start must be in .init", line_no ); goto out; } start_seen = 1; start_entry = code.n; } if( !strcmp( name, "_life" ) ) { if( section != ASM_SEC_TEXT ) { snprintf( err, es, "line %d: _life must be in .text", line_no ); goto out; } life_seen = 1; life_entry = code.n; } if( !strcmp( name, "_fini" ) ) { if( section != ASM_SEC_FINI ) { snprintf( err, es, "line %d: _fini must be in .fini", line_no ); goto out; } fini_entry_seen = 1; fini_entry = code.n; } } else { switch( section ) { case ASM_SEC_DATA: dl = &dlabels; break; case ASM_SEC_CONTROL: dl = &ctlabels; break; case ASM_SEC_INPUT: dl = &ilabels; break; case ASM_SEC_OUTPUT: dl = &olabels; break; case ASM_SEC_TRACE: dl = &tlabels; break; default: break; } if( !dl || addlabel( dl, name, ( section == ASM_SEC_DATA ) ? data.n : ( section == ASM_SEC_CONTROL ) ? control.n : ( section == ASM_SEC_INPUT ) ? input.n : ( section == ASM_SEC_OUTPUT ) ? output.n : trace.n ) ) { snprintf( err, es, "line %d: label outside a valid section or duplicate label", line_no ); goto out; } } p = trim( colon + 1 ); if( !*p ) continue; } switch( section ) { case ASM_SEC_DATA: db = &data; cpfx = &dcptr; pfx = &dptr; salign = &dalign; break; case ASM_SEC_CONTROL: db = &control; cpfx = &ccptr; pfx = &ctptr; salign = &ctalign; break; case ASM_SEC_INPUT: db = &input; cpfx = &icptr; pfx = &iptr; salign = &ialign; break; case ASM_SEC_OUTPUT: db = &output; cpfx = &ocptr; pfx = &optr; salign = &oalign; break; case ASM_SEC_TRACE: db = &trace; cpfx = &tcptr; pfx = &tptr; salign = &talign; break; default:break; } if( db ) { if( datadir( db, p, cpfx, pfx, salign, &eqs, err, es ) ) { char t[256]; snprintf( t, sizeof( t ), "line %d: %s", line_no, err ); snprintf( err, es, "%s", t ); goto out; } continue; } if( section != ASM_SEC_TEXT && section != ASM_SEC_INIT && section != ASM_SEC_FINI ) { snprintf( err, es, "line %d: select a code or data section", line_no ); goto out; } mn = p; while( *p && !isspace( (unsigned char)*p ) ) p++; if( *p ) { *p++ = 0; rest = trim( p ); } else rest = (char *)""; if( streqi( mn, "LDI" ) ) { if( !*rest || emit_ldi( &code, &eqs, rest, err, es ) ) goto badline; } else { if( emit_instruction( &code, &dfix, &cfix, &pfix, &eqs, mn, rest, err, es ) ) goto badline; } continue; badline: { char t[256]; snprintf( t, sizeof( t ), "line %d: %s", line_no, err ); snprintf( err, es, "%s", t ); goto out; } } if( init_seen && !start_seen ) { snprintf( err, es, ".init section requires ABI entry label _start" ); goto out; } if( fini_seen && !fini_entry_seen ) { snprintf( err, es, ".fini section requires ABI entry label _fini" ); goto out; } if( !text_seen ) { snprintf( err, es, "program requires a .text section" ); goto out; } if( !life_seen ) life_entry = first_text; /* backward-compatible legacy .text image */ { size_t i; size_t sizes[8] = { data.n, 0, code.n, control.n, input.n, output.n, trace.n, 0 }; for( i = 0; i < dfix.n; i++ ) { __mpu_uint64_t a; int er = eval_u64( dfix.p[i].expr, &eqs, &dlabels, &a, err, es ); if( er < 0 ) { char cause[256]; snprintf( cause, sizeof( cause ), "%s", err ); snprintf( err, es, "DATA expression '%s': %s", dfix.p[i].expr, cause ); goto out; } if( er > 0 ) { snprintf( err, es, "DATA expression out of unsigned 64-bit range: %s", dfix.p[i].expr ); goto out; } __cpu_code_put_u64( code.p + dfix.p[i].pos, a ); } for( i = 0; i < cfix.n; i++ ) { __mpu_uint64_t a; int er = eval_u64( cfix.p[i].expr, &eqs, &clabels, &a, err, es ); if( er < 0 ) { char cause[256]; snprintf( cause, sizeof( cause ), "%s", err ); snprintf( err, es, "CODE expression '%s': %s", cfix.p[i].expr, cause ); goto out; } if( er > 0 || a >= code.n ) { snprintf( err, es, "CODE expression does not address PROGRAM: %s", cfix.p[i].expr ); goto out; } __cpu_code_put_u64( code.p + cfix.p[i].pos, a ); } for( i = 0; i < pfix.n; i++ ) { __mpu_uint64_t ptr; if( resolve_ptr_expression( pfix.p[i].expr, &eqs, &clabels, &dlabels, &ctlabels, &ilabels, &olabels, &tlabels, sizes, &ptr, err, es ) ) goto out; __cpu_code_put_u64( code.p + pfix.p[i].pos, ptr ); } if( apply_cptr_fixups( &data, &dcptr, &eqs, &clabels, code.n, err, es ) || apply_cptr_fixups( &control, &ccptr, &eqs, &clabels, code.n, err, es ) || apply_cptr_fixups( &input, &icptr, &eqs, &clabels, code.n, err, es ) || apply_cptr_fixups( &output, &ocptr, &eqs, &clabels, code.n, err, es ) || apply_cptr_fixups( &trace, &tcptr, &eqs, &clabels, code.n, err, es ) || apply_ptr_data_fixups( &data, &dptr, &eqs, &clabels, &dlabels, &ctlabels, &ilabels, &olabels, &tlabels, sizes, err, es ) || apply_ptr_data_fixups( &control, &ctptr, &eqs, &clabels, &dlabels, &ctlabels, &ilabels, &olabels, &tlabels, sizes, err, es ) || apply_ptr_data_fixups( &input, &iptr, &eqs, &clabels, &dlabels, &ctlabels, &ilabels, &olabels, &tlabels, sizes, err, es ) || apply_ptr_data_fixups( &output, &optr, &eqs, &clabels, &dlabels, &ctlabels, &ilabels, &olabels, &tlabels, sizes, err, es ) || apply_ptr_data_fixups( &trace, &tptr, &eqs, &clabels, &dlabels, &ctlabels, &ilabels, &olabels, &tlabels, sizes, err, es )) goto out; } im->code = code.p; im->code_size = code.n; code.p = 0; im->data = data.p; im->data_size = data.n; data.p = 0; im->control = control.p; im->control_size = control.n; control.p = 0; im->input = input.p; im->input_size = input.n; input.p = 0; im->output = output.p; im->output_size = output.n; output.p = 0; im->trace = trace.p; im->trace_size = trace.n; trace.p = 0; im->has_init = init_seen; im->init_entry = start_entry; im->has_life = 1; im->life_entry = life_entry; im->has_fini = fini_seen; im->fini_entry = fini_entry; rc = 0; out: free( scan ); free( copy ); free( code.p ); free( data.p ); free( control.p ); free( input.p ); free( output.p ); free( trace.p ); free( clabels.p ); free( dlabels.p ); free( ctlabels.p ); free( ilabels.p ); free( olabels.p ); free( tlabels.p ); free( dfix.p ); free( cfix.p ); free( pfix.p ); free( dcptr.p ); free( ccptr.p ); free( icptr.p ); free( ocptr.p ); free( tcptr.p ); free( dptr.p ); free( ctptr.p ); free( iptr.p ); free( optr.p ); free( tptr.p ); free( eqs.p ); return( rc ); } void cpu_asm_image_free( cpu_asm_image *im ) { size_t i; if( !im ) return; free( im->code ); free( im->data ); free( im->control ); free( im->input ); free( im->output ); free( im->trace ); for( i = 0; i < im->dyn_symbol_count; i++ ) free( im->dyn_symbols[i].name ); free( im->dyn_symbols ); free( im->dyn_relocs ); memset( im, 0, sizeof( *im ) ); } /* ------------------------------------------------------------------------- * Relocatable assembler path -- MELF logical sections. * * ET_REL keeps .init/.text/.fini distinct and stores explicit RELA-like * semantic records in cpu_asm_object. cpu-object.c maps this logical object * one-to-one onto MELF sections/symbols/RELA records. * ------------------------------------------------------------------------- */ static int obj_symbol_defined( const cpu_asm_object *o, const char *name, size_t *idx ) { size_t i; for( i = 0; i < o->symbol_count; i++ ) if( !strcmp( o->symbols[i].name, name ) && o->symbols[i].section != CPU_OBJ_SEC_UNDEF ) { if( idx ) * idx = i; return( 1 ); } return( 0 ); } static int obj_find_symbol( const cpu_asm_object *o, const char *name, size_t *idx ) { size_t i; for( i = 0; i < o->symbol_count; i++ ) if( !strcmp( o->symbols[i].name, name ) ) { if( idx ) * idx = i; return( 0 ); } return( -1 ); } static int obj_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; size_t i; if( !obj_find_symbol( o, name, &i ) ) { cpu_asm_symbol *x = &o->symbols[i]; if( x->section != CPU_OBJ_SEC_UNDEF && section != CPU_OBJ_SEC_UNDEF ) { snprintf( err, es, "duplicate object symbol: %s", name ); return( -1 ); } if( x->section == CPU_OBJ_SEC_UNDEF && section != CPU_OBJ_SEC_UNDEF ) { x->section = section; x->value = value; x->type = type; } if( binding == CPU_OBJ_BIND_WEAK ) x->binding = CPU_OBJ_BIND_WEAK; else if( binding == CPU_OBJ_BIND_GLOBAL && x->binding != CPU_OBJ_BIND_WEAK ) x->binding = CPU_OBJ_BIND_GLOBAL; return( 0 ); } 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 obj_add_reloc( cpu_asm_object *o, unsigned section, unsigned type, size_t sym, __mpu_uint64_t off, __mpu_int64_t addend, char *err, size_t es) { cpu_asm_reloc *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 = sym; o->relocs[o->reloc_count].offset = off; o->relocs[o->reloc_count].addend = addend; o->reloc_count++; return( 0 ); } static int obj_label_lookup( const char *name, const labels_t *init, const labels_t *text, const labels_t *fini, const labels_t *data, const labels_t *bss, const labels_t *control, const labels_t *input, const labels_t *output, const labels_t *trace, unsigned *section, __mpu_uint64_t *value) { if( init && !findlabel( init, name, value ) ) { *section = CPU_OBJ_SEC_INIT; return( 0 ); } if( text && !findlabel( text, name, value ) ) { *section = CPU_OBJ_SEC_TEXT; return( 0 ); } if( fini && !findlabel( fini, name, value ) ) { *section = CPU_OBJ_SEC_FINI; return( 0 ); } if( data && !findlabel( data, name, value ) ) { *section = CPU_OBJ_SEC_DATA; return( 0 ); } if( bss && !findlabel( bss, name, value ) ) { *section = CPU_OBJ_SEC_BSS; return( 0 ); } if( control && !findlabel( control, name, value ) ) { *section = CPU_OBJ_SEC_CONTROL; return( 0 ); } if( input && !findlabel( input, name, value ) ) { *section = CPU_OBJ_SEC_INPUT; return( 0 ); } if( output && !findlabel( output, name, value ) ) { *section = CPU_OBJ_SEC_OUTPUT; return( 0 ); } if( trace && !findlabel( trace, name, value ) ) { *section = CPU_OBJ_SEC_TRACE; return( 0 ); } return( -1 ); } static int obj_name_defined_any( const char *name, const labels_t *init, const labels_t *text, const labels_t *fini, const labels_t *data, const labels_t *bss, const labels_t *control, const labels_t *input, const labels_t *output, const labels_t *trace) { unsigned s; __mpu_uint64_t v; return( !obj_label_lookup( name, init, text, fini, data, bss, control, input, output, trace, &s, &v ) ); } static int reloc_expression_melf( const char *expr, equs_t *eqs, const symdecls_t *decls, const labels_t *init, const labels_t *text, const labels_t *fini, const labels_t *data, const labels_t *bss, const labels_t *control, const labels_t *input, const labels_t *output, const labels_t *trace, char sym[ASM_NAME_MAX], int *has_sym, __mpu_int64_t *addend, __mpu_uint64_t *absolute, char *err, size_t es) { const char *p = expr; char found[ASM_NAME_MAX] = ""; int seen = 0; while( *p ) { if( isdigit( (unsigned char)*p ) ) { char *end = 0; errno = 0; (void)strtoull( p, &end, 0 ); if( end && end > p ) { p = end; continue; } } if( symfirst( (unsigned char)*p ) ) { char n[ASM_NAME_MAX]; size_t k = 0, ei, di; unsigned sec; __mpu_uint64_t val; while( symrest( (unsigned char)*p ) ) { if( k + 1 >= sizeof( n ) ) { snprintf( err, es, "symbol name too long" ); return( -1 ); } n[k++] = *p++; } n[k] = 0; if( !findequ( eqs, n, &ei ) ) continue; if( !obj_label_lookup( n, init, text, fini, data, bss, control, input, output, trace, &sec, &val ) || !finddecl( decls, n, &di ) ) { if( !seen ) { strcpy( found, n ); seen = 1; } else if( strcmp( found, n ) ) { snprintf( err, es, "relocatable expression contains more than one symbol: %s", expr ); return( -1 ); } continue; } snprintf( err, es, "unknown symbol in relocatable expression: %s", n ); return( -1 ); } p++; } if( !seen ) { asm_i128 v; if( eval_expression( expr, eqs, 0, &v, err, es ) ) return( -1 ); if( !i128_unsigned_ok( v ) ) { snprintf( err, es, "absolute expression out of unsigned 64-bit range: %s", expr ); return( -1 ); } *has_sym = 0; *absolute = i128_unsigned( v ); return( 0 ); } else { labels_t fake = { 0 }; label_t one; asm_i128 v0, v1, d; memset( &one, 0, sizeof( one ) ); strcpy( one.name, found ); fake.p = &one; fake.n = fake.cap = 1; one.addr = 0; if( eval_expression( expr, eqs, &fake, &v0, err, es ) ) return( -1 ); one.addr = 1; if( eval_expression( expr, eqs, &fake, &v1, err, es ) ) return( -1 ); if( i128_sub( v1, v0, &d ) ) { snprintf( err, es, "expression overflow" ); return( -1 ); } if( i128_eqsmall( d, 0 ) ) { if( !i128_unsigned_ok( v0 ) ) { snprintf( err, es, "absolute expression out of unsigned 64-bit range: %s", expr ); return( -1 ); } *has_sym = 0; *absolute = i128_unsigned( v0 ); return( 0 ); } if( !i128_eqsmall( d, 1 ) ) { snprintf( err, es, "relocatable expression must be SYMBOL + constant: %s", expr ); return( -1 ); } if( !i128_signed_ok( v0 ) ) { snprintf( err, es, "relocation addend out of signed 64-bit range: %s", expr ); return( -1 ); } strcpy( sym, found ); *has_sym = 1; *addend = i128_signed( v0 ); return( 0 ); } } static int process_obj_fixups_melf( cpu_asm_object *o, buf_t *src, unsigned srcsec, const fixups_t *fx, unsigned type, equs_t *eqs, const symdecls_t *decls, const labels_t *init, const labels_t *text, const labels_t *fini, const labels_t *data, const labels_t *bss, const labels_t *control, const labels_t *input, const labels_t *output, const labels_t *trace, char *err, size_t es) { size_t i; for( i = 0; i < fx->n; i++ ) { char name[ASM_NAME_MAX]; int hs = 0; __mpu_int64_t add = 0; __mpu_uint64_t abs = 0, ptr; size_t si; if( reloc_expression_melf( fx->p[i].expr, eqs, decls, init, text, fini, data, bss, control, input, output, trace, name, &hs, &add, &abs, err, es ) ) return( -1 ); if( hs ) { if( obj_find_symbol( o, name, &si ) ) { snprintf( err, es, "internal assembler symbol lookup failed: %s", name ); return( -1 ); } if( o->symbols[si].section != CPU_OBJ_SEC_UNDEF ) { unsigned ts = o->symbols[si].section; if( type == CPU_RELOC_CODE64 && !cpu_obj_sec_is_program( ts ) ) { snprintf( err, es, "CODE64 relocation target is not PROGRAM: %s", name ); return( -1 ); } if( type == CPU_RELOC_DATA64 && ts != CPU_OBJ_SEC_DATA && ts != CPU_OBJ_SEC_BSS ) { snprintf( err, es, "ADDR64 relocation target is not DATA: %s", name ); return( -1 ); } if( type == CPU_RELOC_CPTR64_DATA && !cpu_obj_sec_is_program( ts ) ) { snprintf( err, es, ".cptr relocation target is not PROGRAM: %s", name ); return( -1 ); } } if( obj_add_reloc( o, srcsec, type, si, fx->p[i].pos, add, err, es ) ) return( -1 ); continue; } switch( type ) { case CPU_RELOC_CODE64: case CPU_RELOC_DATA64: __cpu_code_put_u64( src->p + fx->p[i].pos, abs ); break; case CPU_RELOC_PTR64_CODE: if( __cpu_ptr_make( &ptr, CPU_PTR_SEG_DATA, abs ) ) { snprintf( err, es, "absolute LEA pointer cannot be encoded: %s", fx->p[i].expr ); return( -1 ); } __cpu_code_put_u64( src->p + fx->p[i].pos, ptr ); break; case CPU_RELOC_PTR64_DATA: if( __cpu_ptr_make( &ptr, CPU_PTR_SEG_DATA, abs ) ) { snprintf( err, es, "absolute .ptr cannot be encoded: %s", fx->p[i].expr ); return( -1 ); } __cpu_data_put_u64( src->p + fx->p[i].pos, ptr ); break; case CPU_RELOC_CPTR64_DATA: if( __cpu_ptr_make( &ptr, CPU_PTR_SEG_PROGRAM, abs ) ) { snprintf( err, es, "absolute .cptr cannot be encoded: %s", fx->p[i].expr ); return( -1 ); } __cpu_data_put_u64( src->p + fx->p[i].pos, ptr ); break; default: snprintf( err, es, "internal unknown relocation type" ); return( -1 ); } } return( 0 ); } static unsigned declared_binding( const symdecls_t *d, const char *name ) { size_t i; if( !strcmp( name, "_start" ) || !strcmp( name, "_life" ) || !strcmp( name, "_fini" ) ) return( CPU_OBJ_BIND_GLOBAL ); if( !finddecl( d, name, &i ) ) { if( d->p[i].is_weak ) return( CPU_OBJ_BIND_WEAK ); if( d->p[i].is_global ) return( CPU_OBJ_BIND_GLOBAL ); } return( CPU_OBJ_BIND_LOCAL ); } static int build_obj_symbols_melf( cpu_asm_object *o, const symdecls_t *decls, const labels_t *init, const labels_t *text, const labels_t *fini, const labels_t *data, const labels_t *bss, const labels_t *control, const labels_t *input, const labels_t *output, const labels_t *trace, char *err, size_t es) { const labels_t *ls[9] = { init, text, fini, data, bss, control, input, output, trace }; const unsigned sec[9] = { 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 }; size_t j, i; for( j = 0; j < 9; j++ ) for( i = 0; i < ls[j]->n; i++ ) { unsigned type = cpu_obj_sec_is_program( sec[j] ) ? CPU_OBJ_TYPE_FUNC : CPU_OBJ_TYPE_OBJECT; if( obj_add_symbol( o, ls[j]->p[i].name, sec[j], declared_binding( decls, ls[j]->p[i].name ), type, ls[j]->p[i].addr, err, es ) ) return( -1 ); } for( i = 0; i < decls->n; i++ ) if( !obj_symbol_defined( o, decls->p[i].name, 0 ) ) { const char *name = decls->p[i].name; unsigned b = decls->p[i].is_weak ? CPU_OBJ_BIND_WEAK : CPU_OBJ_BIND_GLOBAL; unsigned type = ( !strcmp( name, "_start" ) || !strcmp( name, "_life" ) || !strcmp( name, "_fini" ) ) ?CPU_OBJ_TYPE_FUNC:CPU_OBJ_TYPE_NOTYPE; if( obj_add_symbol( o, name, CPU_OBJ_SEC_UNDEF, b, type, 0, err, es ) ) return( -1 ); } return( 0 ); } int cpu_assemble_object( const char *src, cpu_asm_object *o, char *err, size_t es ) { char *copy = 0, *scan = 0, *save = 0, *line; buf_t init = { 0 } , text = { 0 } , fini = { 0 } , data = { 0 } , control = { 0 } , input = { 0 } , output = { 0 } , trace = { 0 }; size_t bss_size = 0; labels_t ilabs = { 0 } , tlabs = { 0 } , flabs = { 0 } , dlabs = { 0 } , blabs = { 0 } , ctlabs = { 0 } , inlabs = { 0 } , olabs = { 0 } , trlabs = { 0 }; fixups_t idfix = { 0 } , icfix = { 0 } , ipfix = { 0 } , tdfix = { 0 } , tcfix = { 0 } , tpfix = { 0 } , fdfix = { 0 } , fcfix = { 0 } , fpfix = { 0 }; fixups_t dcptr = { 0 } , ccptr = { 0 } , incptr = { 0 } , ocptr = { 0 } , trcptr = { 0 }; fixups_t dptr = { 0 } , ctptr = { 0 } , inptr = { 0 } , optr = { 0 } , trptr = { 0 }; __mpu_uint64_t dalign = 1, balign = 1, ctalign = 1, inalign = 1, oalign = 1, tralign = 1; equs_t eqs = { 0 }; symdecls_t decls = { 0 }; int section = ASM_SEC_NONE, line_no = 0, rc = -1; int init_seen = 0, text_seen = 0, fini_seen = 0, bss_seen = 0, pseudo_seen = 0; if( !src || !o ) return( -1 ); memset( o, 0, sizeof( *o ) ); if( !mpu_utf8valid( (const __mpu_char8_t *)src ) ) { snprintf( err, es, "assembler source is not valid UTF-8" ); return( -1 ); } scan = strdup( src ); if( !scan ) return( -1 ); for( line = strtok_r( scan, "\n", &save ); line; line = strtok_r( NULL, "\n", &save ) ) { int er; line_no++; strip_comment( line ); if( !*trim( line ) ) continue; er = collect_equ_line( line, &eqs, line_no, err, es ); if( er < 0 ) goto out; } free( scan ); scan = 0; save = 0; { size_t i; asm_i128 v; for( i = 0; i < eqs.n; i++ ) if( equ_value( &eqs, i, &v, err, es ) ) { char t[256]; snprintf( t, sizeof( t ), "line %d: %s", eqs.p[i].line, err ); snprintf( err, es, "%s", t ); goto out; } } scan = strdup( src ); if( !scan ) goto out; line_no = 0; for( line = strtok_r( scan, "\n", &save ); line; line = strtok_r( NULL, "\n", &save ) ) { int er; line_no++; strip_comment( line ); if( !*trim( line ) ) continue; er = collect_decl_line( line, &decls, line_no, err, es ); if( er < 0 ) goto out; } free( scan ); scan = 0; save = 0; { size_t i, j; for( i = 0; i < eqs.n; i++ ) for( j = 0; j < decls.n; j++ ) if( !strcmp( eqs.p[i].name, decls.p[j].name ) ) { snprintf( err, es, "symbol declared global/extern/weak conflicts with .equ: %s", eqs.p[i].name ); goto out; } } copy = strdup( src ); if( !copy ) goto out; line_no = 0; for( line = strtok_r( copy, "\n", &save ); line; line = strtok_r( NULL, "\n", &save ) ) { char *p, *colon, *mn, *rest, *q; buf_t *cb = 0, *db = 0; labels_t *labs = 0; fixups_t *df = 0, *cf = 0, *pf = 0, *cpfx = 0, *gptr = 0; __mpu_uint64_t *salign = 0; int in_bss = 0; line_no++; strip_comment( line ); p = trim( line ); if( !*p ) continue; q = p; while( *q && !isspace( (unsigned char)*q ) ) q++; if( ( (size_t)( q - p ) == 4 && !strncasecmp( p, ".equ", 4 ) ) || ( (size_t)( q - p ) == 7 && !strncasecmp( p, ".global", 7 ) ) || ( (size_t)( q - p ) == 6 && !strncasecmp( p, ".globl", 6 ) ) || ( (size_t)( q - p ) == 7 && !strncasecmp( p, ".extern", 7 ) ) || ( (size_t)( q - p ) == 5 && !strncasecmp( p, ".weak", 5 ) ) ) continue; if( streqi( p, ".init" ) ) { init_seen = 1; section = ASM_SEC_INIT; continue; } if( streqi( p, ".text" ) ) { text_seen = 1; section = ASM_SEC_TEXT; continue; } if( streqi( p, ".fini" ) ) { fini_seen = 1; section = ASM_SEC_FINI; continue; } if( streqi( p, ".data" ) ) { section = ASM_SEC_DATA; continue; } if( streqi( p, ".bss" ) ) { bss_seen = 1; section = ASM_SEC_BSS; continue; } if( streqi( p, ".control" ) ) { section = ASM_SEC_CONTROL; continue; } if( streqi( p, ".input" ) ) { section = ASM_SEC_INPUT; continue; } if( streqi( p, ".output" ) ) { section = ASM_SEC_OUTPUT; continue; } if( streqi( p, ".trace" ) ) { section = ASM_SEC_TRACE; continue; } switch( section ) { case ASM_SEC_INIT: cb = &init; labs = &ilabs; df = &idfix; cf = &icfix; pf = &ipfix; break; case ASM_SEC_TEXT: cb = &text; labs = &tlabs; df = &tdfix; cf = &tcfix; pf = &tpfix; break; case ASM_SEC_FINI: cb = &fini; labs = &flabs; df = &fdfix; cf = &fcfix; pf = &fpfix; break; case ASM_SEC_DATA: db = &data; labs = &dlabs; cpfx = &dcptr; gptr = &dptr; salign = &dalign; break; case ASM_SEC_BSS: labs = &blabs; salign = &balign; in_bss = 1; break; case ASM_SEC_CONTROL: db = &control; labs = &ctlabs; cpfx = &ccptr; gptr = &ctptr; salign = &ctalign; break; case ASM_SEC_INPUT: db = &input; labs = &inlabs; cpfx = &incptr; gptr = &inptr; salign = &inalign; break; case ASM_SEC_OUTPUT: db = &output; labs = &olabs; cpfx = &ocptr; gptr = &optr; salign = &oalign; break; case ASM_SEC_TRACE: db = &trace; labs = &trlabs; cpfx = &trcptr; gptr = &trptr; salign = &tralign; break; default:break; } colon = label_colon( p ); if( colon ) { size_t ei; char *name; __mpu_uint64_t here; *colon = 0; name = trim( p ); if( !valid_symbol_name( name ) ) { snprintf( err, es, "line %d: bad label name", line_no ); goto out; } if( !findequ( &eqs, name, &ei ) ) { snprintf( err, es, "line %d: label conflicts with .equ symbol: %s", line_no, name ); goto out; } if( !strcmp( name, "_start_life" ) ) { if( section != ASM_SEC_TEXT ) { snprintf( err, es, "line %d: _start_life is valid only in .text", line_no ); goto out; } if( pseudo_seen || obj_name_defined_any( "_start", &ilabs, &tlabs, &flabs, &dlabs, &blabs, &ctlabs, &inlabs, &olabs, &trlabs ) || obj_name_defined_any( "_life", &ilabs, &tlabs, &flabs, &dlabs, &blabs, &ctlabs, &inlabs, &olabs, &trlabs ) ) { snprintf( err, es, "line %d: _start_life conflicts with an existing _start/_life definition", line_no ); goto out; } here = (__mpu_uint64_t)text.n; if( addlabel( &tlabs, "_start", here ) || addlabel( &tlabs, "_life", here ) ) { snprintf( err, es, "line %d: cannot define _start_life", line_no ); goto out; } pseudo_seen = 1; } else { if( obj_name_defined_any( name, &ilabs, &tlabs, &flabs, &dlabs, &blabs, &ctlabs, &inlabs, &olabs, &trlabs ) ) { snprintf( err, es, "line %d: duplicate label: %s", line_no, name ); goto out; } if( !labs ) { snprintf( err, es, "line %d: label outside a valid section", line_no ); goto out; } here = cb ? (__mpu_uint64_t)cb->n : in_bss ? (__mpu_uint64_t)bss_size : (__mpu_uint64_t)db->n; if( addlabel( labs, name, here ) ) { snprintf( err, es, "line %d: bad/duplicate label", line_no ); goto out; } if( !strcmp( name, "_start" ) && section != ASM_SEC_INIT && section != ASM_SEC_TEXT ) { snprintf( err, es, "line %d: _start must be in .init or .text", line_no ); goto out; } if( !strcmp( name, "_life" ) && section != ASM_SEC_TEXT ) { snprintf( err, es, "line %d: _life must be in .text", line_no ); goto out; } if( !strcmp( name, "_fini" ) && section != ASM_SEC_FINI ) { snprintf( err, es, "line %d: _fini must be in .fini", line_no ); goto out; } } p = trim( colon + 1 ); if( !*p ) continue; } if( in_bss ) { if( bssdir( &bss_size, p, salign, &eqs, err, es ) ) { char t[256]; snprintf( t, sizeof( t ), "line %d: %s", line_no, err ); snprintf( err, es, "%s", t ); goto out; } continue; } if( db ) { if( datadir( db, p, cpfx, gptr, salign, &eqs, err, es ) ) { char t[256]; snprintf( t, sizeof( t ), "line %d: %s", line_no, err ); snprintf( err, es, "%s", t ); goto out; } continue; } if( !cb ) { snprintf( err, es, "line %d: select a code or data section", line_no ); goto out; } mn = p; while( *p && !isspace( (unsigned char)*p ) ) p++; if( *p ) { *p++ = 0; rest = trim( p ); } else rest = (char *)""; if( streqi( mn, "LDI" ) ) { if( !*rest || emit_ldi( cb, &eqs, rest, err, es ) ) goto badline_obj; } else if( emit_instruction( cb, df, cf, pf, &eqs, mn, rest, err, es ) ) goto badline_obj; continue; badline_obj: { char t[256]; snprintf( t, sizeof( t ), "line %d: %s", line_no, err ); snprintf( err, es, "%s", t ); goto out; } } if( init_seen && obj_name_defined_any( "_start", 0, &tlabs, 0, 0, 0, 0, 0, 0, 0 ) ) { snprintf( err, es, "_start must be in .init when .init exists" ); goto out; } if( init_seen && pseudo_seen ) { snprintf( err, es, "_start_life is invalid when .init exists" ); goto out; } if( build_obj_symbols_melf( o, &decls, &ilabs, &tlabs, &flabs, &dlabs, &blabs, &ctlabs, &inlabs, &olabs, &trlabs, err, es ) ) goto out; #define PF(SRC,SEC,FX,TYPE) process_obj_fixups_melf(o,&(SRC),(SEC),&(FX),(TYPE),&eqs,&decls,&ilabs,&tlabs,&flabs,&dlabs,&blabs,&ctlabs,&inlabs,&olabs,&trlabs,err,es) if( PF( init, CPU_OBJ_SEC_INIT, idfix, CPU_RELOC_DATA64 ) || PF( init, CPU_OBJ_SEC_INIT, icfix, CPU_RELOC_CODE64 ) || PF( init, CPU_OBJ_SEC_INIT, ipfix, CPU_RELOC_PTR64_CODE ) || PF( text, CPU_OBJ_SEC_TEXT, tdfix, CPU_RELOC_DATA64 ) || PF( text, CPU_OBJ_SEC_TEXT, tcfix, CPU_RELOC_CODE64 ) || PF( text, CPU_OBJ_SEC_TEXT, tpfix, CPU_RELOC_PTR64_CODE ) || PF( fini, CPU_OBJ_SEC_FINI, fdfix, CPU_RELOC_DATA64 ) || PF( fini, CPU_OBJ_SEC_FINI, fcfix, CPU_RELOC_CODE64 ) || PF( fini, CPU_OBJ_SEC_FINI, fpfix, CPU_RELOC_PTR64_CODE ) || PF( data, CPU_OBJ_SEC_DATA, dcptr, CPU_RELOC_CPTR64_DATA ) || PF( control, CPU_OBJ_SEC_CONTROL, ccptr, CPU_RELOC_CPTR64_DATA ) || PF( input, CPU_OBJ_SEC_INPUT, incptr, CPU_RELOC_CPTR64_DATA ) || PF( output, CPU_OBJ_SEC_OUTPUT, ocptr, CPU_RELOC_CPTR64_DATA ) || PF( trace, CPU_OBJ_SEC_TRACE, trcptr, CPU_RELOC_CPTR64_DATA ) || PF( data, CPU_OBJ_SEC_DATA, dptr, CPU_RELOC_PTR64_DATA ) || PF( control, CPU_OBJ_SEC_CONTROL, ctptr, CPU_RELOC_PTR64_DATA ) || PF( input, CPU_OBJ_SEC_INPUT, inptr, CPU_RELOC_PTR64_DATA ) || PF( output, CPU_OBJ_SEC_OUTPUT, optr, CPU_RELOC_PTR64_DATA ) || PF( trace, CPU_OBJ_SEC_TRACE, trptr, CPU_RELOC_PTR64_DATA )) goto out; #undef PF o->init = init.p; o->init_size = init.n; o->init_align = 1; o->has_init = init_seen; init.p = 0; o->text = text.p; o->text_size = text.n; o->text_align = 1; o->has_text = text_seen; text.p = 0; o->fini = fini.p; o->fini_size = fini.n; o->fini_align = 1; o->has_fini = fini_seen; fini.p = 0; o->data = data.p; o->data_size = data.n; o->data_align = dalign; o->has_data = ( data.n != 0 ); data.p = 0; o->bss_size = bss_size; o->bss_align = balign; o->has_bss = bss_seen; o->control = control.p; o->control_size = control.n; o->control_align = ctalign; o->has_control = ( control.n != 0 ); control.p = 0; o->input = input.p; o->input_size = input.n; o->input_align = inalign; o->has_input = ( input.n != 0 ); input.p = 0; o->output = output.p; o->output_size = output.n; o->output_align = oalign; o->has_output = ( output.n != 0 ); output.p = 0; o->trace = trace.p; o->trace_size = trace.n; o->trace_align = tralign; o->has_trace = ( trace.n != 0 ); trace.p = 0; 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( scan ); free( copy ); free( init.p ); free( text.p ); free( fini.p ); free( data.p ); free( control.p ); free( input.p ); free( output.p ); free( trace.p ); free( ilabs.p ); free( tlabs.p ); free( flabs.p ); free( dlabs.p ); free( blabs.p ); free( ctlabs.p ); free( inlabs.p ); free( olabs.p ); free( trlabs.p ); free( idfix.p ); free( icfix.p ); free( ipfix.p ); free( tdfix.p ); free( tcfix.p ); free( tpfix.p ); free( fdfix.p ); free( fcfix.p ); free( fpfix.p ); free( dcptr.p ); free( ccptr.p ); free( incptr.p ); free( ocptr.p ); free( trcptr.p ); free( dptr.p ); free( ctptr.p ); free( inptr.p ); free( optr.p ); free( trptr.p ); free( eqs.p ); free( decls.p ); if( rc ) cpu_asm_object_free( o ); return( rc ); } void cpu_asm_object_free( cpu_asm_object *o ) { size_t i; if( !o ) return; free( o->init ); free( o->text ); free( o->fini ); free( o->data ); free( o->control ); free( o->input ); free( o->output ); free( o->trace ); free( o->code ); for( i = 0; i < o->symbol_count; i++ ) free( o->symbols[i].name ); free( o->symbols ); free( o->relocs ); memset( o, 0, sizeof( *o ) ); }