質問

I'm trying to make this OSX code (golfed for ease of discussion) work on Ubuntu Linux.

cat >main.c <<EOF
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
void provided_by_main() { puts("Hello main!"); }
int main() {
  void *provider_so, *needer_so;
  (provider_so = dlopen("provider.so", RTLD_NOW)) || printf("Fail %s\n", dlerror()) && (exit(0),0);
  (needer_so = dlopen("needer.so", RTLD_NOW)) || printf("Fail %s\n", dlerror()) && (exit(0),0);
  void (*needer)() = dlsym(needer_so, "needer");
  needer();
}
EOF

cat >needer.c <<EOF
extern void provider();
void needer() { provider(); }
EOF

cat >provider.c <<EOF
#include <stdio.h>
void provider() { puts("Hello provider!"); }
EOF

gcc -shared -o provider.so provider.c
gcc -shared -o needer.so needer.c -dynamic -undefined dynamic_lookup
gcc -o main main.c -ldl
./main
Hello provider!

On Linux, via trial and error and StackOverflow, I determined that needer cannot refer to anything defined in main unless main has been linked with -rdynamic:

gcc -shared -fpic -o provider.so provider.c
gcc -shared -fpic -o needer.so needer.c -Dprovider=provided_by_main
gcc -o main main.c -ldl -rdynamic
./main
Hello main!

However, I cannot get needer to see anything provided by provider even if the entire "chain" is compiled with -rdynamic:

gcc -shared -fpic -o provider.so provider.c -rdynamic
gcc -shared -fpic -o needer.so needer.c
gcc -o main main.c -ldl -rdynamic
./main
Fail needer.so: undefined symbol: provider

So, how do I make this work?

Or, if it's impossible on Linux by design, then why is it designed to be impossible?

(OSX equivalent: Accessing main program global variables from a dlopen()ed dynamic library in C on OS X)

Real-world bonus complication: In my actual program, provider.so is code-generated at runtime, and the name of the provider symbol isn't determined until after main has been linked. However, even an answer that involves modifying main.c would be a step in the right direction.

役に立ちましたか?

解決

The reason this works on OS X and fails on Linux is that RTLD_GLOBAL is dlopen's default symbol visibility on the former, whereas the latter defaults to RTLD_LOCAL. Use (carefully):

dlopen("provider.so", RTLD_NOW | RTLD_GLOBAL);

-rdynamic on the other hand only applies to missing library symbols that are defined in the executable, but not anywhere else.


sed -i 's/RTLD_NOW/RTLD_NOW|RTLD_GLOBAL/' main.c
gcc -shared -fpic -o provider.so provider.c
gcc -shared -fpic -o needer.so needer.c
gcc -o main main.c -ldl
./main
Hello provider!

他のヒント

The dlopen call is failing at run-time, so it can't find or open provider.so. This is from man dlopen on OS X and might help:

dlopen() searches for a compatible Mach-O file in the directories specified by a set of environment variables and the process's current working directory. When set, the environment variables must contain a colon-separated list of directory paths, which can be absolute or relative to the current working directory. The environment variables are LD_LIBRARY_PATH, DYLD_LIBRARY_PATH, and DYLD_FALLBACK_LIBRARY_PATH. The first two variables have no default value. The default value of DYLD_FALLBACK_LIBRARY_PATH is $HOME/lib;/usr/local/lib;/usr/lib. dlopen() searches the directories specified in the environment variables in the order they are listed.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top