(root)/
glibc-2.38/
scripts/
move-symbol-to-libc.py
       1  #!/usr/bin/python3
       2  # Move symbols from other shared objects into libc.so.
       3  # Copyright (C) 2020-2023 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  
      20  """Move symbols from other shared objects into libc.so.
      21  
      22  This script moves ABI symbols from non-libc abilists in to
      23  libc.abilist.  Symbol versions are preserved.  The script must be
      24  called from the top of the glibc source tree.
      25  
      26  """
      27  
      28  import argparse
      29  import os.path
      30  import sys
      31  
      32  # Make available glibc Python modules.
      33  sys.path.append(os.path.dirname(os.path.realpath(__file__)))
      34  
      35  import glibcsymbols
      36  
      37  def add_to_libc_path(path, new_symbols):
      38      """Add SYMBOLS to the abilist file PATH.
      39  
      40      NEW_SYMBOLS is a dictionary from glibcsymbols.VersionedSymbol
      41      objects to their flags.
      42  
      43      """
      44      original_symbols = glibcsymbols.read_abilist(path)
      45      updated_symbols = original_symbols.copy()
      46      updated_symbols.update(new_symbols)
      47      if updated_symbols != original_symbols:
      48          sys.stdout.write('updating libc abilist {}\n'.format(path))
      49          glibcsymbols.replace_file(
      50              path, glibcsymbols.abilist_lines(updated_symbols))
      51  
      52  # The name of the libc.so abilist file.
      53  libc_abilist = 'libc.abilist'
      54  
      55  def add_to_libc_fallback(directory, subdirs, symbol_lines):
      56      """Add SYMBOL_LINES to the libc.abilist files in SUBDIRS in DIRECTORY.
      57  
      58      All subdirectories must exist.  If they do, return True.  If not,
      59      skip processing and return False.
      60  
      61      """
      62      abilists = [os.path.join(directory, subdir, libc_abilist)
      63                  for subdir in subdirs]
      64      for abilist in abilists:
      65          if not os.path.exists(abilist):
      66              return False
      67      for abilist in abilists:
      68              add_to_libc_path(abilist, symbol_lines)
      69      return True
      70  
      71  def add_to_libc(directory, symbol_lines):
      72  
      73      """Add SYMBOL_LINES (a list of strings) to libc.abilist in DIRECTORY.
      74  
      75      Try specific subdirectories as well if libc.abilist is not found
      76      in DIRECTORY.
      77  
      78      """
      79      libc_path = os.path.join(directory, libc_abilist)
      80      if os.path.exists(libc_path):
      81          add_to_libc_path(libc_path, symbol_lines)
      82          return
      83  
      84      # Special case for powerpc32 and mips32 variants.
      85      if add_to_libc_fallback(directory, ('fpu', 'nofpu'), symbol_lines):
      86          return
      87  
      88      # Special case for mips64.
      89      if add_to_libc_fallback(directory, ('n32', 'n64'), symbol_lines):
      90          return
      91  
      92      raise IOError('No libc.abilist found for: {}'.format(directory))
      93  
      94  def move_symbols_1(path, to_move, moved_symbols):
      95      """Move SYMBOLS from the abilist file PATH to MOVED_SYMBOLS.
      96  
      97      TO_MOVE must be a set of strings.  MOVED_SYMBOLS is a dictionary.
      98  
      99      """
     100      suffix = '.abilist'
     101      assert path.endswith('.abilist')
     102      library = os.path.basename(path)[:-len(suffix)]
     103      placeholder = '__{}_version_placeholder'.format(library)
     104  
     105      new_lines = []
     106      changed = False
     107  
     108      old_symbols = glibcsymbols.read_abilist(path)
     109      old_versions = set(versym.version for versym in old_symbols.keys())
     110      matching_symbols = dict(e for e in old_symbols.items()
     111                              if e[0].symbol in to_move)
     112      if matching_symbols:
     113          sys.stdout.write('updating {} abilist {}\n'.format(library, path))
     114          new_symbols = dict(e for e in old_symbols.items()
     115                             if e[0].symbol not in to_move)
     116  
     117          # Add placeholder symbols to prevent symbol versions from
     118          # going away completely.
     119          new_versions = set(versym.version for versym in new_symbols.keys())
     120          for missing_version in old_versions - new_versions:
     121              new_symbols[glibcsymbols.VersionedSymbol(
     122                  placeholder, missing_version)] = 'F'
     123  
     124          glibcsymbols.replace_file(
     125              path, glibcsymbols.abilist_lines(new_symbols))
     126  
     127          moved_symbols.update(matching_symbols)
     128  
     129  def move_symbols(directory, files, symbols):
     130      """Move SYMBOLS from FILES (a list of abilist file names) in DIRECTORY.
     131  
     132      SYMBOLS must be a set of strings.
     133  
     134      """
     135      moved_symbols = {}
     136      for filename in files:
     137          move_symbols_1(os.path.join(directory, filename), symbols,
     138                         moved_symbols)
     139      if moved_symbols:
     140          add_to_libc(directory, moved_symbols)
     141  
     142  def get_parser():
     143      """Return an argument parser for this module."""
     144      parser = argparse.ArgumentParser(description=__doc__)
     145      parser.add_argument('--only-linux', action='store_true',
     146                          help='Restrict the operation to Linux abilists')
     147      parser.add_argument('symbols', help='name of the symbol to move',
     148                          nargs='+')
     149      return parser
     150  
     151  def main(argv):
     152      """The main entry point."""
     153      parser = get_parser()
     154      opts = parser.parse_args(argv)
     155      if opts.only_linux:
     156          sysdeps = 'sysdeps/unix/sysv/linux'
     157      else:
     158          sysdeps = 'sysdeps'
     159  
     160      symbols = frozenset(opts.symbols)
     161  
     162      for directory, dirs, files in os.walk(sysdeps):
     163          move_symbols(directory, [name for name in files
     164                                   if name != 'libc.abilist'
     165                                   and name.endswith('.abilist')], symbols)
     166  
     167  if __name__ == '__main__':
     168      main(sys.argv[1:])