summaryrefslogtreecommitdiff
path: root/tests/tst-dynamic.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/tst-dynamic.c')
-rw-r--r--tests/tst-dynamic.c407
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 );
+}