Portable GNU-Linux binaries

From OHRRPGCE-Wiki
Jump to: navigation, search

The builds we distribute for GNU/Linux, including those included in exported "Linux Tarballs" and "Debian Linux Packages" aren't portable to other distributions.

32 vs 64 bit[edit]

  • We should have separate x86 and x86_64 builds
  • It's also common for people to distribute tarballs for games on linux which include both 32 and 64 bit builds and pick the right one with a shell script.

Very commonly, a shell script is also used to set a relatively path for the linker for included libraries, although it is possible to set a link-time flag when compiling to look in the same directory as the executable (or some other relative directory) for shared libraries with "gcc -Wl,-rpath,'$ORIGIN'". Using a shell script however makes it easier for people to understand and override this behaviour.

Libraries[edit]

The most important thing is to not require a recent version of glibc. For creating portable binaries I have seen people recommend building on an old distro such as CentOS 5. But using a too-ancient distro will result in requiring libraries like libstdc++5 (gcc 3.2-3.3.3 from 2002-2004) that some distros like Ubuntu don't bother to include by default (while Slackware does, but not libstdc++4). An ELF binary includes fine-grained symbol version requirement tags for glibc and some other libraries, and these requirements don't get added unless you actually use a function or global which is annotated with such a requirement. So theoretically it is OK to compile against a recent glibc as long as you check the result.

I looked at how several Linux games I had were packaged/distributed. Almost all of them included separate binaries for x86 and for x64, though Jason Rohrer only distributed 32 bit binaries and when I pointed out to him that none of his games would work on non-multilib 64 bit linux (e.g. Ubuntu by default) he said no one had complained (but I think his games distributed via steam should always run, since steam provides a full set of libraries).

  • Almost everyone included .sos for SDL/SDL2 and other SDL libraries if they used them, as well as libraries like GLEW
  • A few of the games included libstdc++.so.X or libgcc_s or similiar
  • A few included libogg, etc.
  • Nobody seemed to have statically linked anything such as libc or other common libraries
  • Steam includes heaps of libraries to emulate Ubuntu 12.04 that override many of the system ones; on Slackware certain games would not run until I deleted this whole lot.


It's not necessary to statically link the binaries. I think we should include just SDL and SDL_mixer libraries (each about 450kB). However, this is not enough to hear MIDI music. Android builds also need to include MIDI instrument patches, and I think we found some relatively small ones for that? It might be possible to include patches, but prefer any that are already installed on the system, but I'm guessing this would require patching SDL_mixer.

It might be a good idea to link libstdc++ statically with -static-libstdc++ if it doesn't add much to the file size, just to be sure. The last compatibility-breaking release was with GCC 3.4.0 (20040419).

libstdc++.so[edit]

The required version of libstdc++.so depends on the version of g++ used (see the table here, item 4) and the features of that library which are actually used. These can be checked by looking at GLIBCXX in the objdump -p output; see below. New versions of libstdc++.so.X.Y can be used with any program compiled against libstdc++.so.X.Z with Z <= Y.

Our options:

  • compile with an old version of GCC
  • ensure that we aren't using any functions in libstdc++.so that are new (by checking GLIBCXX)
  • statically link to libstdc++.so
    • CXXLINKFLAGS += ['-static-libstdc++']
  • include libstdc++.so in the packages and link to it with LD_LIBRARY_PATH

On 32-bit x86, statically linking libstdc++.so.6.0.21 adds about 1MB to the binary size (with debug=0), and 400KB after zipping. libstdc++.so itself zips to 500KB. Since unlump, relump, ohrrpgce-game and ohrrpgce-custom all depend on it, it save a bit to ship libstdc++.so

libgcc_s.so[edit]

This dependency can be avoided by adding CXXLINKFLAGS += ['-static-libgcc']. It only adds 15KB to the binary!

libtinfo.so[edit]

Currently nightly linux builds don't run on my Slackware system at all because they require libtinfo.so, which is a very common incompatibility between distros. ncurses can be compiled either as just libncurses, or as separate libtinfo and libncurses libraries, where libtinfo just has terminfo functions. Various distros compile it differently; Debian compiles as two libraries. If you get an error the workaround is to symlink libtinfo.so to libncurses.so. FB itself requires libtinfo if it exists, and otherwise falls back to libncurses. (This is logic in src/compiler/fbc.bas) The proper fix is to make sure it is compiled to only require libncurses. I should submit a patch to FB to override fbc's well-intentioned but annoying behaviour.

Testing compatibility[edit]

To check a binary's dependencies, use objdump -p. E.g., for the most recent x86 nightly build:

> objdump -p ohrrpgce-game 

ohrrpgce-game:     file format elf32-i386

