Sign in to follow this  

Implicit runtime dynamic linking on Windows?

This topic is 3345 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello. With my engine I like to load every external shared library at runtime, as to provide the different codepaths based on what libraries the user has installed. Consider the following example that illustrates what I'm doing:
#include <stdio.h>
#include <dlfcn.h>
#include <zlib.h>

int main( )
{
    void * library = dlopen( "libz.so", RTLD_LAZY | RTLD_GLOBAL );
    if( library == 0 )
        printf( "zlib is not present on the system\n" );
    else
        printf( "%s\n", zlibVersion( ) );
    return 0;
}
Now, this wouldn't be anything special except I'm not linking zlib to my program at all. Here's how I do it:
gcc -fpic -pie example.c -ldl -Wl,--unresolved-symbols=ignore-all
It's all done at runtime. The linker automatically binds the symbols from dynamically loaded library to global symbols used by the program. This saves me the trouble of manually creating abominations like this:
typedef const char * ( *zlibVersion_TYPE )( );
zlibVersion_TYPE zlibVersion_BINDING;

bool load_zlib( )
{
    (...)
    zlibVersion_BINDING = dlsym( library, "zlibVersion" );
    (...)
}

const char * zlibVersion( )
{
    return zlibVersion_BINDING( );
}

So, my question is, while it's a piece of cake to do on a GNU/Linux system (or any other UNIX-like system for that matter), I have absolutely no idea how can this be achived on Windows. I tried doing this on MigGW, but without success. Any help?

Share this post


Link to post
Share on other sites
In your example you can pretty much replace dlopen with LoadLibrary and dlsym with GetProcAddress, the concepts are quite similar.

I'm not sure why you have to ignore unresolved symbols when your example doesn't seem to have any unresolved symbols.

When dynamically loading a dll in windows you never link to the actual dll because you are manually loading the dll and retrieving pointers to the functions.

Can't say I know how it works in linux/unix

Share this post


Link to post
Share on other sites
Quote:
Original post by Ximmer
In your example you can pretty much replace dlopen with LoadLibrary and dlsym with GetProcAddress, the concepts are quite similar.

I'm not sure why you have to ignore unresolved symbols when your example doesn't seem to have any unresolved symbols.

When dynamically loading a dll in windows you never link to the actual dll because you are manually loading the dll and retrieving pointers to the functions.

Can't say I know how it works in linux/unix

My example has unresolved symbols (specifically, zlibVersion) since I'm not linking to the library at compile time. My whole point is to load the library at runtime, without retrieving pointers. On UNIX-like systems I can make the OS to do this automatically, behind the scenes. I'm asking if something like that can be done on Windows and if yes, then how.

Share this post


Link to post
Share on other sites
Delay Loading is what you want. I'm curious; how is this done on Linux?

From my engine code for delay loading D3D9 and D3DX:

//============================================================================
// PD3DLoader.cpp - Functions for loading D3D and D3DX
//============================================================================
#include "PD3DLoader.h"
#include <d3dx9.h>
#include <windows.h>
#include <delayimp.h>

#ifdef BUILD_DEBUG
# pragma comment(lib, "d3dx9d.lib")
#else
# pragma comment(lib, "d3dx9.lib")
#endif
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "dxerr.lib")

//============================================================================
// Structured Exception Handler for delay loaded DLLs
static LONG WINAPI DelayLoadDllExceptionFilter(PEXCEPTION_POINTERS pep, std::string& strError)
{
// If this is a Delay-load problem, ExceptionInformation[0] points
// to a DelayLoadInfo structure that has detailed error info
PDelayLoadInfo pdli = PDelayLoadInfo(pep->ExceptionRecord->ExceptionInformation[0]);
char szBuff[512];
switch(pep->ExceptionRecord->ExceptionCode)
{
case VcppException(ERROR_SEVERITY_ERROR, ERROR_MOD_NOT_FOUND):
_snprintf(szBuff, sizeof(szBuff), "Could not find \"%s\"", pdli->szDll);
strError = szBuff;
return EXCEPTION_EXECUTE_HANDLER;

case VcppException(ERROR_SEVERITY_ERROR, ERROR_PROC_NOT_FOUND):
// The DLL was found but it doesn't contain the function
if(pdli->dlp.fImportByName)
{
_snprintf(szBuff, sizeof(szBuff), "Could not find \"%s\" in \"%s\"",
pdli->dlp.szProcName, pdli->szDll);
}
else
{
_snprintf(szBuff, sizeof(szBuff), "Could not find function ordinal %d in \"%s\"",
pdli->dlp.dwOrdinal, pdli->szDll);
}
strError = szBuff;
return EXCEPTION_EXECUTE_HANDLER;

default:
// We don't recognize this exception
return EXCEPTION_CONTINUE_SEARCH;
break;
}
}

//============================================================================
// Load D3DX DLL by calling one of its functions
static bool InitD3DX(std::string& strError)
{
bool bRet = true;
__try
{
// Do a simple matrix multiply
D3DXMATRIXA16 m1, m2, m3;
memset(&m1, 0, sizeof(m1));
memset(&m2, 0, sizeof(m2));
D3DXMatrixMultiply(&m3, &m1, &m2);
}
__except (DelayLoadDllExceptionFilter(GetExceptionInformation(), strError))
{
bRet = false;
}
return bRet;
}

//============================================================================

IDirect3D9* CreateDirect3DInterface(std::string& strError)
{
// Reset error string
strError.clear();
IDirect3D9* pD3D = NULL;

// Load D3DX
if(!InitD3DX(strError))
return NULL;

__try
{
pD3D = Direct3DCreate9(D3D_SDK_VERSION);
if(!pD3D)
{
char szBuff[512];
_snprintf(szBuff, sizeof(szBuff),
"Direct3DCreate9 failed. GetLastError returns %d.", GetLastError());
strError = szBuff;
}
}
__except (DelayLoadDllExceptionFilter(GetExceptionInformation(), strError))
{
// Nothing to do in here, pD3D is already null
}
return pD3D;
}

//============================================================================

With the following in Linker -> Input -> Delay Loaded DLLs: "d3d9.dll;d3dx9d_35.dll"

Share this post


Link to post
Share on other sites
Evil_Steve: Thanks. That should work. Fortunately I have MSVC 2008 stashed here somewhere, since I assume it cannot be done on MinGW. (;

Well, as for how it's done on GNU/Linux - it's not done. (: Well, seriously, not many people use it, but it can be done as I've illustrated in my first post. You just tell the linker to ignore all unresolved symbols - when the program is run these symbols will be null until you load a library trough dlopen with RTLD_GLOBAL flag. (It means that it would bind symbols loaded from the library to the global namespace, instead of keeping them local to the library.)

It's a little different from Delay Loading that is done on Windows. The good thing is that on GNU/Linux you only need a header file to do it and you can load an entirely different shared object, provided it will contain the symbols you use. (So you, for example, don't really need GLEE to load OpenGL extensions you desire, provided you'll have a header file with them in.) The downside is that, AFAIK, there is no way to catch unresolved symbol errors at runtime, so the safest way is to actually check if a given symbol exists in the library you loaded with dlsym at initialization.

Share this post


Link to post
Share on other sites

This topic is 3345 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this