shared libs for OSX
bitwise via Digitalmars-d
digitalmars-d at puremagic.com
Sun May 24 12:40:04 PDT 2015
I've read through these now, which I missed the first time around, so
sorry for making you guys repeat yourselves ;)
<Runtime issue on Mac OS X>
http://comments.gmane.org/gmane.comp.lang.d.runtime/1214
<ideas for runtime loading of shared libraries.>
http://forum.dlang.org/thread/mailman.2052.1325532031.24802.digitalmars-d@puremagic.com
So in terms of a shared lib having it's own runtime, we have these
problems:
[1] problem
On Fri, 22 May 2015 12:04:24 -0400, Martin Nowak <code at dawg.eu> wrote:
> Yes separate shared libraries (with multiple runtimes) work on every
> other platform.
> The problem for OSX is that onAddImage gets called for the executable
> and every shared library. It would be trivial to only register the image
> containing the current runtime, by comparing the address of a private
> symbol with the memory region of the images.
>
> https://github.com/D-Programming-Language/druntime/blob/6331ab1ae19f3ff82449a5734b59d81b128685f4/src/rt/sections_osx.d#L186
[2] problem
On Thu, 21 May 2015 15:34:56 -0400, Jacob Carlborg <doob at me.com> wrote:
> The runtime uses the "_dyld_register_func_for_add_image" function
> provided by the dynamic linker. This function is used to register a
> callback. The callback will be called for each currently loaded image
> (executable/dynamic library) in the executable. The callback will also
> be called for every newly loaded image, i.e. using dlopen. You need to
> keep track of which image is yourself and which are other images you
> should ignore.
>
> Then, the other problem with "_dyld_register_func_for_add_image" is that
> it's not possible to unregister the callback. Which means, if you
> register a callback using this function from a dynamic library loaded
> with dlopen and then unload it. Next time dlopen is called it will
> crash, because the callback points to an address that doesn't exist
> anymore.
I think I have found solutions for these problems.
[1] solution
I've modified sections_osx.d as follows:
extern (C) void _sections_osx_onAddImage_STUB(in mach_header* h, intptr_t
slide) {
// empty
}
extern (C) void sections_osx_onAddImage(in mach_header* h, intptr_t slide)
{
// on mac osx, Dl_info.dli_fbase is a pointer to the mach_header for the
library. [I]
// here we return unless onAddImage is being called for the current library
mach_header* myHeader = null;
Dl_info info;
// this line also makes sure that the stub isn't
// removed by the linker(it's needed for [2])
if(dladdr(&_sections_osx_onAddImage_STUB, &info))
{
mach_header* mh = cast(mach_header*)info.dli_fbase;
if(mh == cast(mach_header*)h)
myHeader = mh;
}
if(!myHeader)
return;
// initialize sections.....
}
[2] solution
Although the callback passed to "_dyld_register_func_for_add_image" cannot
be removed, it can be replaced, so I've replaced it with a pointer to the
stub located in the main program.
I've modified initSections() in sections_osx.d as follows:
void initSections()
{
pthread_key_create(&_tlsKey, null);
// register the callback as usual. This will call the callback
// for every library currently loaded before returning, so
// once it has returned, it should be safe to set the callback
// to something else(the empty stub)
_dyld_register_func_for_add_image(§ions_osx_onAddImage);
// dlopen(null, ..) will retrieve a handle to the main program [II]
// OSX docs says it returns the first symbol found using
// "RTLD_DEFAULT" or "the default library search order" which
// should(and does as far as I can tell) return the handle
// to the main program
void *main = dlopen(null, RTLD_NOW);
assert(main);
alias typeof(&_sections_osx_onAddImage_STUB) addImgFn;
addImgFn func = cast(addImgFn)dlsym(main,
"_sections_osx_onAddImage_STUB");
assert(func);
// set the callback to the empty stub in the main program
_dyld_register_func_for_add_image(func);
_isRuntimeInitialized = true;
}
[I]
https://developer.apple.com/library/ios/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dladdr.3.html
[II]
https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/dlopen.3.html
http://linux.die.net/man/3/dlopen
So at this point, it seems like these two fixes work as expected, but now,
I'm having some new and very strange problems.
I have a simple shared library and program I've been using to test this:
[main.d]
module main;
import std.stdio;
import std.conv;
import std.string;
import core.sys.posix.dlfcn;
void main(string[] args)
{
alias void function() fnType;
void *handle = dlopen("myShared.dylib", RTLD_NOW);
assert(handle);
fnType init = cast(fnType)dlsym(handle, "initLib");
assert(init);
init();
fnType term = cast(fnType)dlsym(handle, "termLib");
assert(term);
term();
dlclose(handle);
writeln("done");
}
[myShared.d]
module myShared;
import core.runtime;
import std.stdio;
extern(C) void initLib() {
writeln("Initializing Runtime");
Runtime.initialize();
}
extern(C) void termLib() {
writeln("Terminating Runtime");
Runtime.terminate();
}
So, when I run the above program, rt_init() should be called once for the
main program, and once for the shared library. However, when I run the
above program, rt_init() from the main program seems to get called twice.
To clarify, I mean that when I retrieve "initLib()" with dlsym() and call
it, rt_init() from the main module gets called.
This seems to prove the above:
In dmain2.d, I have modified rt_init() as follows:
extern (C) int rt_init()
{
import core.sys.posix.dlfcn;
Dl_info info;
if(dladdr(&rt_init, &info))
fprintf(stdout, "RT INIT: %s\n", info.dli_fname); // this prints
"main" for both calls
if (atomicOp!"+="(_initCount, 1) > 1)
{
fprintf(stdout, "RT ALREADY INITIALIZED\n");
return 1;
}
// ...
fprintf(stdout, "RT INIT COMPLETE\n");
}
When the main program calls rt_init(), the output correctly reads "RT INIT
COMPLETE".
When I load the dynamic library however, I get the output "RT ALREADY
INITIALIZED"
How is this possible? I am not using a shared druntime afaik..
Bit
More information about the Digitalmars-d
mailing list