1import ast
2import distutils.spawn
3import signal
4import subprocess
5import sys
6
7
8def execute_command(cmd, input_str=None):
9    """
10    Execute a command, capture and return its output.
11    """
12    kwargs = {
13        'stdin': subprocess.PIPE,
14        'stdout': subprocess.PIPE,
15        'stderr': subprocess.PIPE,
16    }
17    p = subprocess.Popen(cmd, **kwargs)
18    out, err = p.communicate(input=input_str)
19    exitCode = p.wait()
20    if exitCode == -signal.SIGINT:
21        raise KeyboardInterrupt
22    return out, err, exitCode
23
24
25def execute_command_verbose(cmd, input_str=None):
26    """
27    Execute a command and print its output on failure.
28    """
29    out, err, exitCode = execute_command(cmd, input_str=input_str)
30    if exitCode != 0:
31        report = "Command: %s\n" % ' '.join(["'%s'" % a for a in cmd])
32        report += "Exit Code: %d\n" % exitCode
33        if out:
34            report += "Standard Output:\n--\n%s--" % out
35        if err:
36            report += "Standard Error:\n--\n%s--" % err
37        report += "\n\nFailed!"
38        sys.stderr.write('%s\n' % report)
39    return out, err, exitCode
40
41
42def read_syms_from_list(slist):
43    """
44    Read a list of symbols from a list of strings.
45    Each string is one symbol.
46    """
47    return [ast.literal_eval(l) for l in slist]
48
49
50def read_syms_from_file(filename):
51    """
52    Read a list of symbols in from a file.
53    """
54    with open(filename, 'r') as f:
55        data = f.read()
56    return read_syms_from_list(data.splitlines())
57
58
59def read_blacklist(filename):
60    with open(filename, 'r') as f:
61        data = f.read()
62    lines = [l.strip() for l in data.splitlines() if l.strip()]
63    lines = [l for l in lines if not l.startswith('#')]
64    return lines
65
66
67def write_syms(sym_list, out=None, names_only=False):
68    """
69    Write a list of symbols to the file named by out.
70    """
71    out_str = ''
72    out_list = sym_list
73    if names_only:
74        out_list = [sym['name'] for sym in sym_list]
75        out_list.sort()
76    for sym in out_list:
77        out_str += '%s\n' % sym
78    if out is None:
79        sys.stdout.write(out_str)
80    else:
81        with open(out, 'w') as f:
82            f.write(out_str)
83
84
85_cppfilt_exe = distutils.spawn.find_executable('c++filt')
86
87
88def demangle_symbol(symbol):
89    if _cppfilt_exe is None:
90        return symbol
91    out, _, exit_code = execute_command_verbose(
92        [_cppfilt_exe], input_str=symbol)
93    if exit_code != 0:
94        return symbol
95    return out
96
97
98def is_elf(filename):
99    with open(filename, 'r') as f:
100        magic_bytes = f.read(4)
101    return magic_bytes == '\x7fELF'
102
103
104def is_mach_o(filename):
105    with open(filename, 'r') as f:
106        magic_bytes = f.read(4)
107    return magic_bytes in [
108        '\xfe\xed\xfa\xce',  # MH_MAGIC
109        '\xce\xfa\xed\xfe',  # MH_CIGAM
110        '\xfe\xed\xfa\xcf',  # MH_MAGIC_64
111        '\xcf\xfa\xed\xfe',  # MH_CIGAM_64
112        '\xca\xfe\xba\xbe',  # FAT_MAGIC
113        '\xbe\xba\xfe\xca'   # FAT_CIGAM
114    ]
115
116
117def is_library_file(filename):
118    if sys.platform == 'darwin':
119        return is_mach_o(filename)
120    else:
121        return is_elf(filename)
122
123
124def extract_or_load(filename):
125    import sym_check.extract
126    if is_library_file(filename):
127        return sym_check.extract.extract_symbols(filename)
128    return read_syms_from_file(filename)
129