Implicit runtime dynamic linking on Windows?

Started by
3 comments, last by desudesu 15 years, 6 months ago
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?
Advertisement
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
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.
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 DLLsstatic 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 functionsstatic 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"
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.

This topic is closed to new replies.

Advertisement