123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122 |
- #!/usr/bin/env python3
- # Copyright 2021 The Chromium Authors. All rights reserved.
- # Use of this source code is governed by a BSD-style license that can be
- # found in the LICENSE file.
- """Rewrite incompatible default symbols in glibc.
- """
- import re
- import subprocess
- import sys
- # This constant comes from https://crbug.com/580892
- MAX_ALLOWED_GLIBC_VERSION = [2, 17]
- VERSION_PATTERN = re.compile('GLIBC_([0-9\.]+)')
- SECTION_PATTERN = re.compile(r'^ *\[ *[0-9]+\] +(\S+) +\S+ + ([0-9a-f]+) .*$')
- # Some otherwise disallowed symbols are referenced in the linux-chromeos build.
- # To continue supporting it, allow these symbols to remain enabled.
- SYMBOL_ALLOWLIST = {
- 'fts64_close',
- 'fts64_open',
- 'fts64_read',
- 'memfd_create',
- }
- # The two dictionaries below map from symbol name to
- # (symbol version, symbol index).
- #
- # The default version for a given symbol (which may be unsupported).
- default_version = {}
- # The max supported symbol version for a given symbol.
- supported_version = {}
- # The file name of the binary we're going to rewrite.
- BIN_FILE = sys.argv[1]
- # Populate |default_version| and |supported_version| with data from readelf.
- stdout = subprocess.check_output(['readelf', '--dyn-syms', '--wide', BIN_FILE])
- for line in stdout.decode("utf-8").split('\n'):
- cols = re.split('\s+', line)
- # Skip the preamble.
- if len(cols) < 9:
- continue
- index = cols[1].rstrip(':')
- # Skip the header.
- if not index.isdigit():
- continue
- index = int(index)
- name = cols[8].split('@')
- # Ignore unversioned symbols.
- if len(name) < 2:
- continue
- base_name = name[0]
- version = name[-1]
- # The default version will have '@@' in the name.
- is_default = len(name) > 2
- if version.startswith('XCRYPT_'):
- # Prefer GLIBC_* versioned symbols over XCRYPT_* ones. Set the version to
- # something > MAX_ALLOWED_GLIBC_VERSION so this symbol will not be picked.
- version = [float('inf')]
- else:
- match = re.match(VERSION_PATTERN, version)
- # Ignore symbols versioned with GLIBC_PRIVATE.
- 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)
- # Get the offset into the binary of the .gnu.version section from readelf.
- 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)
- # Rewrite the binary.
- bin_data = bytearray(open(BIN_FILE, 'rb').read())
- for name, (version, index) in default_version.items():
- # No need to rewrite the default if it's already an allowed version.
- 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
- # The .gnu.version section is divided into 16-bit chunks that give the
- # symbol versions. The 16th bit is a flag that's false for the default
- # version. The data is stored in little-endian so we need to add 1 to
- # get the address of the byte we want to flip.
- #
- # Disable the unsupported symbol.
- old_default = gnu_version_addr + 2 * index + 1
- assert (bin_data[old_default] & 0x80) == 0
- bin_data[old_default] ^= 0x80
- # If we found a supported version, enable that as default.
- 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)
|