Rules of static linking: libstdc++, Libc, libgcc (2012)
micro.nicholaswilson.me.uk
micro.nicholaswilson.me.uk
To expand a bit:
1. dlopen has interactions with static linking that you should understand if you use both at the same time. Different libcs expose different symbols for libc functions, so building your dynamic library against one libc may make it incompatible with another. And two libcs in the same program (one from the dylib, one from the binary) is a recipe for a very bad time - imagine freeing a pointer allocated with a different malloc implementation, or a different layout of libc structs
2. the complexity with glibc is it invokes dlopen as part of NSS, a feature that gets used as part of looking up users, among other things (it does this to allow integration with LDAP etc). You can actually see warnings at link time if you statically link glibc but pull in symbols that use NSS. You can disable NSS if you like at build time of glibc itself [0] (i.e. not your binary)
3. musl doesn't have the complexities of NSS and can be statically linked happily by default (I suspect that's probably what musl is most used for)
[0] https://sourceware.org/glibc/wiki/FAQ#Even_statically_linked...
Depends on the exact architecture of the program, y'know. I remember a Windows app written in Delphi (so no libc whatever) that supported loading plugins written it whatever; and I've seen it load simultaneously plugins that linked against msvcrt90.dll and msvcrt100.dll with no problem. But that worked because plugins were required to export a FreeObject function that is supposed to be used to free whatever structures a plugin returned from its other functions. The main program tracked what piece of data came from where and called proper deallocation functions.
But that requires acknowledgement of a fact that there is no "single, global C runtime".
It’s annoying but it’s something you have to handle.
As for supporting multiple DLLs using different run time libs, I’d guess that only works because they are slightly more complex than simple shared libs and can sort out thread local storage and the like during thread attach. Without that I can imagine things going badly wrong.
On Windows, yes, giving rise to situations where you have a dynamic library and the program using it, linked against two different C runtimes. On a Unix like OS you usually have a single, global libc. You would have to deliberately link it statically into a shared library, or a program that uses a shared library, to create a similar situation.
For the standard C use case, if the library malloc()s something and passes the result pointer to the program, the program itself has no way to free it. The two have their own, independent copies of the allocation code that don't share the allocation arena. If the program calls free(), the best case scenario is that the libc of the program realizies it doesn't recognize the pointer, yells "double free" and abort()s.
That's a bit of a portability pit-fall and definitely something one has to consider when designing a library ABI. Pointers returned from the library have to be passed back into the library to free the underlying memory. A solution like the one used by Delphi is required. (For polymorphic objects being passed around, you would typically have something like a destroy() hook anyway).
I'm a bit hesitant to make a guess on what happens in the case of a C++ smart pointer. Technically they can have an allocator, but does that cross the ABI bounds? Is there a template specialization for optimizing it away if you are using the default delete operator? I guess the compiler of the program could also end up instantaiting the programs allocator when inserting the template parameters?
So funcA() from a.dll and funcB from b.dll, are actually resolved as a!funcA and b!funcB, hence why linking against msvcrt90.dll and msvcrt100.dll works without major issues.
Using dlopen when static linking is used is deprecated in glibc.
Should libraries be allocating memory to begin with? They should simply provide the data structures. The main program should decide whether to allocate on the stack, statically or dynamically.
Libaries shouldn't do anything unless called explicitly. Even when called, they should cede as much control as possible. They should probably be exposing the concurrent functions so the caller can decide how to thread them... Every time a library decides to make things easy for the user it leads to insane problems like these.
All this stuff is there because of the built-in threading support in COM which, arguably, was pretty convoluted for some rather strange reasons ― what's the point of such precise RAII-ing of the DLLs? I guess the address space was really precious back then or something.
[0] https://devblogs.microsoft.com/oldnewthing/20131105-00/?p=27...
Can anyone explain? Why can't libc and libgcc be statically linked? I'm not surprised, just curious.
Domain name resolution behaviour should be program-controllable, not system-controllable (and thankfully we are starting to see a bit of sanity with web browsers which do bypass libc's domain name resolution) and especially not plug-in based which causes BS like things working differently whether avahi is installed or not.
Note however that this will only work for simple non-gui apps. GUI apps generally need to load opengl drivers (which are shared objects) - thus you'd need to choose at link time which GL driver you want to use... which is not a practical thing to do.
The only downside of statically linking libgcc, as long as you keep your symbol visibility sane, i.e. not re-exporting everything which is unfortunately the default :(, is C++ exception across shared library boundary is not guaranteed to work. But if you are trying to make your library portable across different distro, having C++ thing in your external interface is never a good idea. Especially when you statically link libstdc++.
This is pure insanity, of course, but i'm sure i'd be entertained by you trying to justify it, if you're inclined.
systemd-resolved offers a nice compromise since, in addition to the rich DBUS API, it also acts as a recursive resolver on 127.0.0.53. So any applications that do implement their own resolution can be pointed there.
I disagree, strongly.
The last thing I want is to get every of my applications Avahi / Zeroconf aware for DNS or SSSD aware in enterprise environment for user authentication. Or worst, having to recompile them for that.
System controllable for aspect like user authentication or service discovery makes perfect sense.
What does not makes sense is to have it implemented as a glibc plugin system. This triggers a lot of problems for system that do not have the glibc (Golang / musl / statically linked binaries )
Ideally, that should be done through an IPC mechanism through to a local daemon to benefit of the best of both worlds.
It's also worth noting that if you statically link Glibc, you'll need to make more of an effort to comply with the LGPL. If you dynamically link, you automatically comply with 4(d).
I'm creating a python binding (on linux). The company tool chain uses clang/llvm/libc++ , whereas python is built with gcc.
what should I do? is it ok to statically link libc++ ?