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.digitalmar...@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 <c...@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 <d...@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