about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorFlorent Monjalet <florent.monjalet@gmail.com>2015-12-15 16:35:31 +0100
committerFlorent Monjalet <florent.monjalet@gmail.com>2016-01-18 14:02:32 +0100
commit03b3a84e0dd4d4d01b471f1767d4aec68b9a90ad (patch)
tree55d0116b6f64987ad09c5ce580bc069c5ce060c3
parent0379f8e91fa54fe641948f01bb98a76fab47033a (diff)
downloadmiasm-03b3a84e0dd4d4d01b471f1767d4aec68b9a90ad.tar.gz
miasm-03b3a84e0dd4d4d01b471f1767d4aec68b9a90ad.zip
Types: Support anonymous Struct/Union/BitField
See the test addition for an example. A Struct, Union, or BitField field
with no name will be considered anonymous: all its fields will be added
to the parent Struct/Union/BitField. This implements this kind of C
declaration:

struct foo {
    int a;
    union {
        int bar;
        struct {
            short baz;
            short foz;
        };
    };
}
-rw-r--r--miasm2/core/types.py83
-rw-r--r--test/core/types.py27
2 files changed, 99 insertions, 11 deletions
diff --git a/miasm2/core/types.py b/miasm2/core/types.py
index 8d9687eb..4bab3bde 100644
--- a/miasm2/core/types.py
+++ b/miasm2/core/types.py
@@ -370,7 +370,7 @@ class Ptr(Num):
     MemType.
 
     Mapped to MemPtr (see its doc for more info):
-        
+
         assert isinstance(mystruct.ptr, MemPtr)
         mystruct.ptr = 0x4000 # Assign the Ptr numeric value
         mystruct.ptr.val = 0x4000 # Also assigns the Ptr numeric value
@@ -503,6 +503,24 @@ class Struct(Type):
         # Toto1
         class Toto(MemStruct):
             fields = [("f1", Num("I")), ("f2", Num("I"))]
