1  #!/usr/bin/python3
       2  # Dump the output of LD_TRACE_LOADED_OBJECTS in architecture neutral format.
       3  # Copyright (C) 2022-2023 Free Software Foundation, Inc.
       4  # Copyright The GNU Toolchain Authors.
       5  # This file is part of the GNU C Library.
       6  #
       7  # The GNU C Library is free software; you can redistribute it and/or
       8  # modify it under the terms of the GNU Lesser General Public
       9  # License as published by the Free Software Foundation; either
      10  # version 2.1 of the License, or (at your option) any later version.
      11  #
      12  # The GNU C Library is distributed in the hope that it will be useful,
      13  # but WITHOUT ANY WARRANTY; without even the implied warranty of
      14  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15  # Lesser General Public License for more details.
      16  #
      17  # You should have received a copy of the GNU Lesser General Public
      18  # License along with the GNU C Library; if not, see
      19  # <https://www.gnu.org/licenses/>.
      20  
      21  import argparse
      22  import os
      23  import subprocess
      24  import sys
      25  
      26  try:
      27      subprocess.run
      28  except:
      29      class ESC[4;38;5;81m_CompletedProcess:
      30          def __init__(self, args, returncode, stdout=None, stderr=None):
      31              self.args = args
      32              self.returncode = returncode
      33              self.stdout = stdout
      34              self.stderr = stderr
      35  
      36      def _run(*popenargs, input=None, timeout=None, check=False, **kwargs):
      37          assert(timeout is None)
      38          with subprocess.Popen(*popenargs, **kwargs) as process:
      39              try:
      40                  stdout, stderr = process.communicate(input)
      41              except:
      42                  process.kill()
      43                  process.wait()
      44                  raise
      45              returncode = process.poll()
      46              if check and returncode:
      47                  raise subprocess.CalledProcessError(returncode, popenargs)
      48          return _CompletedProcess(popenargs, returncode, stdout, stderr)
      49  
      50      subprocess.run = _run
      51  
      52  def is_vdso(lib):
      53      return lib.startswith('linux-gate') or lib.startswith('linux-vdso')
      54  
      55  
      56  def parse_trace(cmd, fref):
      57      new_env = os.environ.copy()
      58      new_env['LD_TRACE_LOADED_OBJECTS'] = '1'
      59      trace_out = subprocess.run(cmd, stdout=subprocess.PIPE, check=True,
      60                                 universal_newlines=True, env=new_env).stdout
      61      trace = []
      62      for line in trace_out.splitlines():
      63          line = line.strip()
      64          if is_vdso(line):
      65              continue
      66          fields = line.split('=>' if '=>' in line else ' ')
      67          lib = os.path.basename(fields[0].strip())
      68          if lib.startswith('ld'):
      69              lib = 'ld'
      70          elif lib.startswith('libc'):
      71              lib = 'libc'
      72          found = 1 if fields[1].strip() != 'not found' else 0
      73          trace += ['{} {}'.format(lib, found)]
      74      trace = sorted(trace)
      75  
      76      reference = sorted(line.replace('\n','') for line in fref.readlines())
      77  
      78      ret = 0 if trace == reference else 1
      79      if ret != 0:
      80          for i in reference:
      81              if i not in trace:
      82                  print("Only in {}: {}".format(fref.name, i))
      83          for i in trace:
      84              if i not in reference:
      85                  print("Only in trace: {}".format(i))
      86  
      87      sys.exit(ret)
      88  
      89  
      90  def get_parser():
      91      parser = argparse.ArgumentParser(description=__doc__)
      92      parser.add_argument('command',
      93                          help='comand to run')
      94      parser.add_argument('reference',
      95                          help='reference file to compare')
      96      return parser
      97  
      98  
      99  def main(argv):
     100      parser = get_parser()
     101      opts = parser.parse_args(argv)
     102      with open(opts.reference, 'r') as fref:
     103          # Remove the initial 'env' command.
     104          parse_trace(opts.command.split()[1:], fref)
     105  
     106  
     107  if __name__ == '__main__':
     108      main(sys.argv[1:])