Program Header:
    PHDR off    0x00000034 vaddr 0x08048034 paddr 0x08048034 align 2**2
         filesz 0x00000100 memsz 0x00000100 flags r-x
  INTERP off    0x00000134 vaddr 0x08048134 paddr 0x08048134 align 2**0
         filesz 0x00000013 memsz 0x00000013 flags r--
    LOAD off    0x00000000 vaddr 0x08048000 paddr 0x08048000 align 2**12
         filesz 0x0020460a memsz 0x0020460a flags r-x
    LOAD off    0x0020460c vaddr 0x0824d60c paddr 0x0824d60c align 2**12
         filesz 0x00008ccc memsz 0x000321e4 flags rw-
 DYNAMIC off    0x00204698 vaddr 0x0824d698 paddr 0x0824d698 align 2**2
         filesz 0x00000160 memsz 0x00000160 flags rw-
    NOTE off    0x00000148 vaddr 0x08048148 paddr 0x08048148 align 2**2
         filesz 0x00000044 memsz 0x00000044 flags r--
EH_FRAME off    0x002021ec vaddr 0x0824a1ec paddr 0x0824a1ec align 2**2
         filesz 0x000004dc memsz 0x000004dc flags r--
   STACK off    0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**2
         filesz 0x00000000 memsz 0x00000000 flags rwx

Dynamic Section:
  NEEDED               libncurses.so.5
  NEEDED               libtinfo.so.5
  NEEDED               libpthread.so.0
  NEEDED               libSDL-1.2.so.0
  NEEDED               libSDL_mixer-1.2.so.0
  NEEDED               libX11.so.6
  NEEDED               libXext.so.6
  NEEDED               libXpm.so.4
  NEEDED               libXrandr.so.2
  NEEDED               libXrender.so.1
  NEEDED               libstdc++.so.6
  NEEDED               libm.so.6
  NEEDED               libgcc_s.so.1
  NEEDED               libc.so.6
  NEEDED               libdl.so.2
  INIT                 0x0804c304
  FINI                 0x0823133c
  INIT_ARRAY           0x0824d60c
  INIT_ARRAYSZ         0x0000006c
  FINI_ARRAY           0x0824d678
  FINI_ARRAYSZ         0x0000001c
  HASH                 0x0804818c
  GNU_HASH             0x08048b1c
  STRTAB               0x0804a188
  SYMTAB               0x08048bd8
  STRSZ                0x000012a3
  SYMENT               0x00000010
  DEBUG                0x00000000
  PLTGOT               0x0824d800
  PLTRELSZ             0x00000a48
  PLTREL               0x00000011
  JMPREL               0x0804b8bc
  REL                  0x0804b874
  RELSZ                0x00000048
  RELENT               0x00000008
  VERNEED              0x0804b6e4
  VERNEEDNUM           0x00000006
  VERSYM               0x0804b42c

Version References:
  required from libgcc_s.so.1:
    0x0b792650 0x00 20 GCC_3.0
    0x0d696910 0x00 15 GLIBC_2.0
  required from libdl.so.2:
    0x0d696910 0x00 18 GLIBC_2.0
    0x0d696911 0x00 12 GLIBC_2.1
  required from libm.so.6:
    0x0d696911 0x00 19 GLIBC_2.1
    0x0d696910 0x00 10 GLIBC_2.0
  required from libstdc++.so.6:
    0x056bafd3 0x00 14 CXXABI_1.3
    0x0297f865 0x00 08 GLIBCXX_3.4.15
    0x08922974 0x00 06 GLIBCXX_3.4
  required from libc.so.6:
    0x09691f73 0x00 17 GLIBC_2.1.3
    0x0d696912 0x00 16 GLIBC_2.2
    0x0d696913 0x00 11 GLIBC_2.3
    0x0d696910 0x00 07 GLIBC_2.0
    0x09691a73 0x00 05 GLIBC_2.2.3
    0x0d696911 0x00 03 GLIBC_2.1
  required from libpthread.so.0:
    0x0d696912 0x00 13 GLIBC_2.2
    0x0d696911 0x00 09 GLIBC_2.1
    0x0d696910 0x00 04 GLIBC_2.0
    0x09691972 0x00 02 GLIBC_2.3.2

See [1] for making sense of this. It seems this binary requires glibc 2.3.2+, which was released 2003-02-28. libstdc++.so.6 was first introduced in GCC 3.4.0 GLIBCXX_3.4.15 is GCC 4.6.0+, which was released 20110325. For example Ubuntu 11.10 defaults to installing gcc 4.6 (I don't know what ) but some conservative distros released in 2012 may not support this binary. I guess at present (2016) requiring a 2012 release of a linux distro is acceptable.

Comparing the above to a binary built on slackware64-current with a very recent glibc, the version requirements are identical except that libtinfo.so.5 isn't required and there are no GLIBC_* tags at all for libdl.


See Also[edit]