import re
import subprocess
import sys
MAX_ALLOWED_GLIBC_VERSION = [2, 26]
VERSION_PATTERN = re.compile('GLIBC_([0-9\.]+)')
SECTION_PATTERN = re.compile(r'^ *\[ *[0-9]+\] +(\S+) +\S+ + ([0-9a-f]+) .*$')
SYMBOL_ALLOWLIST = {
'fts64_close',
'fts64_open',
'fts64_read',
'memfd_create',
}
default_version = {}
supported_version = {}
BIN_FILE = sys.argv[1]
stdout = subprocess.check_output(['readelf', '--dyn-syms', '--wide', BIN_FILE])
for line in stdout.decode("utf-8").split('\n'):
cols = re.split('\s+', line)
if len(cols) < 9:
continue
index = cols[1].rstrip(':')
if not index.isdigit():
continue
index = int(index)
name = cols[8].split('@')
if len(name) < 2:
continue
base_name = name[0]
version = name[-1]
is_default = len(name) > 2
if version.startswith('XCRYPT_'):
version = [float('inf')]
else:
match = re.match(VERSION_PATTERN, version)
if not match:
continue
version = [int(part) for part in match.group(1).split('.')]
if version < MAX_ALLOWED_GLIBC_VERSION:
old_supported_version = supported_version.get(base_name, ([-1], -1))
supported_version[base_name] = max((version, index), old_supported_version)
if is_default:
default_version[base_name] = (version, index)
stdout = subprocess.check_output(['readelf', '--sections', '--wide', BIN_FILE])
for line in stdout.decode("utf-8").split('\n'):
if match := SECTION_PATTERN.match(line):
section_name, address = match.groups()
if section_name == '.gnu.version':
gnu_version_addr = int(address, base=16)
break
else:
print('No .gnu.version section found', file=sys.stderr)
sys.exit(1)
bin_data = bytearray(open(BIN_FILE, 'rb').read())
for name, (version, index) in default_version.items():
if version <= MAX_ALLOWED_GLIBC_VERSION:
continue
if name in SYMBOL_ALLOWLIST:
continue
elif name in supported_version:
_, supported_index = supported_version[name]
else:
supported_index = -1
old_default = gnu_version_addr + 2 * index + 1
assert (bin_data[old_default] & 0x80) == 0
bin_data[old_default] ^= 0x80
if supported_index != -1:
new_default = gnu_version_addr + 2 * supported_index + 1
assert (bin_data[new_default] & 0x80) == 0x80
bin_data[new_default] ^= 0x80
open(BIN_FILE, 'wb').write(bin_data)