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(&sections_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

Reply via email to