Search code examples
windowsdlldllimportdllexport

Have dll import symbols from its calling .exe


Related to but not equivalent to DLL Get Symbols From Its Parent (Loader)

Is there a way to convince the Windows loader to resolve a particular symbol referenced by A.dll from either the loading executable or an intermediate dll without specifying the file to resolve symbols from in A.dll?

It's pretty obvious how to do it if the loading .exe has a known name, but if it isn't ...

Here's a good reason why you'd actually want to do this: https://www.gnu.org/software/libc/manual/html_node/Replacing-malloc.html

If this can be done, a good answer would say how to do it some way or another.

I'm half-expecting the answer is it can't be done. In that case, a good answer would show why this is impossible. "The build tools don't support this." is a bad answer.


Solution

  • when we use import we need exactly indicate module name and function name. and we can not use complex algorithms. also for exe not exist well known alias which we can use in place exactly exe name. for compare: in case get GetModuleHandle we can use NULL for get handle to the file used to create the calling process (.exe file). but in case LoadLibraryExW we can not use 0 or empty string (L"") or some another alias for say - we want handle to exe. when loader load our module - he read dll name from IMAGE_IMPORT_DESCRIPTOR and try found or load module with this name first by low level, private, core of LoadLibraryExW. here need exactly name. or load fail. as result use import - not a solution here, if we dont know exe name at build time

    possible variant - resolve functions pointers yourself at runtime. here we can get exe HMODULE by GetModuleHandle(0). also if need we can search function not only in exe but somewhere else. can implement any search algorithm.

    here exist several ways. for concrete example let we need get pointer to function with signature:

    void WINAPI fn(int i);
    

    we can declare pointer to this function and resolve it in runtime

    void (WINAPI *fn)(int);
    
    *(void**)&fn = GetProcAddress(GetModuleHandleW(0), "fn");
    

    say on DLL_PROCESS_ATTACH

    a slightly different solution (although at the binary level it is completely equivalent) declare function with __declspec(dllimport) attribute. this is for CL.EXE (more known as MSVC) compiler only. so

    __declspec(dllimport) void fn(int i);
    

    in this case CL yourself generate pointer to function with name __imp_ ## __FUNCDNAME__ name. so by fact the same as in first variant, when we declare pointer yourself. only difference in syntax and.. symbol name. it will be look like __imp_?fn2@@YAXH@Z. problem here that __imp_?fn2@@YAXH@Z not valid name for c/c++ - we can not direct assign value to it from c/c++. even if we declare function with extern "C" - function name will be containing @ symbol (illegal for c++) for __stdcall and __fastcall functions, for x86. also name will be different for different platforms (x86, x64, etc). for access such names - need or use external asm file (for asm ? and @ symbols valid in name) or use /alternatename linker option - for set alias for such name and access symbol via it. say like

    __pragma(comment(linker, "/alternatename:__imp_?fn@@YAXH@Z=__imp_fn"))
    

    and init via

    *(void**)&__imp_fn = GetProcAddress(GetModuleHandle(0), "fn");
    

    another option use __declspec(dllimport) in function declarations + add import library, where all __imp___FUNCDNAME__ (such __imp_?fn2@@YAXH@Z) is defined. (even if we have not such library we can easy create it yourself - all what need - correct function declarations with empty implementation). and after we add such import lib to linker input - add /DELAYLOAD:dllname where dllname - exactly name from import lib. sense that this dllname will(can) be not match to exe - all what need - it must be unique. and we need yourself handle delayload (called when we first time call fn). for implement delayload we need implement

    extern "C" FARPROC WINAPI __delayLoadHelper2(   
       PCImgDelayDescr pidd,  
       FARPROC * ppfnIATEntry  
    ); 
    

    we can implement it yourself, or add delayimp.lib to our project. here (in delayimp.lib) the delayLoadHelper2 and implemented. however we must customize this process (default implementation(look in /include/DelayHlp.cpp) will be use LoadLibraryExA with dllname which is not excepted in our case - otherwise we can be simply use import as is). so we need mandatory implement __pfnDliNotifyHook2:

    for example:

    FARPROC WINAPI MyDliHook(
                                 unsigned        dliNotify,
                                 PDelayLoadInfo  pdli
                                 )
    {
        switch (dliNotify)
        {
        case dliNotePreLoadLibrary:
            if (!strcmp(pdli->szDll, "unique_exe_alias"))
            {
                return (FARPROC)GetModuleHandle(0);
            }
        }
    
        return 0;
    }
    
    const PfnDliHook  __pfnDliNotifyHook2 = MyDliHook;
    

    we can look for dliNotePreLoadLibrary notification and instead default LoadLibraryEx(dli.szDll, NULL, 0); use GetModuleHandle(0); for get base of exe. the "unique_exe_alias" (which linker got from import library) here play role not real exe name, which is unknown, but unique tag(alias) for exe