1from typing import Tuple, Optional
2
3from ..sim_type import parse_file, parse_cpp_file, normalize_cpp_function_name, SimTypeCppFunction, SimTypeFd, \
4    register_types, parse_types
5
6
7def get_function_name(s):
8    """
9    Get the function name from a C-style function declaration string.
10
11    :param str s: A C-style function declaration string.
12    :return:      The function name.
13    :rtype:       str
14    """
15
16    s = s.strip()
17    if s.startswith("__attribute__"):
18        # Remove "__attribute__ ((foobar))"
19        if "))" not in s:
20            raise ValueError("__attribute__ is present, but I cannot find double-right parenthesis in the function "
21                             "declaration string.")
22
23        s = s[s.index("))") + 2 : ].strip()
24
25    if '(' not in s:
26        raise ValueError("Cannot find any left parenthesis in the function declaration string.")
27
28    func_name = s[:s.index('(')].strip()
29
30    for i, ch in enumerate(reversed(func_name)):
31        if ch == ' ':
32            pos = len(func_name) - 1 - i
33            break
34    else:
35        raise ValueError('Cannot find any space in the function declaration string.')
36
37    func_name = func_name[pos + 1 : ]
38    return func_name
39
40def register_kernel_types():
41    register_types(parse_types("""
42    typedef int mode_t;
43    typedef unsigned int umode_t;
44    typedef int clockid_t;
45    typedef int pid_t;
46    typedef int qid_t;
47    typedef int key_t;
48    typedef int mqd_t;
49    typedef void *timer_t;
50    typedef uint32_t u32;
51    typedef uint32_t __u32;
52    typedef uint64_t u64;
53    typedef int32_t __s32;
54    typedef int64_t loff_t;
55    """))
56
57
58def convert_cproto_to_py(c_decl):
59    """
60    Convert a C-style function declaration string to its corresponding SimTypes-based Python representation.
61
62    :param str c_decl:              The C-style function declaration string.
63    :return:                        A tuple of the function name, the prototype, and a string representing the
64                                    SimType-based Python representation.
65    :rtype:                         tuple
66    """
67
68    s = [ ]
69
70    try:
71        s.append('# %s' % c_decl)  # comment string
72
73        parsed = parse_file(c_decl)
74        parsed_decl = parsed[0]
75        if not parsed_decl:
76            raise ValueError('Cannot parse the function prototype.')
77
78        func_name, func_proto = next(iter(parsed_decl.items()))
79
80        s.append('"%s": %s,' % (func_name, func_proto._init_str()))  # The real Python string
81
82    except Exception:  # pylint:disable=broad-except
83        # Silently catch all parsing errors... supporting all function declarations is impossible
84        try:
85            func_name = get_function_name(c_decl)
86            func_proto = None
87            s.append('"%s": None,' % func_name)
88        except ValueError:
89            # Failed to extract the function name. Is it a function declaration?
90            func_name, func_proto = None, None
91
92    return func_name, func_proto, "\n".join(s)
93
94
95def convert_cppproto_to_py(cpp_decl: str,
96                           with_param_names: bool=False) -> Tuple[Optional[str],Optional[SimTypeCppFunction],Optional[str]]:
97    """
98    Pre-process a C++-style function declaration string to its corresponding SimTypes-based Python representation.
99
100    :param cpp_decl:    The C++-style function declaration string.
101    :return:            A tuple of the function name, the prototype, and a string representing the SimType-based Python
102                        representation.
103    """
104
105    s = [ ]
106    try:
107        s.append("# %s" % cpp_decl)
108
109        parsed = parse_cpp_file(cpp_decl, with_param_names=with_param_names)
110        parsed_decl = parsed[0]
111        if not parsed_decl:
112            raise ValueError("Cannot parse the function prototype.")
113
114        func_name, func_proto = next(iter(parsed_decl.items()))
115
116        s.append('"%s": %s,' % (func_name, func_proto._init_str()))  # The real Python string
117
118    except Exception:  # pylint:disable=broad-except
119        try:
120            func_name = get_function_name(cpp_decl)
121            func_proto = None
122            s.append('"%s": None,' % func_name)
123        except ValueError:
124            # Failed to extract the function name. Is it a function declaration?
125            func_name, func_proto = None, None
126
127    return func_name, func_proto, "\n".join(s)
128
129
130def cprotos2py(cprotos, fd_spots=frozenset(), remove_sys_prefix=False):
131    """
132    Parse a list of C function declarations and output to Python code that can be embedded into
133    angr.procedures.definitions.
134
135    >>> # parse the list of glibc C prototypes and output to a file
136    >>> from angr.procedures.definitions import glibc
137    >>> with open("glibc_protos", "w") as f: f.write(cprotos2py(glibc._libc_c_decls))
138
139    :param list cprotos:    A list of C prototype strings.
140    :return:                A Python string.
141    :rtype:                 str
142    """
143    s = ""
144    for decl in cprotos:
145        func_name, proto_, str_ = convert_cproto_to_py(decl)  # pylint:disable=unused-variable
146        if remove_sys_prefix and func_name.startswith('sys'):
147            func_name = '_'.join(func_name.split('_')[1:])
148        if proto_ is not None:
149            if (func_name, -1) in fd_spots:
150                proto_.returnty = SimTypeFd(label=proto_.returnty.label)
151            for i, arg in enumerate(proto_.args):
152                if (func_name, i) in fd_spots:
153                    proto_.args[i] = SimTypeFd(label=arg.label)
154
155        line1 = ' '*8 + '# ' + decl + '\n'
156        line2 = ' '*8 + repr(func_name) + ": " + (proto_._init_str() if proto_ is not None else "None") + ',' + '\n'
157        s += line1 + line2
158    return s
159
160
161def get_cpp_function_name(demangled_name, specialized=True, qualified=True):
162    if not specialized:
163        # remove "<???>"s
164        name = normalize_cpp_function_name(demangled_name)
165    else:
166        name = demangled_name
167
168    if not qualified:
169        # remove leading namespaces
170        chunks = name.split("::")
171        name = "::".join(chunks[-2:])
172
173    # remove arguments
174    if "(" in name:
175        name = name[: name.find("(")]
176
177    return name
178