Honest question: what's the difference between storing dependencies in the executable (statically) and storing them in the Docker image?

When statically linking the linker can exclude functions that aren't referenced (directly or indirectly) by the main application. For something like libc or, especially, the C++ standard library many and often even most routines can be excluded.

OTOH, container images seem to be bloated by unused binaries and completely unused libraries, not unused functions in shared libraries.

What are the main advantages of excluding unnecessary library functions / libraries / executables?

Here are some possible advantages I can think of: less surface area for security, less surface area for maintenance, smaller container image, implying faster time to pull image, and perhaps also faster time to boot image.

Disadvantages: you have to do the work to set it up in the first place and maintain it (putting in extra effort to avoid adding superfluous dependencies in future), while the above advantages may be marginal.

One advantage I've recently encountered is that CVE scanners like Nessus or Stackrox seem to be naive. They don't seem to examine binaries for statically linked libraries, so a container with only a single statically linked binary is likely to stay nominally CVE-free in perpetuity.

This might also be true if you simply copy shared libraries. AFAICT these scanners rely on the package index to identify potential CVE exploits. But I may be wrong, and in any event it'd be much more difficult to identify the version of a statically linked library than a shared library, which can be identified by its name or hash.

This doesn't excuse avoiding keeping containers updated, but if you want to be cynical about vulnerability management, i.e. that any trick which improves the signal-to-noise ratio is worth exploring, then it's something to keep in mind.

It sounds like static linking would also hide CVEs that do affect the functions that were compiled into and are used by your statically linked binary in a way that would introduce security issues for that binary.

BTW IIRC normal static linking doesn't strip out all unused functions, you need LTO (Link Time Optimisation) to do that.

Normal static linking does not include objects that aren’t referenced. If you put every function in its own object file, then unused functions will not be linked in, regardless of how many objects files are in the library. If you have 10,000 functions in two object files, then you can link none, either, or both - all sunsets of object files, but likely many unused functions.

LTO does something more complex than just pick the right routines - it does the last few compilation/optimization stages when you run the linker. As a result, you get to inline across modules, drop some unused functions and more - however, this would only work for object files that were properly LTO compiled themselves. If you have e.g. a third party library that wasn’t LTO compiled for your specific tool chain, you are back to the “whole object file or bust” link regime.

The wide gap between those two options is kind of dissatisfying, it would be cool if LTO could disassemble existing object files and do the unused code removal on the assembly language layer. I guess that Facebook BOLT and Google Propeller are working on an approach like that.

https://github.com/facebookincubator/BOLT https://www.phoronix.com/scan.php?page=news_item&px=Google-P...