23

Let me explain the problem with an example. I use some old program in my day to day work, like for example xfig and pdfedit.

Now, these programs are quite old and not updated too often; my fear is that one day or the other they will not work anymore for lack of some library or some incompatible update.

If the program is easy to compile now, on a running system, the solution is handy: try to hack a bit the source and compile it statically --- the resulting executable will be big and not so efficient, but it will work for the foreseeable future(1). This seem to be the case for xfig and I will try it as soon as possible.

But, for example, pdfedit depends on Qt3, and setting up a system to compile it is quite complex at this time. Fortunately it can be run right now, thanks to the fact that the library it needs do not conflict with anything. But this can change in the future, so I would like to solve this problem:

How can can I make a static binary (or similar thing) if I have a dynamic one and all the libraries, but no source code, on Ubuntu?

I searched around. One possibility is statifier(2), but it has a lot of problems with the address randomization, so it's a no-no. The non-free version, Ermine, seems to work, but I would really prefer an open source option.

Another possibility is to use docker or a similar packaging system. But all the tutorial I found are quite RedHat-oriented; and, honestly, quite complex to follow.


Footnotes:

(1) is not so crazy. I use a static ffmpeg for example, works ok and without any compatibility problems...

(2) to compile statifier, see https://stackoverflow.com/questions/23498237/compile-program-for-32bit-on-64bit-linux-os-causes-fatal-error

Rmano
  • 31,627
  • 16
  • 118
  • 187

4 Answers4

22

You can solve your problem in another and more simple way:

Use ldd on your executable to see the linked libraries, for example:

$ ldd /bin/bash
linux-vdso.so.1 =>  (0x00007fffb2fd4000)
libtinfo.so.5 => /lib/x86_64-linux-gnu/libtinfo.so.5 (0x00007fac9ef91000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fac9ed8d000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fac9e9c6000)
/lib64/ld-linux-x86-64.so.2 (0x00007fac9f1e1000)

Then collect all the libraries in a folder and set the LD_LIBRARY_PATH environment variable before running your program to point at this folder:

$ LD_LIBRARY_PATH="/opt/my_program/lib" /opt/my_program/start

Alternatively you can add an entry for the lib folder to /etc/ld.so.conf.d/. But that would apply the change systemwide.

Klaus D.
  • 791
  • 4
  • 9
  • 1
    This is a good idea --- although I would really like to find a way to package all this into an executable; this solution can be affected by changes in the loader (although I hope no one will do such a thing in non-backward compatible way). Will award the bounty if no better solutions arise --- thanks. – Rmano Nov 07 '14 at 11:30
  • Well you could add this to a little installation script and link it to the local path. I like this solution, could have used it a time back. – WalyKu Nov 09 '14 at 19:04
  • 1
    @Klaus, `linux-vdso.so.1` is nowhere to be seen, I suppose it's in the kernel, correct? – Rmano Nov 12 '14 at 09:48
  • 1
    Yes. From `man 7 vdso`: "The "vDSO" (virtual dynamic shared object) is a small shared library that the kernel automatically maps into the address space of all user-space applications." – Klaus D. Nov 12 '14 at 09:49
  • Although this is not *strictly* an answer to the question, it is a reasonable workaround. Thanks. – Rmano Nov 13 '14 at 09:40
  • on osx, `otool -L` instead of `ldd`, btw. – keen May 17 '18 at 15:34
3

Opensource mkblob can make a new binary executable which includes all the dependencies your program needs and you will be able to distribute it to other (also later) distributions than the one it was compiled on. It works a little like Statifier and Ermine which was mentioned.

JoeMittile
  • 31
  • 1
  • Hi! Seems nice, but I haven't been able to use it on Ubuntu. Have you any hint? – Rmano Oct 03 '20 at 09:02
  • There is a precompiled binary and deb/rpm in https://github.com/sigurd-dev/mkblob/tree/master/binary_X86_64 Also it seems I could compile it in Ubuntu 18.04 with the makeall_el8.sh. – JoeMittile Oct 04 '20 at 14:08
  • Thanks --- yes, I installed the .deb but it does not seems to work with a couple of binaries I checked... I mean, the binary runs, but it does not succeed in creating a static binary with some programs. Well, time for another question! Thanks anyway. – Rmano Oct 04 '20 at 17:37
3

I wrote packelf.sh based on Klaus D's idea.

You can use it like this:

~ # packelf.sh `which perf` /root/perf
~ # /root/perf --version
perf version 3.10.0-1160.49.1.el7.x86_64.debug

packelf.sh:

#!/bin/bash
set -eo pipefail

[ $# -lt 2 ] && {
    echo "usage: $0 <ELF_SRC_PATH> <DST_PATH> [ADDITIONAL_LIBS ...]"
    exit 1
}

src="$1"
dst="$2"
shift
shift

cat >"$dst" <<EOF
#!/bin/bash
tmp_dir="\$(mktemp -d)"
check_path="\$tmp_dir/__check_permission__"
trap 'rm -rf \$tmp_dir' 0 1 2 3 6
if ! (touch "\$check_path" && chmod +x "\$check_path" && [ -x "\$check_path" ]); then
    rm -rf "\$tmp_dir"
    tmp_dir="\$(TMPDIR="\$(pwd)" mktemp -d)"
fi
sed '1,/^#__END__$/d' "\$0" | tar -xz -C "\$tmp_dir"
"\$tmp_dir/LD_SO" --library-path "\$tmp_dir" "\$tmp_dir/PROGRAM" "\$@"
exit \$?
#__END__
EOF

libs="$(ldd "$src" | grep -F '/' | sed -E 's|[^/]*/([^ ]+).*?|/\1|')"
ld_so="$(echo "$libs" | grep -F '/ld-linux-')"
sed -E -i -e 's|PROGRAM|'"$(basename "$src")"'|' -e 's|LD_SO|'"$(basename "$ld_so")"'|' "$dst"
tar -czh --transform 's/.*\///g' "$src" $libs "$@" >>"$dst" 2> >(grep -v 'Removing leading' >&2)
chmod +x "$dst"
oufme
  • 31
  • 2
2

One suggestion regarding statifier:

If address space layout randomization (ASLR) is causing it to fail you don't have to turn it off for the whole machine. You can turn it off just for that process:

$ setarch `uname -m` -R statified_pdfedit [args...]

It'll run that command with randomized layout disabled (no need to be root).

lemonsqueeze
  • 1,624
  • 12
  • 17