einpoklum
einpoklum

Reputation: 131385

Can I statically-link a dynamically-linked executable?

I've dynamically-linked an executable, my_app, which has some dependencies it would fill at load time, e.g. libm.so.6, libpthread.so.0 etc. And - the dependencies are satisfied on the system I'm on.

Now, I want to get a statically-linked version of my_app, but - I don't want to do that by rebuilding it from source; I want to bake in the libraries that are currently found on my system, which allow me to run my_app.

Is this possible? Can this be done easily?

Notes:

Upvotes: 1

Views: 3540

Answers (1)

silvergasp
silvergasp

Reputation: 1677

Can I statically-link a dynamically-linked executable?

No not really, but here are some alternatives that might suite your needs.

Creating a wrapper over a dynamically linked library to behave as if it were statically linked is rather challenging and only something I'd ever consider if I didn't have access to the source code. But going the other way i.e. static->dynamically linked library is a lot easier.

What you are describing would be better achieved by crafting your build pipeline to start with statically linkable archives/objects and then optionally transforming them into a dynamic/statically linked executables. The predicate for this is that you have access to a statically linked version of your third-party deps.

Consider the example where you have the following files;

  • foo.{c/h} (depends on libmath)
  • bar.{c/h} (depends on libpthread)
  • main.{c/h} (depends on foo an bar)

You could build this project using the following steps;

# Compile everything as it's own object file, including your main.
clang -c -o foo.o foo.c -fPIC
clang -c -o bar.o bar.c -fPIC
clang -c -o main.o main.o -fPIC
# Create the main archive with all your internal symbols resolved.
llvm-ar r main.a foo.o bar.o main.o

# Create your dynamically linked executable (clangs default). i.e. 
# Resolve your external library symbols.
clang -o main_dynamic main.a -lm -lpthread

# Create a statically linked version. Note that we aren't recompiling from
# source rather we are changing the final stage in our build to choose a
# different linking mode.
clang -o main_static main.a -lm -lpthread -static

# Confirm we don't have any dynamic symbols.
ldd main_static

Something to keep in mind is that statically linking is not as well supported with linux libs as they are with dynamic so expect to run into weird errors like 'DYNAMIC_RELOC' when statically linking etc. A general thing to note as well is that when it comes to libc, it tends to be backwards compatible, but not forwards compatible. So when building your project in statically linked mode, do so on the oldest system that you expect to support, otherwise, you'll run into some really strange bugs, in areas where libc is interacting with the kernel i.e. syscalls.

When you don't have static archives for third-party deps

In this case I'd highly recommend using something along the lines of ubuntu's 'snap' packager or fedoras 'flatpack' which allows you to bundle all your dynamic libs and other assets into an executable format. Think docker, but for regular executables.

You absolutely have to wrap a dynamic library so it behaves as static

I'm going to give a brief summary here as a complete response to this would be too long form for StackOverflow. Though there is a more complete guide here "Position-Independent Code with GCC for ARM Cortex-M" by Erich Styger, where he describes how to psuedo-statically link a dynamically linked executable for a microcontroller. In summary, this was the process to do so;

  • Compile a dynamically linked library
  • Compile your executable and link it against your dynamic library
  • Write your own loader to load the dynamically linked library
    • Parse the ELF headers in your dynamic lib
    • Create a jump table that redirects calls to your dynamic lib through the global offset table (GOT).
    • Implement lazy loading so that you only initialise your GOT when you actually call a function in your dynamic lib.
  • Dump the dynamic lib into an ELF section so that it is included in the same binary.

Upvotes: 2

Related Questions