Thursday, February 25, 2016

Patching binaries

My friend asked for help - he has a legacy system that he wants to migrate to new hardware. His Linux OS is 10 years old and it becomes more and more challenging to find hardware to run it on. Long story short, I was asked to make his ten years old binaries to run on a modern Ubuntu.

Fortunately Linux has very impressive ABI compatibility, so my job was down to arranging executables and their dependent libraries. Well, almost.

There three ways of telling an executable (or actually interpreter) where to search for its libraries

  • Setting rpath on the executable itself.
  • Setting LD_LIBRARY_PATH environment variable.
  • Changing system wide configuration for to look into additional directories.

The binaries were setuid, and thus LD_LIBRARY_PATH was ruled out.

Next, I've tried to overcome it by putting libraries in /opt/old-stuff/lib and adding it to /etc/ This gave me some progress, but I hit the wall with naming collisions - my oldy binary was relying on older libreadline and I had two libs - one in /lib and one in /opt/old-stuff/lib. The latter was obviously further down the search path, since otherwise it would break practically every command-line tool in the system.

So I needed to make my binary to use its own specific version of libreadline and to leave others using the default one. The only way to go was using rpath. Fortunately there is nifty utility out there called patchelf:

patchelf --set-rpath /opt/old-stuff/lib /opt/old-stuff/bin/foo
That almost did the trick. The caveat was that foo was using other library and only that library itself utilized libreadline. So the solution was to set rpath on all libraries as well:
for file in /opt/old-stuff/lib/*; do
    patchelf --set-rpath /opt/old-stuff/lib "$file"
Overall that was quite a shift from my current daily programming routine. I did not have to think about linkers for quite a lot of time by now and it was fun to have a taste of this stuff back again.