+
+    Anonymous Struct, Union or BitField can be used if their field name
+    evaluates to False ("" or None). Such anonymous Struct field will generate
+    fields to the parent Struct, e.g.:
+        bla = Struct("Bla", [
+                   ("a", Num("B")),
+                   ("", Union([("b1", Num("B")), ("b2", Num("H"))])),
+                   ("", Struct("", [("c1", Num("B")), ("c2", Num("B"))])),
+               ]
+    Will have a b1, b2 and c1, c2 field directly accessible. The anonymous
+    fields are renamed to "__anon_<num>", with <num> an incremented number.
+
+    In such case, bla.fields will not contain b1, b2, c1 and c2 (only the 3
+    actual fields, with the anonymous ones renamed), but bla.all_fields will
+    return the 3 fields + b1, b2, c1 and c2 (and an information telling if it
+    has been generated from an anonymous Struct/Union).
+
+    bla.get_field(vm, addr, "b1") will work.
     """
 
     def __init__(self, name, fields):
@@ -518,24 +536,67 @@ class Struct(Type):
         # Build a proper (name, Field()) list, handling cases where the user
         # supplies a MemType subclass instead of a Type instance
         real_fields = []
-        for name, field in fields:
+        uniq_count = 0
+        for fname, field in fields:
             if isinstance(field, type) and issubclass(field, MemType):
                 if field._type is None:
                     raise ValueError("%r has no static type; use a subclasses "
                                      "with a non null _type or use a "
                                      "Type instance")
                 field = field.get_type()
-            real_fields.append((name, field))
 
             # For reflexion
             field._set_self_type(self)
-            self._fields_desc[name] = {"field": field, "offset": offset}
-            offset += field.size
+
+            # Anonymous Struct/Union
+            if not fname and isinstance(field, Struct):
+                # Generate field information
+                updated_fields = {
+                    name: {
+                        # Same field type than the anon field subfield
+                        'field': fd['field'],
+                        # But the current offset is added
+                        'offset': fd['offset'] + offset,
+                    }
+                    for name, fd in field._fields_desc.iteritems()
+                }
+
+                # Add the newly generated fields from the anon field
+                self._fields_desc.update(updated_fields)
+                real_fields += [(name, fld, True)
+                                for name, fld in field.fields]
+
+                # Rename the anonymous field
+                fname = '__anon_%x' % uniq_count
+                uniq_count += 1
+
+            self._fields_desc[fname] = {"field": field, "offset": offset}
+            real_fields.append((fname, field, False))
+            offset = self._next_offset(field, offset)
+
         # fields is immutable
         self._fields = tuple(real_fields)
 
+    def _next_offset(self, field, orig_offset):
+        return orig_offset + field.size
+
     @property
     def fields(self):
+        """Returns a sequence of (name, field) describing the fields of this
+        Struct, in order of offset.
+
+        Fields generated from anonymous Unions or Structs are excluded from
+        this sequence.
+        """
+        return tuple((name, field) for name, field, anon in self._fields
+                                   if not anon)
+
+    @property
+    def all_fields(self):
+        """Returns a sequence of (<name>, <field (Type instance)>, <is_anon>),
+        where is_anon is True when a field is generated from an anonymous
+        Struct or Union, and False for the fields that have been provided as is.
+        """
         return self._fields
 
     def set(self, vm, addr, val):
@@ -600,7 +661,7 @@ class Struct(Type):
 
 class Union(Struct):
     """Represents a C union.
-    
+
     Allows to put multiple fields at the same offset in a MemStruct,
     similar to unions in C. The Union will have the size of the largest of its
     fields.
@@ -629,8 +690,8 @@ class Union(Struct):
     def size(self):
         return max(field.size for _, field in self.fields)
 
-    def get_offset(self, field_name):
-        return 0
+    def _next_offset(self, field, orig_offset):
+        return orig_offset
 
     def _get_pinned_base_class(self):
         return MemUnion
@@ -1019,7 +1080,7 @@ class Str(Type):
 
 class Void(Type):
     """Represents the C void type.
-    
+
     Mapped to MemVoid.
     """
 
@@ -1035,7 +1096,7 @@ class Void(Type):
 
 class Self(Void):
     """Special marker to reference a type inside itself.
-    
+
     Mapped to MemSelf.
 
     Example:
@@ -1375,7 +1436,7 @@ class MemStruct(MemType):
     @classmethod
     def _gen_attributes(cls):
         # Generate self.<name> getter and setters
-        for name, field in cls._type.fields:
+        for name, field, _ in cls._type.all_fields:
             setattr(cls, name, property(
                 lambda self, name=name: self.get_field(name),
                 lambda self, val, name=name: self.set_field(name, val)
diff --git a/test/core/types.py b/test/core/types.py
index c59a68d6..7ad8ad13 100644
--- a/test/core/types.py
+++ b/test/core/types.py
@@ -491,6 +491,33 @@ assert Array(Num("B"), 19).lval != Array(Num("B"), 20).lval
 assert MyStruct == Struct(MyStruct.__name__, MyStruct.fields).lval
 assert MyStruct.get_type() == Struct(MyStruct.__name__, MyStruct.fields)
 
+# Anonymous Unions
+class Anon(MemStruct):
+    fields = [
+        ("a", Num("B")),
+        # If a field name evaluates to False ("" or None for example) and the
+        # field type is a Struct subclass (Struct, Union, BitField), the field
+        # is considered as an anonymous struct or union. Therefore, Anon will
+        # have b1, b2 and c1, c2 attributes in that case.
+        ("", Union([("b1", Num("B")), ("b2", Num("H"))])),
+        ("", Struct("", [("c1", Num("B")), ("c2", Num("B"))])),
+        ("d", Num("B")),
+    ]
+
+anon = Anon(jitter.vm)
+anon.memset()
+anon.a = 0x07
+anon.b2 = 0x0201
+anon.c1 = 0x55
+anon.c2 = 0x77
+anon.d = 0x33
+assert anon.a == 0x07
+assert anon.b1 == 0x01
+assert anon.b2 == 0x0201
+assert anon.c1 == 0x55
+assert anon.c2 == 0x77
+assert anon.d == 0x33
+
 
 # Repr tests