in reply to How to setup the DynaLoader in a dynamically loaded perl?

On second glance, you did not dlsym'ed boot_DynaLoader() that's why xs_init() had trouble.

This is a complete, compilable and runnable example:

/* author: bliako date: 05/05/2020 for: https://perlmonks.org/?node_id=11116476 compile with: $(perl -MConfig -e 'print $Config{cc}') a.c $(perl -MExtUtils::Embed - +e ccopts -e ldopts) -o aaa -ldl run with: ./aaa -e 'print "hello there, I am a tiny wee Perl!!\n"' */ #include <EXTERN.h> #include <perl.h> #include "XSUB.h" #include <dlfcn.h> // void boot_DynaLoader (pTHX_ CV* cv); /* this declares a function pointer as opposed to an external function + above */ void (*boot_DynaLoader)(pTHX_ CV* cv); void xs_init(pTHX) { static const char file[] = __FILE__; dXSUB_SYS; PERL_UNUSED_CONTEXT; newXS( "DynaLoader::boot_DynaLoader", boot_DynaLoader, file ); } static PerlInterpreter *my_perl; int main( int argc, char **argv, char **env ) { /* find path to shared library */ /* open shared lib */ void *handle = dlopen("/usr/lib64/libperl.so.5.28.2", RTLD_NOW); /* Get entry point for perl_alloc */ void* (*perl_alloc)(); perl_alloc = (void *(*)() )dlsym(handle, "perl_alloc"); /* Get all the functions you will be using from the library, t +his one is obvious */ boot_DynaLoader = (void (*)(pTHX_ CV* cv) )dlsym(handle, "boot_Dyn +aLoader"); /* Call perl_alloc */ my_perl = perl_alloc(); /* and so on */ perl_construct(my_perl); perl_parse( my_perl, xs_init, argc, argv, env ); int result = perl_run(my_perl); perl_destruct(my_perl); perl_free(my_perl); PERL_SYS_TERM(); return result; }

Edit: added a typecast to boot_DynaLoader = (void (*)(pTHX_ CV* cv) )dlsym(handle, "boot_DynaLoader");

bw, bliako

Replies are listed 'Best First'.
Re^2: How to setup the DynaLoader in a dynamically loaded perl?
by sciurius (Beadle) on May 05, 2020 at 20:00 UTC
    Thanks for your helpful suggestions. Your example compiles and runs, but:
    $ ldd aaa linux-vdso.so.1 (0x00007ffd0e4c6000) libperl.so.5.30 => /lib64/libperl.so.5.30 (0x00007fe9264b5000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fe926493000) libdl.so.2 => /lib64/libdl.so.2 (0x00007fe92648c000) libc.so.6 => /lib64/libc.so.6 (0x00007fe9262c3000) libm.so.6 => /lib64/libm.so.6 (0x00007fe92617d000) libcrypt.so.2 => /lib64/libcrypt.so.2 (0x00007fe926142000)
    So it is already linked to the perl shared library. If I remove -lperl from the link arguments I get:
    in function `xs_init': /home/jv/tmp/aaa.c:28: undefined reference to `PL_thr_key' /bin/ld: /home/jv/tmp/aaa.c:28: undefined reference to `pthread_getspe +cific' /bin/ld: /home/jv/tmp/aaa.c:28: undefined reference to `Perl_newXS'
    EDIT: Managed to get it going by adding a Perl_newXS function as follows:
    CV* Perl_newXS(pTHX_ const char *name, XSUBADDR_t subaddr, const char +*filename) { CV* (*imp)(tTHX, const char*, XSUBADDR_t, const char*); imp = (CV* (*)(tTHX, const char*, XSUBADDR_t, const char*)) dlsym( h +andle, "Perl_newXS" ); return (*imp)(my_perl, name, subaddr, filename); }
    The complete proof-of-concept program is now:
    #include <stdio.h> #include <stdlib.h> #include <dlfcn.h> #include <EXTERN.h> #include <perl.h> void *handle; /* for dl */ static PerlInterpreter *my_perl; void (*boot_DynaLoader)(pTHX_ CV* cv); CV* Perl_newXS(pTHX_ const char *name, XSUBADDR_t subaddr, const char +*filename) { CV* (*imp)(tTHX, const char*, XSUBADDR_t, const char*); imp = (CV* (*)(tTHX, const char*, XSUBADDR_t, const char*)) dlsym( h +andle, "Perl_newXS" ); return (*imp)(my_perl, name, subaddr, filename); } void xs_init(pTHX) { static const char file[] = __FILE__; dXSUB_SYS; PERL_UNUSED_CONTEXT; newXS( "DynaLoader::boot_DynaLoader", boot_DynaLoader, file ); } int main( int argc, char **argv, char **env ) { /* open shared lib */ handle = dlopen("perl530.so", RTLD_LAZY); if ( !handle ) { fprintf( stderr, "%s\n", dlerror() ); exit(EXIT_FAILURE); } /* Get entry point for perl_alloc */ void* (*perl_alloc)(); perl_alloc = (void*(*)()) dlsym(handle, "perl_alloc"); /* Call perl_alloc */ my_perl = (*perl_alloc)(); /* perl_construct */ void (*perl_construct)(void*); perl_construct = (void(*)(void*)) dlsym(handle, "perl_construct"); (*perl_construct)(my_perl); /* boot_DynaLoader */ boot_DynaLoader = (void (*)(pTHX_ CV* cv)) dlsym(handle, "boot_DynaL +oader"); /* perl_parse */ void (*perl_parse)(void*, void*, int, char**, char**); perl_parse = (void(*)(void*, void*, int, char**, char**)) dlsym(hand +le, "perl_parse"); (*perl_parse)(my_perl, xs_init, argc, argv, env); /* perl_run */ int (*perl_run)(void*); perl_run = (int(*)(void*)) dlsym(handle, "perl_run"); int result = (*perl_run)(my_perl); /* perl_destruct */ void(*perl_destruct)(void*); perl_destruct = (void(*)(void*)) dlsym(handle, "perl_destruct"); (*perl_destruct)(my_perl); /* perl_free */ void(*perl_free)(void*); perl_free = (void(*)(void*)) dlsym(handle, "perl_free"); (*perl_free)(my_perl); return result; }
    Build with: $(perl -MConfig -e 'print $Config{cc}') p.c $(perl -MExtUtils::Embed -e ccopts ) -o aaa -ldl -Wl,--rpath=. Although it seems that dynamic loading is not yet functional... For example, this fails:
    aaa -MFcntl=:flock -E "say 1+LOCK_NB" Undefined subroutine &Fcntl::LOCK_NB called at -e line 1.

      As I said, you will need to dlsym anything that you may use later.

      However, when I did this:

      CV* (*Perl_newXS)(pTHX_ const char *name, XSUBADDR_t subaddr, const ch +ar *filename); .... Perl_newXS = (CV* (*)(pTHX_ const char *name, XSUBADDR_t subaddr, cons +t char *filename) )dlsym(handle, "Perl_newXS");

      I got

      error: ‘Perl_newXS’ redeclared as different kind of symbol /usr/lib64/perl5/CORE/proto.h:2517:19: note: previous declaration of ‘ +Perl_newXS’ was here 2517 | PERL_CALLCONV CV* Perl_newXS(pTHX_ const char *name, XSUBADDR_ +t subaddr, const char *filename);

      So, that's a neat trick you have there! (though I would make imp static)