202c95c216
After last commit, our executables should export no symbols anymore. To make sure that this stays the case, verify this in the symbol checker script.
113 lines
4.1 KiB
Python
Executable file
113 lines
4.1 KiB
Python
Executable file
#!/usr/bin/python
|
|
# Copyright (c) 2014 Wladimir J. van der Laan
|
|
# Distributed under the MIT/X11 software license, see the accompanying
|
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
'''
|
|
A script to check that the (Linux) executables produced by gitian only contain
|
|
allowed gcc, glibc and libstdc++ version symbols. This makes sure they are
|
|
still compatible with the minimum supported Linux distribution versions.
|
|
|
|
Example usage:
|
|
|
|
find ../gitian-builder/build -type f -executable | xargs python contrib/devtools/symbol-check.py
|
|
'''
|
|
from __future__ import division, print_function
|
|
import subprocess
|
|
import re
|
|
import sys
|
|
|
|
# Debian 6.0.9 (Squeeze) has:
|
|
#
|
|
# - g++ version 4.4.5 (https://packages.debian.org/search?suite=default§ion=all&arch=any&searchon=names&keywords=g%2B%2B)
|
|
# - libc version 2.11.3 (https://packages.debian.org/search?suite=default§ion=all&arch=any&searchon=names&keywords=libc6)
|
|
# - libstdc++ version 4.4.5 (https://packages.debian.org/search?suite=default§ion=all&arch=any&searchon=names&keywords=libstdc%2B%2B6)
|
|
#
|
|
# Ubuntu 10.04.4 (Lucid Lynx) has:
|
|
#
|
|
# - g++ version 4.4.3 (http://packages.ubuntu.com/search?keywords=g%2B%2B&searchon=names&suite=lucid§ion=all)
|
|
# - libc version 2.11.1 (http://packages.ubuntu.com/search?keywords=libc6&searchon=names&suite=lucid§ion=all)
|
|
# - libstdc++ version 4.4.3 (http://packages.ubuntu.com/search?suite=lucid§ion=all&arch=any&keywords=libstdc%2B%2B&searchon=names)
|
|
#
|
|
# Taking the minimum of these as our target.
|
|
#
|
|
# According to GNU ABI document (http://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html) this corresponds to:
|
|
# GCC 4.4.0: GCC_4.4.0
|
|
# GCC 4.4.2: GLIBCXX_3.4.13, CXXABI_1.3.3
|
|
# (glibc) GLIBC_2_11
|
|
#
|
|
MAX_VERSIONS = {
|
|
'GCC': (4,4,0),
|
|
'CXXABI': (1,3,3),
|
|
'GLIBCXX': (3,4,13),
|
|
'GLIBC': (2,11)
|
|
}
|
|
READELF_CMD = '/usr/bin/readelf'
|
|
CPPFILT_CMD = '/usr/bin/c++filt'
|
|
|
|
class CPPFilt(object):
|
|
'''
|
|
Demangle C++ symbol names.
|
|
|
|
Use a pipe to the 'c++filt' command.
|
|
'''
|
|
def __init__(self):
|
|
self.proc = subprocess.Popen(CPPFILT_CMD, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
|
|
|
def __call__(self, mangled):
|
|
self.proc.stdin.write(mangled + '\n')
|
|
return self.proc.stdout.readline().rstrip()
|
|
|
|
def close(self):
|
|
self.proc.stdin.close()
|
|
self.proc.stdout.close()
|
|
self.proc.wait()
|
|
|
|
def read_symbols(executable, imports=True):
|
|
'''
|
|
Parse an ELF executable and return a list of (symbol,version) tuples
|
|
for dynamic, imported symbols.
|
|
'''
|
|
p = subprocess.Popen([READELF_CMD, '--dyn-syms', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
|
|
(stdout, stderr) = p.communicate()
|
|
if p.returncode:
|
|
raise IOError('Could not read symbols for %s: %s' % (executable, stderr.strip()))
|
|
syms = []
|
|
for line in stdout.split('\n'):
|
|
line = line.split()
|
|
if len(line)>7 and re.match('[0-9]+:$', line[0]):
|
|
(sym, _, version) = line[7].partition('@')
|
|
is_import = line[6] == 'UND'
|
|
if version.startswith('@'):
|
|
version = version[1:]
|
|
if is_import == imports:
|
|
syms.append((sym, version))
|
|
return syms
|
|
|
|
def check_version(max_versions, version):
|
|
if '_' in version:
|
|
(lib, _, ver) = version.rpartition('_')
|
|
else:
|
|
lib = version
|
|
ver = '0'
|
|
ver = tuple([int(x) for x in ver.split('.')])
|
|
if not lib in max_versions:
|
|
return False
|
|
return ver <= max_versions[lib]
|
|
|
|
if __name__ == '__main__':
|
|
cppfilt = CPPFilt()
|
|
retval = 0
|
|
for filename in sys.argv[1:]:
|
|
# Check imported symbols
|
|
for sym,version in read_symbols(filename, True):
|
|
if version and not check_version(MAX_VERSIONS, version):
|
|
print('%s: symbol %s from unsupported version %s' % (filename, cppfilt(sym), version))
|
|
retval = 1
|
|
# Check exported symbols
|
|
for sym,version in read_symbols(filename, False):
|
|
print('%s: export of symbol %s not allowed' % (filename, cppfilt(sym)))
|
|
retval = 1
|
|
|
|
exit(retval)
|
|
|
|
|