move-symbol-to-libc.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. #!/usr/bin/python3
  2. # Move symbols from other shared objects into libc.so.
  3. # Copyright (C) 2020-2026 Free Software Foundation, Inc.
  4. # This file is part of the GNU C Library.
  5. #
  6. # The GNU C Library is free software; you can redistribute it and/or
  7. # modify it under the terms of the GNU Lesser General Public
  8. # License as published by the Free Software Foundation; either
  9. # version 2.1 of the License, or (at your option) any later version.
  10. #
  11. # The GNU C Library is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. # Lesser General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU Lesser General Public
  17. # License along with the GNU C Library; if not, see
  18. # <https://www.gnu.org/licenses/>.
  19. """Move symbols from other shared objects into libc.so.
  20. This script moves ABI symbols from non-libc abilists in to
  21. libc.abilist. Symbol versions are preserved. The script must be
  22. called from the top of the glibc source tree.
  23. """
  24. import argparse
  25. import os.path
  26. import sys
  27. # Make available glibc Python modules.
  28. sys.path.append(os.path.dirname(os.path.realpath(__file__)))
  29. import glibcsymbols
  30. def add_to_libc_path(path, new_symbols):
  31. """Add SYMBOLS to the abilist file PATH.
  32. NEW_SYMBOLS is a dictionary from glibcsymbols.VersionedSymbol
  33. objects to their flags.
  34. """
  35. original_symbols = glibcsymbols.read_abilist(path)
  36. updated_symbols = original_symbols.copy()
  37. updated_symbols.update(new_symbols)
  38. if updated_symbols != original_symbols:
  39. sys.stdout.write('updating libc abilist {}\n'.format(path))
  40. glibcsymbols.replace_file(
  41. path, glibcsymbols.abilist_lines(updated_symbols))
  42. # The name of the libc.so abilist file.
  43. libc_abilist = 'libc.abilist'
  44. def add_to_libc_fallback(directory, subdirs, symbol_lines):
  45. """Add SYMBOL_LINES to the libc.abilist files in SUBDIRS in DIRECTORY.
  46. All subdirectories must exist. If they do, return True. If not,
  47. skip processing and return False.
  48. """
  49. abilists = [os.path.join(directory, subdir, libc_abilist)
  50. for subdir in subdirs]
  51. for abilist in abilists:
  52. if not os.path.exists(abilist):
  53. return False
  54. for abilist in abilists:
  55. add_to_libc_path(abilist, symbol_lines)
  56. return True
  57. def add_to_libc(directory, symbol_lines):
  58. """Add SYMBOL_LINES (a list of strings) to libc.abilist in DIRECTORY.
  59. Try specific subdirectories as well if libc.abilist is not found
  60. in DIRECTORY.
  61. """
  62. libc_path = os.path.join(directory, libc_abilist)
  63. if os.path.exists(libc_path):
  64. add_to_libc_path(libc_path, symbol_lines)
  65. return
  66. # Special case for powerpc32 and mips32 variants.
  67. if add_to_libc_fallback(directory, ('fpu', 'nofpu'), symbol_lines):
  68. return
  69. # Special case for mips64.
  70. if add_to_libc_fallback(directory, ('n32', 'n64'), symbol_lines):
  71. return
  72. raise IOError('No libc.abilist found for: {}'.format(directory))
  73. def move_symbols_1(path, to_move, moved_symbols):
  74. """Move SYMBOLS from the abilist file PATH to MOVED_SYMBOLS.
  75. TO_MOVE must be a set of strings. MOVED_SYMBOLS is a dictionary.
  76. """
  77. suffix = '.abilist'
  78. assert path.endswith('.abilist')
  79. library = os.path.basename(path)[:-len(suffix)]
  80. placeholder = '__{}_version_placeholder'.format(library)
  81. new_lines = []
  82. changed = False
  83. old_symbols = glibcsymbols.read_abilist(path)
  84. old_versions = set(versym.version for versym in old_symbols.keys())
  85. matching_symbols = dict(e for e in old_symbols.items()
  86. if e[0].symbol in to_move)
  87. if matching_symbols:
  88. sys.stdout.write('updating {} abilist {}\n'.format(library, path))
  89. new_symbols = dict(e for e in old_symbols.items()
  90. if e[0].symbol not in to_move)
  91. # Add placeholder symbols to prevent symbol versions from
  92. # going away completely.
  93. new_versions = set(versym.version for versym in new_symbols.keys())
  94. for missing_version in old_versions - new_versions:
  95. new_symbols[glibcsymbols.VersionedSymbol(
  96. placeholder, missing_version)] = 'F'
  97. glibcsymbols.replace_file(
  98. path, glibcsymbols.abilist_lines(new_symbols))
  99. moved_symbols.update(matching_symbols)
  100. def move_symbols(directory, files, symbols):
  101. """Move SYMBOLS from FILES (a list of abilist file names) in DIRECTORY.
  102. SYMBOLS must be a set of strings.
  103. """
  104. moved_symbols = {}
  105. for filename in files:
  106. move_symbols_1(os.path.join(directory, filename), symbols,
  107. moved_symbols)
  108. if moved_symbols:
  109. add_to_libc(directory, moved_symbols)
  110. def get_parser():
  111. """Return an argument parser for this module."""
  112. parser = argparse.ArgumentParser(description=__doc__)
  113. parser.add_argument('--only-linux', action='store_true',
  114. help='Restrict the operation to Linux abilists')
  115. parser.add_argument('symbols', help='name of the symbol to move',
  116. nargs='+')
  117. return parser
  118. def main(argv):
  119. """The main entry point."""
  120. parser = get_parser()
  121. opts = parser.parse_args(argv)
  122. if opts.only_linux:
  123. sysdeps = 'sysdeps/unix/sysv/linux'
  124. else:
  125. sysdeps = 'sysdeps'
  126. symbols = frozenset(opts.symbols)
  127. for directory, dirs, files in os.walk(sysdeps):
  128. move_symbols(directory, [name for name in files
  129. if name != 'libc.abilist'
  130. and name.endswith('.abilist')], symbols)
  131. if __name__ == '__main__':
  132. main(sys.argv[1:])