# pylint:disable=abstract-method,line-too-long,missing-class-docstring
from collections import OrderedDict, defaultdict, ChainMap
import copy
import re
from typing import Optional, Dict, Any, Tuple, List, Union, Type, TYPE_CHECKING
import logging
try:
import pycparser
except ImportError:
pycparser = None
try:
import CppHeaderParser
except ImportError:
CppHeaderParser = None
from archinfo import Endness
import claripy
from angr.errors import AngrMissingTypeError
from .misc.ux import deprecated
if TYPE_CHECKING:
from angr.procedures.definitions import SimTypeCollection
l = logging.getLogger(name=__name__)
# pycparser hack to parse type expressions
errorlog = logging.getLogger(name=__name__ + ".yacc")
errorlog.setLevel(logging.ERROR)
[docs]class SimType:
"""
SimType exists to track type information for SimProcedures.
"""
_fields = ()
_arch = None
_size = None
_can_refine_int = False
_base_name = None
base = True
[docs] def __init__(self, label=None):
"""
:param label: the type label.
"""
self.label = label
def __eq__(self, other, avoid=None):
if type(self) != type(other):
return False
for attr in self._fields:
if attr == "size" and self._arch is None and other._arch is None:
continue
attr_self = getattr(self, attr)
attr_other = getattr(other, attr)
if isinstance(attr_self, SimType):
if attr_other is attr_self:
return True
if avoid is not None and attr_self in avoid["self"] and attr_other in avoid["other"]:
continue
if not attr_self.__eq__(attr_other, avoid=avoid):
return False
else:
if attr_self != attr_other:
return False
return True
def __ne__(self, other):
# wow many efficient
return not self == other
def __hash__(self):
# very hashing algorithm many secure wow
out = hash(type(self))
for attr in self._fields:
out ^= hash(getattr(self, attr))
return out
def _refine_dir(self): # pylint: disable=no-self-use
return []
def _refine(self, view, k): # pylint: disable=unused-argument,no-self-use
raise KeyError(f"{k} is not a valid refinement")
@property
def size(self):
"""
The size of the type in bits.
"""
if self._size is not None:
return self._size
return NotImplemented
@property
def alignment(self):
"""
The alignment of the type in bytes.
"""
if self._arch is None:
raise ValueError("Can't tell my alignment without an arch!")
if self.size is NotImplemented:
return NotImplemented
return self.size // self._arch.byte_width
[docs] def with_arch(self, arch):
if arch is None:
return self
if self._arch is not None and self._arch == arch:
return self
else:
return self._with_arch(arch)
def _with_arch(self, arch):
cp = copy.copy(self)
cp._arch = arch
return cp
def _init_str(self):
return f"NotImplemented({self.__class__.__name__})"
[docs] def c_repr(self, name=None, full=0, memo=None, indent=0):
if name is None:
return repr(self)
else:
return f"{str(self) if self.label is None else self.label} {name}"
[docs] def copy(self):
raise NotImplementedError()
[docs]class TypeRef(SimType):
"""
A TypeRef is a reference to a type with a name. This allows for interactivity in type analysis, by storing a type
and having the option to update it later and have all references to it automatically update as well.
"""
[docs] def __init__(self, name, ty):
super().__init__()
self.type = ty
self._name = name
@property
def name(self):
"""
This is a read-only property because it is desirable to store typerefs in a mapping from name to type, and we
want the mapping to be in the loop for any updates.
"""
return self._name
def __eq__(self, other, avoid=None):
return type(other) is TypeRef and self.type == other.type
def __hash__(self):
return hash(self.type)
def __repr__(self):
return self.name
@property
def _arch(self):
return self.type._arch
@property
def size(self):
return self.type.size
@property
def alignment(self):
return self.type.alignment
[docs] def with_arch(self, arch):
self.type = self.type.with_arch(arch)
return self
[docs] def c_repr(self, name=None, full=0, memo=None, indent=0):
if not full:
if name is not None:
return f"{self.name} {name}"
else:
return self.name
else:
return self.type.c_repr(name=name, full=full, memo=memo, indent=indent)
[docs] def copy(self):
raise NotImplementedError("copy() for TypeRef is ill-defined. What do you want this to do?")
[docs]class NamedTypeMixin:
"""
SimType classes with this mixin in the class hierarchy allows setting custom class names. A typical use case is
to represent same or similar type classes with different qualified names, such as "std::basic_string" vs
"std::__cxx11::basic_string". In such cases, .name stores the qualified name, and .unqualified_name() returns the
unqualified name of the type.
"""
[docs] def __init__(self, *args, name: Optional[str] = None, **kwargs):
super().__init__(*args, **kwargs)
self._name = name
@property
def name(self) -> str:
if self._name is None:
self._name = repr(self)
return self._name
@name.setter
def name(self, v):
self._name = v
[docs] def unqualified_name(self, lang: str = "c++") -> str:
if lang == "c++":
splitter = "::"
n = self.name.split(splitter)
return n[-1]
raise NotImplementedError(f"Unsupported language {lang}.")
[docs]class SimTypeBottom(SimType):
"""
SimTypeBottom basically represents a type error.
"""
_base_name = "bot"
[docs] def __init__(self, label=None):
super().__init__(label)
def __repr__(self):
return self.label or "BOT"
[docs] def c_repr(self, name=None, full=0, memo=None, indent=0):
if name is None:
return "int"
else:
return f'{"int" if self.label is None else self.label} {name}'
def _init_str(self):
return "{}({})".format(self.__class__.__name__, ('label="%s"' % self.label) if self.label else "")
[docs] def copy(self):
return SimTypeBottom(self.label)
[docs]class SimTypeTop(SimType):
"""
SimTypeTop represents any type (mostly used with a pointer for void*).
"""
_fields = ("size",)
[docs] def __init__(self, size=None, label=None):
SimType.__init__(self, label)
self._size = size
def __repr__(self):
return "TOP"
[docs] def copy(self):
return SimTypeTop(size=self.size, label=self.label)
[docs]class SimTypeReg(SimType):
"""
SimTypeReg is the base type for all types that are register-sized.
"""
_fields = ("size",)
[docs] def __init__(self, size, label=None):
"""
:param label: the type label.
:param size: the size of the type (e.g. 32bit, 8bit, etc.).
"""
SimType.__init__(self, label=label)
self._size = size
def __repr__(self):
return f"reg{self.size}_t"
[docs] def store(self, state, addr, value):
store_endness = state.arch.memory_endness
try:
value = value.ast
except AttributeError:
pass
if isinstance(value, claripy.ast.Bits): # pylint:disable=isinstance-second-argument-not-valid-type
if value.size() != self.size:
raise ValueError("size of expression is wrong size for type")
elif isinstance(value, int):
value = state.solver.BVV(value, self.size)
elif isinstance(value, bytes):
store_endness = "Iend_BE"
else:
raise TypeError(f"unrecognized expression type for SimType {type(self).__name__}")
state.memory.store(addr, value, endness=store_endness)
[docs] def copy(self):
return self.__class__(self.size, label=self.label)
[docs]class SimTypeNum(SimType):
"""
SimTypeNum is a numeric type of arbitrary length
"""
_fields = SimType._fields + ("signed", "size")
[docs] def __init__(self, size, signed=True, label=None):
"""
:param size: The size of the integer, in bits
:param signed: Whether the integer is signed or not
:param label: A label for the type
"""
super().__init__(label)
self._size = size
self.signed = signed
def __repr__(self):
return "{}int{}_t".format("" if self.signed else "u", self.size)
[docs] def store(self, state, addr, value):
store_endness = state.arch.memory_endness
if isinstance(value, claripy.ast.Bits): # pylint:disable=isinstance-second-argument-not-valid-type
if value.size() != self.size:
raise ValueError("size of expression is wrong size for type")
elif isinstance(value, int):
value = state.solver.BVV(value, self.size)
elif isinstance(value, bytes):
store_endness = "Iend_BE"
else:
raise TypeError(f"unrecognized expression type for SimType {type(self).__name__}")
state.memory.store(addr, value, endness=store_endness)
[docs] def copy(self):
return SimTypeNum(self.size, signed=self.signed, label=self.label)
[docs]class SimTypeInt(SimTypeReg):
"""
SimTypeInt is a type that specifies a signed or unsigned C integer.
"""
_fields = tuple(x for x in SimTypeReg._fields if x != "size") + ("signed",)
_base_name = "int"
[docs] def __init__(self, signed=True, label=None):
"""
:param signed: True if signed, False if unsigned
:param label: The type label
"""
super().__init__(None, label=label)
self.signed = signed
[docs] def c_repr(self, name=None, full=0, memo=None, indent=0):
out = self._base_name
if not self.signed:
out = "unsigned " + out
if name is None:
return out
return f"{out} {name}"
def __repr__(self):
name = self._base_name
if not self.signed:
name = "unsigned " + name
try:
return name + " (%d bits)" % self.size
except ValueError:
return name
@property
def size(self):
if self._arch is None:
raise ValueError("Can't tell my size without an arch!")
try:
return self._arch.sizeof[self._base_name]
except KeyError:
raise ValueError(f"Arch {self._arch.name} doesn't have its {self._base_name} type defined!")
def _init_str(self):
return "{}(signed={}{})".format(
self.__class__.__name__,
self.signed,
(', label="%s"' % self.label) if self.label is not None else "",
)
def _refine_dir(self):
return ["signed", "unsigned"]
def _refine(self, view, k):
if k == "signed":
ty = copy.copy(self)
ty.signed = True
elif k == "unsigned":
ty = copy.copy(self)
ty.signed = False
else:
raise KeyError(k)
return view._deeper(ty=ty)
[docs] def copy(self):
return self.__class__(signed=self.signed, label=self.label)
[docs]class SimTypeShort(SimTypeInt):
_base_name = "short"
[docs]class SimTypeLong(SimTypeInt):
_base_name = "long"
[docs]class SimTypeLongLong(SimTypeInt):
_base_name = "long long"
[docs]class SimTypeChar(SimTypeReg):
"""
SimTypeChar is a type that specifies a character;
this could be represented by a byte, but this is meant to be interpreted as a character.
"""
_base_name = "char"
[docs] def __init__(self, signed=True, label=None):
"""
:param label: the type label.
"""
# FIXME: Now the size of a char is state-dependent.
SimTypeReg.__init__(self, 8, label=label)
self.signed = signed
def __repr__(self):
return "char"
[docs] def store(self, state, addr, value):
# FIXME: This is a hack.
self._size = state.arch.byte_width
try:
super().store(state, addr, value)
except TypeError:
if isinstance(value, bytes) and len(value) == 1:
value = state.solver.BVV(value[0], state.arch.byte_width)
super().store(state, addr, value)
else:
raise
def _init_str(self):
return "{}({})".format(
self.__class__.__name__,
('label="%s"' % self.label) if self.label is not None else "",
)
[docs] def copy(self):
return self.__class__(signed=self.signed, label=self.label)
[docs]class SimTypeWideChar(SimTypeReg):
"""
SimTypeWideChar is a type that specifies a wide character (a UTF-16 character).
"""
_base_name = "char"
[docs] def __init__(self, signed=True, label=None):
"""
:param label: the type label.
"""
SimTypeReg.__init__(self, 16, label=label)
self.signed = signed
def __repr__(self):
return "wchar"
[docs] def store(self, state, addr, value):
self._size = state.arch.byte_width
try:
super().store(state, addr, value)
except TypeError:
if isinstance(value, bytes) and len(value) == 2:
value = state.solver.BVV(value[0], state.arch.byte_width)
super().store(state, addr, value)
else:
raise
def _init_str(self):
return "{}({})".format(
self.__class__.__name__,
('label="%s"' % self.label) if self.label is not None else "",
)
[docs] def copy(self):
return self.__class__(signed=self.signed, label=self.label)
[docs]class SimTypeBool(SimTypeChar):
_base_name = "bool"
def __repr__(self):
return "bool"
[docs] def store(self, state, addr, value):
return super().store(state, addr, int(value))
def _init_str(self):
return f"{self.__class__.__name__}()"
[docs]class SimTypeFd(SimTypeReg):
"""
SimTypeFd is a type that specifies a file descriptor.
"""
_fields = SimTypeReg._fields
[docs] def __init__(self, label=None):
"""
:param label: the type label
"""
# file descriptors are always 32 bits, right?
# TODO: That's so closed-minded!
super().__init__(32, label=label)
def __repr__(self):
return "fd_t"
[docs] def copy(self):
return SimTypeFd(label=self.label)
def _init_str(self):
return "{}({})".format(
self.__class__.__name__,
('label="%s"' % self.label) if self.label is not None else "",
)
[docs]class SimTypePointer(SimTypeReg):
"""
SimTypePointer is a type that specifies a pointer to some other type.
"""
_fields = tuple(x for x in SimTypeReg._fields if x != "size") + ("pts_to",)
[docs] def __init__(self, pts_to, label=None, offset=0):
"""
:param label: The type label.
:param pts_to: The type to which this pointer points.
"""
super().__init__(None, label=label)
self.pts_to = pts_to
self.signed = False
self.offset = offset
def __repr__(self):
return f"{self.pts_to}*"
[docs] def c_repr(self, name=None, full=0, memo=None, indent=0):
# if pts_to is SimTypeBottom, we return a void*
if isinstance(self.pts_to, SimTypeBottom):
out = "void*"
if name is None:
return out
return f"{out} {name}"
# if it points to an array, we do not need to add a *
deref_chr = "*" if not isinstance(self.pts_to, SimTypeArray) else ""
name_with_deref = deref_chr if name is None else f"{deref_chr}{name}"
return self.pts_to.c_repr(name_with_deref, full, memo, indent)
[docs] def make(self, pts_to):
new = type(self)(pts_to)
new._arch = self._arch
return new
@property
def size(self):
if self._arch is None:
raise ValueError("Can't tell my size without an arch!")
return self._arch.bits
def _with_arch(self, arch):
out = SimTypePointer(self.pts_to.with_arch(arch), self.label)
out._arch = arch
return out
def _init_str(self):
return "%s(%s%s, offset=%d)" % (
self.__class__.__name__,
self.pts_to._init_str(),
(', label="%s"' % self.label) if self.label is not None else "",
self.offset,
)
[docs] def copy(self):
return SimTypePointer(self.pts_to, label=self.label, offset=self.offset)
[docs]class SimTypeReference(SimTypeReg):
"""
SimTypeReference is a type that specifies a reference to some other type.
"""
[docs] def __init__(self, refs, label=None):
super().__init__(None, label=label)
self.refs: SimType = refs
def __repr__(self):
return f"{self.refs}&"
[docs] def c_repr(self, name=None, full=0, memo=None, indent=0):
name = "&" if name is None else "&%s" % name
return self.refs.c_repr(name, full, memo, indent)
[docs] def make(self, refs):
new = type(self)(refs)
new._arch = self._arch
return new
@property
def size(self):
if self._arch is None:
raise ValueError("Can't tell my size without an arch!")
return self._arch.bits
def _with_arch(self, arch):
out = SimTypeReference(self.refs.with_arch(arch), label=self.label)
out._arch = arch
return out
def _init_str(self):
return "{}({}{})".format(
self.__class__.__name__,
self.refs._init_str(),
(', label="%s"' % self.label) if self.label is not None else "",
)
[docs] def copy(self):
return SimTypeReference(self.refs, label=self.label)
[docs]class SimTypeArray(SimType):
"""
SimTypeArray is a type that specifies a series of data laid out in sequence.
"""
_fields = ("elem_type", "length")
[docs] def __init__(self, elem_type, length=None, label=None):
"""
:param label: The type label.
:param elem_type: The type of each element in the array.
:param length: An expression of the length of the array, if known.
"""
super().__init__(label=label)
self.elem_type: SimType = elem_type
self.length: Optional[int] = length
def __repr__(self):
return "{}[{}]".format(self.elem_type, "" if self.length is None else self.length)
[docs] def c_repr(self, name=None, full=0, memo=None, indent=0):
if name is None:
return repr(self)
name = "{}[{}]".format(name, self.length if self.length is not None else "")
return self.elem_type.c_repr(name, full, memo, indent)
@property
def size(self):
if self.length is None:
return 0
return self.elem_type.size * self.length
@property
def alignment(self):
return self.elem_type.alignment
def _with_arch(self, arch):
out = SimTypeArray(self.elem_type.with_arch(arch), self.length, self.label)
out._arch = arch
return out
[docs] def copy(self):
return SimTypeArray(self.elem_type, length=self.length, label=self.label)
_can_refine_int = True
def _refine(self, view, k):
return view._deeper(
addr=view._addr + k * (self.elem_type.size // view.state.arch.byte_width), ty=self.elem_type
)
[docs] def store(self, state, addr, values):
for i, val in enumerate(values):
self.elem_type.store(state, addr + i * (self.elem_type.size // state.arch.byte_width), val)
def _init_str(self):
return "{}({}, {}{})".format(
self.__class__.__name__,
self.elem_type._init_str(),
self.length,
f", {self.label}" if self.label is not None else "",
)
SimTypeFixedSizeArray = SimTypeArray
[docs]class SimTypeString(NamedTypeMixin, SimTypeArray):
"""
SimTypeString is a type that represents a C-style string,
i.e. a NUL-terminated array of bytes.
"""
_fields = SimTypeArray._fields + ("length",)
[docs] def __init__(self, length=None, label=None, name: Optional[str] = None):
"""
:param label: The type label.
:param length: An expression of the length of the string, if known.
"""
super().__init__(SimTypeChar(), label=label, length=length, name=name)
def __repr__(self):
return "string_t"
_can_refine_int = True
def _refine(self, view, k):
return view._deeper(addr=view._addr + k, ty=SimTypeChar())
@property
def size(self):
if self.length is None:
return 4096 # :/
return (self.length + 1) * 8
@property
def alignment(self):
return 1
def _with_arch(self, arch):
return self
[docs] def copy(self):
return SimTypeString(length=self.length, label=self.label, name=self.name)
[docs]class SimTypeWString(NamedTypeMixin, SimTypeArray):
"""
A wide-character null-terminated string, where each character is 2 bytes.
"""
_fields = SimTypeArray._fields + ("length",)
[docs] def __init__(self, length=None, label=None, name: Optional[str] = None):
super().__init__(SimTypeNum(16, False), label=label, length=length, name=name)
def __repr__(self):
return "wstring_t"
_can_refine_int = True
def _refine(self, view, k):
return view._deeper(addr=view._addr + k * 2, ty=SimTypeNum(16, False))
@property
def size(self):
if self.length is None:
return 4096
return (self.length * 2 + 2) * 8
@property
def alignment(self):
return 2
def _with_arch(self, arch):
return self
[docs] def copy(self):
return SimTypeWString(length=self.length, label=self.label, name=self.name)
[docs]class SimTypeFunction(SimType):
"""
SimTypeFunction is a type that specifies an actual function (i.e. not a pointer) with certain types of arguments and
a certain return value.
"""
_fields = ("args", "returnty")
base = False
[docs] def __init__(self, args: List[SimType], returnty: Optional[SimType], label=None, arg_names=None, variadic=False):
"""
:param label: The type label
:param args: A tuple of types representing the arguments to the function
:param returnty: The return type of the function, or none for void
:param variadic: Whether the function accepts varargs
"""
super().__init__(label=label)
self.args: List[SimType] = args
self.returnty: Optional[SimType] = returnty
self.arg_names = arg_names if arg_names else ()
self.variadic = variadic
def __hash__(self):
return hash(type(self)) ^ hash(tuple(self.args)) ^ hash(self.returnty)
def __repr__(self):
argstrs = [str(a) for a in self.args]
if self.variadic:
argstrs.append("...")
return "({}) -> {}".format(", ".join(argstrs), self.returnty)
[docs] def c_repr(self, name=None, full=0, memo=None, indent=0):
formatted_args = [
a.c_repr(n, full - 1, memo, indent)
for a, n in zip(self.args, self.arg_names if self.arg_names and full else (None,) * len(self.args))
]
if self.variadic:
formatted_args.append("...")
proto = f"({name or ''})({', '.join(formatted_args)})"
return f"void {proto}" if self.returnty is None else self.returnty.c_repr(proto, full, memo, indent)
@property
def size(self):
return 4096 # ???????????
def _with_arch(self, arch):
out = SimTypeFunction(
[a.with_arch(arch) for a in self.args],
self.returnty.with_arch(arch) if self.returnty is not None else None,
label=self.label,
arg_names=self.arg_names,
variadic=self.variadic,
)
out._arch = arch
return out
def _arg_names_str(self, show_variadic=True):
argnames = list(self.arg_names)
if self.variadic and show_variadic:
argnames.append("...")
return ", ".join('"%s"' % arg_name for arg_name in argnames)
def _init_str(self):
return "{}([{}], {}{}{}{})".format(
self.__class__.__name__,
", ".join([arg._init_str() for arg in self.args]),
self.returnty._init_str(),
(', label="%s"' % self.label) if self.label else "",
(", arg_names=[%s]" % self._arg_names_str(show_variadic=False)) if self.arg_names else "",
", variadic=True" if self.variadic else "",
)
[docs] def copy(self):
return SimTypeFunction(
self.args, self.returnty, label=self.label, arg_names=self.arg_names, variadic=self.variadic
)
[docs]class SimTypeCppFunction(SimTypeFunction):
"""
SimTypeCppFunction is a type that specifies an actual C++-style function with information about arguments, return
value, and more C++-specific properties.
:ivar ctor: Whether the function is a constructor or not.
:ivar dtor: Whether the function is a destructor or not.
"""
[docs] def __init__(
self, args, returnty, label=None, arg_names: Tuple[str] = None, ctor: bool = False, dtor: bool = False
):
super().__init__(args, returnty, label=label, arg_names=arg_names, variadic=False)
self.ctor = ctor
self.dtor = dtor
def __repr__(self):
argstrs = [str(a) for a in self.args]
if self.variadic:
argstrs.append("...")
return str(self.label) + "({}) -> {}".format(", ".join(argstrs), self.returnty)
def _init_str(self):
return "{}([{}], {}{}{}{})".format(
self.__class__.__name__,
", ".join([arg._init_str() for arg in self.args]),
self.returnty,
(", label=%s" % self.label) if self.label else "",
(", arg_names=[%s]" % self._arg_names_str(show_variadic=False)) if self.arg_names else "",
", variadic=True" if self.variadic else "",
)
[docs] def copy(self):
return SimTypeCppFunction(
self.args,
self.returnty,
label=self.label,
arg_names=self.arg_names,
ctor=self.ctor,
dtor=self.dtor,
)
[docs]class SimTypeLength(SimTypeLong):
"""
SimTypeLength is a type that specifies the length of some buffer in memory.
...I'm not really sure what the original design of this class was going for
"""
_fields = tuple(x for x in SimTypeReg._fields if x != "size") + ("addr", "length") # ?
[docs] def __init__(self, signed=False, addr=None, length=None, label=None):
"""
:param signed: Whether the value is signed or not
:param label: The type label.
:param addr: The memory address (expression).
:param length: The length (expression).
"""
super().__init__(signed=signed, label=label)
self.addr = addr
self.length = length
def __repr__(self):
return "size_t"
@property
def size(self):
if self._arch is None:
raise ValueError("I can't tell my size without an arch!")
return self._arch.bits
def _init_str(self):
return "%s(size=%d)" % (self.__class__.__name__, self.size)
[docs] def copy(self):
return SimTypeLength(signed=self.signed, addr=self.addr, length=self.length, label=self.label)
[docs]class SimTypeFloat(SimTypeReg):
"""
An IEEE754 single-precision floating point number
"""
_base_name = "float"
[docs] def __init__(self, size=32):
super().__init__(size)
sort = claripy.FSORT_FLOAT
signed = True
[docs] def store(self, state, addr, value):
if type(value) in (int, float):
value = claripy.FPV(float(value), self.sort)
return super().store(state, addr, value)
def __repr__(self):
return "float"
def _init_str(self):
return "%s(size=%d)" % (self.__class__.__name__, self.size)
[docs] def copy(self):
return SimTypeFloat(self.size)
[docs]class SimTypeDouble(SimTypeFloat):
"""
An IEEE754 double-precision floating point number
"""
_base_name = "double"
[docs] def __init__(self, align_double=True):
self.align_double = align_double
super().__init__(64)
sort = claripy.FSORT_DOUBLE
def __repr__(self):
return "double"
@property
def alignment(self):
return 8 if self.align_double else 4
def _init_str(self):
return f"{self.__class__.__name__}(align_double={self.align_double})"
[docs] def copy(self):
return SimTypeDouble(align_double=self.align_double)
[docs]class SimStruct(NamedTypeMixin, SimType):
_fields = ("name", "fields")
[docs] def __init__(self, fields: Union[Dict[str, SimType], OrderedDict], name=None, pack=False, align=None):
super().__init__(None, name="<anon>" if name is None else name)
self._pack = pack
self._align = align
self.fields = fields
self._arch_memo = {}
@property
def packed(self):
return self._pack
@property
def offsets(self) -> Dict[str, int]:
offsets = {}
offset_so_far = 0
for name, ty in self.fields.items():
if isinstance(ty, SimTypeBottom):
l.warning(
"Found a bottom field in struct %s. Ignore and increment the offset using the default "
"element size.",
self.name,
)
continue
if not self._pack:
align = ty.alignment
if align is NotImplemented:
# hack!
align = 1
if offset_so_far % align != 0:
offset_so_far += align - offset_so_far % align
offsets[name] = offset_so_far
offset_so_far += ty.size // self._arch.byte_width
else:
offsets[name] = offset_so_far // self._arch.byte_width
offset_so_far += ty.size
return offsets
def _with_arch(self, arch):
if arch.name in self._arch_memo:
return self._arch_memo[arch.name]
out = SimStruct(None, name=self.name, pack=self._pack, align=self._align)
out._arch = arch
self._arch_memo[arch.name] = out
out.fields = OrderedDict((k, v.with_arch(arch)) for k, v in self.fields.items())
# Fixup the offsets to byte aligned addresses for all SimTypeNumOffset types
offset_so_far = 0
for name, ty in out.fields.items():
if isinstance(ty, SimTypeNumOffset):
out._pack = True
ty.offset = offset_so_far % arch.byte_width
offset_so_far += ty.size
return out
def __repr__(self):
return "struct %s" % self.name
[docs] def c_repr(self, name=None, full=0, memo=None, indent=0):
if not full or (memo is not None and self in memo):
return super().c_repr(name, full, memo, indent)
indented = " " * indent if indent is not None else ""
new_indent = indent + 4 if indent is not None else None
new_indented = " " * new_indent if indent is not None else ""
newline = "\n" if indent is not None else " "
new_memo = (self,) + (memo if memo is not None else ())
members = newline.join(
new_indented + v.c_repr(k, full - 1, new_memo, new_indent) + ";" for k, v in self.fields.items()
)
return "struct {} {{{}{}{}{}}}{}".format(
self.name, newline, members, newline, indented, "" if name is None else " " + name
)
def __hash__(self):
return hash((SimStruct, self._name, self._align, self._pack, tuple(self.fields.keys())))
@property
def size(self):
if not self.offsets:
return 0
last_name, last_off = list(self.offsets.items())[-1]
last_type = self.fields[last_name]
if isinstance(last_type, SimTypeNumOffset):
return last_off * self._arch.byte_width + (last_type.size + last_type.offset)
else:
return last_off * self._arch.byte_width + last_type.size
@property
def alignment(self):
if self._align is not None:
return self._align
if all(val.alignment is NotImplemented for val in self.fields.values()):
return NotImplemented
return max(val.alignment if val.alignment is not NotImplemented else 1 for val in self.fields.values())
def _refine_dir(self):
return list(self.fields.keys())
def _refine(self, view, k):
offset = self.offsets[k]
ty = self.fields[k]
return view._deeper(ty=ty, addr=view._addr + offset)
[docs] def store(self, state, addr, value):
if type(value) is dict:
pass
elif type(value) is SimStructValue:
value = value._values
else:
raise TypeError("Can't store struct of type %s" % type(value))
if len(value) != len(self.fields):
raise ValueError("Passed bad values for %s; expected %d, got %d" % (self, len(self.offsets), len(value)))
for field, offset in self.offsets.items():
ty = self.fields[field]
ty.store(state, addr + offset, value[field])
@staticmethod
def _field_str(field_name, field_type):
return f'("{field_name}", {field_type._init_str()})'
def _init_str(self):
return '{}(OrderedDict(({},)), name="{}", pack={}, align={})'.format(
self.__class__.__name__,
", ".join([self._field_str(f, ty) for f, ty in self.fields.items()]),
self._name,
self._pack,
self._align,
)
[docs] def copy(self):
return SimStruct(dict(self.fields), name=self.name, pack=self._pack, align=self._align)
def __eq__(self, other, avoid=None):
if not isinstance(other, SimStruct):
return False
if not (
self._pack == other._pack
and self._align == other._align
and self.label == other.label
and self._name == other._name
and self._arch == other._arch
):
return False
# fields comparison that accounts for self references
if not self.fields and not other.fields:
return True
keys_self = list(self.fields)
keys_other = list(other.fields)
if keys_self != keys_other:
return False
if avoid is None:
avoid = {"self": {self}, "other": {other}}
for key in keys_self:
field_self = self.fields[key]
field_other = other.fields[key]
if field_self in avoid["self"] and field_other in avoid["other"]:
continue
if not field_self.__eq__(field_other, avoid=avoid):
return False
return True
[docs]class SimStructValue:
"""
A SimStruct type paired with some real values
"""
[docs] def __init__(self, struct, values=None):
"""
:param struct: A SimStruct instance describing the type of this struct
:param values: A mapping from struct fields to values
"""
self._struct = struct
# since the keys are specified, also support specifying the values as just a list
if values is not None and hasattr(values, "__iter__") and not hasattr(values, "items"):
values = dict(zip(struct.fields.keys(), values))
self._values = defaultdict(lambda: None, values or ())
@property
def struct(self):
return self._struct
def __indented_repr__(self, indent=0):
fields = []
for name in self._struct.fields:
value = self._values[name]
try:
f = getattr(value, "__indented_repr__")
s = f(indent=indent + 2)
except AttributeError:
s = repr(value)
fields.append(" " * (indent + 2) + f".{name} = {s}")
return "{{\n{}\n{}}}".format(",\n".join(fields), " " * indent)
def __repr__(self):
return self.__indented_repr__()
def __getattr__(self, k):
return self[k]
def __getitem__(self, k):
if type(k) is int:
k = self._struct.fields[k]
if k not in self._values:
for f in self._struct.fields:
if isinstance(f, NamedTypeMixin) and f.name is None:
try:
return f[k]
except KeyError:
continue
else:
return self._values[k]
return self._values[k]
[docs] def copy(self):
return SimStructValue(self._struct, values=defaultdict(lambda: None, self._values))
[docs]class SimUnion(NamedTypeMixin, SimType):
fields = ("members", "name")
[docs] def __init__(self, members, name=None, label=None):
"""
:param members: The members of the union, as a mapping name -> type
:param name: The name of the union
"""
super().__init__(label, name=name if name is not None else "<anon>")
self.members = members
@property
def size(self):
return max(ty.size for ty in self.members.values() if not isinstance(ty, SimTypeBottom))
@property
def alignment(self):
if all(val.alignment is NotImplemented for val in self.members.values()):
return NotImplemented
return max(val.alignment if val.alignment is not NotImplemented else 1 for val in self.members.values())
def _refine_dir(self):
return list(self.members.keys())
def _refine(self, view, k):
ty = self.members[k]
return view._deeper(ty=ty, addr=view._addr)
def __repr__(self):
# use the str instead of repr of each member to avoid exceed recursion
# depth when representing self-referential unions
return "union {} {{\n\t{}\n}}".format(
self.name, "\n\t".join(f"{name} {str(ty)};" for name, ty in self.members.items())
)
[docs] def c_repr(self, name=None, full=0, memo=None, indent=0):
if not full or (memo is not None and self in memo):
return super().c_repr(name, full, memo, indent)
indented = " " * indent if indent is not None else ""
new_indent = indent + 4 if indent is not None else None
new_indented = " " * new_indent if indent is not None else ""
newline = "\n" if indent is not None else " "
new_memo = (self,) + (memo if memo is not None else ())
members = newline.join(
new_indented + v.c_repr(k, full - 1, new_memo, new_indent) + ";" for k, v in self.members.items()
)
return "union {} {{{}{}{}{}}}{}".format(
self.name, newline, members, newline, indented, "" if name is None else " " + name
)
def _init_str(self):
return '{}({{{}}}, name="{}", label="{}")'.format(
self.__class__.__name__,
", ".join([self._field_str(f, ty) for f, ty in self.members.items()]),
self._name,
self.label,
)
@staticmethod
def _field_str(field_name, field_type):
return f'"{field_name}": {field_type._init_str()}'
def __str__(self):
return f"union {self.name}"
def _with_arch(self, arch):
out = SimUnion({name: ty.with_arch(arch) for name, ty in self.members.items()}, self.label)
out._arch = arch
return out
[docs] def copy(self):
return SimUnion(dict(self.members), name=self.name, label=self.label)
[docs]class SimUnionValue:
"""
A SimStruct type paired with some real values
"""
[docs] def __init__(self, union, values=None):
"""
:param union: A SimUnion instance describing the type of this union
:param values: A mapping from union members to values
"""
self._union = union
self._values = defaultdict(lambda: None, values or ())
def __indented_repr__(self, indent=0):
fields = []
for name, value in self._values.items():
try:
f = getattr(value, "__indented_repr__")
s = f(indent=indent + 2)
except AttributeError:
s = repr(value)
fields.append(" " * (indent + 2) + f".{name} = {s}")
return "{{\n{}\n{}}}".format(",\n".join(fields), " " * indent)
def __repr__(self):
return self.__indented_repr__()
def __getattr__(self, k):
return self[k]
def __getitem__(self, k):
if k not in self._values:
return super().__getitem__(k)
return self._values[k]
[docs] def copy(self):
return SimUnionValue(self._union, values=self._values)
[docs]class SimCppClass(SimStruct):
[docs] def __init__(
self,
members: Optional[Dict[str, SimType]] = None,
function_members: Optional[Dict[str, SimTypeCppFunction]] = None,
vtable_ptrs=None,
name: Optional[str] = None,
pack: bool = False,
align=None,
):
super().__init__(members, name=name, pack=pack, align=align)
# these are actually addresses in the binary
self.function_members = function_members
# this should also be added to the fields once we know the offsets of the members of this object
self.vtable_ptrs = [] if vtable_ptrs is None else vtable_ptrs
@property
def members(self):
return self.fields
def __repr__(self):
return "class %s" % self.name
[docs] def store(self, state, addr, value):
if type(value) is dict:
pass
elif type(value) is SimCppClassValue:
value = value._values
else:
raise TypeError("Can't store struct of type %s" % type(value))
if len(value) != len(self.fields):
raise ValueError("Passed bad values for %s; expected %d, got %d" % (self, len(self.offsets), len(value)))
for field, offset in self.offsets.items():
ty = self.fields[field]
ty.store(state, addr + offset, value[field])
[docs] def copy(self):
return SimCppClass(
dict(self.fields),
name=self.name,
pack=self._pack,
align=self._align,
function_members=self.function_members,
vtable_ptrs=self.vtable_ptrs,
)
[docs]class SimCppClassValue:
"""
A SimCppClass type paired with some real values
"""
[docs] def __init__(self, class_type, values):
self._class = class_type
self._values = defaultdict(lambda: None, values or ())
def __indented_repr__(self, indent=0):
fields = []
for name in self._class.fields:
value = self._values[name]
try:
f = getattr(value, "__indented_repr__")
s = f(indent=indent + 2)
except AttributeError:
s = repr(value)
fields.append(" " * (indent + 2) + f".{name} = {s}")
return "{{\n{}\n{}}}".format(",\n".join(fields), " " * indent)
def __repr__(self):
return self.__indented_repr__()
def __getattr__(self, k):
return self[k]
def __getitem__(self, k):
if type(k) is int:
k = self._class.fields[k]
if k not in self._values:
for f in self._class.fields:
if isinstance(f, NamedTypeMixin) and f.name is None:
try:
return f[k]
except KeyError:
continue
else:
return self._values[k]
return self._values[k]
[docs] def copy(self):
return SimCppClassValue(self._class, values=defaultdict(lambda: None, self._values))
[docs]class SimTypeNumOffset(SimTypeNum):
"""
like SimTypeNum, but supports an offset of 1 to 7 to a byte aligned address to allow structs with bitfields
"""
_fields = SimTypeNum._fields + ("offset",)
[docs] def __init__(self, size, signed=True, label=None, offset=0):
super().__init__(size, signed, label)
self.offset = offset
def __repr__(self):
return super().__repr__()
[docs] def store(self, state, addr, value):
raise NotImplementedError()
[docs] def copy(self):
return SimTypeNumOffset(self.size, signed=self.signed, label=self.label, offset=self.offset)
[docs]class SimTypeRef(SimType):
"""
SimTypeRef is a to-be-resolved reference to another SimType.
SimTypeRef is not SimTypeReference.
"""
[docs] def __init__(self, name, original_type: Type[SimStruct]):
super().__init__(label=name)
self.original_type = original_type
@property
def name(self) -> str:
return self.label
[docs] def set_size(self, v: int):
self._size = v
[docs] def c_repr(self, name=None, full=0, memo=None, indent=0) -> str:
prefix = "unknown"
if self.original_type is SimStruct:
prefix = "struct"
if name is None:
name = ""
return f"{prefix}{name} {self.name}"
def _init_str(self) -> str:
original_type_name = self.original_type.__name__.split(".")[-1]
return f'SimTypeRef("{self.name}", {original_type_name})'
ALL_TYPES = {}
BASIC_TYPES = {
"char": SimTypeChar(),
"signed char": SimTypeChar(),
"unsigned char": SimTypeChar(signed=False),
"short": SimTypeShort(True),
"signed short": SimTypeShort(True),
"unsigned short": SimTypeShort(False),
"short int": SimTypeShort(True),
"signed short int": SimTypeShort(True),
"unsigned short int": SimTypeShort(False),
"int": SimTypeInt(True),
"signed": SimTypeInt(True),
"unsigned": SimTypeInt(False),
"signed int": SimTypeInt(True),
"unsigned int": SimTypeInt(False),
"long": SimTypeLong(True),
"signed long": SimTypeLong(True),
"long signed": SimTypeLong(True),
"unsigned long": SimTypeLong(False),
"long int": SimTypeLong(True),
"signed long int": SimTypeLong(True),
"unsigned long int": SimTypeLong(False),
"long unsigned int": SimTypeLong(False),
"long long": SimTypeLongLong(True),
"signed long long": SimTypeLongLong(True),
"unsigned long long": SimTypeLongLong(False),
"long long int": SimTypeLongLong(True),
"signed long long int": SimTypeLongLong(True),
"unsigned long long int": SimTypeLongLong(False),
"__int128": SimTypeNum(128, True),
"unsigned __int128": SimTypeNum(128, False),
"__int256": SimTypeNum(256, True),
"unsigned __int256": SimTypeNum(256, False),
"bool": SimTypeBool(),
"_Bool": SimTypeBool(),
"float": SimTypeFloat(),
"double": SimTypeDouble(),
"long double": SimTypeDouble(),
"void": SimTypeBottom(label="void"),
}
ALL_TYPES.update(BASIC_TYPES)
STDINT_TYPES = {
"int8_t": SimTypeNum(8, True),
"uint8_t": SimTypeNum(8, False),
"byte": SimTypeNum(8, False),
"int16_t": SimTypeNum(16, True),
"uint16_t": SimTypeNum(16, False),
"word": SimTypeNum(16, False),
"int32_t": SimTypeNum(32, True),
"uint32_t": SimTypeNum(32, False),
"dword": SimTypeNum(32, False),
"int64_t": SimTypeNum(64, True),
"uint64_t": SimTypeNum(64, False),
"qword": SimTypeNum(64, False),
"ptrdiff_t": SimTypeLong(True),
"size_t": SimTypeLength(False),
"ssize_t": SimTypeLength(True),
"ssize": SimTypeLength(False),
"uintptr_t": SimTypeLong(False),
"wchar_t": SimTypeShort(True),
}
ALL_TYPES.update(STDINT_TYPES)
# Most glibc internal basic types are defined in the following two files:
# https://github.com/bminor/glibc/blob/master/bits/typesizes.h
# https://github.com/bminor/glibc/blob/master/posix/bits/types.h
# Anything that is defined in a different file should probably have a permalink
GLIBC_INTERNAL_BASIC_TYPES = {
"__off_t": ALL_TYPES["long int"],
"__off64_t": ALL_TYPES["long long int"],
"__pid_t": ALL_TYPES["int"],
"__ino_t": ALL_TYPES["unsigned long int"],
"__ino64_t": ALL_TYPES["unsigned long long int"],
"__mode_t": ALL_TYPES["unsigned int"],
"__dev_t": ALL_TYPES["uint64_t"],
"__nlink_t": ALL_TYPES["unsigned int"],
"__uid_t": ALL_TYPES["unsigned int"],
"__gid_t": ALL_TYPES["unsigned int"],
"__time_t": ALL_TYPES["long int"],
# https://github.com/bminor/glibc/blob/a01a13601c95f5d111d25557656d09fe661cfc89/sysdeps/unix/sysv/linux/x86/bits/siginfo-arch.h#L12
"__clock_t": ALL_TYPES["uint32_t"],
"__suseconds_t": ALL_TYPES["int64_t"],
}
ALL_TYPES.update(GLIBC_INTERNAL_BASIC_TYPES)
GLIBC_EXTERNAL_BASIC_TYPES = {
"off_t": ALL_TYPES["__off_t"],
"off64_t": ALL_TYPES["__off64_t"],
"pid_t": ALL_TYPES["__pid_t"],
# https://www.gnu.org/software/libc/manual/html_node/Attribute-Meanings.html
# This is "no narrower than unsigned int" but may be wider...
# TODO: This should be defined based on the architecture
"ino_t": ALL_TYPES["__ino_t"],
"ino64_t": ALL_TYPES["__ino64_t"],
# https://github.com/bminor/glibc/blob/a01a13601c95f5d111d25557656d09fe661cfc89/bits/sockaddr.h#L28
"sa_family_t": ALL_TYPES["unsigned short int"],
# https://github.com/bminor/glibc/blob/a01a13601c95f5d111d25557656d09fe661cfc89/inet/netinet/in.h#L123
"in_port_t": ALL_TYPES["uint16_t"],
# https://github.com/bminor/glibc/blob/a01a13601c95f5d111d25557656d09fe661cfc89/bits/termios.h#L102
"tcflag_t": ALL_TYPES["unsigned long int"],
# https://github.com/bminor/glibc/blob/a01a13601c95f5d111d25557656d09fe661cfc89/bits/termios.h#L105
"cc_t": ALL_TYPES["unsigned char"],
# https://github.com/bminor/glibc/blob/a01a13601c95f5d111d25557656d09fe661cfc89/bits/termios.h#L108
"speed_t": ALL_TYPES["long int"],
"clock_t": ALL_TYPES["__clock_t"],
"rlim_t": ALL_TYPES["unsigned long int"],
"rlim64_t": ALL_TYPES["uint64_t"],
# https://github.com/bminor/glibc/blob/a01a13601c95f5d111d25557656d09fe661cfc89/bits/types/error_t.h#L22
"error_t": ALL_TYPES["int"],
}
ALL_TYPES.update(GLIBC_EXTERNAL_BASIC_TYPES)
CXX_TYPES = {
"string": SimTypeString(),
"wstring": SimTypeWString(),
"basic_string": SimTypeString(),
"CharT": SimTypeChar(),
}
ALL_TYPES.update(CXX_TYPES)
# Note about structs with self/next pointers -- they will be defined as memberless
# name-only structs the same way they would be in C as a forward declaration
# This dictionary is defined in two steps to allow structs that are members of other
# structs to be defined first
GLIBC_INTERNAL_TYPES = {
"sigval": SimUnion(
{
"sival_int": ALL_TYPES["int"],
"sival_ptr": SimTypePointer(ALL_TYPES["void"], label="void *"),
},
name="sigval",
),
"__mbstate_t": SimStruct(
{
"__count": ALL_TYPES["int"],
"__value": SimUnion(
{
"__wch": ALL_TYPES["unsigned int"],
"__wchb": SimTypeArray(ALL_TYPES["char"], length=4),
}
),
},
name="__mbstate_t",
),
"_IO_codecvt": SimStruct(
{
"__cd_in": SimStruct({}, name="_IO_iconv_t"),
"__cd_out": SimStruct({}, name="_IO_iconv_t"),
},
name="_IO_codecvt",
),
"argp_option": SimStruct(
{
"name": SimTypePointer(ALL_TYPES["char"], label="char *"),
"key": ALL_TYPES["int"],
"arg": SimTypePointer(ALL_TYPES["char"], label="char *"),
"flags": ALL_TYPES["int"],
"doc": SimTypePointer(ALL_TYPES["char"], label="char *"),
"group": ALL_TYPES["int"],
},
name="argp_option",
),
"argp_child": SimStruct(
{
"argp": SimStruct({}, name="argp"),
"flags": ALL_TYPES["int"],
"header": SimTypePointer(ALL_TYPES["char"], label="char *"),
"group": ALL_TYPES["int"],
},
name="argp_child",
),
"argp_parser_t": SimTypeFunction(
(
ALL_TYPES["int"],
SimTypePointer(ALL_TYPES["char"], label="char *"),
SimTypePointer(SimStruct({}, name="argp_state")),
),
ALL_TYPES["error_t"],
arg_names=("__key", "__arg", "__state"),
),
}
GLIBC_INTERNAL_TYPES.update(
{
"_obstack_chunk": SimStruct(
{
"limit": SimTypePointer(ALL_TYPES["char"], label="char *"),
"prev": SimTypePointer(SimStruct({}, name="_obstack_chunk", pack=False, align=None)),
"contents": SimTypeArray(ALL_TYPES["char"], length=4, label="char"),
},
name="_obstack_chunk",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/misc/search.h#L69
"_ENTRY": SimStruct(
{
"key": SimTypePointer(ALL_TYPES["char"], label="char *"),
"data": SimTypePointer(ALL_TYPES["void"], label="void *"),
},
name="_ENTRY",
),
# https://man7.org/linux/man-pages/man7/sigevent.7.html
"sigevent": SimStruct(
{
"sigev_notify": ALL_TYPES["int"],
"sigev_signo": ALL_TYPES["int"],
"sigev_value": GLIBC_INTERNAL_TYPES["sigval"],
"sigev_notify_function": SimTypeFunction(
(GLIBC_INTERNAL_TYPES["sigval"],),
SimTypePointer(ALL_TYPES["void"], label="void *"),
),
"sigev_notify_attributes": SimTypePointer(ALL_TYPES["void"], label="void *"),
"sigev_notify_thread_id": ALL_TYPES["pid_t"],
},
name="sigevent",
),
"in_addr": SimStruct({"s_addr": ALL_TYPES["uint32_t"]}, name="in_addr"),
"_IO_marker": SimStruct(
{
"_next": SimTypePointer(SimStruct({}, name="_IO_marker"), label="struct _IO_marker *"),
"_sbuf": SimTypePointer(SimStruct({}, name="FILE"), label="FILE *"),
"_pos": ALL_TYPES["int"],
},
name="_IO_marker",
),
"_IO_iconv_t": SimStruct(
{
# TODO: Define __gconv structs
"step": SimTypePointer(SimStruct({}, name="__gconv_step"), label="struct __gconv_step *"),
"step_data": SimStruct({}, name="__gconv_step_data"),
},
name="_IO_iconv_t",
),
"_IO_codecvt": GLIBC_INTERNAL_TYPES["_IO_codecvt"],
"_IO_lock_t": SimStruct({}, name="pthread_mutex_t"),
"__mbstate_t": GLIBC_INTERNAL_TYPES["__mbstate_t"],
"_IO_wide_data": SimStruct(
{
"_IO_read_ptr": SimTypePointer(ALL_TYPES["wchar_t"], label="wchar_t *"),
"_IO_read_end": SimTypePointer(ALL_TYPES["wchar_t"], label="wchar_t *"),
"_IO_read_base": SimTypePointer(ALL_TYPES["wchar_t"], label="wchar_t *"),
"_IO_write_base": SimTypePointer(ALL_TYPES["wchar_t"], label="wchar_t *"),
"_IO_write_ptr": SimTypePointer(ALL_TYPES["wchar_t"], label="wchar_t *"),
"_IO_write_end": SimTypePointer(ALL_TYPES["wchar_t"], label="wchar_t *"),
"_IO_buf_base": SimTypePointer(ALL_TYPES["wchar_t"], label="wchar_t *"),
"_IO_buf_end": SimTypePointer(ALL_TYPES["wchar_t"], label="wchar_t *"),
"_IO_save_base": SimTypePointer(ALL_TYPES["wchar_t"], label="wchar_t *"),
"_IO_backup_base": SimTypePointer(ALL_TYPES["wchar_t"], label="wchar_t *"),
"_IO_save_end": SimTypePointer(ALL_TYPES["wchar_t"], label="wchar_t *"),
"_IO_state": GLIBC_INTERNAL_TYPES["__mbstate_t"],
"_IO_last_state": GLIBC_INTERNAL_TYPES["__mbstate_t"],
"_codecvt": GLIBC_INTERNAL_TYPES["_IO_codecvt"],
"_shortbuf": SimTypeArray(ALL_TYPES["wchar_t"], length=1, label="wchar_t[1]"),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/libio/libioP.h#L293
"_wide_vtable": SimStruct({}, name="_IO_jump_t"),
},
name="_IO_wide_data",
),
"argp": SimStruct(
{
"options": SimTypePointer(GLIBC_INTERNAL_TYPES["argp_option"], label="struct argp_option *"),
"parser": GLIBC_INTERNAL_TYPES["argp_parser_t"],
"args_doc": SimTypePointer(ALL_TYPES["char"], label="char *"),
"doc": SimTypePointer(ALL_TYPES["char"], label="char *"),
"children": SimTypePointer(GLIBC_INTERNAL_TYPES["argp_child"], label="struct argp_child *"),
"help_filter": SimTypeFunction(
(
ALL_TYPES["int"],
SimTypePointer(ALL_TYPES["char"], label="char *"),
SimTypePointer(ALL_TYPES["void"], label="void *"),
),
SimTypePointer(ALL_TYPES["char"], label="char *"),
arg_names=("__key", "__text", "__input"),
),
"argp_domain": SimTypePointer(ALL_TYPES["char"], label="char *"),
},
name="argp",
),
"timeval": SimStruct(
{
# TODO: This should be architecture dependent
"tv_sec": ALL_TYPES["__time_t"],
"tv_usec": ALL_TYPES["__suseconds_t"],
},
name="timeval",
),
# https://github.com/bminor/glibc/blob/a01a13601c95f5d111d25557656d09fe661cfc89/time/bits/types/struct_timespec.h#L11
"timespec": SimStruct(
{
# TODO: This should be architecture dependent
"tv_sec": ALL_TYPES["__time_t"],
"tv_nsec": ALL_TYPES["long int"],
# TODO: This should be architecture dependent (byte order)
"_pad0": ALL_TYPES["uint32_t"],
},
name="timeval",
),
# https://github.com/bminor/glibc/blob/a01a13601c95f5d111d25557656d09fe661cfc89/bits/utmp.h#L50
"exit_status": SimStruct(
{
"e_termination": ALL_TYPES["short int"],
"e_exit": ALL_TYPES["short int"],
},
name="exit_status",
),
}
)
ALL_TYPES.update(GLIBC_INTERNAL_TYPES)
GLIBC_TYPES = {
# DO NOT use the glibc manual to define these structs! It is not accurate and does
# not contain all fields or even the fields in the correct order!. Instead, you
# need to use the glibc source and actually find the struct. In most cases,
# a link to the struct is provided.
# ABI-defined, for x86_64 it can be found here in sec 3.34:
# https://github.com/hjl-tools/x86-psABI/wiki/x86-64-psABI-1.0.pdf
# TODO: This should be architecture dependent
"va_list": SimTypeArray(
SimStruct(
{
"gp_offset": ALL_TYPES["unsigned int"],
"fp_offset": ALL_TYPES["unsigned int"],
"overflow_arg_area": SimTypePointer(ALL_TYPES["void"], label="void *"),
"reg_save_area": SimTypePointer(ALL_TYPES["void"], label="void *"),
},
name="va_list",
),
length=1,
label="va_list[1]",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/malloc/malloc.h#L82
"mallinfo": SimStruct(
{
"arena": ALL_TYPES["int"],
"ordblks": ALL_TYPES["int"],
"smblks": ALL_TYPES["int"],
"hblks": ALL_TYPES["int"],
"hblkhd": ALL_TYPES["int"],
"usmblks": ALL_TYPES["int"],
"fsmblks": ALL_TYPES["int"],
"uordblks": ALL_TYPES["int"],
"fordblks": ALL_TYPES["int"],
"keepcost": ALL_TYPES["int"],
},
name="mallinfo",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/malloc/malloc.h#L99
"mallinfo2": SimStruct(
{
"arena": ALL_TYPES["size_t"],
"ordblks": ALL_TYPES["size_t"],
"smblks": ALL_TYPES["size_t"],
"hblks": ALL_TYPES["size_t"],
"hblkhd": ALL_TYPES["size_t"],
"usmblks": ALL_TYPES["size_t"],
"fsmblks": ALL_TYPES["size_t"],
"uordblks": ALL_TYPES["size_t"],
"fordblks": ALL_TYPES["size_t"],
"keepcost": ALL_TYPES["size_t"],
},
name="mallinfo2",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/malloc/obstack.h#L153
"obstack": SimStruct(
{
"chunk_size": SimTypeLong(signed=True, label="long"),
"chunk": GLIBC_INTERNAL_TYPES["_obstack_chunk"],
"object_base": SimTypePointer(ALL_TYPES["char"], label="char *"),
"next_free": SimTypePointer(ALL_TYPES["char"], label="char *"),
"chunk_limit": SimTypePointer(ALL_TYPES["char"], label="char *"),
"temp": SimUnion(
{
"tempint": ALL_TYPES["ptrdiff_t"],
"tempptr": SimTypePointer(ALL_TYPES["void"], label="void *"),
}
),
"alignment_mask": ALL_TYPES["int"],
"chunkfun": SimTypeFunction(
(SimTypePointer(ALL_TYPES["void"], label="void *"), ALL_TYPES["long"]),
SimTypePointer(ALL_TYPES["_obstack_chunk"], label="struct _obstack_chunk *"),
),
"freefun": SimTypeFunction(
(
SimTypePointer(ALL_TYPES["void"], label="void *"),
SimTypePointer(ALL_TYPES["_obstack_chunk"], label="_obstack_chunk *"),
),
ALL_TYPES["void"],
),
"extra_arg": SimTypePointer(ALL_TYPES["void"], label="void *"),
"use_extra_arg": SimTypeNumOffset(1, signed=False, label="unsigned"),
"maybe_extra_object": SimTypeNumOffset(1, signed=False, label="unsigned"),
"alloc_failed": SimTypeNumOffset(1, signed=False, label="unsigned"),
},
name="obstack",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/locale/locale.h#L51
"lconv": SimStruct(
{
"decimal_point": SimTypePointer(ALL_TYPES["char"], label="char *"),
"thousands_sep": SimTypePointer(ALL_TYPES["char"], label="char *"),
"grouping": SimTypePointer(ALL_TYPES["char"], label="char *"),
"int_curr_symbol": SimTypePointer(ALL_TYPES["char"], label="char *"),
"currency_symbol": SimTypePointer(ALL_TYPES["char"], label="char *"),
"mon_decimal_point": SimTypePointer(ALL_TYPES["char"], label="char *"),
"mon_thousands_sep": SimTypePointer(ALL_TYPES["char"], label="char *"),
"mon_grouping": SimTypePointer(ALL_TYPES["char"], label="char *"),
"positive_sign": SimTypePointer(ALL_TYPES["char"], label="char *"),
"negative_sign": SimTypePointer(ALL_TYPES["char"], label="char *"),
"int_frac_digits": ALL_TYPES["char"],
"frac_digits": ALL_TYPES["char"],
"p_cs_precedes": ALL_TYPES["char"],
"p_sep_by_space": ALL_TYPES["char"],
"n_cs_precedes": ALL_TYPES["char"],
"n_sep_by_space": ALL_TYPES["char"],
"p_sign_posn": ALL_TYPES["char"],
"n_sign_posn": ALL_TYPES["char"],
"int_p_cs_precedes": ALL_TYPES["char"],
"int_p_sep_by_space": ALL_TYPES["char"],
"int_n_cs_precedes": ALL_TYPES["char"],
"int_n_sep_by_space": ALL_TYPES["char"],
"int_p_sign_posn": ALL_TYPES["char"],
"int_n_sign_posn": ALL_TYPES["char"],
},
name="lconv",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/misc/search.h#L97
"hsearch_data": SimStruct(
{
"table": SimTypePointer(ALL_TYPES["_ENTRY"], label="struct _ENTRY *"),
"size": ALL_TYPES["unsigned int"],
"filled": ALL_TYPES["unsigned int"],
},
name="hsearch_data",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/libio/bits/types/struct_FILE.h#L49
"FILE_t": SimStruct(
{
"_flags": ALL_TYPES["int"],
"_IO_read_ptr": SimTypePointer(ALL_TYPES["char"], label="char *"),
"_IO_read_end": SimTypePointer(ALL_TYPES["char"], label="char *"),
"_IO_read_base": SimTypePointer(ALL_TYPES["char"], label="char *"),
"_IO_write_base": SimTypePointer(ALL_TYPES["char"], label="char *"),
"_IO_write_ptr": SimTypePointer(ALL_TYPES["char"], label="char *"),
"_IO_write_end": SimTypePointer(ALL_TYPES["char"], label="char *"),
"_IO_buf_base": SimTypePointer(ALL_TYPES["char"], label="char *"),
"_IO_buf_end": SimTypePointer(ALL_TYPES["char"], label="char *"),
"_IO_save_base": SimTypePointer(ALL_TYPES["char"], label="char *"),
"_IO_backup_base": SimTypePointer(ALL_TYPES["char"], label="char *"),
"_IO_save_end": SimTypePointer(ALL_TYPES["char"], label="char *"),
"_markers": SimTypePointer(ALL_TYPES["_IO_marker"]),
"_chain": SimTypePointer(SimStruct({}, name="_IO_FILE"), label="struct _IO_FILE *"),
"_fileno": ALL_TYPES["int"],
"_flags2": ALL_TYPES["int"],
"_old_offset": ALL_TYPES["__off_t"],
"_cur_column": ALL_TYPES["unsigned short"],
"_vtable_offset": ALL_TYPES["signed char"],
"_shortbuf": SimTypeArray(ALL_TYPES["char"], length=1, label="char[1]"),
"_lock": SimTypePointer(ALL_TYPES["_IO_lock_t"]),
"_offset": ALL_TYPES["__off64_t"],
"_codecvt": SimTypePointer(ALL_TYPES["_IO_codecvt"], label="struct _IO_codecvt *"),
"_wide_data": SimTypePointer(ALL_TYPES["_IO_wide_data"], label="struct _IO_wide_data *"),
"_freeres_list": SimTypePointer(SimStruct({}, name="_IO_FILE"), label="struct _IO_FILE *"),
"__pad5": ALL_TYPES["size_t"],
"_mode": ALL_TYPES["int"],
"_unused2": SimTypeArray(
ALL_TYPES["char"],
length=20,
label="char[15 * sizeof (int) - 4 * sizeof (void *) - sizeof (size_t)]",
),
},
name="FILE_t",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/stdio-common/printf.h#L34
"printf_info": SimStruct(
{
"prec": ALL_TYPES["int"],
"width": ALL_TYPES["int"],
"spec": ALL_TYPES["wchar_t"],
"is_long_double": SimTypeNumOffset(1, signed=False, label="unsigned int"),
"is_short": SimTypeNumOffset(1, signed=False, label="unsigned int"),
"is_long": SimTypeNumOffset(1, signed=False, label="unsigned int"),
"alt": SimTypeNumOffset(1, signed=False, label="unsigned int"),
"space": SimTypeNumOffset(1, signed=False, label="unsigned int"),
"left": SimTypeNumOffset(1, signed=False, label="unsigned int"),
"showsign": SimTypeNumOffset(1, signed=False, label="unsigned int"),
"group": SimTypeNumOffset(1, signed=False, label="unsigned int"),
"extra": SimTypeNumOffset(1, signed=False, label="unsigned int"),
"is_char": SimTypeNumOffset(1, signed=False, label="unsigned int"),
"wide": SimTypeNumOffset(1, signed=False, label="unsigned int"),
"i18n": SimTypeNumOffset(1, signed=False, label="unsigned int"),
"is_binary128": SimTypeNumOffset(1, signed=False, label="unsigned int"),
"__pad": SimTypeNumOffset(3, signed=False, label="unsigned int"),
"user": ALL_TYPES["unsigned short int"],
"pad": ALL_TYPES["wchar_t"],
},
name="printf_info",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/rt/aio.h#L34
"aiocb": SimStruct(
{
"aio_filedes": ALL_TYPES["int"],
"aio_lio_opcode": ALL_TYPES["int"],
"aio_reqprio": ALL_TYPES["int"],
"aio_buf": SimTypePointer(ALL_TYPES["void"], label="void *"),
"aio_nbytes": ALL_TYPES["size_t"],
"aio_sigevent": ALL_TYPES["sigevent"],
"__next_prio": SimTypePointer(SimStruct({}, name="aiocb"), label="struct aiocb *"),
"__abs_prio": ALL_TYPES["int"],
"__policy": ALL_TYPES["int"],
"__error_code": ALL_TYPES["int"],
"__return_value": ALL_TYPES["ssize_t"],
# TODO: This should be architecture dependent
"aio_offset": ALL_TYPES["off_t"],
"__glibc_reserved": SimTypeArray(ALL_TYPES["char"], length=32, label="char[32]"),
},
name="aiocb",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/rt/aio.h#L62
"aiocb64": SimStruct(
{
"aio_filedes": ALL_TYPES["int"],
"aio_lio_opcode": ALL_TYPES["int"],
"aio_reqprio": ALL_TYPES["int"],
"aio_buf": SimTypePointer(ALL_TYPES["void"], label="void *"),
"aio_nbytes": ALL_TYPES["size_t"],
"aio_sigevent": ALL_TYPES["sigevent"],
"__next_prio": SimTypePointer(SimStruct({}, name="aiocb"), label="struct aiocb *"),
"__abs_prio": ALL_TYPES["int"],
"__policy": ALL_TYPES["int"],
"__error_code": ALL_TYPES["int"],
"__return_value": ALL_TYPES["ssize_t"],
"aio_offset": ALL_TYPES["off64_t"],
"__glibc_reserved": SimTypeArray(ALL_TYPES["char"], length=32, label="char[32]"),
},
name="aiocb64",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/rt/aio.h#L86
"aioinit": SimStruct(
{
"aio_threads": ALL_TYPES["int"],
"aio_num": ALL_TYPES["int"],
"aio_locks": ALL_TYPES["int"],
"aio_debug": ALL_TYPES["int"],
"aio_numusers": ALL_TYPES["int"],
"aio_idle_time": ALL_TYPES["int"],
"aio_reserved": ALL_TYPES["int"],
},
name="aioinit",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/bits/dirent.h#L23
"dirent": SimStruct(
{
"d_ino": ALL_TYPES["ino_t"],
"d_reclen": ALL_TYPES["unsigned short int"],
"d_type": ALL_TYPES["unsigned char"],
"d_namelen": ALL_TYPES["unsigned char"],
"d_name": SimTypeArray(ALL_TYPES["char"], length=1, label="char[1]"),
},
name="dirent",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/bits/dirent.h#L39
"dirent64": SimStruct(
{
"d_ino": ALL_TYPES["ino64_t"],
"d_reclen": ALL_TYPES["unsigned short int"],
"d_type": ALL_TYPES["unsigned char"],
"d_namelen": ALL_TYPES["unsigned char"],
"d_name": SimTypeArray(ALL_TYPES["char"], length=1, label="char[1]"),
},
name="dirent64",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/bits/stat.h#L31
"stat": SimStruct(
{
"st_mode": ALL_TYPES["__mode_t"],
# TODO: This should be architecture dependent
"st_ino": ALL_TYPES["__ino_t"],
"st_dev": ALL_TYPES["__dev_t"],
"st_nlink": ALL_TYPES["__nlink_t"],
"st_uid": ALL_TYPES["__uid_t"],
"st_gid": ALL_TYPES["__gid_t"],
# TODO: This should be architecture dependent
"st_size": ALL_TYPES["__off_t"],
"st_atime": ALL_TYPES["__time_t"],
"st_mtime": ALL_TYPES["__time_t"],
"st_ctime": ALL_TYPES["__time_t"],
},
name="stat",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/bits/stat.h#L86
"stat64": SimStruct(
{
"st_mode": ALL_TYPES["__mode_t"],
# TODO: This should be architecture dependent
"st_ino": ALL_TYPES["__ino64_t"],
"st_dev": ALL_TYPES["__dev_t"],
"st_nlink": ALL_TYPES["__nlink_t"],
"st_uid": ALL_TYPES["__uid_t"],
"st_gid": ALL_TYPES["__gid_t"],
# TODO: This should be architecture dependent
"st_size": ALL_TYPES["__off64_t"],
"st_atime": ALL_TYPES["__time_t"],
"st_mtime": ALL_TYPES["__time_t"],
"st_ctime": ALL_TYPES["__time_t"],
},
name="stat64",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/io/utime.h#L36
"utimbuf": SimStruct(
{
# TODO: This should be architecture dependent
"actime": ALL_TYPES["__time_t"],
"modtime": ALL_TYPES["__time_t"],
},
name="utimbuf",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/bits/socket.h#L152
"sockaddr": SimStruct(
{
"sin_family": ALL_TYPES["sa_family_t"],
"sa_data": SimTypeArray(ALL_TYPES["char"], length=14, label="char[14]"),
},
name="sockaddr",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/inet/netinet/in.h#L245
"sockaddr_in": SimStruct(
{
"sin_family": ALL_TYPES["sa_family_t"],
"sin_port": ALL_TYPES["in_port_t"],
"sin_addr": ALL_TYPES["in_addr"],
"sin_zero": SimTypeArray(
ALL_TYPES["unsigned char"],
length=8,
label=(
"unsigned char[sizeof (struct sockaddr) - __SOCKADDR_COMMON_SIZE - "
"sizeof (in_port_t) - sizeof (struct in_addr)]"
),
),
},
name="sockaddr_in",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/sysdeps/gnu/net/if.h#L33
"if_nameindex": SimStruct(
{
"if_index": ALL_TYPES["unsigned int"],
"if_name": SimTypePointer(ALL_TYPES["char"], label="char *"),
},
name="if_nameindex",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/resolv/netdb.h#L98
"hostent": SimStruct(
{
"h_name": SimTypePointer(ALL_TYPES["char"], label="char *"),
"h_aliases": SimTypePointer(SimTypePointer(ALL_TYPES["char"], label="char *"), label="char **"),
"h_addrtype": ALL_TYPES["int"],
"h_length": ALL_TYPES["int"],
"h_addr_list": SimTypePointer(SimTypePointer(ALL_TYPES["char"], label="char *"), label="char **"),
},
name="hostent",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/resolv/netdb.h#L255
"servent": SimStruct(
{
"s_name": SimTypePointer(ALL_TYPES["char"], label="char *"),
"s_aliases": SimTypePointer(SimTypePointer(ALL_TYPES["char"], label="char *"), label="char **"),
"s_port": ALL_TYPES["int"],
"s_proto": SimTypePointer(ALL_TYPES["char"], label="char *"),
},
name="servent",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/resolv/netdb.h#L324
"protoent": SimStruct(
{
"p_name": SimTypePointer(ALL_TYPES["char"], label="char *"),
"p_aliases": SimTypePointer(SimTypePointer(ALL_TYPES["char"], label="char *"), label="char **"),
"p_proto": ALL_TYPES["int"],
},
name="protoent",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/bits/netdb.h#L26
"netent": SimStruct(
{
"n_name": SimTypePointer(ALL_TYPES["char"], label="char *"),
"n_aliases": SimTypePointer(SimTypePointer(ALL_TYPES["char"], label="char *"), label="char **"),
"n_addrtype": ALL_TYPES["int"],
"n_net": ALL_TYPES["uint32_t"],
},
name="netent",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/bits/termios.h#L111
"termios": SimStruct(
{
"c_iflag": ALL_TYPES["tcflag_t"],
"c_oflag": ALL_TYPES["tcflag_t"],
"c_cflag": ALL_TYPES["tcflag_t"],
"c_lflag": ALL_TYPES["tcflag_t"],
"c_cc": SimTypeArray(ALL_TYPES["cc_t"], length=20, label="cc_t[20]"),
"__ispeed": ALL_TYPES["speed_t"],
"__ospeed": ALL_TYPES["speed_t"],
},
name="termios",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/bits/ioctl-types.h#L56
"sgttyb": SimStruct(
{
"sg_ispeed": ALL_TYPES["char"],
"sg_ospeed": ALL_TYPES["char"],
"sg_erase": ALL_TYPES["char"],
"sg_kill": ALL_TYPES["char"],
"sg_flags": ALL_TYPES["short int"],
},
name="sgttyb",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/bits/ioctl-types.h#L70
"winsize": SimStruct(
{
"ws_row": ALL_TYPES["unsigned short int"],
"ws_col": ALL_TYPES["unsigned short int"],
"ws_xpixel": ALL_TYPES["unsigned short int"],
"ws_ypixel": ALL_TYPES["unsigned short int"],
},
name="winsize",
),
# This type is legitimately opaque
"random_data": SimStruct({}),
# This type is also legitimately opaque
"drand48_data": SimStruct({}),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/posix/sys/times.h#L32
"tms": SimStruct(
{
"tms_utime": ALL_TYPES["clock_t"],
"tms_stime": ALL_TYPES["clock_t"],
"tms_cutime": ALL_TYPES["clock_t"],
"tms_cstime": ALL_TYPES["clock_t"],
},
name="tms",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/time/sys/time.h#L52
"timezone": SimStruct(
{
"tz_minuteswest": ALL_TYPES["int"],
"tz_dsttime": ALL_TYPES["int"],
},
name="timezone",
),
"timeval": ALL_TYPES["timeval"],
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/sysdeps/unix/sysv/linux/bits/timex.h#L26
"timex": SimStruct(
# TODO: This should be architecture dependent
{
"modes": ALL_TYPES["unsigned int"],
"_pad0": ALL_TYPES["uint32_t"],
"offset": ALL_TYPES["long long"],
"freq": ALL_TYPES["long long"],
"maxerror": ALL_TYPES["long long"],
"esterror": ALL_TYPES["long long"],
"status": ALL_TYPES["int"],
"_pad1": ALL_TYPES["uint32_t"],
"constant": ALL_TYPES["long long"],
"precision": ALL_TYPES["long long"],
"tolerance": ALL_TYPES["long long"],
"time": ALL_TYPES["timeval"],
"tick": ALL_TYPES["long long"],
"ppsfreq": ALL_TYPES["long long"],
"jitter": ALL_TYPES["long long"],
"shift": ALL_TYPES["int"],
"_pad2": ALL_TYPES["uint32_t"],
"stabil": ALL_TYPES["long long"],
"jitcnt": ALL_TYPES["long long"],
"calcnt": ALL_TYPES["long long"],
"errcnt": ALL_TYPES["long long"],
"stbcnt": ALL_TYPES["long long"],
"tai": ALL_TYPES["int"],
"_pad3": SimTypeArray(ALL_TYPES["uint32_t"], length=11, label="int :32[11]"),
},
name="timex",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/time/bits/types/struct_tm.h#L7
"tm": SimStruct(
{
"tm_sec": ALL_TYPES["int"],
"tm_min": ALL_TYPES["int"],
"tm_hour": ALL_TYPES["int"],
"tm_mday": ALL_TYPES["int"],
"tm_mon": ALL_TYPES["int"],
"tm_year": ALL_TYPES["int"],
"tm_wday": ALL_TYPES["int"],
"tm_yday": ALL_TYPES["int"],
"tm_isdst": ALL_TYPES["int"],
"tm_gmtoff": ALL_TYPES["long int"],
"tm_zone": SimTypePointer(ALL_TYPES["char"], label="char *"),
},
name="tm",
),
# https://github.com/bminor/glibc/blob/a01a13601c95f5d111d25557656d09fe661cfc89/sysdeps/unix/sysv/linux/sys/timex.h#L30
"ntptimeval": SimStruct(
{
"time": ALL_TYPES["timeval"],
"maxerror": ALL_TYPES["long int"],
"esterror": ALL_TYPES["long int"],
"tai": ALL_TYPES["long int"],
"__glibc_reserved1": ALL_TYPES["long int"],
"__glibc_reserved2": ALL_TYPES["long int"],
"__glibc_reserved3": ALL_TYPES["long int"],
"__glibc_reserved4": ALL_TYPES["long int"],
},
name="ntptimeval",
),
# https://github.com/bminor/glibc/blob/a01a13601c95f5d111d25557656d09fe661cfc89/misc/bits/types/struct_iovec.h#L26
"iovec": SimStruct(
{
"iov_base": SimTypePointer(ALL_TYPES["void"], label="void *"),
"iov_len": ALL_TYPES["size_t"],
}
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/time/sys/time.h#L130
"itimerval": SimStruct(
{
"it_interval": ALL_TYPES["timeval"],
"it_value": ALL_TYPES["timeval"],
},
name="itimerval",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/resource/bits/types/struct_rusage.h#L33
"rusage": SimStruct(
{
"ru_utime": ALL_TYPES["timeval"],
"ru_stime": ALL_TYPES["timeval"],
"ru_maxrss": ALL_TYPES["long int"],
"ru_ixrss": ALL_TYPES["long int"],
"ru_idrss": ALL_TYPES["long int"],
"ru_isrss": ALL_TYPES["long int"],
"ru_minflt": ALL_TYPES["long int"],
"ru_majflt": ALL_TYPES["long int"],
"ru_nswap": ALL_TYPES["long int"],
"ru_inblock": ALL_TYPES["long int"],
"ru_oublock": ALL_TYPES["long int"],
"ru_msgsnd": ALL_TYPES["long int"],
"ru_msgrcv": ALL_TYPES["long int"],
"ru_nsignals": ALL_TYPES["long int"],
"ru_nvcsw": ALL_TYPES["long int"],
"ru_nivcsw": ALL_TYPES["long int"],
},
name="rusage",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/resource/vtimes.c#L28
"vtimes": SimStruct(
{
"vm_utime": ALL_TYPES["int"],
"vm_stime": ALL_TYPES["int"],
"vm_idsrss": ALL_TYPES["unsigned int"],
"vm_ixrss": ALL_TYPES["unsigned int"],
"vm_maxrss": ALL_TYPES["int"],
"vm_maxflt": ALL_TYPES["int"],
"vm_minflt": ALL_TYPES["int"],
"vm_nswap": ALL_TYPES["int"],
"vm_inblk": ALL_TYPES["int"],
"vm_outblk": ALL_TYPES["int"],
},
name="vtimes",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/sysdeps/unix/sysv/linux/bits/resource.h#L139
"rlimit": SimStruct(
{
"rlim_cur": ALL_TYPES["rlim_t"],
"rlim_max": ALL_TYPES["rlim_t"],
},
name="rlimit",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/sysdeps/unix/sysv/linux/bits/resource.h#L148
"rlimit64": SimStruct(
{
"rlim_cur": ALL_TYPES["rlim64_t"],
"rlim_max": ALL_TYPES["rlim64_t"],
},
name="rlimit64",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/bits/types/struct_sched_param.h#L23
"sched_param": SimStruct(
{"sched_priority": ALL_TYPES["int"]},
name="sched_param",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/signal/bits/types/struct_sigstack.h#L23
"sigstack": SimStruct(
{
"ss_sp": SimTypePointer(ALL_TYPES["void"], label="void *"),
"ss_onstack": ALL_TYPES["int"],
},
name="sigstack",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/posix/bits/getopt_ext.h#L50
"option": SimStruct(
{
"name": SimTypePointer(ALL_TYPES["char"], label="char *"),
"has_arg": ALL_TYPES["int"],
"flag": SimTypePointer(ALL_TYPES["int"], label="int *"),
"val": ALL_TYPES["int"],
},
name="option",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/argp/argp.h#L273
"argp_state": SimStruct(
{
"root_argp": ALL_TYPES["argp"],
"argc": ALL_TYPES["int"],
"argv": SimTypePointer(SimTypePointer(ALL_TYPES["char"], label="char *"), label="char **"),
"next": ALL_TYPES["int"],
"flags": ALL_TYPES["unsigned"],
"arg_num": ALL_TYPES["unsigned"],
"quoted": ALL_TYPES["int"],
"input": SimTypePointer(ALL_TYPES["void"], label="void *"),
"child_inputs": SimTypePointer(SimTypePointer(ALL_TYPES["void"], label="void *"), label="void **"),
"hook": SimTypePointer(ALL_TYPES["void"], label="void *"),
"name": SimTypePointer(ALL_TYPES["char"], label="char *"),
"err_stream": SimStruct({}, name="FILE"),
"pstate": SimTypePointer(ALL_TYPES["void"], label="void *"),
},
name="argp_state",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/sysvipc/sys/sem.h#L40
"sembuf": SimStruct(
{
"sem_num": ALL_TYPES["unsigned short int"],
"sem_op": ALL_TYPES["short int"],
"sem_flg": ALL_TYPES["short int"],
},
name="sembuf",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/bits/utmp.h#L58
"utmp": SimStruct(
{
"ut_type": ALL_TYPES["short int"],
"ut_pid": ALL_TYPES["pid_t"],
"ut_line": SimTypeArray(ALL_TYPES["char"], length=32, label="char[32]"),
"ut_id": SimTypeArray(ALL_TYPES["char"], length=4, label="char[32]"),
"ut_user": SimTypeArray(ALL_TYPES["char"], length=32, label="char[32]"),
"ut_host": SimTypeArray(ALL_TYPES["char"], length=256, label="char[32]"),
"ut_exit": ALL_TYPES["exit_status"],
"ut_session": ALL_TYPES["long int"],
"ut_tv": ALL_TYPES["timeval"],
"ut_addr_v6": SimTypeArray(ALL_TYPES["int32_t"], length=4, label="int32_t[4]"),
"__glibc_reserved": SimTypeArray(ALL_TYPES["char"], length=20, label="char[20]"),
},
name="utmp",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/sysdeps/gnu/bits/utmpx.h#L55
"utmpx": SimStruct(
{
"ut_type": ALL_TYPES["short int"],
"ut_pid": ALL_TYPES["pid_t"],
"ut_line": SimTypeArray(ALL_TYPES["char"], length=32, label="char[32]"),
"ut_id": SimTypeArray(ALL_TYPES["char"], length=4, label="char[32]"),
"ut_user": SimTypeArray(ALL_TYPES["char"], length=32, label="char[32]"),
"ut_host": SimTypeArray(ALL_TYPES["char"], length=256, label="char[32]"),
"ut_exit": ALL_TYPES["exit_status"],
"ut_session": ALL_TYPES["long int"],
"ut_tv": ALL_TYPES["timeval"],
"ut_addr_v6": SimTypeArray(ALL_TYPES["int32_t"], length=4, label="int32_t[4]"),
"__glibc_reserved": SimTypeArray(ALL_TYPES["char"], length=20, label="char[20]"),
},
name="utmx",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/pwd/pwd.h#L49
"passwd": SimStruct(
{
"pw_name": SimTypePointer(ALL_TYPES["char"], label="char *"),
"pw_passwd": SimTypePointer(ALL_TYPES["char"], label="char *"),
"pw_uid": ALL_TYPES["__uid_t"],
"pw_gid": ALL_TYPES["__gid_t"],
"pw_gecos": SimTypePointer(ALL_TYPES["char"], label="char *"),
"pw_dir": SimTypePointer(ALL_TYPES["char"], label="char *"),
"pw_shell": SimTypePointer(ALL_TYPES["char"], label="char *"),
},
name="passwd",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/grp/grp.h#L42
"group": SimStruct(
{
"gr_name": SimTypePointer(ALL_TYPES["char"], label="char *"),
"gr_passwd": SimTypePointer(ALL_TYPES["char"], label="char *"),
"gr_gid": ALL_TYPES["__gid_t"],
"gr_mem": SimTypePointer(SimTypePointer(ALL_TYPES["char"], label="char *"), label="char **"),
},
name="group",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/posix/sys/utsname.h#L48
"utsname": SimStruct(
{
"sysname": SimTypeArray(ALL_TYPES["char"], length=1024, label="char[1024]"),
"nodename": SimTypeArray(ALL_TYPES["char"], length=1024, label="char[1024]"),
"release": SimTypeArray(ALL_TYPES["char"], length=1024, label="char[1024]"),
"version": SimTypeArray(ALL_TYPES["char"], length=1024, label="char[1024]"),
"machine": SimTypeArray(ALL_TYPES["char"], length=1024, label="char[1024]"),
"domain": SimTypeArray(ALL_TYPES["char"], length=1024, label="char[1024]"),
},
name="utsname",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/misc/fstab.h#L57
"fstab": SimStruct(
{
"fs_spec": SimTypePointer(ALL_TYPES["char"], label="char *"),
"fs_file": SimTypePointer(ALL_TYPES["char"], label="char *"),
"fs_vfstype": SimTypePointer(ALL_TYPES["char"], label="char *"),
"fs_mntops": SimTypePointer(ALL_TYPES["char"], label="char *"),
"fs_type": SimTypePointer(ALL_TYPES["char"], label="char *"),
"fs_freq": ALL_TYPES["int"],
"fs_passno": ALL_TYPES["int"],
},
name="fstab",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/misc/mntent.h#L51
"mntent": SimStruct(
{
"mnt_fsname": SimTypePointer(ALL_TYPES["char"], label="char *"),
"mnt_dir": SimTypePointer(ALL_TYPES["char"], label="char *"),
"mnt_type": SimTypePointer(ALL_TYPES["char"], label="char *"),
"mnt_opts": SimTypePointer(ALL_TYPES["char"], label="char *"),
"mnt_freq": ALL_TYPES["int"],
"mnt_passno": ALL_TYPES["int"],
},
name="mntent",
),
# https://github.com/bminor/glibc/blob/2d5ec6692f5746ccb11db60976a6481ef8e9d74f/crypt/crypt.h#L43
"crypt_data": SimStruct(
{
"keysched": SimTypeArray(ALL_TYPES["char"], length=16 * 8, label="char[16 * 8]"),
"sb0": SimTypeArray(ALL_TYPES["char"], length=32768, label="char[32768]"),
"sb1": SimTypeArray(ALL_TYPES["char"], length=32768, label="char[32768]"),
"sb2": SimTypeArray(ALL_TYPES["char"], length=32768, label="char[32768]"),
"sb3": SimTypeArray(ALL_TYPES["char"], length=32768, label="char[32768]"),
"crypt_3_buf": SimTypeArray(ALL_TYPES["char"], length=14, label="char[14]"),
"current_salt": SimTypeArray(ALL_TYPES["char"], length=2, label="char[2]"),
"current_saltbits": ALL_TYPES["long int"],
"direction": ALL_TYPES["int"],
"initialized": ALL_TYPES["int"],
},
name="crypt_data",
),
}
ALL_TYPES.update(GLIBC_TYPES)
def _make_scope(predefined_types=None):
"""
Generate CParser scope_stack argument to parse method
"""
all_types = ChainMap(predefined_types or {}, ALL_TYPES)
scope = {}
for ty in all_types:
if ty in BASIC_TYPES:
continue
if " " in ty:
continue
typ = all_types[ty]
if type(typ) is TypeRef:
typ = typ.type
if isinstance(typ, (SimTypeFunction, SimTypeString, SimTypeWString)):
continue
scope[ty] = True
return [scope]
@deprecated(replacement="register_types(parse_type(struct_expr))")
def define_struct(defn):
"""
Register a struct definition globally
>>> define_struct('struct abcd {int x; int y;}')
"""
struct = parse_type(defn)
ALL_TYPES[struct.name] = struct
ALL_TYPES["struct " + struct.name] = struct
return struct
[docs]def register_types(types):
"""
Pass in some types and they will be registered to the global type store.
The argument may be either a mapping from name to SimType, or a plain SimType.
The plain SimType must be either a struct or union type with a name present.
>>> register_types(parse_types("typedef int x; typedef float y;"))
>>> register_types(parse_type("struct abcd { int ab; float cd; }"))
"""
if type(types) is SimStruct:
if types.name == "<anon>":
raise ValueError("Cannot register anonymous struct")
ALL_TYPES["struct " + types.name] = types
elif type(types) is SimUnion:
if types.name == "<anon>":
raise ValueError("Cannot register anonymous union")
ALL_TYPES["union " + types.name] = types
else:
ALL_TYPES.update(types)
[docs]def do_preprocess(defn, include_path=()):
"""
Run a string through the C preprocessor that ships with pycparser but is weirdly inaccessible?
"""
from pycparser.ply import lex, cpp # pylint:disable=import-outside-toplevel
lexer = lex.lex(cpp)
p = cpp.Preprocessor(lexer)
for included in include_path:
p.add_path(included)
p.parse(defn)
return "".join(tok.value for tok in p.parser if tok.type not in p.ignore)
[docs]def parse_signature(defn, preprocess=True, predefined_types=None, arch=None):
"""
Parse a single function prototype and return its type
"""
try:
parsed = parse_file(
defn.strip(" \n\t;") + ";", preprocess=preprocess, predefined_types=predefined_types, arch=arch
)
return next(iter(parsed[0].values()))
except StopIteration as e:
raise ValueError("No declarations found") from e
[docs]def parse_defns(defn, preprocess=True, predefined_types=None, arch=None):
"""
Parse a series of C definitions, returns a mapping from variable name to variable type object
"""
return parse_file(defn, preprocess=preprocess, predefined_types=predefined_types, arch=arch)[0]
[docs]def parse_types(defn, preprocess=True, predefined_types=None, arch=None):
"""
Parse a series of C definitions, returns a mapping from type name to type object
"""
return parse_file(defn, preprocess=preprocess, predefined_types=predefined_types, arch=arch)[1]
_include_re = re.compile(r"^\s*#include")
[docs]def parse_file(defn, preprocess=True, predefined_types: Optional[Dict[Any, SimType]] = None, arch=None):
"""
Parse a series of C definitions, returns a tuple of two type mappings, one for variable
definitions and one for type definitions.
"""
if pycparser is None:
raise ImportError("Please install pycparser in order to parse C definitions")
defn = "\n".join(x for x in defn.split("\n") if _include_re.match(x) is None)
if preprocess:
defn = do_preprocess(defn)
node = pycparser.c_parser.CParser().parse(defn, scope_stack=_make_scope(predefined_types))
if not isinstance(node, pycparser.c_ast.FileAST):
raise ValueError("Something went horribly wrong using pycparser")
out = {}
extra_types = {}
# populate extra_types
if predefined_types:
extra_types = dict(predefined_types)
for piece in node.ext:
if isinstance(piece, pycparser.c_ast.FuncDef):
out[piece.decl.name] = _decl_to_type(piece.decl.type, extra_types, arch=arch)
elif isinstance(piece, pycparser.c_ast.Decl):
ty = _decl_to_type(piece.type, extra_types, arch=arch)
if piece.name is not None:
out[piece.name] = ty
# Don't forget to update typedef types
if (isinstance(ty, SimStruct) or isinstance(ty, SimUnion)) and ty.name != "<anon>":
for _, i in extra_types.items():
if type(i) is type(ty) and i.name == ty.name:
if isinstance(ty, SimStruct):
i.fields = ty.fields
else:
i.members = ty.members
elif isinstance(piece, pycparser.c_ast.Typedef):
extra_types[piece.name] = copy.copy(_decl_to_type(piece.type, extra_types, arch=arch))
extra_types[piece.name].label = piece.name
return out, extra_types
_type_parser_singleton = None
[docs]def type_parser_singleton() -> Optional[pycparser.CParser]:
global _type_parser_singleton # pylint:disable=global-statement
if pycparser is not None:
if _type_parser_singleton is None:
_type_parser_singleton = pycparser.CParser()
_type_parser_singleton.cparser = pycparser.ply.yacc.yacc(
module=_type_parser_singleton,
start="parameter_declaration",
debug=False,
optimize=False,
errorlog=errorlog,
)
return _type_parser_singleton
[docs]def parse_type(defn, preprocess=True, predefined_types=None, arch=None): # pylint:disable=unused-argument
"""
Parse a simple type expression into a SimType
>>> parse_type('int *')
"""
return parse_type_with_name(defn, preprocess=preprocess, predefined_types=predefined_types, arch=arch)[0]
[docs]def parse_type_with_name(
defn, preprocess=True, predefined_types: Optional[Dict[Any, SimType]] = None, arch=None
): # pylint:disable=unused-argument
"""
Parse a simple type expression into a SimType, returning a tuple of the type object and any associated name
that might be found in the place a name would go in a type declaration.
>>> parse_type_with_name('int *foo')
"""
if pycparser is None:
raise ImportError("Please install pycparser in order to parse C definitions")
if preprocess:
defn = re.sub(r"/\*.*?\*/", r"", defn)
node = type_parser_singleton().parse(text=defn, scope_stack=_make_scope(predefined_types))
if not isinstance(node, pycparser.c_ast.Typename) and not isinstance(node, pycparser.c_ast.Decl):
raise pycparser.c_parser.ParseError("Got an unexpected type out of pycparser")
decl = node.type
extra_types = {} if not predefined_types else dict(predefined_types)
return _decl_to_type(decl, extra_types=extra_types, arch=arch), node.name
def _accepts_scope_stack():
"""
pycparser hack to include scope_stack as parameter in CParser parse method
"""
def parse(self, text, filename="", debug=False, scope_stack=None):
self.clex.filename = filename
self.clex.reset_lineno()
self._scope_stack = [{}] if scope_stack is None else scope_stack
self._last_yielded_token = None
return self.cparser.parse(input=text, lexer=self.clex, debug=debug)
setattr(pycparser.CParser, "parse", parse)
def _decl_to_type(decl, extra_types=None, bitsize=None, arch=None) -> SimType:
if extra_types is None:
extra_types = {}
if isinstance(decl, pycparser.c_ast.FuncDecl):
argtyps = (
()
if decl.args is None
else [
(
...
if type(x) is pycparser.c_ast.EllipsisParam
else (
SimTypeBottom().with_arch(arch)
if type(x) is pycparser.c_ast.ID
else _decl_to_type(x.type, extra_types, arch=arch)
)
)
for x in decl.args.params
]
)
arg_names = (
[arg.name for arg in decl.args.params if type(arg) is not pycparser.c_ast.EllipsisParam]
if decl.args
else None
)
# special handling: func(void) is func()
if len(argtyps) == 1 and isinstance(argtyps[0], SimTypeBottom) and arg_names[0] is None:
argtyps = ()
arg_names = None
if argtyps and argtyps[-1] is ...:
argtyps.pop()
variadic = True
else:
variadic = False
r = SimTypeFunction(
argtyps, _decl_to_type(decl.type, extra_types, arch=arch), arg_names=arg_names, variadic=variadic
)
r._arch = arch
return r
elif isinstance(decl, pycparser.c_ast.TypeDecl):
if decl.declname == "TOP":
r = SimTypeTop()
r._arch = arch
return r
return _decl_to_type(decl.type, extra_types, bitsize=bitsize, arch=arch)
elif isinstance(decl, pycparser.c_ast.PtrDecl):
pts_to = _decl_to_type(decl.type, extra_types, arch=arch)
r = SimTypePointer(pts_to)
r._arch = arch
return r
elif isinstance(decl, pycparser.c_ast.ArrayDecl):
elem_type = _decl_to_type(decl.type, extra_types, arch=arch)
if decl.dim is None:
r = SimTypeArray(elem_type)
r._arch = arch
return r
try:
size = _parse_const(decl.dim, extra_types=extra_types, arch=arch)
except ValueError as e:
l.warning("Got error parsing array dimension, defaulting to zero: %s", e)
size = 0
r = SimTypeFixedSizeArray(elem_type, size)
r._arch = arch
return r
elif isinstance(decl, pycparser.c_ast.Struct):
if decl.decls is not None:
fields = OrderedDict(
(field.name, _decl_to_type(field.type, extra_types, bitsize=field.bitsize, arch=arch))
for field in decl.decls
)
else:
fields = OrderedDict()
if decl.name is not None:
key = "struct " + decl.name
struct = extra_types.get(key, None)
from_global = False
if struct is None:
struct = ALL_TYPES.get(key, None)
from_global = True
if struct is not None:
struct = struct.with_arch(arch)
if struct is None:
struct = SimStruct(fields, decl.name)
struct._arch = arch
elif not struct.fields:
struct.fields = fields
elif fields and struct.fields != fields:
if from_global:
struct = SimStruct(fields, decl.name)
struct._arch = arch
else:
raise ValueError("Redefining body of " + key)
extra_types[key] = struct
else:
struct = SimStruct(fields)
struct._arch = arch
return struct
elif isinstance(decl, pycparser.c_ast.Union):
if decl.decls is not None:
fields = {field.name: _decl_to_type(field.type, extra_types, arch=arch) for field in decl.decls}
else:
fields = {}
if decl.name is not None:
key = "union " + decl.name
if key in extra_types:
union = extra_types[key]
elif key in ALL_TYPES:
union = ALL_TYPES[key]
else:
union = None
if union is None:
union = SimUnion(fields, decl.name)
union._arch = arch
elif not union.members:
union.members = fields
elif fields and union.members != fields:
raise ValueError("Redefining body of " + key)
extra_types[key] = union
else:
union = SimUnion(fields)
union._arch = arch
return union
elif isinstance(decl, pycparser.c_ast.IdentifierType):
key = " ".join(decl.names)
if bitsize is not None:
return SimTypeNumOffset(int(bitsize.value), signed=False)
elif key in extra_types:
return extra_types[key]
elif key in ALL_TYPES:
return ALL_TYPES[key].with_arch(arch)
else:
raise TypeError("Unknown type '%s'" % key)
elif isinstance(decl, pycparser.c_ast.Enum):
# See C99 at 6.7.2.2
return ALL_TYPES["int"].with_arch(arch)
raise ValueError("Unknown type!")
def _parse_const(c, arch=None, extra_types=None):
if type(c) is pycparser.c_ast.Constant:
return int(c.value, base=0)
elif type(c) is pycparser.c_ast.BinaryOp:
if c.op == "+":
return _parse_const(c.children()[0][1], arch, extra_types) + _parse_const(
c.children()[1][1], arch, extra_types
)
if c.op == "-":
return _parse_const(c.children()[0][1], arch, extra_types) - _parse_const(
c.children()[1][1], arch, extra_types
)
if c.op == "*":
return _parse_const(c.children()[0][1], arch, extra_types) * _parse_const(
c.children()[1][1], arch, extra_types
)
if c.op == "/":
return _parse_const(c.children()[0][1], arch, extra_types) // _parse_const(
c.children()[1][1], arch, extra_types
)
if c.op == "<<":
return _parse_const(c.children()[0][1], arch, extra_types) << _parse_const(
c.children()[1][1], arch, extra_types
)
if c.op == ">>":
return _parse_const(c.children()[0][1], arch, extra_types) >> _parse_const(
c.children()[1][1], arch, extra_types
)
raise ValueError("Binary op %s" % c.op)
elif type(c) is pycparser.c_ast.UnaryOp:
if c.op == "sizeof":
return _decl_to_type(c.expr.type, extra_types=extra_types, arch=arch).size
else:
raise ValueError("Unary op %s" % c.op)
elif type(c) is pycparser.c_ast.Cast:
return _parse_const(c.expr, arch, extra_types)
else:
raise ValueError(c)
def _cpp_decl_to_type(decl: Any, extra_types: Dict[str, SimType], opaque_classes=True):
if isinstance(decl, CppHeaderParser.CppMethod):
the_func = decl
func_name = the_func["name"]
if "__deleting_dtor__" in func_name:
the_func["destructor"] = True
elif "__base_dtor__" in func_name:
the_func["destructor"] = True
elif "__dtor__" in func_name:
the_func["destructor"] = True
# translate parameters
args = []
arg_names: List[str] = []
for param in the_func["parameters"]:
arg_type = param["type"]
args.append(_cpp_decl_to_type(arg_type, extra_types, opaque_classes=opaque_classes))
arg_name = param["name"]
arg_names.append(arg_name)
args = tuple(args)
arg_names: Tuple[str] = tuple(arg_names)
# returns
if not the_func["returns"].strip():
returnty = SimTypeBottom()
else:
returnty = _cpp_decl_to_type(the_func["returns"].strip(), extra_types, opaque_classes=opaque_classes)
# other properties
ctor = the_func["constructor"]
dtor = the_func["destructor"]
func = SimTypeCppFunction(args, returnty, arg_names=arg_names, ctor=ctor, dtor=dtor)
return func
elif isinstance(decl, str):
# a string that represents type
if decl.endswith("&"):
# reference
subdecl = decl.rstrip("&").strip()
subt = _cpp_decl_to_type(subdecl, extra_types, opaque_classes=opaque_classes)
t = SimTypeReference(subt)
return t
if decl.endswith("*"):
# pointer
subdecl = decl.rstrip("*").strip()
subt = _cpp_decl_to_type(subdecl, extra_types, opaque_classes=opaque_classes)
t = SimTypePointer(subt)
return t
if decl.endswith(" const"):
# drop const
return _cpp_decl_to_type(decl[:-6].strip(), extra_types, opaque_classes=opaque_classes)
if "::" in decl:
unqualified_name = decl.split("::")[-1]
else:
unqualified_name = decl
key = unqualified_name
if key in extra_types:
t = extra_types[key]
elif key in ALL_TYPES:
t = ALL_TYPES[key]
elif opaque_classes is True:
# create a class without knowing the internal members
t = SimCppClass({}, name=decl)
else:
raise TypeError("Unknown type '%s'" % " ".join(key))
if unqualified_name != decl:
t = t.copy()
t.name = decl
return t
raise NotImplementedError()
[docs]def normalize_cpp_function_name(name: str) -> str:
_s = name
s = None
while s != _s:
_s = s if s is not None else _s
s = re.sub(r"<[^<>]+>", "", _s)
m = re.search(r"{([a-z\s]+)}", s)
if m is not None:
s = s[: m.start()] + "__" + m.group(1).replace(" ", "_") + "__" + s[m.end() :]
return s
[docs]def parse_cpp_file(cpp_decl, with_param_names: bool = False):
#
# A series of hacks to make CppHeaderParser happy with whatever C++ function prototypes we feed in
#
if CppHeaderParser is None:
raise ImportError("Please install CppHeaderParser to parse C++ definitions")
# CppHeaderParser does not support specialization
s = normalize_cpp_function_name(cpp_decl)
# CppHeaderParser does not like missing parameter names
# FIXME: The following logic is only dealing with *one* C++ function declaration. Support multiple declarations
# FIXME: when needed in the future.
if not with_param_names:
last_pos = 0
i = 0
while True:
idx = s.find(",", last_pos)
if idx == -1:
break
arg_name = "a%d" % i
i += 1
s = s[:idx] + " " + arg_name + s[idx:]
last_pos = idx + len(arg_name) + 1 + 1
# the last parameter
idx = s.find(")", last_pos)
if idx != -1:
# TODO: consider the case where there are one or multiple spaces between ( and )
if s[idx - 1] != "(":
arg_name = "a%d" % i
s = s[:idx] + " " + arg_name + s[idx:]
# CppHeaderParser does not like missing function body
s += "\n\n{}"
try:
h = CppHeaderParser.CppHeader(s, argType="string")
except CppHeaderParser.CppParseError:
return None, None
if not h.functions:
return None, None
func_decls: Dict[str, SimTypeCppFunction] = {}
for the_func in h.functions:
# FIXME: We always assume that there is a "this" pointer but it is not the case for static methods.
proto: Optional[SimTypeCppFunction] = _cpp_decl_to_type(the_func, {}, opaque_classes=True)
if proto is not None and the_func["class"]:
func_name = the_func["class"] + "::" + the_func["name"]
proto.args = (
SimTypePointer(pts_to=SimTypeBottom(label="void")),
) + proto.args # pylint:disable=attribute-defined-outside-init
proto.arg_names = ("this",) + proto.arg_names # pylint:disable=attribute-defined-outside-init
else:
func_name = the_func["name"]
func_decls[func_name] = proto
return func_decls, {}
[docs]def dereference_simtype(
t: SimType, type_collections: List["SimTypeCollection"], memo: Optional[Dict[str, SimType]] = None
) -> SimType:
if memo is None:
memo = {}
if isinstance(t, SimTypeRef):
real_type = None
if t.name in memo:
return memo[t.name]
if type_collections:
for tc in type_collections:
try:
real_type = tc.get(t.name)
break
except AngrMissingTypeError:
continue
if real_type is None:
raise AngrMissingTypeError(f"Missing type {t.name}")
return dereference_simtype(real_type, type_collections, memo=memo)
# the following code prepares a real_type SimType object that will be returned at the end of this method
if isinstance(t, SimStruct):
if t.name in memo:
return memo[t.name]
real_type = t.copy()
memo[t.name] = real_type
fields = OrderedDict((k, dereference_simtype(v, type_collections, memo=memo)) for k, v in t.fields.items())
real_type.fields = fields
elif isinstance(t, SimTypePointer):
real_pts_to = dereference_simtype(t.pts_to, type_collections, memo=memo)
real_type = t.copy()
real_type.pts_to = real_pts_to
elif isinstance(t, SimTypeArray):
real_elem_type = dereference_simtype(t.elem_type, type_collections, memo=memo)
real_type = t.copy()
real_type.elem_type = real_elem_type
elif isinstance(t, SimUnion):
real_members = {k: dereference_simtype(v, type_collections, memo=memo) for k, v in t.members.items()}
real_type = t.copy()
real_type.members = real_members
elif isinstance(t, SimTypeFunction):
real_args = [dereference_simtype(arg, type_collections, memo=memo) for arg in t.args]
real_return_type = (
dereference_simtype(t.returnty, type_collections, memo=memo) if t.returnty is not None else None
)
real_type = t.copy()
real_type.args = real_args
real_type.returnty = real_return_type
else:
return t
if t._arch is not None:
real_type = real_type.with_arch(t._arch)
return real_type
if pycparser is not None:
_accepts_scope_stack()
try:
register_types(
parse_types(
"""
typedef long time_t;
struct timespec {
time_t tv_sec;
long tv_nsec;
};
struct timeval {
time_t tv_sec;
long tv_usec;
};
"""
)
)
except ImportError:
pass
from .state_plugins.view import SimMemView
from .state_plugins import SimState