#include #include #include #include #include #include "mcpu-asm.h" #include "mcpu-link.h" #include "mcpu-dyn.h" static int add_symbol( cpu_asm_object *o, const char *name, unsigned sec, unsigned bind, unsigned type, __mpu_uint64_t value ) { cpu_asm_symbol *q = (cpu_asm_symbol *)realloc( o->symbols, ( o->symbol_count + 1 ) * sizeof( *q ) ); if( !q ) return( -1 ); o->symbols = q; memset( &o->symbols[o->symbol_count], 0, sizeof( *q ) ); o->symbols[o->symbol_count].name = strdup( name ); if( !o->symbols[o->symbol_count].name ) return( -1 ); o->symbols[o->symbol_count].section = sec; o->symbols[o->symbol_count].binding = bind; o->symbols[o->symbol_count].type = type; o->symbols[o->symbol_count].value = value; ++o->symbol_count; return( 0 ); } static int add_reloc( cpu_asm_object *o, unsigned sec, unsigned type, size_t sym, __mpu_uint64_t off ) { cpu_asm_reloc *q = (cpu_asm_reloc *)realloc( o->relocs, ( o->reloc_count + 1 ) * sizeof( *q ) ); if( !q ) return( -1 ); o->relocs = q; o->relocs[o->reloc_count] = ( cpu_asm_reloc ) { sec, type, sym, off, 0 }; ++o->reloc_count; return( 0 ); } static void init_text( cpu_asm_object *o, size_t n ) { memset( o, 0, sizeof( *o ) ); o->text = (__mpu_UNIT_t *)calloc( n, 1 ); o->text_size = n; o->text_align = 1; o->has_text = 1; } static int tempname( char *buf, size_t n, const char *tag ) { int fd; if( snprintf( buf, n, "/tmp/libcpu-%s-XXXXXX", tag ) < 0 ) return( -1 ); fd = mkstemp( buf ); if( fd < 0 ) return( -1 ); close( fd ); return( 0 ); } int main( void ) { cpu_asm_object mo = { 0 } , ao = { 0 } , bo = { 0 } , wo = { 0 } , co = { 0 }; cpu_asm_image mi = { 0 } , ai = { 0 } , bi = { 0 } , wi = { 0 } , ci = { 0 } , si = { 0 }; cpu_exec_options opt = { 256, 0, 0, 0 }; cpu_exec_loaded ld; const char *needed_a[] = { "liba.so.1" }; cpu_dyn_metadata v3_main = { 0, 0, 0 }; cpu_dyn_metadata v3_liba = { "liba.so.1", 0, 0 }; cpu_dyn_metadata v3_needed = { 0, needed_a, 1 }; char me[128], ae[128], be[128], we[128], ce[128]; char v3me[128], v3ae[128], v3ne[128], pre[520], err[512]; char *av[] = { "main-program" }; int rc = 1; __mpu_init(); init_text( &mo, 24 ); init_text( &ao, 16 ); init_text( &bo, 16 ); init_text( &wo, 16 ); init_text( &co, 16 ); ao.data = (__mpu_UNIT_t *)calloc( 16, 1 ); ao.data_size = 16; ao.data_align = 8; ao.has_data = 1; if( !mo.text || !ao.text || !ao.data || !bo.text || !wo.text || !co.text ) goto out; if( add_symbol( &mo, "_start", CPU_OBJ_SEC_TEXT, CPU_OBJ_BIND_GLOBAL, CPU_OBJ_TYPE_FUNC, 0 ) || add_symbol( &mo, "_life", CPU_OBJ_SEC_TEXT, CPU_OBJ_BIND_GLOBAL, CPU_OBJ_TYPE_FUNC, 0 ) || add_symbol( &mo, "foo", CPU_OBJ_SEC_TEXT, CPU_OBJ_BIND_GLOBAL, CPU_OBJ_TYPE_FUNC, 16 ) || add_symbol( &mo, "shared_data", CPU_OBJ_SEC_UNDEF, CPU_OBJ_BIND_WEAK, CPU_OBJ_TYPE_OBJECT, 0 ) || add_reloc( &mo, CPU_OBJ_SEC_TEXT, CPU_RELOC_PTR64_CODE, 3, 0 ) || add_reloc( &mo, CPU_OBJ_SEC_TEXT, CPU_RELOC_CODE64, 2, 8 ) || add_symbol( &ao, "foo", CPU_OBJ_SEC_TEXT, CPU_OBJ_BIND_GLOBAL, CPU_OBJ_TYPE_FUNC, 4 ) || add_symbol( &ao, "shared_data", CPU_OBJ_SEC_DATA, CPU_OBJ_BIND_GLOBAL, CPU_OBJ_TYPE_OBJECT, 0 ) || add_symbol( &ao, "local_code", CPU_OBJ_SEC_TEXT, CPU_OBJ_BIND_LOCAL, CPU_OBJ_TYPE_FUNC, 12 ) || add_reloc( &ao, CPU_OBJ_SEC_TEXT, CPU_RELOC_CODE64, 2, 0 ) || add_symbol( &ao, "local_data", CPU_OBJ_SEC_DATA, CPU_OBJ_BIND_LOCAL, CPU_OBJ_TYPE_OBJECT, 8 ) || add_reloc( &ao, CPU_OBJ_SEC_DATA, CPU_RELOC_PTR64_DATA, 3, 0 ) || add_symbol( &bo, "foo", CPU_OBJ_SEC_TEXT, CPU_OBJ_BIND_GLOBAL, CPU_OBJ_TYPE_FUNC, 6 ) || add_symbol( &bo, "helper", CPU_OBJ_SEC_TEXT, CPU_OBJ_BIND_GLOBAL, CPU_OBJ_TYPE_FUNC, 10 ) || add_symbol( &wo, "foo", CPU_OBJ_SEC_TEXT, CPU_OBJ_BIND_WEAK, CPU_OBJ_TYPE_FUNC, 8 ) || add_symbol( &co, "helper", CPU_OBJ_SEC_UNDEF, CPU_OBJ_BIND_GLOBAL, CPU_OBJ_TYPE_FUNC, 0 ) || add_reloc( &co, CPU_OBJ_SEC_TEXT, CPU_RELOC_CODE64, 0, 0 )) goto out; if( cpu_link_objects( &mo, 1, &si, err, sizeof( err ) ) ) { fprintf( stderr, "static compatibility link: %s\n", err ); goto out; } if( si.dyn_symbol_count || si.dyn_reloc_count || __cpu_code_get_u64( si.code ) != 0 || __cpu_code_get_u64( si.code + 8 ) != 16 ) { fprintf( stderr, "ABI-v1 static link compatibility mismatch\n" ); goto out; } if( cpu_link_objects_mode( &mo, 1, &mi, CPU_LINK_DYNAMIC_EXEC, err, sizeof( err ) ) ) { fprintf( stderr, "main link: %s\n", err ); goto out; } if( cpu_link_objects_mode( &ao, 1, &ai, CPU_LINK_SHARED, err, sizeof( err ) ) ) { fprintf( stderr, "a link: %s\n", err ); goto out; } if( cpu_link_objects_mode( &bo, 1, &bi, CPU_LINK_SHARED, err, sizeof( err ) ) ) { fprintf( stderr, "b link: %s\n", err ); goto out; } if( cpu_link_objects_mode( &wo, 1, &wi, CPU_LINK_SHARED, err, sizeof( err ) ) ) { fprintf( stderr, "weak link: %s\n", err ); goto out; } if( cpu_link_objects_mode( &co, 1, &ci, CPU_LINK_SHARED, err, sizeof( err ) ) ) { fprintf( stderr, "cross link: %s\n", err ); goto out; } if( mi.dyn_reloc_count != 2 || mi.dyn_symbol_count != 2 ) { fprintf( stderr, "dynamic metadata count mismatch\n" ); goto out; } if( ai.dyn_reloc_count != 2 || ai.dyn_symbol_count != 4 ) { fprintf( stderr, "LOCAL ET_DYN metadata count mismatch\n" ); goto out; } if( tempname( me, sizeof( me ), "main" ) || tempname( ae, sizeof( ae ), "a" ) || tempname( be, sizeof( be ), "b" ) || tempname( we, sizeof( we ), "weak" ) || tempname( ce, sizeof( ce ), "cross" ) || tempname( v3me, sizeof( v3me ), "v3-main" ) || tempname( v3ae, sizeof( v3ae ), "v3-a" ) || tempname( v3ne, sizeof( v3ne ), "v3-needed" ) ) goto out; if( cpu_dyn_write( me, &mi, &opt, 0, err, sizeof( err ) ) || cpu_dyn_write( ae, &ai, 0, 1, err, sizeof( err ) ) || cpu_dyn_write( be, &bi, 0, 1, err, sizeof( err ) ) || cpu_dyn_write( we, &wi, 0, 1, err, sizeof( err ) ) || cpu_dyn_write( ce, &ci, 0, 1, err, sizeof( err ) ) ) { fprintf( stderr, "write ABI-v2: %s\n", err ); goto files; } if( cpu_dyn_write_abi3(v3me, &mi, &opt, 0, &v3_main, err, sizeof( err )) || cpu_dyn_write_abi3(v3ae, &ai, 0, 1, &v3_liba, err, sizeof( err )) || cpu_dyn_write_abi3(v3ne, &mi, &opt, 0, &v3_needed, err, sizeof( err )) ) { fprintf( stderr, "write ABI-v3: %s\n", err ); goto files; } __cpu_init(); if( cpu_dyn_load( me, NULL, 1, av, &ld, err, sizeof( err ) ) ) { fprintf( stderr, "load main: %s\n", err ); __cpu_free_context(); goto files; } if( __cpu_code_get_u64( __cpu_program_ptr() + 8 ) != 16 || __cpu_code_get_u64( __cpu_program_ptr() ) != 0 ) { fprintf( stderr, "main/weak definition mismatch\n" ); __cpu_free_context(); goto files; } __cpu_free_context(); snprintf( pre, sizeof( pre ), "%s:%s", ae, be ); __cpu_init(); if( cpu_dyn_load( me, pre, 1, av, &ld, err, sizeof( err ) ) ) { fprintf( stderr, "load: %s\n", err ); __cpu_free_context(); goto files; } /* main PROGRAM=24, a=16, b=16; later strong b wins: 24+16+6 = 46. */ if( __cpu_code_get_u64( __cpu_program_ptr() + 8 ) != 46 ) { fprintf( stderr, "interposition mismatch: got %llu\n", (unsigned long long)__cpu_code_get_u64( __cpu_program_ptr() + 8 ) ); __cpu_free_context(); goto files; } /* main has no DATA; loader creates the single process guard and places liba DATA at offset 16. ET_DYN itself contains no per-library guard. */ if( __cpu_code_get_u64( __cpu_program_ptr() ) != 16 ) { fprintf( stderr, "shared DATA base/guard mismatch\n" ); __cpu_free_context(); goto files; } /* LOCAL ET_DYN references are not interposable, but they still require startup rebasing by the final module PROGRAM/DATA base. */ if( __cpu_code_get_u64( __cpu_program_ptr() + 24 ) != 36 ) { fprintf( stderr, "LOCAL ET_DYN CODE64 rebase mismatch\n" ); __cpu_free_context(); goto files; } { __mpu_uint64_t p = 0; if( __cpu_ptr_make( &p, CPU_PTR_SEG_DATA, 24 ) || __cpu_data_get_u64( __cpu_data_ptr() + 16 ) != p ) { fprintf( stderr, "LOCAL ET_DYN PTR64_DATA rebase mismatch\n" ); __cpu_free_context(); goto files; } } __cpu_free_context(); /* A shared object can import from another explicitly preloaded object without DT_NEEDED. cross is placed at 24, b at 40, helper at 40+10. */ snprintf( pre, sizeof( pre ), "%s:%s", ce, be ); __cpu_init(); if( cpu_dyn_load( me, pre, 1, av, &ld, err, sizeof( err ) ) ) { fprintf( stderr, "cross-library load: %s\n", err ); __cpu_free_context(); goto files; } if( __cpu_code_get_u64( __cpu_program_ptr() + 24 ) != 50 ) { fprintf( stderr, "cross-library explicit-preload resolution mismatch\n" ); __cpu_free_context(); goto files; } __cpu_free_context(); /* A later WEAK never preempts an earlier strong GLOBAL, including main. */ __cpu_init(); if( cpu_dyn_load( me, we, 1, av, &ld, err, sizeof( err ) ) ) { fprintf( stderr, "weak load: %s\n", err ); __cpu_free_context(); goto files; } if( __cpu_code_get_u64( __cpu_program_ptr() + 8 ) != 16 ) { fprintf( stderr, "weak incorrectly preempted main GLOBAL\n" ); __cpu_free_context(); goto files; } __cpu_free_context(); /* Strong library a beats later weak: a base=24, foo=4 => 28. */ snprintf( pre, sizeof( pre ), "%s:%s", ae, we ); __cpu_init(); if( cpu_dyn_load( me, pre, 1, av, &ld, err, sizeof( err ) ) ) { fprintf( stderr, "strong/weak load: %s\n", err ); __cpu_free_context(); goto files; } if( __cpu_code_get_u64( __cpu_program_ptr() + 8 ) != 28 ) { fprintf( stderr, "strong/weak binding mismatch\n" ); __cpu_free_context(); goto files; } __cpu_free_context(); /* ABI-v3 without NEEDED keeps the established startup-relocation semantics and can still use the explicit preload compatibility path. */ __cpu_init(); if( cpu_dyn_load( v3me, v3ae, 1, av, &ld, err, sizeof( err ) ) ) { fprintf( stderr, "ABI-v3 explicit-preload load: %s\n", err ); __cpu_free_context(); goto files; } if( __cpu_code_get_u64( __cpu_program_ptr() + 8 ) != 28 ) { fprintf( stderr, "ABI-v3 explicit-preload binding mismatch\n" ); __cpu_free_context(); goto files; } __cpu_free_context(); /* The low-level loader still refuses to ignore unresolved DT_NEEDED. mcpu-run first resolves the graph and then uses cpu_dyn_load_resolved(). */ __cpu_init(); if( cpu_dyn_load( v3ne, NULL, 1, av, &ld, err, sizeof( err ) ) == 0 ) { fprintf( stderr, "low-level ABI-v3 DT_NEEDED unexpectedly ignored\n" ); __cpu_free_context(); goto files; } if( strstr( err, "DT_NEEDED loading is not implemented yet" ) == NULL ) { fprintf( stderr, "unexpected ABI-v3 DT_NEEDED diagnostic: %s\n", err ); __cpu_free_context(); goto files; } __cpu_free_context(); /* A static ABI-v1 executable must not silently accept preload; this is checked by cpu_exec_load_preload runner tests. Here verify no missing explicit library dependency machinery is needed: the list alone resolves. */ printf( "libcpu ABI-v2 compatibility / ABI-v3 metadata regression: PASS\n" ); rc = 0; files: unlink( me ); unlink( ae ); unlink( be ); unlink( we ); unlink( ce ); unlink( v3me ); unlink( v3ae ); unlink( v3ne ); out: cpu_asm_image_free( &si ); cpu_asm_image_free( &mi ); cpu_asm_image_free( &ai ); cpu_asm_image_free( &bi ); cpu_asm_image_free( &wi ); cpu_asm_image_free( &ci ); cpu_asm_object_free( &mo ); cpu_asm_object_free( &ao ); cpu_asm_object_free( &bo ); cpu_asm_object_free( &wo ); cpu_asm_object_free( &co ); __mpu_free_context(); return( rc ); }