How to run software that requires legacy library version?

I have an old app that requires old OpenCV version (<=2.4.9) and crashes on newer versions (OpenCV partially dropped support for C API since 2.5). I used to just use really really old distro and blacklist opencv update however as that release is no longer supported at all - I've been forced to update. Current version of openCV is 3.1.

Can I use containers for that? I only need this one library to be old. I can compile old OpenCV however I'm a bit worried about X support - it's graphical app that obviously uses camera. Or maybe there's better solution?


Solution 1:

The problem with just replacing OpenVZ is that many libraries depend on each other (the old "DLL Hell" problem). Some libraries depend on OpenVZ, and OpenVZ depends on other libraries, which depend on yet more libraries, etc.

Depending on how old you need to go, you could avoid using containers by bringing in a "cohesive subset" of old libraries into a directory, then use LD_LIBRARY_PATH environment variable to point the dynamic loader at your old libs.

This usually works well if the version of libstdc++ and libc on your host system (in /lib or /usr/lib) is ABI-compatible with the version that was used to compile and link your old version of OpenCV (and its dependencies). Unfortunately, unlike the Linux kernel ABI, the libc ABI changes occasionally, and the libstdc++ ABI changes relatively frequently.

So, grabbing an old binary of OpenCV with the version you need would come with roughly this process:

  • Try just the old OpenCV library in the LD_LIBRARY_PATH directory. If it doesn't work, you'll get errors about missing libraries (assuming the dependencies do versioning correctly); go grab those missing libraries and put them in the same directory as the old OpenCV. Repeat until missing library errors go away.
  • If you get to a point where you get symbol lookup errors in libstdc++ or libc, or complaints about bad glibc version, you're up a creek without a paddle, and your only solutions (other than virtualization and installing an old OS version) are...

Flatpak

http://flatpak.org/ - an application packaging format that includes all dependency libs :)

and

Containers

Containers is a good approach, because good container solutions like LXC and LXD completely isolate the guest, and even let the guest run its own PID 1 (init daemon). Basically, to start any process on Linux, certain things have to be compatible between what you're running already and what you're starting, because for example the dynamic loader (libdl) has to be able to load the shared libraries. But containers have a way of completely isolating that, so you can use incompatible versions of libc, libdl, and libstdc++ on the same host kernel.

I'd recommend LXD if you run Ubuntu >= 16.04, or OpenVZ if you run an old Debian or CentOS/RHEL host system. Unfortunately LXD isn't too easy to set up on non-Ubuntu distros, and OpenVZ doesn't (yet) support the latest distros.

The nice thing about the Linux kernel is that, with a few exceptions for driver-specific interfaces (like the Direct Rendering Manager), the Linux kernel ABI is stable for a long time. That means:

  • Old "userspaces" (everything that runs on top of the kernel as opposed to within it) should work on quite a lot newer kernels.
  • New userspaces should work on quite a lot older kernels.

In practice, what this means is that unless you're using 3d graphics drivers or other sorts of specialized hardware in your container, it should "Just Work" regardless of the age difference between the container system and the host kernel (up to a certain limit; software compiled for Linux kernel older than 2.6.9 generally won't work on a modern kernel, and older than 2.6.0 definitely will not work.)

This leaves a gradation of "minimal effort required" to run older libraries (or binaries that depend on older libraries), depending on just how old and how deep the oldness goes:

  • Minimal: Drop in the old version of the library into a directory, set LD_LIBRARY_PATH and you're done.
  • Typical (this is used with many proprietary games and programs): Drop i the old version of the library, and all of its dependencies except for libc, set LD_LIBRARY_PATH and you're done.
  • Extensive library replacement (buggy; might not work): Drop the old version of the library, libstdc++, libc, libdl, and all of its dependencies into a directory, set LD_LIBRARY_PATH, and you're done.
  • Containers: Install a complete base system for another, older OS on top of your host kernel without running any virtualization (you're still only running one copy of the Linux kernel). Fast, but takes up several gigs of disk space. Extremely compatible with just about anything you can throw at it (including ancient distros).
  • Virtual machines: Can run anything, even non-Linux OSes, and compatibility isn't a concern as long as the guest OS software runs on the same CPU as your hardware is running.