Today, we had a chat with a colleague about whether a C/C++ binary (ELF) built on one Linux distribution would work on another. After some research, I found that default GCC builds are dynamically linked, and the ELF file contains:
- Your program's code
- A list of dynamic dependencies (shared libraries) it needs
- Symbols that need to be resolved at runtime
However, it does not contain the actual shared libraries - those need to be present on the system where you run the program. You can see these dependencies using ldd. For example, a simple C "hello world" program with only a printf() call can be built with gcc hello.c -o hello. ldd hello output:
linux-vdso.so.1 (0x00007fff6a0e1000)libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fdaf6400000)/lib64/ld-linux-x86-64.so.2 (0x00007fdaf66b3000)
The files size is 15960 bytes. To make is truly independent of any shared library we would build it with gcc -static hello.c -o hello_static. ldd hello_static shows:
not a dynamic executable
The size of hello_static is a whopping 900344 bytes, 56X more than the dynamically linked build.
The good news is that these libraries are present by default on virtually every Linux distribution, so including them in every executable would waste a lot of space. However, you must ensure that C++ version (C++17, C++20, etc.) specific features used in your code are supported by the gcc/g++ version on the target Linux distribution. Of course, the CPU architecture has to be the same too — that goes without saying.
On Windows, C++ libraries come with Visual C++ Redistributable:
Note that if a Windows PC can successfully load and run a DLL compiled with a specific C++ version, it can also run an EXE compiled with that C++ version because the C++ runtime requirements are the same whether the code is in a DLL or EXE. The only difference is how the code is packaged and loaded, not its runtime requirements.