This is a good primer on the subject.<p>One of the major projects I maintain at $work is an apache module, which has given me so much grief because of the fact that it gets dynamically loaded (and then unloaded, and then reloaded)<p>It's been very enlightening, but there's no shortage of frustration.<p>One major points is that a dynamically loaded library can have undefined symbols that are found (or not!) at runtime. This can introduce a lot of trouble when trying to make a test binary. Especially in apache's case, since any of the `ap_` symbols are only compiled into the executable, and aren't available as a static or shared library.<p>In that situation, the only recourse that I'm aware of is to supply your own implementations of those functions that get compiled into the test binary (<i>not</i> the dynamic library under test, since that will create a collision when it's loaded)<p>Another thing to consider is that linux/elf shared libraries can be either dynamically loaded or dynamically linked. This is not true on all platforms, so it's not advisable to lean into it if portability matters.<p>I recommend reading the ld.so manpage[0] -- in particular it outlines many environment variables to control the runtime linker. Notably, LD_DEBUG can be a life saver.<p>Lastly, I'll also warn against using any global/static memory in a dynamically loaded library. It can be unloaded/reloaded, and that can create a lot of havoc, especially if a pointer to that memory gets saved somewhere that survives the unload, and then is accessible after the library gets reloaded.<p>libprotobuf is a major victim of these types of issues - which is why (or a major contributor) to why the libprotobuf-lite library exists.<p>[0] <a href="https://man7.org/linux/man-pages/man8/ld.so.8.html" rel="nofollow">https://man7.org/linux/man-pages/man8/ld.so.8.html</a>
I have to care about this sort of thing at great length in my current work. Targeting hundreds of linux / embedded environments. I picked up a tip or two and in general this is a very well written overview of the moving pieces I wish I'd found when I first had to dig into this domain
The first time I learned about dynamic loading was while watching an episode of Casey Muratori's Handmade Hero - a series where he builds a video game, in C, from scratch.<p>He compiles the game code as a dll and dynamically loads it at runtime in the Win32 platform layer code. This way he can keep platform code and game code separate and reload the game code at runtime if any changes are made. Being new to this technique, I was impressed to say the least.<p>I always assumed the same functionality was available in the linux environment, but I hadn't bothered to look it up. Now I know.<p>For those who want to learn how to do this in Win32, here's the episode I mentioned above - <a href="https://www.youtube.com/watch?v=WMSBRk5WG58" rel="nofollow">https://www.youtube.com/watch?v=WMSBRk5WG58</a>
From the other side of it, there's "How to Write Shared Libraries": <a href="https://akkadia.org/drepper/dsohowto.pdf" rel="nofollow">https://akkadia.org/drepper/dsohowto.pdf</a><p>Edit: LD_PRELOAD would have been useful to mention in the article. It's a useful mechanism, e.g. for profiling by hooking relevant functions, like the PMPI mechanism in the MPI standard.
This article, along with [1] (HN discussion [2]) is what finally made the linking process "click" in my brain.<p>[1] <a href="https://werat.dev/blog/how-wine-works-101/" rel="nofollow">https://werat.dev/blog/how-wine-works-101/</a><p>[2] <a href="https://news.ycombinator.com/item?id=33156727" rel="nofollow">https://news.ycombinator.com/item?id=33156727</a>
Very useful article! I wish I had read this a long time ago, would've made a lot of things much easier to understand.<p>I'd like to comment on this:<p>> When the relocations are complete, the dynamic linker allows any loaded shared object to execute optional initialization code. This functionality allows the library to initialize internal data and prepare for use.<p>> This code is defined in the .init section of the ELF image. When the library is unloaded, it may also call a termination function (defined as the .fini section in the image).<p>There are huge problems with this stuff. Are these sections still used? I know you can add functions to these sections with a GCC attribute but I don't know why anyone would do that. Do other languages like C++ use these sections?<p><a href="https://blogs.oracle.com/solaris/post/init-and-fini-processing-who-designed-this" rel="nofollow">https://blogs.oracle.com/solaris/post/init-and-fini-processi...</a>
Very helpful article, clear and informative. Understanding at least the basics of how the linker works at a basic level is very illuminating, and for embedded programming even more so. Found this recently which is a nice complement to this article:<p><a href="https://microcontrollerslab.com/bare-metal-embedded-systems-linker-script-file/" rel="nofollow">https://microcontrollerslab.com/bare-metal-embedded-systems-...</a>
Concerning ldd, see the security warning in ldd(1). I'm not sure if it's still an issue in current GNU binutils, but lddtree is supposed to be safe and the tree may be more useful.