I am doing some research into platform independent code and found mention of the dlfcn API. It was the first time I came across mention of it and did further research into it. Now hopefully my lack of experience/understanding of platform independent code as well as compiling/linking isn’t going to show in this post but to me the dlfcn API just lets us do the same dynamic linking programmatically that the ld utility does. If I have misconceptions please correct me as I would like to know. Regarding what I think I know about the ld utility and the dlfcn API I have some questions.
What are the advantages of using either the ld utility vs. dlfcn API to dynamically link?
My first thought was that the dlfcn API seems like a waste of my time since I need to request pointers to the functions vs. having ld examine a symbol table for undefined symbols and then linking them. Similarly ld does everything for me while I have to do everything by hand with the dlfcn API (i.e. open/load the library, get a function pointer, close the library, etc.). But on second glance I thought that there may be some advantages. One being that we can load a library out of memory after we are done using it.
In this way memory could be saved if we knew we didn’t need to utilize a library the whole time. I am unsure if there is any “memory/library” management for libraries dynamically linked by ld? Similarly I am unsure of what scenarios/environments would we be interested in using the dlfcn API to save said memory as it seems this wouldn’t be a problem in modern day systems. I presume one would be the usage of the library on a system with very very very limited resources (maybe some embedded system?).
What other advantages or disadvantages may there be?
What “coding pattern” is used for platform independent code in regards to dynamic linking?
If I was making platform independent code that depended on system calls I could see myself achieving platform independent code by coding in one of three styles:
- Logical branching directly in my libraries code via macros. Something like:
- Create generic system call functions and use those in my libraries code. Something like:
- Similar to one but add a layer of abstraction by using the dlfcn API
void myAwesomeFunction()
{
...
#if defined(_MSC_VER)
// Call some Windows system call
#elif defined(__GNUC__)
// Call some Unix system call
...
}
OS_Calls.h
void OS_openFile(string myFile)
{
...
#if defined(_MSC_VER)
// Call Windows system call to open file
#elif defined(__GNUC__)
// Call Unix system call to open file
...
}
MyAwesomeFunctions.cpp
#include "OS_Calls.h"
void myAwesomeFunction()
{
...
OS_openFile("my awesome file");
...
}
MyLibraryLoader.h
void* GetLibraryFunction(void* lib, char* funcName)
{
...
return dlsym(lib, funcName);
}
MyAwesomeFunctions.cpp
#include "MyLibraryLoader.h"
void myAwesomeFunction()
{
Result result = GetLibraryFunction(someLib, someFunc)(arguments...);
}
What ones are typically used and why? And if there are any others that aren’t listed and preferred to mine please let me know.
Thanks for reading this post. I will keep it updated so that it may serve as a future informative reference.
dlfcn and ld does not solve the same problem: in fact you can use both in your project.
The dlfcn API is meant to support plugin architectures, in which you define an interface which modules should implement. An application can then load different implementations of that interface, for various reasons (extensibility, customization, etc.).
ld, well, links the libraries your application request, but does that at compile time, not at runtime time. It doesn’t support in any way plugin architectures, since ld links objects specified in the command line.
Of course you can only use the dlfcn API, but it is not meant to be used in that way and, of course, using it in that way would be a huge pain in your rectum.
For your second question, I think the best pattern is the second one.
Branching “directly in the code” can be confusing, because it’s not immediately obvious what the two branches accomplish, something which is well-defined if you define a proper abstraction and you implement it using multiple branches for each supported architecture.
Using the dlfcn API is pretty pointless, because you don’t have a uniform interface to call (that’s exactly the argument that supports the second pattern), so it just adds bloats in your code.
HTH