diff options
Diffstat (limited to 'tests/tst-dynamic.c')
| -rw-r--r-- | tests/tst-dynamic.c | 407 |
1 files changed, 407 insertions, 0 deletions
diff --git a/tests/tst-dynamic.c b/tests/tst-dynamic.c new file mode 100644 index 0000000..a70d9e1 --- /dev/null +++ b/tests/tst-dynamic.c @@ -0,0 +1,407 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <libmcpu.h> + +#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 ); +} |
