swehack has asked for the wisdom of the Perl Monks concerning the following question:

Hi, i have a problem with dynamically loading shared objects which in turn are linked with perl libs and load DynaLoader using newXS.

I have this program that dynamically loads several modules, shared objects, which give the program its functionality. So far so good but yesterday i decided to add a module that would evaluate perl code using eval_pv and instructions i learned about in perlembed.

First of all, this required me to change my Makefile because perlembed states that i must compile my code EXACTLY the same as the perl interpreter was compiled, and i was using two different rules for compiling and linking. So now i'm using one rule and doing the linking through gcc(cc) in order to use the perl -MExtUtils::Embed -e ccopts -e ldopts command to generate my arguments.

I wanted to load Data::Dumper in the perl code being evaluated in order to return a general string for all sorts of variables returned so i followed the perlembed instructions for using xs_init and newXS to load DynaLoader so i could use Data::Dumper, which worked.

So to make a long story short i ran into a problem, running the code as a standalone executable file works fine but as soon as i turn it into a shared object and load it using dlopen from my main program i get this error: Undefined symbol "boot_DynaLoader"

So instead of forcing you through my project, i wrote up a small example program that reproduces the problem and mimicks the functionality of my program enough for you to understand what i'm trying to do and still read it through easily.

I hope someone can hint me as to what i'm doing wrong here and i'm sorry for any errors in this post but i finished this up pretty late in the evening. I was thinking about posting the same to perl-xs but i figured i'd see what you guys say about it first.

I found some other posts on perl-xs that resembled my problem but they were either unsolved or didn't even have any replies.

Here is the code for perl_test1.c.

/* compiled with cc -Wall -pedantic -ansi -rpath . -L. `perl -MExtUtil +s::Embed -e ccopts -e ldopts` -fPIC -shared -soname perl_test1.so -o +perl_test1.so -lc perl_test1.c */ #include <stdlib.h> #include <EXTERN.h> #include <perl.h> #ifndef PERL_NO_SHORT_NAMES #define PERL_NO_SHORT_NAMES #endif void pprint(void); static void xs_init(pTHX); EXTERN_C void boot_DynaLoader(pTHX_ CV* cv); static PerlInterpreter *perl; EXTERN_C void xs_init(pTHX) { char *file = __FILE__; newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file); } void pprint(void) { int argc = 0; char **argv = NULL; char **env = NULL; SV *retval; STRLEN n_a; char *embedding[] = { "", "-e", "0" }; PL_origalen = 1; PERL_SYS_INIT3(&argc, &argv, &env); perl = perl_alloc(); perl_construct(perl); perl_parse(perl, xs_init, 3, embedding, NULL); PL_exit_flags |= PERL_EXIT_DESTRUCT_END; perl_run(perl); eval_pv("use Data::Dumper qw(Dumper);" "local $Data::Dumper::Terse = 1;" "local $Data::Dumper::Quotekeys = 0;" "local $Data::Dumper::Indent = 0;" "local $Data::Dumper::Useqq = 1;", TRUE); retval = eval_pv("my $code='\"this is a string\"';my $ret=eval $co +de;return(Dumper($ret));", TRUE); printf("%s", SvPV(retval, n_a)); perl_destruct(perl); perl_free(perl); PERL_SYS_TERM(); exit(0); }

And here is the code for perl_test2.c.

/* compiled with cc -Wall -pedantic -ansi -rpath . -L. -o perl_test2 p +erl_test2.c */ #include <stdio.h> #include <stdlib.h> #include <dlfcn.h> int main(void) { void *mod = NULL; void (*func)(void); if((mod = dlopen("perl_test1.so", (RTLD_LAZY | RTLD_LOCAL))) == NU +LL) { fprintf(stderr, "Failed loading module: %s\n", dlerror()); exit(-1); } if((func = (void *)dlsym(mod, "pprint")) == NULL) { fprintf(stderr, "Failed locating symbol: %s\n", dlerror()); dlclose(mod); } else { (*func)(); } exit(0); }

While searching your database i found this post with the exact same problem but no replies. He mentions something i've been suspecting based on other finds on the net, that DynaLoader.a is not compiled to be shared like this, its name alone hints on this.

