1#!/usr/bin/python3
2# Dump the output of LD_TRACE_LOADED_OBJECTS in architecture neutral format.
3# Copyright (C) 2022 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
21import argparse
22import os
23import subprocess
24import sys
25
26try:
27    subprocess.run
28except:
29    class _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
52def is_vdso(lib):
53    return lib.startswith('linux-gate') or lib.startswith('linux-vdso')
54
55
56def 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
90def 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
99def 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
107if __name__ == '__main__':
108    main(sys.argv[1:])
109