Yes there are various other libc's that you can use instead of glibc which are friendlier to static linking.
The problem is that if you link against a newer version of glibc than is present on the machine where your program will run, then it might make use of newer functions not present in that version.
You have the same problem with the version of GCC used: they may have dependencies on libgcc*.so libraries
The general advice is to not link glibc or other system libraries (like libgcc) statically, but instead ensure that you only require old versions of them.
When I was working on making my own programs portable, I wrote a program to check what versions of glibc and gcc are actually required, based on versioned symbols in the elf file:
Code: Select all
#!/usr/bin/env python
"""
A tool for testing portability.
Check which versions of the glibc, libgcc, and libstdc++ shared libraries
a GNU/Linux ELF binary depends upon.
Note that libstdc++ version requirements are reported as GCC requirements,
because each libstdc++ version is tied to a specific GCC version.
Old versions before ~2010 are lumped together, and GCC versions newer than 6.1
aren't supported yet.
Written 2017, Ralph Versteegen. Released into the Public Domain.
(Part of the OHRRPGCE build system.)
"""
import sys
import subprocess
import re
def get_command_output(cmd, args, shell = True):
"""Runs a shell command and returns stdout as a string"""
if shell:
# Argument must be a single string (additional arguments get passed as extra /bin/sh args)
if isinstance(args, (list, tuple)):
args = ' '.join(args)
cmdargs = '"' + cmd + '" ' + args
else:
assert isinstance(args, (list, tuple))
cmdargs = [cmd] + args
proc = subprocess.Popen(cmdargs, shell=shell, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
errtext = proc.stderr.read()
outtext = proc.stdout.read()
# Annoyingly fbc prints (at least some) error messages to stdout instead of stderr
if len(errtext) > 0 or proc.returncode:
raise Exception("subprocess.Popen(%s) failed;\n%s\n%s" % (cmdargs, outtext, errtext))
return outtext.strip()
def check_lib_requirements(binary):
"""Check and print which versions of glibc and gcc dependency libraries (including libstdc++.so)
that an ELF binary requires"""
libraries = []
current_lib = None
req = {'CXXABI': (), 'GLIBC': (), 'GLIBCXX': (), 'GCC': ()}
for line in get_command_output("objdump", ["-p", binary]).split('\n'):
match = re.search("required from (.*):", line)
if match:
current_lib = match.group(1)
libraries.append(current_lib)
match = re.search("(CXXABI|GCC|GLIBC|GLIBCXX)_([0-9.]*)", line)
if match:
symbol = match.group(1)
version = tuple(map(int, match.group(2).split('.')))
#print symbol, version
req[symbol] = max(req[symbol], version)
# Tables giving the required version of GCC corresponding to each GLIBCXX symbol versioning tag
GLIBCXX_to_gcc = {
(3,4,10): (4,3,0),
(3,4,11): (4,4,0),
(3,4,12): (4,4,1),
(3,4,13): (4,4,2),
(3,4,14): (4,5,0),
(3,4,15): (4,6,0),
(3,4,16): (4,6,1),
(3,4,17): (4,7,0),
(3,4,18): (4,8,0),
(3,4,19): (4,8,3),
(3,4,20): (4,9,0),
(3,4,21): (5,1,0),
(3,4,22): (6,1,0),
}
# Ditto for CXXABI
CXXABI_to_gcc = {
(1,3,2): (4,3,0),
(1,3,3): (4,4,0),
(1,3,3): (4,4,1),
(1,3,3): (4,4,2),
(1,3,4): (4,5,0),
(1,3,5): (4,6,0),
(1,3,5): (4,6,1),
(1,3,6): (4,7,0),
(1,3,7): (4,8,0),
(1,3,7): (4,8,3),
(1,3,8): (4,9,0),
(1,3,9): (5,1,0),
(1,3,10): (6,1,0),
}
gcc_release_dates = {
(4,3,0): 'March 5, 2008',
(4,4,0): 'April 21, 2009',
(4,4,1): 'July 22, 2009',
(4,4,2): 'October 15, 2009',
(4,5,0): 'April 14, 2010',
(4,6,0): 'March 25, 2011',
(4,6,1): 'June 27, 2011',
(4,7,0): 'March 22, 2012',
(4,8,0): 'March 22, 2013',
(4,8,3): 'May 22, 2014',
(4,9,0): 'April 22, 2014',
(5,1,0): 'April 22, 2015',
(6,1,0): 'April 27, 2016',
}
glibc_release_dates = {
(2,26): '2017-08-01',
(2,25): '2017-02-01',
(2,24): '2016-08-04',
(2,23): '2016-02-19',
(2,22): '2015-08-14',
(2,21): '2015-02-06',
(2,20): '2014-09-08',
(2,19): '2014-02-07',
(2,18): '2013-08-12',
(2,17): '2012-12-25',
(2,16): '2012-06-30',
(2,15): '2012-03-21',
(2,14,1): '2011-10-07',
(2,14): '2011-06-01',
(2,13): '2011-02-01',
(2,12,2): '2010-12-13',
(2,12,1): '2010-08-03',
(2,12): '2010-05-03',
}
#print req
def verstring(version_tuple):
return '.'.join(map(str, version_tuple))
def lookup_version(version_tuple, table):
if version_tuple < min(table):
return "before " + table[min(table)]
elif version_tuple > max(table):
return "after " + table[max(table)]
elif version_tuple in table:
return table[version_tuple]
return "unknown"
gcc_ver_reqs = []
gcc_req = ''
if 'libstdc++.so.6' in libraries:
gcc_ver_reqs.append((3,4,0))
if req['GLIBCXX'] > (3,4,22) or req['CXXABI'] > (1,3,10):
gcc_req = '>6.1.0'
else:
if req['GCC']:
gcc_ver_reqs.append(req['GCC'])
# fixme: this isn't very good
if req['CXXABI'] < (1,3,2):
pass
else: #if req['CXXABI'] in GLIBCXX_to_gcc:
gcc_ver_reqs.append(CXXABI_to_gcc.get(req['CXXABI'], (9, 'unknown')))
if req['GLIBCXX'] < (3,4,10):
pass
else: #if req['GLIBCXX'] in GLIBCXX_to_gcc:
gcc_ver_reqs.append(GLIBCXX_to_gcc.get(req['GLIBCXX'], (9, 'unknown')))
if gcc_ver_reqs:
max_version = max(gcc_ver_reqs)
gcc_req = verstring(max_version) + ' (released %s)' % lookup_version(max_version, gcc_release_dates)
if gcc_req:
gcc_req = 'and libs for gcc ' + gcc_req
glibc_release = lookup_version(req['GLIBC'], glibc_release_dates)
print ">> %s requires glibc %s (released %s) %s" % (
binary, verstring(req['GLIBC']), glibc_release, gcc_req)
if len(sys.argv) < 2:
print("Usage: %s binaryfile" % sys.argv[0])
else:
check_lib_requirements(sys.argv[1])
Example output (produced by running "for fil in /usr/local/bin/*; do if file $fil | grep -qi elf; then ./check_lib_reqs.py $fil; fi; done"):
On my system, binaries produced by fbc generally are reported as: "requires glibc 2.7 (released before 2010-05-03)" but this is completely system-dependent. Use an old distro in a virtual machine if you find the requirements too high.
As for linking X11 libraries statically, I don't know if that's safe but I would assume it is, since it's a well defined protocol, and those libraries are just for speaking the protocol.