from typing import List, Tuple, Optional, TYPE_CHECKING
from ..sim_type import (
parse_file,
parse_cpp_file,
normalize_cpp_function_name,
SimTypeCppFunction,
SimTypeFd,
register_types,
parse_types,
)
if TYPE_CHECKING:
from ..sim_type import SimTypeFunction
[docs]def get_function_name(s):
"""
Get the function name from a C-style function declaration string.
:param str s: A C-style function declaration string.
:return: The function name.
:rtype: str
"""
s = s.strip()
if s.startswith("__attribute__"):
# Remove "__attribute__ ((foobar))"
if "))" not in s:
raise ValueError(
"__attribute__ is present, but I cannot find double-right parenthesis in the function "
"declaration string."
)
s = s[s.index("))") + 2 :].strip()
if "(" not in s:
raise ValueError("Cannot find any left parenthesis in the function declaration string.")
func_name = s[: s.index("(")].strip()
for i, ch in enumerate(reversed(func_name)):
if ch == " ":
pos = len(func_name) - 1 - i
break
else:
raise ValueError("Cannot find any space in the function declaration string.")
func_name = func_name[pos + 1 :]
return func_name
[docs]def register_kernel_types():
register_types(
parse_types(
"""
typedef int mode_t;
typedef unsigned int umode_t;
typedef int clockid_t;
typedef int pid_t;
typedef int qid_t;
typedef int key_t;
typedef int mqd_t;
typedef void *timer_t;
typedef uint32_t u32;
typedef uint32_t __u32;
typedef uint64_t u64;
typedef int32_t __s32;
typedef int64_t loff_t;
"""
)
)
[docs]def convert_cproto_to_py(c_decl) -> Tuple[str, "SimTypeFunction", str]:
"""
Convert a C-style function declaration string to its corresponding SimTypes-based Python representation.
:param str c_decl: The C-style function declaration string.
:return: A tuple of the function name, the prototype, and a string representing the
SimType-based Python representation.
"""
s = []
try:
s.append("# %s" % c_decl) # comment string
parsed = parse_file(c_decl)
parsed_decl = parsed[0]
if not parsed_decl:
raise ValueError("Cannot parse the function prototype.")
func_name, func_proto = next(iter(parsed_decl.items()))
s.append(f'"{func_name}": {func_proto._init_str()},') # The real Python string
except Exception: # pylint:disable=broad-except
# Silently catch all parsing errors... supporting all function declarations is impossible
try:
func_name = get_function_name(c_decl)
func_proto = None
s.append('"%s": None,' % func_name)
except ValueError:
# Failed to extract the function name. Is it a function declaration?
func_name, func_proto = None, None
return func_name, func_proto, "\n".join(s)
[docs]def convert_cppproto_to_py(
cpp_decl: str, with_param_names: bool = False
) -> Tuple[Optional[str], Optional[SimTypeCppFunction], Optional[str]]:
"""
Pre-process a C++-style function declaration string to its corresponding SimTypes-based Python representation.
:param cpp_decl: The C++-style function declaration string.
:return: A tuple of the function name, the prototype, and a string representing the SimType-based Python
representation.
"""
s = []
try:
s.append("# %s" % cpp_decl)
parsed = parse_cpp_file(cpp_decl, with_param_names=with_param_names)
parsed_decl = parsed[0]
if not parsed_decl:
raise ValueError("Cannot parse the function prototype.")
func_name, func_proto = next(iter(parsed_decl.items()))
s.append(f'"{func_name}": {func_proto._init_str()},') # The real Python string
except Exception: # pylint:disable=broad-except
try:
func_name = get_function_name(cpp_decl)
func_proto = None
s.append('"%s": None,' % func_name)
except ValueError:
# Failed to extract the function name. Is it a function declaration?
func_name, func_proto = None, None
return func_name, func_proto, "\n".join(s)
[docs]def parsedcprotos2py(
parsed_cprotos: List[Tuple[str, "SimTypeFunction", str]], fd_spots=frozenset(), remove_sys_prefix=False
) -> str:
"""
Parse a list of C function declarations and output to Python code that can be embedded into
angr.procedures.definitions.
>>> # parse the list of glibc C prototypes and output to a file
>>> from angr.procedures.definitions import glibc
>>> with open("glibc_protos", "w") as f: f.write(cprotos2py(glibc._libc_c_decls))
:param parsed_cprotos: A list of tuples where each tuple is (function name, parsed C function prototype,
the original function declaration).
:return: A Python string.
"""
s = ""
for func_name, proto_, decl in parsed_cprotos:
if remove_sys_prefix and func_name.startswith("sys"):
func_name = "_".join(func_name.split("_")[1:])
if proto_ is not None:
if (func_name, -1) in fd_spots:
proto_.returnty = SimTypeFd(label=proto_.returnty.label)
for i, arg in enumerate(proto_.args):
if (func_name, i) in fd_spots:
proto_.args[i] = SimTypeFd(label=arg.label)
line1 = " " * 8 + "# " + decl + "\n"
line2 = " " * 8 + repr(func_name) + ": " + (proto_._init_str() if proto_ is not None else "None") + "," + "\n"
s += line1 + line2
return s
[docs]def cprotos2py(cprotos: List[str], fd_spots=frozenset(), remove_sys_prefix=False) -> str:
"""
Parse a list of C function declarations and output to Python code that can be embedded into
angr.procedures.definitions.
>>> # parse the list of glibc C prototypes and output to a file
>>> from angr.procedures.definitions import glibc
>>> with open("glibc_protos", "w") as f: f.write(cprotos2py(glibc._libc_c_decls))
:param cprotos: A list of C prototype strings.
:return: A Python string.
"""
parsed_cprotos = []
for decl in cprotos:
func_name, proto_, _ = convert_cproto_to_py(decl) # pylint:disable=unused-variable
parsed_cprotos.append((func_name, proto_, decl))
return parsedcprotos2py(parsed_cprotos, fd_spots=fd_spots, remove_sys_prefix=remove_sys_prefix)
[docs]def get_cpp_function_name(demangled_name, specialized=True, qualified=True):
if not specialized:
# remove "<???>"s
name = normalize_cpp_function_name(demangled_name)
else:
name = demangled_name
if not qualified:
# remove leading namespaces
chunks = name.split("::")
name = "::".join(chunks[-2:])
# remove arguments
if "(" in name:
name = name[: name.find("(")]
return name