Replies are listed 'Best First'.
Re: Problem with dlopen and shared objects using perl libs
by almut (Canon) on Mar 11, 2008 at 02:02 UTC

    AFAICT, there are two problems:

    • the output of  `perl -MExtUtils::Embed -e ccopts -e ldopts` (in particular the -lperl therein) is inserted too early on the command line. As the linker (ld) by default only searches once to satisfy externally referenced symbols, the perl library didn't get included.

    • when you load the shared lib using dlopen, you need to specify RTLD_GLOBAL (instead of RTLD_LOCAL), so the symbols will be made visible to any subsequently loaded libs, such as Data/Dumper/Dumper.so.

    Here's how I got it to work on my Linux box (with Perl installed under /usr/local/perl/5.10.0):

    # compile/link shared lib which includes Perl interpreter: gcc -Wall -pedantic -fPIC -shared \ -o perl_test1.so perl_test1.c \ `/usr/local/perl/5.10.0/bin/perl -MExtUtils::Embed -e ccopts -e ld +opts` # compile/link the test program that loads it using dlopen: gcc -Wall -pedantic -Wl,-rpath=. \ -o perl_test2 perl_test2.c \ -ldl # ...and run it: $ ./perl_test2 $VAR1 = 'this is a string';

      Thank you very much for your help almut.

      I can't believe it was as simple as gcc arguments being in the wrong order, it now compiles and runs fine on my FreeBSD to.

      I guess that's what i get from not fully understanding my compiler and linker.

      I knew about the RTLD_GLOBAL though from listposts but i first and foremost wanted to find out what i did wrong with the linking.

      Hi again, perhaps you can help me with this new problem i have related to the last.

      I'm stuck because even if the program i wrote as an example compiles fine, the real module for my project does not and i believe it's because PERL_NO_SHORT_NAMES is not defined or respected in my project while it is in the example program.

      I can make no sense at all of this because in my example program, i have Perl_doing_taint even if i remove the #define PERL_NO_SHORT_NAMES directive.

      While it always uses the short names in my project, what is causing this? I've tried modifying the arguments i use to compile so many times and the way i see it the main differences are that in my project i have -lircbot which links all my modules to a library with some common functions for them to use.

      Besides that i bring in the ccopts and ldopts in a configure script which strips the newline and puts it all into a variable so it looks like this: PERL_OPTS=  -Wl,-R/usr/local/lib/perl5/5.8.8/mach/CORE  -Wl,-E -L/usr/local/lib /usr/local/lib/perl5/5.8.8/mach/auto/DynaLoader/DynaLoader.a -L/usr/local/lib/perl5/5.8.8/mach/CORE -lperl -lm -lcrypt -lutil -DAPPLLIB_EXP="/usr/local/lib/perl5/5.8.8/BSDPAN" -DHAS_FPSETMASK -DHAS_FLOATINGPOINT_H -fno-strict-aliasing -pipe -Wdeclaration-after-statement -I/usr/local/include  -I/usr/local/lib/perl5/5.8.8/mach/CORE but i've also tried just using `perl -MExtUtils::Embed -e ccopts -e ldopts` directly in the Makefile rule and it didn't help.

      So the way i see it, the only difference in the arguments is -Isrc -I.. -L../.. -lircbot which must be included in the arguments for my projects modules. How could this possibly cause PERL_NO_SHORT_NAMES to be ignored?! And how on earth is it defined in the example program even if i remove the preprocessing directives defining it and refuse to define it on the command line?

      I even tried compiling and linking the example program with -lircbot like my projects module is, it didn't affect the fact that nm returned the symbol Perl_doing_taint still, even though i nowhere define PERL_NO_SHORT_NAMES anymore.

      I must note that i can actually compile my example program module without the #define PERL_NO_SHORT_NAMES and when i check it's symbols with nm perl_test1.so | grep taint i see that it's not using short names, i see Perl_doing_taint. But when i check the symbols on my module i see doing_taint so i must assume that getting my module to respect PERL_NO_SHORT_NAMES will solve my problem because right now the program exits with this message:

      /libexec/ld-elf.so.1: modules/peval.so: Undefined symbol "doing_taint"

      Things like this just boggles my mind. Here is the Makefile i use along with the primary Makefile that defines ccopts and ldopts.

      Makefile:
      SHELL=/bin/sh CC=gcc LD=ld LDFLAGS+=-rpath . -L. CFLAGS+=-g -Wall -pedantic -ansi -DSETPROCTITLE -DPERL_NO_SHORT_NAMES LIBS+=-lircbot INCLUDES+=-Isrc -I.. HEADERS=bicebot.h ircbot.h PERL_OPTS= -Wl,-R/usr/local/lib/perl5/5.8.8/mach/CORE -Wl,-E -L/usr/ +local/lib /usr/local/lib/perl5/5.8.8/mach/auto/DynaLoader/DynaLoader. +a -L/usr/local/lib/perl5/5.8.8/mach/CORE -lperl -lm -lcrypt -lutil -D +APPLLIB_EXP="/usr/local/lib/perl5/5.8.8/BSDPAN" -DHAS_FPSETMASK -DHAS +_FLOATINGPOINT_H -fno-strict-aliasing -pipe -Wdeclaration-after-state +ment -I/usr/local/include -I/usr/local/lib/perl5/5.8.8/mach/CORE SRC=src SRC_MODS:=$(SRC)/mods # TODO: some sort of PREFIX to install elsewhere # also install target MODULES=modules export all: $(MAKE) -f $(SRC)/Makefile $(MAKE) -C $(SRC_MODS) @echo 'All done, now run bicebot.' modules: $(MAKE) -C $(SRC_MODS) # TODO: so you can rebuild only modules clean-modules: rm -f $(MODULES)/*.so $(SRC_MODS)/*.o clean: rm -f *.o bicebot *.so $(MODULES)/*.so $(SRC_MODS)/*.o
      src/mods/Makefile
      MODS_SRCS=$(wildcard *.c) MODS_OBJS=$(MODS_SRCS:%.c=%.o) MODS_TRGT=$(MODS_OBJS:%.o=%.so) LDFLAGS+=-L../.. all: $(MODS_TRGT) #gcc -Wall -fPIC -shared -o perl_test1.so perl_test1.c `perl -MExtUtil +s::Embed -e ccopts -e ldopts` $(MODS_TRGT): $(MODS_SRCS) $(CC) -Wall -Isrc -I.. -rpath . -L. -L../.. -fPIC -shared -o ../.. +/$(MODULES)/$@ $(@:%.so=%.c) `perl -MExtUtils::Embed -e ccopts -e ldo +pts` -lircbot -lc
      I've been trying and testing tons of different methods in the last Makefile so excuse the mess but maybe you can figure out why it acts like this if i show you how i compile and link the module.