Portable executables in this context are executables compiled from source code on one machine, which can then be transferred to and run on another machine. This document only applies to Unix-based systems as I don't have any experience of non-Unix systems (Macintosh, MS Windows) and can't comment on the portability issues for those platforms.
The content of this page is based on personal experience and should be treated with some scepticism.
There are two key factors that influence portability:
These factors are discussed in the following sections.
Differences in architecture and implementation between different operating systems mean that executables built for example on an SGI will not run on a DEC Alpha. More subtly, differences of this nature between versions of a single basic operating system may also mean that executables built on one version are not portable to another - usually this is a backwards compatibility issue, for example: OSF1 V4.0 executables will run (suboptimally) on OSF1 V5.1, however by default V5.1 executables will not run on V4.0.
In practice there are limited strategies available for overcoming these differences. One option is cross-compilation (building executables on one system to run on another, but this is something I have no experience of). See the section on Platform-specific portability issues for more discussion.
Libraries are either static archives or shared objects (also called dynamic archives). An executable which is linked against a static library is referred to here as a statically-linked executable and includes a copy of the code that it requires from that library inside itself - so at runtime the required code is always available to the executable.
An executable which is linked against a dynamic library is referred to as a dynamically-linked executable. It doesn't include the code from the library, instead it has to load the required code at runtime. The dynamically-linked executable is smaller, but it relies on the shared object being available and accessible at runtime.
By contrast the statically-linked executable is larger (because it contains additional code from the library) but it doesn't need the library to be present at runtime, and so is more portable.
A totally portable executable is one which is not dynamically linked to any of the libraries that it depends on. In practice this may not be possible to achieve (see the section on Platform-specific portability issues, below). However, dynamically-linked executables can still be considered to be portable, provided that the target platform also has dynamic versions of the required libraries. Thus the target for portability becomes minimising the number of dynamically linked dependencies.
Generally if both static and dynamic achives of the same library are available, then executables will by default be linked against the dynamic archive. This default behaviour can be modified by using linker flags, e.g. for the GNU compiler set:
These arguments are positionally-sensitive in that they affect the subsequent -l options, and can be used multiple times. This means that it is possible to specify a mixture of static and dynamic libraries in a single compile/link line. For example:
gcc ... -non_shared -lmylib1 -lmylib2 -call_shared -lmylib3 ...
would force linking against static versions of libmylib1 and libmylib2, and then switch to dynamic linking for libmylib3.
First, try to build purely statically-linked executables by including the -non_shared (or equivalent) option.
If the executable compiles at this stage then it is highly likely that it will be completely portable (within the limits of operating system portability).
If the executable fails to compile due to "missing symbols", then it is likely that it is dependent on one or more libraries which are only available as dynamic libraries.
You can try and isolate which of the libraries explicitly specified are causing the problem, by using a mixture of -non_shared/-call_shared options (or equivalent; see above). For those libraries identified as only being available as dynamic libraries, you can either: try to find/build static versions of these libraries to link against instead (may be possible if you have installed these libraries yourself), or: accept that this is a known dependency (at least for this particular system, which will most likely be the case if the offender is a system runtime library).
(To the best of my knowledge it is not possible to create a static archive from a dynamic one.)
Note that not all dependencies are made "explicit" on the compile/link line, for example executables will also depend on low-level system runtime libraries which are implicitly linked in. On some platforms these low-level libraries may only be available as dynamic libraries, however in these cases a general rule of thumb is that the target machines will also have these dynamic libraries available and so this should not affect portability.
This list is not definitive and the information is anecdotal.
IRIX 6.5: has a runtime library crt0 which is only available as a dynamic library, so it is not possible to create pure static executables on this system. However crt0 seems to be standard on IRIX so this doesn't seem to cause a portability problem in practice.
OSF1 V4.0: can use -non_shared to make portable executables. This may be necessary because for OSF1 V4.0 systems without compilers it seems that some low-level runtime libraries are not provided either.
OSF1 V5.1: seems to have similar issues to IRIX 6.5, in that one or more system library is only available as a dynamic version. It is therefore not possible to use -non_shared as is the case with V4.0. Also, executables compiled on V5.1 (and probably V5.0) are not backwardly compatible with V4.0, unless the -D__V40_OBJ_COMPAT option is used at compile time (this used to be used in the CCP4 configure for OSF1 V5.0 up until release 4.2.1). It appears that V4.0 executables will run on V5.0 however.
Linux: portability between different flavours and versions of Linux is not at all well-defined (and is further complicated
by issues such as differeing compiler versions etc). My only experience in this context is with Red Hat versions 8.0 and
9. It is my understanding that RH 8.0 executables built with GNU compilers will work with RH 9 (but not the other way
around) and that RH 8 executables are also usable on other up-to-date flavours of Linux (SuSE, Mandrake, Debian).
Alternatively, Kevin Cowtan has been advocating the use of autopackage (apgcc), which claims to produce executable code which is completely portable across all Linux platforms. See Kevin's CCP4 projects page.
Currently CCP4 provides precompiled executables built on the following platforms:
|Irix||SGI operating system||6.5|
|OSF1||DEC Alpha/Compaq Tru64 Unix||V4.0, V5.1|
|SunOS||Solaris operating system||5.8|
|Linux||Linux||Red Hat 8.0, Red Hat 9|
For the listed operating systems, binaries of the programs are built using the configure line:
> ./configure system --with-netlib-lapack --with-x
This default build only makes static versions of the CCP4 libraries, and the resulting executables are linked statically with respect to those libraries. --with-netlib-lapack forces the building of and linking against (static) LAPACK and BLAS libraries, and is used to prevent the executables being linked against system-provided versions of the same libraries (which might only be available on the source system as dynamic libraries, and not on the target system). (--with-x forces building of the CCP4 X-windows programs and is not relevant to the discussions here.)