1

Context: I have an open source project which uses JNI. While it's possible to build it yourself, I expect most users will use standard Java tools like Maven, Gradle, or SBT to download the JAR, which contains the pre-compiled binary image. This is no problem on Windows, but on Linux it gets complex.

I'm wondering how much to statically link when creating these packages so it's compatible with most Linux distributions. I know that, for example, Alpine Linux does not come with libstdc++, meaning that it would fail when in a small docker container.

There's also the possibility of older versions. For example, a quick look at nm suggests it's linking _ZNSt11logic_errorC1EPKc@@GLIBCXX_3.4.21 and __vsnprintf_chk@@GLIBC_2.3.4. What if the host has versions older than 3.4.21 and 2.3.4?

However, most literature I've seen tells me not to link against libgcc. Is that still true? Is it the same if I switch to clang (which has its own standard libs?)

  • I think you answered your own question. The first line of link starts with "Statically linking to libstdc++ is fine". For the reasons you give and the reasons in the article I would link statically libstdc. Maybe not for libc. – cauchy Jan 8 at 13:14
  • So as far as I understand there are 3 things to worry about. Libstdc++ (static link), libc (never static link) and libgcc (which comtains pthreads and stuff). The old link says this shouldn't be linked on GNU systems, understandably, but then alpine is broken. – Robert Fraser Jan 8 at 13:31
  • I think one piece of context you haven't really provided here is what the native JNI library actually does. If the native library is simply a computational optimisation (e.g. vector instructions), then you might not actually depend on libc at all. If it is used to call kernel features that aren't supported by Java (e.g. fork) then static linking libc means you'll be depending directly on the Linux kernel. If the part of libc that you used is dependant on system configuration (e.g. dns lookup, passwd file), then you'll be even more dependant on using the particular libc version of the distro. – Lie Ryan Jan 9 at 10:45
4

Static linking glibc is a no-no. My understanding is that is often customized for the distro, so you don't want to be distributing that to machines running different linux distros/releases. There are drop-in replacements for it that you can statically link, like MUSL. I've never tried them, so the rest in that regard is left up to the reader. Static linking everything else should be fine.

One approach I've used very successfully is to build your binary on an old Linux release (including doing static linking where you can). Those binaries will be forward compatible. So you should be able to get yourself some assurance that your binary will run on most distros from the past decade (though as you point out, Alpine is different - it uses MUSL - so you may need to build on Alpine to ensure compatibility). Technically speaking you can use modern releases to target older glibc versions for compatibility, but I found just using the old release to be the simpler.

  • 1
    Besides distro customizations, glibc uses dlopen() to dynamically load parts of itself even when statically linked. The authors say it's not designed for that use case. – Min4Builder Jan 9 at 16:02
0

In general, to be a good system citizen, you'll want to depend as much as possible on system libraries, and avoid static linking any libraries. Your code should only have source level dependency, rather than relying on specific ABI. Since your project is an open source project, you should be fine if you just let the person doing packaging to recompile your app with whatever system libraries are available on the target system. This produces a lean application that is as small as possible for the system, and improves overall system security as users won't have to patch twenty different programs separately when they all statically link to the same library with a security vulnerability.

If your project had been closed source software, this would've been more tricky, as you'd have to recompile for every system yourself as the packager may not be able to recompile your application.

Additionally, you can try to provide a universal app that is statically linked with all dependencies, this is to let users who are using distros that have not packaged your app to run your app without having to recompile themselves. When providing statically linked program, you have two options: try to statically link everything except the few libraries that you know are available in your supported target system (this comes with a nightmare of its own), or you can package the app as snaps or flatpak. I'd suggest the latter is the easiest way to provide a universal app.

Is it good practice to statically link libstdc++ and/or libgcc when creating distributable binaries for Linux?

You'll have to look at your planned supported target system. If you plan to support minimal distros that may not have libstdc++ preinstalled then yes, you should statically link them. Otherwise, you don't need to, they'll just bloat your app, these basic system libraries are designed to have very stable ABIs even across different distros; barring a few precautions, if they're available on the system, you can almost be sure that your app will run just fine with whatever version the system has installed.

If you plan to statically link your application, consider using a libc that's designed to be statically linked, such as musl libc.

  • I'm not sure the answer takes into account that it's a JNI library. Java users aren't sued to needing to compile native code. While users always have the option to compile it, I want to be able to distribute it through Maven, not through any Linux distribution tools, and for that to work it needs a prebuilt library. – Robert Fraser Jan 8 at 14:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.