The problem is that if you do loadlib "/home/rogers/foo/bar/baz"
Parrot_load_lib finds and loads "/home/rogers/foo/bar/baz.so" just fine, but then the library isn't initialized properly because it attempts to look up the address of "Parrot_lib_/home/rogers/foo/bar/baz_load" via dlsym, and similarly if you add an explicit ".so" to the arg. This patch does the following: 1. Computes a trimmed "lib_name" as the part between the last "/" or "\" and the last ".", exclusive, and uses that for constructing entry names for dlsym. 2. Attempts to fix an apparent memory leak, via the addition of line 172 to src/dynext.c. (C heavies, please scrutinize.) 3. Calls Parrot_locate_runtime_file on files with explicit extensions, for completeness. 4. Rewrites the test for a missing extension slightly, so that it won't get confused by a "." in a directory name. (Not specifically tested.) 5. Includes four new test cases for loadlib, with absolute/partial pathnames with/without an extension. There is also this "#ifdef WIN32" business about dropping a "lib" prefix from the name if all else fails; I am guessing that this no longer works, but I have no way of testing it myself, so I haven't tried to resuscitate it. But I'm not sure that it ever worked; seems to me that this would have to be folded into the Parrot_locate_runtime_file library directory search in order to do the right thing. -- Bob Rogers http://rgrjr.dyndns.org/ ------------------------------------------------------------------------ Index: src/dynext.c =================================================================== --- src/dynext.c (revision 8203) +++ src/dynext.c (working copy) @@ -123,26 +123,41 @@ /* =item C<static STRING * -get_path(Interp *interpreter, STRING *lib, void **handle)> +get_path(Interp *interpreter, STRING *lib, void **handle, char **lib_name)> -Return path and handle of a dynamic lib. +Return path and handle of a dynamic lib, setting lib_name to just the filestem +(i.e. without path or extension) as a freshly-allocated C string. =cut */ static STRING * -get_path(Interp *interpreter, STRING *lib, void **handle) +get_path(Interp *interpreter, STRING *lib, void **handle, char **lib_name) { STRING *path; char *full_name, *file_name, *file_w_ext = NULL; + char *tmp_lib_name, *path_end, *ext_start; const char *err; + /* Find the pure library name, without path or extension. */ + file_name = string_to_cstring(interpreter, lib); + tmp_lib_name = file_name; + path_end = strrchr(tmp_lib_name, '/'); + if (! path_end) + path_end = strrchr(tmp_lib_name, '\\'); + if (path_end) + tmp_lib_name = path_end+1; + *lib_name = malloc(strlen(tmp_lib_name)+1); + strcpy(*lib_name, tmp_lib_name); + ext_start = strrchr(*lib_name, '.'); + if (ext_start) + *ext_start = '\0'; + /* - * first try file with extension if it got none + * first, try to add an extension to the file if it has none. */ - file_name = string_to_cstring(interpreter, lib); - if (!strchr(file_name, '.')) { + if (! ext_start) { file_w_ext = malloc(strlen(file_name) + strlen(PARROT_LOAD_EXT) + 1); strcpy(file_w_ext, file_name); @@ -154,6 +169,7 @@ if (*handle) { path = string_from_cstring(interpreter, full_name, 0); string_cstring_free(file_name); + string_cstring_free(full_name); string_cstring_free(file_w_ext); return path; } @@ -207,16 +223,25 @@ } } /* - * then the given file name as is + * finally, try the given file name as is. we still use + * Parrot_locate_runtime_file so that (a) relative pathnames are searched in + * the standard locations, and (b) the angle of the slashes are adjusted as + * required for non-Unix systems. */ - *handle = Parrot_dlopen(file_name); - if (*handle) { - path = string_from_cstring(interpreter, file_name, 0); - string_cstring_free(file_name); - return path; + full_name = Parrot_locate_runtime_file(interpreter, file_name, + PARROT_RUNTIME_FT_DYNEXT); + if (full_name) { + *handle = Parrot_dlopen(full_name); + if (*handle) { + path = string_from_cstring(interpreter, full_name, 0); + string_cstring_free(file_name); + string_cstring_free(full_name); + return path; + } } /* * and on windows strip a leading "lib" + * [shouldn't this happen in Parrot_locate_runtime_file instead?] */ #ifdef WIN32 if (memcmp(file_name, "lib", 3) == 0) { @@ -297,9 +322,10 @@ void (*init_func)(Interp *, PMC *); char *cinit_func_name, *cload_func_name; PMC *lib_pmc; + char *lib_name; /* library stem without path or extension. */ UNUSED(initializer); - path = get_path(interpreter, lib, &handle); + path = get_path(interpreter, lib, &handle, &lib_name); if (!path || !handle) { /* * XXX internal_exception? return PMCNULL? @@ -325,17 +351,20 @@ */ Parrot_block_DOD(interpreter); /* get load_func */ - load_func_name = Parrot_sprintf_c(interpreter, "Parrot_lib_%Ss_load", lib); + load_func_name = Parrot_sprintf_c(interpreter, "Parrot_lib_%s_load", + lib_name); cload_func_name = string_to_cstring(interpreter, load_func_name); load_func = (PMC * (*)(Interp *))D2FPTR(Parrot_dlsym(handle, cload_func_name)); string_cstring_free(cload_func_name); /* get init_func */ - init_func_name = Parrot_sprintf_c(interpreter, "Parrot_lib_%Ss_init", lib); + init_func_name = Parrot_sprintf_c(interpreter, "Parrot_lib_%s_init", + lib_name); cinit_func_name = string_to_cstring(interpreter, init_func_name); init_func = (void (*)(Interp *, PMC *))D2FPTR(Parrot_dlsym(handle, cinit_func_name)); string_cstring_free(cinit_func_name); + string_cstring_free(lib_name); lib_pmc = Parrot_init_lib(interpreter, load_func, init_func); Index: t/dynclass/foo.t =================================================================== --- t/dynclass/foo.t (revision 8203) +++ t/dynclass/foo.t (working copy) @@ -16,7 +16,7 @@ =cut -use Parrot::Test tests => 3; +use Parrot::Test tests => 7; use Parrot::Config; pir_output_is(<< 'CODE', << 'OUTPUT', "get_integer"); @@ -34,6 +34,99 @@ 42 OUTPUT +pir_output_is(<< 'CODE', << 'OUTPUT', "loadlib with relative pathname, no ext"); +.sub main @MAIN + ## load a relative pathname without the extension. loadlib will convert the + ## '/' characters to '\\' on windows. + $S0 = "runtime/parrot/dynext/foo" + loadlib P1, $S0 + + ## ensure that we can still make Foo instances. + find_type $I0, "Foo" + new $P1, $I0 + $I1 = $P1 + print $I1 + print "\n" +.end +CODE +42 +OUTPUT + +pir_output_is(<< 'CODE', << 'OUTPUT', "loadlib with absolute pathname, no ext"); +.sub main @MAIN + ## get cwd in $S0. + .include "iglobals.pasm" + $P11 = getinterp + $P12 = $P11[.IGLOBALS_CONFIG_HASH] + $S0 = $P12["prefix"] + + ## convert cwd to an absolute pathname without the extension, and load it. + ## this should always find the version in the build directory, since that's + ## the only place "make test" will work. + $S0 = concat "/runtime/parrot/dynext/foo" + loadlib P1, $S0 + + ## ensure that we can still make Foo instances. + find_type $I0, "Foo" + new $P1, $I0 + $I1 = $P1 + print $I1 + print "\n" +.end +CODE +42 +OUTPUT + +pir_output_is(<< 'CODE', << 'OUTPUT', "loadlib with relative pathname & ext"); +.sub main @MAIN + ## get share_ext in $S0. + .include "iglobals.pasm" + $P11 = getinterp + $P12 = $P11[.IGLOBALS_CONFIG_HASH] + $S0 = $P12["share_ext"] + + ## load a relative pathname with an extension. + $S0 = concat "runtime/parrot/dynext/foo", $S0 + loadlib P1, $S0 + + ## ensure that we can still make Foo instances. + find_type $I0, "Foo" + new $P1, $I0 + $I1 = $P1 + print $I1 + print "\n" +.end +CODE +42 +OUTPUT + +pir_output_is(<< 'CODE', << 'OUTPUT', "loadlib with absolute pathname & ext"); +.sub main @MAIN + ## get cwd in $S0, share_ext in $S1. + .include "iglobals.pasm" + $P11 = getinterp + $P12 = $P11[.IGLOBALS_CONFIG_HASH] + $S0 = $P12["prefix"] + $S1 = $P12["share_ext"] + + ## convert $S0 to an absolute pathname with extension, and load it. + ## this should always find the version in the build directory, since that's + ## the only place "make test" will work. + $S0 = concat $S0, "/runtime/parrot/dynext/foo" + $S0 = concat $S0, $S1 + loadlib P1, $S0 + + ## ensure that we can still make Foo instances. + find_type $I0, "Foo" + new $P1, $I0 + $I1 = $P1 + print $I1 + print "\n" +.end +CODE +42 +OUTPUT + SKIP: { skip("No BigInt Lib configured", 1) if !$PConfig{gmp}; pir_output_is(<< 'CODE', << 'OUTPUT', "inherited add");