6 A script to check that the (Linux) executables produced by gitian only contain 7 allowed gcc, glibc and libstdc++ version symbols. This makes sure they are 8 still compatible with the minimum supported Linux distribution versions. 12 find ../gitian-builder/build -type f -executable | xargs python contrib/devtools/symbol-check.py 14 from __future__
import division, print_function
50 '_edata',
'_end',
'_init',
'__bss_start',
'_fini',
'_IO_stdin_used' 52 READELF_CMD = os.getenv(
'READELF',
'/usr/bin/readelf')
53 CPPFILT_CMD = os.getenv(
'CPPFILT',
'/usr/bin/c++filt')
63 'ld-linux-x86-64.so.2',
76 Demangle C++ symbol names. 78 Use a pipe to the 'c++filt' command. 81 self.
proc = subprocess.Popen(CPPFILT_CMD, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
84 self.
proc.stdin.write(mangled +
'\n')
85 return self.
proc.stdout.readline().rstrip()
88 self.
proc.stdin.close()
89 self.
proc.stdout.close()
94 Parse an ELF executable and return a list of (symbol,version) tuples 95 for dynamic, imported symbols. 97 p = subprocess.Popen([READELF_CMD,
'--dyn-syms',
'-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
98 (stdout, stderr) = p.communicate()
100 raise IOError(
'Could not read symbols for %s: %s' % (executable, stderr.strip()))
102 for line
in stdout.split(
'\n'):
104 if len(line)>7
and re.match(
'[0-9]+:$', line[0]):
105 (sym, _, version) = line[7].partition(
'@')
106 is_import = line[6] ==
'UND' 107 if version.startswith(
'@'):
108 version = version[1:]
109 if is_import == imports:
110 syms.append((sym, version))
115 (lib, _, ver) = version.rpartition(
'_')
119 ver = tuple([int(x)
for x
in ver.split(
'.')])
120 if not lib
in max_versions:
122 return ver <= max_versions[lib]
125 p = subprocess.Popen([READELF_CMD,
'-d',
'-W', filename], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
126 (stdout, stderr) = p.communicate()
128 raise IOError(
'Error opening file')
130 for line
in stdout.split(
'\n'):
131 tokens = line.split()
132 if len(tokens)>2
and tokens[1] ==
'(NEEDED)':
133 match = re.match(
'^Shared library: \[(.*)\]$',
' '.join(tokens[2:]))
135 libraries.append(match.group(1))
137 raise ValueError(
'Unparseable (NEEDED) specification')
140 if __name__ ==
'__main__':
143 for filename
in sys.argv[1:]:
147 print(
'%s: symbol %s from unsupported version %s' % (filename,
cppfilt(sym), version))
151 if sym
in IGNORE_EXPORTS:
153 print(
'%s: export of symbol %s not allowed' % (filename,
cppfilt(sym)))
157 if library_name
not in ALLOWED_LIBRARIES:
158 print(
'%s: NEEDED library %s is not allowed' % (filename, library_name))
def check_version(max_versions, version)
def read_symbols(executable, imports=True)
def __call__(self, mangled)
def read_libraries(filename)