diff options
| author | Fabrice Desclaux <fabrice.desclaux@cea.fr> | 2017-02-13 11:17:03 +0100 |
|---|---|---|
| committer | Fabrice Desclaux <fabrice.desclaux@cea.fr> | 2017-02-17 14:50:40 +0100 |
| commit | ae4941a6e8777f3a24dedbb0410b34478b4ed15f (patch) | |
| tree | 4bf1d6eac3431562a574d74a02d730355e18efdb | |
| parent | d30ea3788133fffc294602ee6cce88b407caaf4a (diff) | |
| download | miasm-ae4941a6e8777f3a24dedbb0410b34478b4ed15f.tar.gz miasm-ae4941a6e8777f3a24dedbb0410b34478b4ed15f.zip | |
Core: add C manipulators
This module implements C basic manipulators.
| -rw-r--r-- | miasm2/arch/x86/ctype.py | 50 | ||||
| -rw-r--r-- | miasm2/core/ctypesmngr.py | 375 | ||||
| -rw-r--r-- | miasm2/core/objc.py | 1280 |
3 files changed, 1705 insertions, 0 deletions
diff --git a/miasm2/arch/x86/ctype.py b/miasm2/arch/x86/ctype.py new file mode 100644 index 00000000..6b5844d7 --- /dev/null +++ b/miasm2/arch/x86/ctype.py @@ -0,0 +1,50 @@ +from miasm2.core.objc import CTypeTemplate, ObjCDecl + + +class CTypeAMD64_unk(CTypeTemplate): + """Define C types sizes/alignement for x86_64 architecture""" + + obj_char = ObjCDecl("char", 1, 1) + obj_short = ObjCDecl("short", 2, 2) + obj_int = ObjCDecl("int", 4, 4) + obj_long = ObjCDecl("long", 8, 8) + + obj_uchar = ObjCDecl("uchar", 1, 1) + obj_ushort = ObjCDecl("ushort", 2, 2) + obj_uint = ObjCDecl("uint", 4, 4) + obj_ulong = ObjCDecl("ulong", 8, 8) + obj_void = ObjCDecl("void", 1, 1) + + obj_enum = ObjCDecl("enum", 4, 4) + + + def __init__(self): + self.types = { + ('char',): self.obj_char, + ('short',): self.obj_short, + ('int',): self.obj_int, + ('void',): self.obj_void, + ('enum',): self.obj_enum, + + ('signed', 'char'): self.obj_char, + ('unsigned', 'char'): self.obj_uchar, + ('signed', 'short', 'int'): self.obj_short, + ('short', 'int'): self.obj_short, + ('unsigned', 'short'): self.obj_ushort, + ('unsigned', 'short', 'int'): self.obj_ushort, + ('signed', 'int'): self.obj_int, + ('unsigned', 'int'): self.obj_uint, + ('long', 'int'): self.obj_long, + ('unsigned', 'long'): self.obj_ulong, + ('signed', 'long', 'int'): self.obj_long, + ('unsigned', 'long', 'int'): self.obj_ulong, + ('long',): self.obj_long, + ('unsigned', ): self.obj_uint, + + ('signed', 'long', 'long', 'int'): self.obj_long, + ('long', 'unsigned', 'int'): self.obj_ulong, + ('unsigned', 'long', 'long'): self.obj_ulong, + ('long', 'long', 'int'): self.obj_long, + ('unsigned', 'long', 'long', 'int'): self.obj_ulong, + ('void*',): self.obj_ulong, + } diff --git a/miasm2/core/ctypesmngr.py b/miasm2/core/ctypesmngr.py new file mode 100644 index 00000000..518c328d --- /dev/null +++ b/miasm2/core/ctypesmngr.py @@ -0,0 +1,375 @@ +from pycparser import c_ast +from miasm2.core.objc import ObjCStruct, ObjCUnion, ObjCDecl, ObjCPtr, \ + ObjCArray, _ObjCRecurse, c_to_ast + + +def fix_recursive_objects(types_mngr, obj): + """Replace _ObjCRecurse objects by its parent""" + + void_type = types_mngr.void_ptr + + if isinstance(obj, ObjCStruct): + for i, (name, fieldtype, offset, size) in enumerate(obj.fields): + fieldtype = fix_recursive_objects(types_mngr, fieldtype) + obj.fields[i] = (name, fieldtype, offset, size) + elif isinstance(obj, ObjCDecl): + return obj + elif isinstance(obj, ObjCPtr): + target_obj = fix_recursive_objects(types_mngr, obj.objtype) + obj = ObjCPtr(obj.name, target_obj, void_type.align, void_type.size) + elif isinstance(obj, ObjCArray): + target_obj = fix_recursive_objects(types_mngr, obj.objtype) + obj = ObjCArray(target_obj, obj.elems) + elif isinstance(obj, ObjCUnion): + for i, (name, fieldtype, offset, size) in enumerate(obj.fields): + fieldtype = fix_recursive_objects(types_mngr, fieldtype) + obj.fields[i] = (name, fieldtype, offset, size) + elif isinstance(obj, _ObjCRecurse): + obj = types_mngr.get_type((obj.name,)) + else: + raise NotImplementedError("Unknown type") + return obj + + +class CTypesManager(object): + """Store all defined C types""" + + def __init__(self, knowntypes): + self._types = dict(knowntypes) + self.cpt = 0 + + def gen_uniq_name(self): + """Generate uniq name for unamed strucs/union""" + cpt = self.cpt + self.cpt += 1 + return "__TYPE_INTERNAL__%d" % cpt + + def add_type(self, type_id, type_obj): + """Add new C type + @type_id: Type descriptor + @type_obj: ObjC* instance""" + self._types[type_id] = type_obj + + def get_type(self, type_id): + """Get C type + @type_id: Type descriptor + """ + return self._types[type_id] + + def is_known_type(self, type_id): + """Return true if @type_id is known + @type_id: Type descriptor + """ + return type_id in self._types + + def add_c_decl_from_ast(self, ast): + """ + Adds types from a C ast + @ast: C ast + """ + self.ast_parse_declarations(ast) + + def add_c_decl(self, c_str): + """ + Adds types from a C string types declaring + Note: will ignore lines containing code refs ie: + '# 23 "miasm.h"' + Returns the C ast + @c_str: C string containing C types declarations + """ + ast = c_to_ast(c_str) + self.add_c_decl_from_ast(ast) + + return ast + + @property + def void_ptr(self): + """Return the void* type""" + return self.get_type(('void*',)) + + def ast_eval_size(self, ast): + """Evaluates the size of a C ast object + + @ast: parsed pycparser.c_ast object + """ + + if isinstance(ast, c_ast.TypeDecl): + result = self.ast_eval_size(ast.type) + elif isinstance(ast, c_ast.PtrDecl): + void_type = self.void_ptr + result = void_type.size + elif isinstance(ast, c_ast.IdentifierType): + obj = self.get_type(tuple(ast.names)) + result = obj.size + else: + raise NotImplementedError('TODO') + return result + + def ast_eval_int(self, ast): + """Eval a C ast object integer + + @ast: parsed pycparser.c_ast object + """ + + if isinstance(ast, c_ast.BinaryOp): + left = self.ast_eval_int(ast.left) + right = self.ast_eval_int(ast.right) + if ast.op == '*': + result = left * right + elif ast.op == '/': + assert left % right == 0 + result = left / right + elif ast.op == '+': + result = left + right + elif ast.op == '-': + result = left - right + else: + raise NotImplementedError("Not implemented!") + elif isinstance(ast, c_ast.UnaryOp): + if ast.op == 'sizeof' and isinstance(ast.expr, c_ast.Typename): + result = self.ast_eval_size(ast.expr.type) + else: + raise NotImplementedError("Not implemented!") + + elif isinstance(ast, c_ast.Constant): + result = int(ast.value, 0) + elif isinstance(ast, c_ast.Cast): + # TODO: Can trunc intergers? + result = self.ast_eval_int(ast.expr) + else: + raise NotImplementedError("Not implemented!") + return result + + + def ast_get_align_size(self, ast): + """Evaluates the size/alignment of a C ast object + + @ast: parsed pycparser.c_ast object + """ + + if isinstance(ast, c_ast.Decl): + return self.ast_get_align_size(ast.type) + elif isinstance(ast, c_ast.TypeDecl): + return self.ast_get_align_size(ast.type) + elif isinstance(ast, c_ast.IdentifierType): + assert isinstance(ast, c_ast.IdentifierType) + names = ast.names + names = tuple(names) + if not self.is_known_type(names): + raise RuntimeError("Unknown type %r" % names) + obj = self.get_type(names) + elif isinstance(ast, c_ast.ArrayDecl): + subobj = self.ast_get_align_size(ast.type) + dim = ast.dim + value = self.ast_eval_int(dim) + obj = ObjCArray(subobj, value) + elif isinstance(ast, c_ast.Union): + obj = self.ast_gen_union_align_size(ast) + elif isinstance(ast, c_ast.Struct): + obj = self.ast_gen_struct_align_size(ast) + elif isinstance(ast, c_ast.PtrDecl): + void_type = self.void_ptr + subobj = self.ast_get_align_size(ast.type) + obj = ObjCPtr('noname', subobj, void_type.align, void_type.size) + else: + raise NotImplementedError("Not implemented!") + assert isinstance(obj, _ObjCRecurse) or obj.align in [ + 1, 2, 4, 8, 16, 32, 64, 128, 256] + return obj + + def struct_compute_field_offset(self, obj, offset): + """Compute the offset of the field @obj in the current structure""" + raise NotImplementedError("Abstract method") + + def struct_compute_align_size(self, align_max, size): + """Compute the alignment and size of the current structure""" + raise NotImplementedError("Abstract method") + + def union_compute_align_size(self, align_max, size): + """Compute the alignment and size of the current union""" + raise NotImplementedError("Abstract method") + + def ast_gen_struct_align_size(self, ast): + """Evaluates the size/alignment of a C ast structure + (default packed) + + @ast: parsed pycparser.c_ast object + """ + + offset = 0 + align_max = 1 + + if ast.name is None: + name = self.gen_uniq_name() + else: + name = ast.name + new_obj = ObjCStruct(name) + if ast.decls is None: + # If object is unknown, it's a recursive struct + if self.is_known_type((name,)): + obj = self.get_type((name,)) + else: + obj = _ObjCRecurse(name) + return obj + for arg in ast.decls: + obj = self.ast_get_align_size(arg) + align_max = max(align_max, obj.align) + offset = self.struct_compute_field_offset(obj, offset) + new_obj.add_field(arg.name, obj, offset, obj.size) + offset += obj.size + + # Structure alignement is its field max alignement + align, size = self.struct_compute_align_size(align_max, offset) + new_obj.set_align_size(align, size) + self.add_type((name, ), new_obj) + return new_obj + + def ast_gen_union_align_size(self, ast): + """Evaluates the size/alignment of a C ast union + @ast: parsed pycparser.c_ast object + """ + offset = 0 + align_max, size_max = 0, 0 + + if ast.name is None: + name = self.gen_uniq_name() + else: + name = ast.name + new_obj = ObjCUnion(name) + + for arg in ast.decls: + obj = self.ast_get_align_size(arg) + align_max = max(align_max, obj.align) + size_max = max(size_max, obj.size) + new_obj.add_field(arg.name, obj, + offset, obj.size) + + align, size = self.union_compute_align_size(align_max, size_max) + new_obj.set_align_size(align, size) + self.add_type((name, ), new_obj) + return new_obj + + def ast_gen_obj_align_size(self, ast): + """Evaluates the size/alignment of a C ast struct/union + (packed style in type manager) + + @ast: parsed pycparser.c_ast object + """ + + if isinstance(ast, c_ast.Struct): + obj = self.ast_gen_struct_align_size(ast) + elif isinstance(ast, c_ast.Union): + obj = self.ast_gen_union_align_size(ast) + else: + raise NotImplementedError("Not implemented!") + + fix_recursive_objects(self, obj) + return obj + + def ast_parse_declarations(self, ast): + """Add ast types declaration to the type manager + (packed style in type manager) + + @ast: parsed pycparser.c_ast object + """ + + for ext in ast.ext: + if isinstance(ext, c_ast.Decl) and\ + ext.name is None and\ + isinstance(ext.type, (c_ast.Struct, c_ast.Union)): + obj = self.ast_gen_obj_align_size(ext.type) + self.add_type((ext.type.name, ), obj) + + elif isinstance(ext, c_ast.Typedef) and\ + isinstance(ext.type.type, (c_ast.Struct, c_ast.Union)) and\ + not ext.type.type.decls: + new_type = ext.name + obj = self.get_type((ext.type.type.name,)) + self.add_type((ext.name,), obj) + + elif isinstance(ext, c_ast.Typedef) and\ + isinstance(ext.type.type, (c_ast.Struct, c_ast.Union)) and\ + ext.type.type.decls: + obj = self.ast_gen_obj_align_size(ext.type.type) + self.add_type((ext.type.declname, ), obj) + + elif isinstance(ext, c_ast.Typedef) and\ + isinstance(ext.type, c_ast.TypeDecl) and\ + isinstance(ext.type.type, c_ast.IdentifierType): + ext.show() + names = tuple(ext.type.type.names) + new_type = ext.name + + if not self.is_known_type(names): + raise RuntimeError("Unknown type %s" % repr(names)) + obj = self.get_type(names) + self.add_type((new_type,), obj) + + elif isinstance(ext, c_ast.Typedef) and\ + isinstance(ext.type.type, c_ast.Enum) and\ + isinstance(ext.type.type.values, c_ast.EnumeratorList): + # Enum are ints + obj = self.get_type(('enum',)) + self.add_type((ext.name,), obj) + + elif isinstance(ext, c_ast.Typedef) and\ + isinstance(ext.type, c_ast.ArrayDecl) and\ + isinstance(ext.type.type.type, c_ast.IdentifierType) and\ + self.is_known_type(tuple(ext.type.type.type.names)): + obj = self.get_type(tuple(ext.type.type.type.names)) + array = ext.type + + value = self.ast_eval_int(array.dim) + subobj = self.ast_get_align_size(array.type) + + obj = ObjCArray(subobj, value) + self.add_type((ext.name,), obj) + + elif isinstance(ext, c_ast.FuncDef) or\ + isinstance(ext.type, c_ast.FuncDecl): + continue + else: + raise NotImplementedError("strange type %r" % ext) + + +class CTypesManagerNotPacked(CTypesManager): + """Store defined C types (not packed)""" + + def struct_compute_field_offset(self, obj, offset): + """Compute the offset of the field @obj in the current structure + (not packed)""" + + if obj.align > 1: + offset = (offset + obj.align - 1) & ~(obj.align - 1) + return offset + + def struct_compute_align_size(self, align_max, size): + """Compute the alignment and size of the current structure + (not packed)""" + if align_max > 1: + size = (size + align_max - 1) & ~(align_max - 1) + return align_max, size + + def union_compute_align_size(self, align_max, size): + """Compute the alignment and size of the current union + (not packed)""" + return align_max, size + + +class CTypesManagerPacked(CTypesManager): + """Store defined C types (packed form)""" + + def struct_compute_field_offset(self, _, offset): + """Compute the offset of the field @obj in the current structure + (packed form)""" + return offset + + def struct_compute_align_size(self, _, size): + """Compute the alignment and size of the current structure + (packed form)""" + return 1, size + + def union_compute_align_size(self, align_max, size): + """Compute the alignment and size of the current union + (packed form)""" + return 1, size diff --git a/miasm2/core/objc.py b/miasm2/core/objc.py new file mode 100644 index 00000000..1b595442 --- /dev/null +++ b/miasm2/core/objc.py @@ -0,0 +1,1280 @@ +""" +C helper for Miasm: +* raw C to Miasm expression +* Miasm expression to raw C +* Miasm expression to C type +""" + +import re + +from pycparser import c_parser, c_ast + +from miasm2.expression.expression_reduce import ExprReducer +from miasm2.expression.expression import ExprInt, ExprId, ExprOp, ExprMem + +RE_HASH_CMT = re.compile(r'^#\s*\d+.*$', flags=re.MULTILINE) + + +class ObjC(object): + """Generic ObjC""" + + def set_align_size(self, align, size): + """Set C object alignment and size""" + + self.align = align + self.size = size + + +class ObjCDecl(ObjC): + """C Declaration identified""" + + def __init__(self, name, align, size): + super(ObjCDecl, self).__init__() + self.name, self.align, self.size = name, align, size + + def __repr__(self): + return '<%s %s>' % (self.__class__.__name__, self.name) + + +class ObjCInt(ObjC): + """C integer""" + + def __init__(self): + super(ObjCInt, self).__init__() + self.size = None + self.align = None + + +class ObjCPtr(ObjC): + """C Pointer""" + + def __init__(self, name, objtype, void_p_align, void_p_size): + """Init ObjCPtr + + @name: object name + @objtype: pointer target ObjC + @void_p_align: pointer alignment (in bytes) + @void_p_size: pointer size (in bytes) + """ + + super(ObjCPtr, self).__init__() + self.name, self.objtype = name, objtype + self.align = void_p_align + self.size = void_p_size + + def __repr__(self): + return '<PTR %r>' % (self.objtype) + + def __str__(self): + return '<PTR %r>' % (self.objtype) + + +class ObjCArray(ObjC): + """C array (test[XX])""" + + def __init__(self, objtype, elems): + """Init ObjCArray + + @objtype: pointer target ObjC + @elems: number of elements in the array + """ + + super(ObjCArray, self).__init__() + self.elems = elems + self.objtype = objtype + self.align = objtype.align + self.size = elems * objtype.size + + def __repr__(self): + return '<%r[%d]>' % (self.objtype, self.elems) + + +class _ObjCRecurse(ObjC): + """Special C object array, used in recursive declarations. Used in parser + *only*: this object is not intend to be in final objects + """ + + def __init__(self, name): + super(_ObjCRecurse, self).__init__() + self.name = name + + def __repr__(self): + return '<%r>' % (self.name) + + +class ObjCStruct(ObjC): + """C object for structures""" + + def __init__(self, name): + super(ObjCStruct, self).__init__() + self.name = name + self.fields = [] + + def add_field(self, name, objtype, offset, size): + """Add a field into the structure + @name: field name + @objtype: field type + @offset: field offset in the structure + @size: field size + """ + + self.fields.append((name, objtype, offset, size)) + + def __repr__(self): + return '<%s %s>' % (self.__class__.__name__, self.name) + + def __str__(self): + out = [] + out.append("Struct %s: (align: %d)" % (self.name, self.align)) + out.append(" off sz name") + for name, objtype, offset, size in self.fields: + out.append(" %-3d %-3d %-10s %r" % (offset, size, name, objtype)) + return '\n'.join(out) + + +class ObjCUnion(ObjC): + """C object for unions""" + + def __init__(self, name): + super(ObjCUnion, self).__init__() + self.name = name + self.fields = [] + + def add_field(self, name, objtype, offset, size): + """Add a field into the structure + @name: field name + @objtype: field type + @offset: field offset in the structure + @size: field size + """ + + self.fields.append((name, objtype, offset, size)) + + def __repr__(self): + return '<%s %s>' % (self.__class__.__name__, self.name) + + def __str__(self): + out = [] + out.append("Union %s: (align: %d)" % (self.name, self.align)) + out.append(" off sz name") + for name, objtype, offset, size in self.fields: + out.append(" %-3d %-3d %-10s %r" % (offset, size, name, objtype)) + return '\n'.join(out) + + +def access_simplifier(expr): + """Expression visitor to simplify a C access represented in Miasm + + @expr: Miasm expression representing the C access + + Example: + + IN: (In c: ['*(&((&((*(ptr_Test)).a))[0]))']) + [ExprOp('deref', ExprOp('addr', ExprOp('[]', ExprOp('addr', + ExprOp('field', ExprOp('deref', ExprId('ptr_Test', 64)), + ExprId('a', 64))), ExprInt(0x0, 64))))] + + OUT: (In c: ['(ptr_Test)->a']) + [ExprOp('->', ExprId('ptr_Test', 64), ExprId('a', 64))] + """ + + if (expr.is_op("addr") and + expr.args[0].is_op("[]") and + expr.args[0].args[1] == ExprInt(0, 64)): + return expr.args[0].args[0] + elif (expr.is_op("[]") and + expr.args[0].is_op("addr") and + expr.args[1] == ExprInt(0, 64)): + return expr.args[0].args[0] + elif (expr.is_op("addr") and + expr.args[0].is_op("deref")): + return expr.args[0].args[0] + elif (expr.is_op("deref") and + expr.args[0].is_op("addr")): + return expr.args[0].args[0] + elif (expr.is_op("field") and + expr.args[0].is_op("deref")): + return ExprOp("->", expr.args[0].args[0], expr.args[1]) + return expr + + +def access_str(expr): + """Return the C string of a C access represented in Miasm + + @expr: Miasm expression representing the C access + + In: + ExprOp('->', ExprId('ptr_Test', 64), ExprId('a', 64)) + OUT: + '(ptr_Test)->a' + """ + + if isinstance(expr, ExprId): + out = str(expr) + elif isinstance(expr, ExprInt): + out = str(int(expr)) + elif expr.is_op("addr"): + out = "&(%s)" % access_str(expr.args[0]) + elif expr.is_op("deref"): + out = "*(%s)" % access_str(expr.args[0]) + elif expr.is_op("field"): + out = "(%s).%s" % (access_str(expr.args[0]), access_str(expr.args[1])) + elif expr.is_op("->"): + out = "(%s)->%s" % (access_str(expr.args[0]), access_str(expr.args[1])) + elif expr.is_op("[]"): + out = "(%s)[%s]" % (access_str(expr.args[0]), access_str(expr.args[1])) + else: + raise RuntimeError("unknown op") + + return out + + +class CGen(object): + """Generic object to represent a C expression""" + + default_size = 64 + + def to_c(self): + """Generate corresponding C""" + + raise NotImplementedError("Virtual") + + def to_expr(self): + """Generate Miasm expression representing the C access""" + + raise NotImplementedError("Virtual") + + +class CGenInt(CGen): + """Int C object""" + + def __init__(self, integer): + assert isinstance(integer, (int, long)) + self.integer = integer + self.ctype = ObjCInt() + + def to_c(self): + """Generate corresponding C""" + + return "0x%X" % self.integer + + def __repr__(self): + return "<%s %s>" % (self.__class__.__name__, + self.integer) + + def to_expr(self): + """Generate Miasm expression representing the C access""" + + return ExprInt(self.integer, self.default_size) + + +class CGenId(CGen): + """ID of a C object""" + + def __init__(self, ctype, name): + self.ctype = ctype + self.name = name + assert isinstance(name, str) + + def __repr__(self): + return "<%s %s>" % (self.__class__.__name__, + self.name) + + def to_c(self): + """Generate corresponding C""" + + return "%s" % (self.name) + + def to_expr(self): + """Generate Miasm expression representing the C access""" + + return ExprId(self.name, self.default_size) + + +class CGenField(CGen): + """ + Field of a C struct/union + + IN: + - struct (not ptr struct) + - field name + OUT: + - input type of the field => output type + - X[] => X[] + - X => X* + """ + + def __init__(self, struct, field, fieldtype, void_p_align, void_p_size): + self.struct = struct + self.field = field + assert isinstance(field, str) + if isinstance(fieldtype, ObjCArray): + ctype = fieldtype + else: + ctype = ObjCPtr(field, fieldtype, void_p_align, void_p_size) + self.ctype = ctype + + def to_c(self): + """Generate corresponding C""" + + if isinstance(self.ctype, ObjCArray): + return "(%s).%s" % (self.struct.to_c(), self.field) + elif isinstance(self.ctype, ObjCPtr): + return "&((%s).%s)" % (self.struct.to_c(), self.field) + else: + raise RuntimeError("Strange case") + + def __repr__(self): + return "<%s %s %s>" % (self.__class__.__name__, + self.struct, + self.field) + + def to_expr(self): + """Generate Miasm expression representing the C access""" + + if isinstance(self.ctype, ObjCArray): + return ExprOp("field", self.struct.to_expr(), ExprId(self.field, self.default_size)) + elif isinstance(self.ctype, ObjCPtr): + return ExprOp("addr", ExprOp("field", self.struct.to_expr(), ExprId(self.field, self.default_size))) + else: + raise RuntimeError("Strange case") + + +class CGenArray(CGen): + """ + C Array + + This object does *not* deref the source, it only do object casting. + + IN: + - obj + OUT: + - X* => X* + - ..[][] => ..[] + - X[] => X* + """ + + def __init__(self, name, element, void_p_align, void_p_size): + ctype = name.ctype + if isinstance(ctype, ObjCPtr): + pass + elif isinstance(ctype, ObjCArray) and isinstance(ctype.objtype, ObjCArray): + ctype = ctype.objtype + elif isinstance(ctype, ObjCArray): + ctype = ObjCPtr('noname', ctype.objtype, void_p_align, void_p_size) + else: + raise TypeError("Strange case") + self.ctype = ctype + self.name = name + self.element = element + + def __repr__(self): + return "<%s %s>" % (self.__class__.__name__, + self.name) + + def to_c(self): + """Generate corresponding C""" + + if isinstance(self.ctype, ObjCPtr): + out_str = "&((%s)[%d])" % (self.name.to_c(), self.element) + elif isinstance(self.ctype, ObjCArray): + out_str = "(%s)[%d]" % (self.name.to_c(), self.element) + else: + raise RuntimeError("Strange case") + return out_str + + def to_expr(self): + """Generate Miasm expression representing the C access""" + + if isinstance(self.ctype, ObjCPtr): + return ExprOp("addr", ExprOp("[]", self.name.to_expr(), ExprInt(self.element, self.default_size))) + elif isinstance(self.ctype, ObjCArray): + return ExprOp("[]", self.name.to_expr(), ExprInt(self.element, self.default_size)) + else: + raise RuntimeError("Strange case") + + +class CGenDeref(CGen): + """ + C dereference + + IN: + - ptr + OUT: + - X* => X + """ + + def __init__(self, mem): + assert isinstance(mem.ctype, ObjCPtr) + self.ctype = mem.ctype.objtype + self.mem = mem + + def __repr__(self): + return "<%s %s>" % (self.__class__.__name__, + self.mem) + + def to_c(self): + """Generate corresponding C""" + + if not isinstance(self.mem.ctype, ObjCPtr): + raise RuntimeError() + return "*(%s)" % (self.mem.to_c()) + + def to_expr(self): + """Generate Miasm expression representing the C access""" + + if not isinstance(self.mem.ctype, ObjCPtr): + raise RuntimeError() + return ExprOp("deref", self.mem.to_expr()) + + +def ast_get_c_access_expr(ast, expr_types, lvl=0): + """Transform C ast object into a C Miasm expression + + @ast: parsed pycparser.c_ast object + @expr_types: a dictionnary linking ID names to their types + @lvl: actual recursion level + + Example: + + IN: + StructRef: -> + ID: ptr_Test + ID: a + + OUT: + ExprOp('->', ExprId('ptr_Test', 64), ExprId('a', 64)) + """ + + if isinstance(ast, c_ast.Constant): + obj = ExprInt(int(ast.value), 64) + elif isinstance(ast, c_ast.StructRef): + name, field = ast.name, ast.field.name + name = ast_get_c_access_expr(name, expr_types) + if ast.type == "->": + s_name = name + s_field = ExprId(field, 64) + obj = ExprOp('->', s_name, s_field) + elif ast.type == ".": + s_name = name + s_field = ExprId(field, 64) + obj = ExprOp("field", s_name, s_field) + else: + raise RuntimeError("Unknown struct access") + elif isinstance(ast, c_ast.UnaryOp) and ast.op == "&": + tmp = ast_get_c_access_expr(ast.expr, expr_types, lvl + 1) + obj = ExprOp("addr", tmp) + elif isinstance(ast, c_ast.ArrayRef): + tmp = ast_get_c_access_expr(ast.name, expr_types, lvl + 1) + index = ast_get_c_access_expr(ast.subscript, expr_types, lvl + 1) + obj = ExprOp("[]", tmp, index) + elif isinstance(ast, c_ast.ID): + assert ast.name in expr_types + obj = ExprId(ast.name, 64) + elif isinstance(ast, c_ast.UnaryOp) and ast.op == "*": + tmp = ast_get_c_access_expr(ast.expr, expr_types, lvl + 1) + obj = ExprOp("deref", tmp) + else: + raise NotImplementedError("Unknown type") + return obj + + +def c_to_ast(c_str): + """Transform a @c_str into a C ast + Note: will ignore lines containing code refs ie: + # 23 "miasm.h" + """ + + new_str = re.sub(RE_HASH_CMT, "", c_str) + parser = c_parser.CParser() + return parser.parse(new_str, filename='<stdin>') + + + +def parse_access(c_access): + """Parse C access + + @c_access: C access string + """ + + main = ''' + int main() { + %s; + } + ''' % c_access + + parser = c_parser.CParser() + node = parser.parse(main, filename='<stdin>') + access = node.ext[-1].body.block_items[0] + return access + + +class CTypeAnalyzer(ExprReducer): + """ + Return the C type(s) of a native Miasm expression + """ + + def __init__(self, expr_types, types_mngr): + """Init TypeAnalyzer + @expr_types: a dictionnary linking ID names to their types + @types_mngr: types manager + """ + + self.expr_types = expr_types + self.types_mngr = types_mngr + + def updt_expr_types(self, expr_types): + """Update expr_types + @expr_types: Dictionnary associating name to type + """ + + self.expr_types = expr_types + + CST = ObjCInt() + + def get_typeof(self, base_type, offset, deref, lvl=0): + """Return a list of pointers (or None) on the element at @offset of an + object of type @base_type + + In case of no @deref, stops recursion as soon as we reached the base of + an object. + In other cases, we need to go down to the final dereferenced object + + @base_type: type of main object + @offset: offset (in bytes) of the target sub object + @deref: get type for a pointer or a deref + @lvl: actual recursion level + """ + void_type = self.types_mngr.void_ptr + + if isinstance(base_type, ObjCStruct): + if offset == 0 and not deref: + # In this case, return the struct* + obj = ObjCPtr('noname', base_type, + void_type.align, void_type.size) + new_type = [obj] + return new_type + for _, subtype, f_offset, size in base_type.fields: + if not f_offset <= offset < f_offset + size: + continue + new_type = self.get_typeof( + subtype, offset - f_offset, deref, lvl + 1) + break + else: + raise RuntimeError('cannot find struct field') + elif isinstance(base_type, ObjCArray): + sub_offset = offset % (base_type.objtype.size) + element_num = offset / (base_type.objtype.size) + if element_num >= base_type.elems: + return None + if offset == 0 and not deref: + # In this case, return the array + return [base_type] + obj = self.get_typeof( + base_type.objtype, sub_offset, deref, lvl + 1) + new_type = obj + elif isinstance(base_type, ObjCDecl): + if offset != 0: + return [] + obj = ObjCPtr('noname', base_type, void_type.align, void_type.size) + + new_type = [obj] + elif isinstance(base_type, ObjCUnion): + out = [] + if offset == 0 and not deref: + # In this case, return the struct* + obj = ObjCPtr('noname', base_type, + void_type.align, void_type.size) + new_type = [obj] + return new_type + for _, objtype, f_offset, size in base_type.fields: + if not f_offset <= offset < f_offset + size: + continue + new_type = self.get_typeof( + objtype, offset - f_offset, deref, lvl + 1) + out += new_type + new_type = out + elif isinstance(base_type, ObjCPtr): + obj = ObjCPtr('noname', base_type, void_type.align, void_type.size) + new_type = [obj] + else: + raise NotImplementedError("deref type %r" % base_type) + return new_type + + def reduce_id(self, node, _): + """Get type of ExprId""" + + if not(isinstance(node.expr, ExprId) and node.expr.name in self.expr_types): + return None + return [self.expr_types[node.expr.name]] + + def reduce_int(self, node, _): + """Get type of ExprInt""" + + if not isinstance(node.expr, ExprInt): + return None + return [self.CST] + + def reduce_ptr_plus_cst(self, node, lvl): + """Get type of ptr + CST""" + + if not (isinstance(node.expr, ExprOp) and + node.expr.op == "+" and + len(node.args) == 2 and + set(type(x) for x in node.args[0].info + node.args[1].info) == set([ObjCInt, ObjCPtr])): + return None + arg0, arg1 = node.args + out = [] + ptr_offset = int(arg1.expr) + for info in arg0.info: + ptr_basetype = info.objtype + # Array-like: int* ptr; ptr[1] = X + out += self.get_typeof(ptr_basetype, + ptr_offset % ptr_basetype.size, + False, + lvl) + + return out + + def reduce_cst_op_cst(self, node, _): + """Get type of CST + CST""" + + if not (isinstance(node.expr, ExprOp) and + node.expr.op == "+" and + set(type(x) for x in node.args[0].info + node.args[1].info) == set([ObjCInt])): + return None + return [self.CST] + + def reduce_deref(self, node, lvl): + """Get type of a dereferenced expression: + * @NN[ptr<elem>] -> elem (type) + * @64[ptr<ptr<elem>>] -> ptr<elem> + * @32[ptr<struct>] -> struct.00 + """ + + if not isinstance(node.expr, ExprMem): + return None + if node.arg.info is None: + return None + found = [] + for subtype in node.arg.info: + # subtype : ptr<elem> + if not isinstance(subtype, (ObjCPtr, ObjCArray)): + return None + target = subtype.objtype + # target : type(elem) + for ptr_target in self.get_typeof(target, 0, True, lvl): + r_target = ptr_target.objtype + # ptr_target: ptr<elem> + # r_target: elem + if r_target.size != node.expr.size / 8: + continue + found.append(r_target) + if not found: + return None + return found + + reduction_rules = [reduce_id, reduce_int, + reduce_ptr_plus_cst, reduce_cst_op_cst, + reduce_deref, + ] + + def get_type(self, expr): + """Return the C type(s) of the native Miasm expression @expr + @expr: Miasm expression""" + + return self.reduce(expr) + + +class ExprToAccessC(ExprReducer): + """ + Generate the C access object(s) for a given native Miasm expression + Example: + IN: + @32[ptr_Test] + OUT: + [<CGenDeref <CGenArray <CGenField <CGenDeref <CGenId ptr_Test>> a>>>] + + An expression may be represented by multiple accessor (due to unions). + """ + + def __init__(self, expr_types, types_mngr, enforce_strict_access=True): + """Init GenCAccess + + @expr_types: a dictionnary linking ID names to their types + @types_mngr: types manager + @enforce_strict_access: If false, generate access even on expression + pointing to a middle of an object. If true, raise exception if such a + pointer is encountered + """ + + self.expr_types = expr_types + self.types_mngr = types_mngr + self.enforce_strict_access = enforce_strict_access + + def updt_expr_types(self, expr_types): + """Update expr_types + @expr_types: Dictionnary associating name to type + """ + + self.expr_types = expr_types + + def cgen_access(self, cgenobj, base_type, offset, deref, lvl=0): + """Return the access(es) which lead to the element at @offset of an + object of type @base_type + + In case of no @deref, stops recursion as soon as we reached the base of + an object. + In other cases, we need to go down to the final dereferenced object + + @cgenobj: current object access + @base_type: type of main object + @offset: offset (in bytes) of the target sub object + @deref: get type for a pointer or a deref + @lvl: actual recursion level + + + IN: + - base_type: struct Toto{ + int a + int b + } + - base_name: var + - 4 + OUT: + - CGenField(var, b) + + + + IN: + - base_type: int a + - 0 + OUT: + - CGenAddr(a) + + IN: + - base_type: X = int* a + - 0 + OUT: + - CGenAddr(X) + + IN: + - X = int* a + - 8 + OUT: + - ASSERT + + + IN: + - struct toto{ + int a + int b[10] + } + - 8 + OUT: + - CGenArray(CGenField(toto, b), 1) + """ + + void_type = self.types_mngr.void_ptr + if isinstance(base_type, ObjCStruct): + assert 0 <= offset < base_type.size + if offset == 0 and not deref: + # In this case, return the struct* + return [cgenobj] + + out = [] + for fieldname, subtype, f_offset, size in base_type.fields: + if not f_offset <= offset < f_offset + size: + continue + fieldptr = CGenField(CGenDeref(cgenobj), fieldname, subtype, + void_type.align, void_type.size) + ret = self.cgen_access( + fieldptr, subtype, offset - f_offset, deref, lvl + 1) + for sname in ret: + finalobj = sname + out.append(finalobj) + new_type = out + break + else: + raise RuntimeError('Cannot find struct field') + elif isinstance(base_type, ObjCArray): + element_num = offset / (base_type.objtype.size) + assert element_num < base_type.elems + f_offset = offset % base_type.objtype.size + cur_objtype = base_type + curobj = cgenobj + subtype = cur_objtype.objtype + if subtype == ObjCArray: + raise NotImplementedError("TODO") + else: + if f_offset != 0: + curobj = CGenArray(curobj, element_num, + void_type.align, void_type.size) + ret = self.cgen_access( + curobj, curobj.ctype.objtype, f_offset, deref, lvl + 1) + else: + curobj = CGenArray(curobj, element_num, + void_type.align, void_type.size) + ret = [curobj] + new_type = ret + elif isinstance(base_type, ObjCDecl): + if self.enforce_strict_access: + if offset % base_type.size != 0: + return [] + elem_num = offset / base_type.size + + nobj = CGenArray(cgenobj, elem_num, + void_type.align, void_type.size) + new_type = [(nobj)] + + elif isinstance(base_type, ObjCUnion): + out = [] + if offset == 0 and not deref: + # In this case, return the struct* + return [cgenobj] + + for fieldname, objtype, f_offset, size in base_type.fields: + if not f_offset <= offset < f_offset + size: + continue + field = CGenField(CGenDeref(cgenobj), fieldname, objtype, + void_type.align, void_type.size) + new_type = self.cgen_access( + field, objtype, offset - f_offset, deref, lvl + 1) + if new_type is None: + continue + for sname in new_type: + finalobj = sname + out.append(finalobj) + new_type = out + elif isinstance(base_type, ObjCPtr): + elem_num = offset / base_type.size + + if elem_num == 0: + if self.enforce_strict_access: + assert offset % base_type.size == 0 + nobj = CGenArray(cgenobj, elem_num, + void_type.align, void_type.size) + new_type = [(nobj)] + else: + if self.enforce_strict_access: + assert offset % base_type.size == 0 + nobj = CGenArray(cgenobj, elem_num, + void_type.align, void_type.size) + new_type = [(nobj)] + + else: + raise NotImplementedError("deref type %r" % base_type) + return new_type + + def reduce_id(self, node, _): + """Generate access for ExprId""" + + if not (isinstance(node.expr, ExprId) and + node.expr.name in self.expr_types): + return None + + out = CGenId(self.expr_types[node.expr.name], node.expr.name) + return [out] + + def reduce_int(self, node, _): + """Generate access for ExprInt""" + + if not isinstance(node.expr, ExprInt): + return None + return [CGenInt(int(node.expr))] + + def reduce_op(self, node, lvl): + """Generate access for ExprOp""" + + if not (isinstance(node.expr, ExprOp) and + node.expr.op == "+" and + len(node.args) == 2 and + set(type(x.ctype) for x in node.args[0].info + node.args[1].info) == set([ObjCInt, ObjCPtr])): + return None + + arg0, arg1 = node.args + out = [] + ptr_offset = int(arg1.expr) + for name in arg0.info: + assert isinstance(name.ctype, ObjCPtr) + ptr_basetype = name.ctype.objtype + # Array-like: int* ptr; ptr[1] = X + ret = self.cgen_access(name, + ptr_basetype, + ptr_offset, False, lvl) + for subcgenobj in ret: + out.append(subcgenobj) + return out + + def reduce_mem(self, node, lvl): + """Generate access for ExprMem: + * @NN[ptr<elem>] -> elem (type) + * @64[ptr<ptr<elem>>] -> ptr<elem> + * @32[ptr<struct>] -> struct.00 + """ + + if not isinstance(node.expr, ExprMem): + return None + + assert isinstance(node.arg.info, list) + found = [] + for subcgenobj in node.arg.info: + assert isinstance(subcgenobj.ctype, ObjCPtr) + target = subcgenobj.ctype.objtype + # target : type(elem) + if isinstance(target, (ObjCStruct, ObjCUnion)): + for finalcgenobj in self.cgen_access(subcgenobj, target, 0, True, lvl): + target = finalcgenobj.ctype.objtype + if not(self.enforce_strict_access) or target.size == node.expr.size / 8: + nobj = CGenDeref(finalcgenobj) + found.append(nobj) + elif isinstance(target, ObjCArray): + final = target.objtype + if not(self.enforce_strict_access) or final.size == node.expr.size / 8: + nobj = CGenDeref(subcgenobj) + found.append(nobj) + + else: + if not(self.enforce_strict_access) or target.size == node.expr.size / 8: + nobj = CGenDeref(subcgenobj) + found.append(nobj) + assert found + return found + + reduction_rules = [reduce_id, + reduce_int, + reduce_op, + reduce_mem, + ] + + def get_access(self, expr): + """Generate C access(es) for the native Miasm expression @expr + @expr: native Miasm expression + """ + + return self.reduce(expr) + + +class ExprCToExpr(ExprReducer): + """Translate a Miasm expression (representing a C access) into a native + Miasm expression and its C type: + + Example: + + IN: ((ptr_struct -> f_mini) field x) + OUT: @32[ptr_struct + 0x80], int + + + Tricky cases: + Struct S0 { + int x; + int y[0x10]; + } + + Struct S1 { + int a; + S0 toto; + } + + S1* ptr; + + Case 1: + ptr->toto => ptr + 0x4 + &(ptr->toto) => ptr + 0x4 + + Case 2: + (ptr->toto).x => @32[ptr + 0x4] + &((ptr->toto).x) => ptr + 0x4 + + Case 3: + (ptr->toto).y => ptr + 0x8 + &((ptr->toto).y) => ptr + 0x8 + + Case 4: + (ptr->toto).y[1] => @32[ptr + 0x8 + 0x4] + &((ptr->toto).y[1]) => ptr + 0x8 + 0x4 + + """ + + def __init__(self, expr_types, types_mngr): + """Init ExprCAccess + + @expr_types: a dictionnary linking ID names to their types + @types_mngr: types manager + """ + + self.expr_types = expr_types + self.types_mngr = types_mngr + + def updt_expr_types(self, expr_types): + """Update expr_types + @expr_types: Dictionnary associating name to type + """ + + self.expr_types = expr_types + + CST = "CST" + + def reduce_id(self, node, _): + """Reduce ExprId""" + + if not isinstance(node.expr, ExprId): + return None + if node.expr.name in self.expr_types: + out = (node.expr, self.expr_types[node.expr.name]) + else: + out = (node.expr, None) + return out + + def reduce_int(self, node, _): + """Reduce ExprInt""" + + if not isinstance(node.expr, ExprInt): + return None + return self.CST + + def reduce_op_memberof(self, node, _): + """Reduce -> operator""" + + if not node.expr.is_op('->'): + return None + assert len(node.args) == 2 + out = [] + assert isinstance(node.args[1].expr, ExprId) + field = node.args[1].expr.name + src, src_type = node.args[0].info + assert isinstance(src_type, (ObjCPtr, ObjCArray)) + struct_dst = src_type.objtype + assert isinstance(struct_dst, ObjCStruct) + + found = False + for name, objtype, offset, _ in struct_dst.fields: + if name != field: + continue + expr = src + ExprInt(offset, src.size) + if isinstance(objtype, (ObjCArray, ObjCStruct, ObjCUnion)): + pass + else: + expr = ExprMem(expr, objtype.size * 8) + assert not found + found = True + out = (expr, objtype) + assert found + return out + + def reduce_op_field(self, node, _): + """Reduce field operator (Struct or Union)""" + + if not node.expr.is_op('field'): + return None + assert len(node.args) == 2 + out = [] + assert isinstance(node.args[1].expr, ExprId) + field = node.args[1].expr.name + src, src_type = node.args[0].info + struct_dst = src_type + + if isinstance(struct_dst, ObjCStruct): + found = False + for name, objtype, offset, _ in struct_dst.fields: + if name != field: + continue + expr = src + ExprInt(offset, src.size) + if isinstance(objtype, ObjCArray): + # Case 4 + pass + elif isinstance(objtype, (ObjCStruct, ObjCUnion)): + # Case 1 + pass + else: + # Case 2 + expr = ExprMem(expr, objtype.size * 8) + assert not found + found = True + out = (expr, objtype) + elif isinstance(struct_dst, ObjCUnion): + found = False + for name, objtype, offset, _ in struct_dst.fields: + if name != field: + continue + expr = src + ExprInt(offset, src.size) + if isinstance(objtype, ObjCArray): + # Case 4 + pass + elif isinstance(objtype, (ObjCStruct, ObjCUnion)): + # Case 1 + pass + else: + # Case 2 + expr = ExprMem(expr, objtype.size * 8) + assert not found + found = True + out = (expr, objtype) + else: + raise NotImplementedError("unknown ObjC") + assert found + return out + + def reduce_op_array(self, node, _): + """Reduce array operator""" + + if not node.expr.is_op('[]'): + return None + assert len(node.args) == 2 + out = [] + assert isinstance(node.args[1].expr, ExprInt) + cst = node.args[1].expr + src, src_type = node.args[0].info + objtype = src_type.objtype + expr = src + cst * ExprInt(objtype.size, cst.size) + if isinstance(src_type, ObjCPtr): + if isinstance(objtype, ObjCArray): + final = objtype.objtype + expr = src + cst * ExprInt(final.size, cst.size) + objtype = final + expr = ExprMem(expr, final.size * 8) + found = True + else: + expr = ExprMem(expr, objtype.size * 8) + found = True + elif isinstance(src_type, ObjCArray): + if isinstance(objtype, ObjCArray): + final = objtype + found = True + elif isinstance(objtype, ObjCStruct): + found = True + else: + expr = ExprMem(expr, objtype.size * 8) + found = True + else: + raise NotImplementedError("Unknown access" % node.expr) + assert found + out = (expr, objtype) + return out + + def reduce_op_addr(self, node, _): + """Reduce addr operator""" + + if not node.expr.is_op('addr'): + return None + assert len(node.args) == 1 + out = [] + src, src_type = node.args[0].info + + void_type = self.types_mngr.void_ptr + + if isinstance(src_type, ObjCArray): + out = (src.arg, ObjCPtr('noname', src_type.objtype, + void_type.align, void_type.size)) + elif isinstance(src, ExprMem): + out = (src.arg, ObjCPtr('noname', src_type, + void_type.align, void_type.size)) + elif isinstance(src_type, ObjCStruct): + out = (src, ObjCPtr('noname', src_type, + void_type.align, void_type.size)) + elif isinstance(src_type, ObjCUnion): + out = (src, ObjCPtr('noname', src_type, + void_type.align, void_type.size)) + else: + raise NotImplementedError("unk type") + return out + + def reduce_op_deref(self, node, _): + """Reduce deref operator""" + + if not node.expr.is_op('deref'): + return None + out = [] + src, src_type = node.args[0].info + assert isinstance(src_type, (ObjCPtr, ObjCArray)) + size = src_type.objtype.size * 8 + out = (ExprMem(src, size), (src_type.objtype)) + return out + + reduction_rules = [reduce_id, + reduce_int, + reduce_op_memberof, + reduce_op_field, + reduce_op_array, + reduce_op_addr, + reduce_op_deref, + ] + + def get_expr(self, expr): + """Translate a Miasm expression @expr (representing a C access) into a + native Miasm expression and its C type + + @expr: Miasm expression (representing a C access) + """ + + return self.reduce(expr) + + +class CHandler(object): + """ + C manipulator for Miasm + Miasm expr <-> C + """ + + exprCToExpr_cls = ExprCToExpr + cTypeAnalyzer_cls = CTypeAnalyzer + exprToAccessC_cls = ExprToAccessC + + def __init__(self, types_mngr, expr_types, + simplify_c=access_simplifier, + enforce_strict_access=True): + self.exprc2expr = self.exprCToExpr_cls(expr_types, types_mngr) + self.type_analyzer = self.cTypeAnalyzer_cls(expr_types, types_mngr) + self.access_c_gen = self.exprToAccessC_cls(expr_types, + types_mngr, + enforce_strict_access) + self.simplify_c = simplify_c + self.expr_types = expr_types + + def updt_expr_types(self, expr_types): + """Update expr_types + @expr_types: Dictionnary associating name to type + """ + + self.expr_types = expr_types + self.exprc2expr.updt_expr_types(expr_types) + self.type_analyzer.updt_expr_types(expr_types) + self.access_c_gen.updt_expr_types(expr_types) + + def expr_to_c(self, expr): + """Convert a Miasm @expr into it's C equivatlent string + @expr: Miasm expression""" + + expr_access = self.access_c_gen.get_access(expr) + accesses = [access for access in expr_access.info] + accesses_simp = [access_str(access.to_expr().visit(self.simplify_c)) + for access in accesses] + return accesses_simp + + def expr_to_types(self, expr): + """Get the possible types of the Miasm @expr + @expr: Miasm expression""" + + return self.type_analyzer.get_type(expr).info + + def c_to_expr(self, c_str): + """Convert a C string expression to a Miasm expression + @c_str: C string""" + + ast = parse_access(c_str) + access_c = ast_get_c_access_expr(ast, self.expr_types) + return self.exprc2expr.get_expr(access_c).info[0] + + def c_to_type(self, c_str): + """Get the type of a C string expression + @expr: Miasm expression""" + + ast = parse_access(c_str) + access_c = ast_get_c_access_expr(ast, self.expr_types) + ret_type = self.exprc2expr.get_expr(access_c).info[1] + return ret_type + + +class CTypeTemplate(object): + """Define C types sizes/alignement for a given architecture""" + pass |