xref: /xnu-8796.141.3/tools/lldbmacros/structanalyze.py (revision 1b191cb58250d0705d8a51287127505aa4bc0789)
1from __future__ import absolute_import, division, print_function
2
3from builtins import range
4
5import lldb
6
7from xnu import *
8
9_UnionStructClass = [ lldb.eTypeClassStruct, lldb.eTypeClassClass, lldb.eTypeClassUnion ]
10
11def _get_offset_formatter(ctx, fmt_hex, fmt_dec):
12    """ Returns a formatter of struct member offsets and sizes.
13
14    params:
15        ctx - configuration context
16        fmt_hex - hexadecimal format
17        fmt_dec - decimal format
18    returns:
19        offset formatter
20    """
21    O = ctx[0]
22    use_hex = ctx[1]
23    if use_hex:
24        fmt = fmt_hex
25    else:
26        fmt = fmt_dec
27    return lambda o, s: O.format(fmt, o, s)
28
29def _get_num_formatter(ctx, fmt_hex, fmt_dec):
30    """ Returns a number formatter.
31
32    params:
33        ctx - configuration context
34        fmt_hex - hexadecimal format
35        fmt_dec - decimal format
36    returns:
37        number formatter
38    """
39    O = ctx[0]
40    use_hex = ctx[1]
41    if use_hex:
42        fmt = fmt_hex
43    else:
44        fmt = fmt_dec
45    return lambda n: O.format(fmt, n)
46
47def _showStructPacking(ctx, symbol, begin_offset=0, symsize=0, typedef=None, outerSize=0, memberName=None):
48    """ Recursively parse the field members of structure.
49
50        params :
51            ctx - context containing configuration settings and the output formatter (standard.py) symbol (lldb.SBType) reference to symbol in binary
52        returns:
53            string containing lines of output.
54    """
55
56    O = ctx[0]
57    format_offset = _get_offset_formatter(ctx, "{:#06x},[{:#6x}]", "{:04d},[{:4d}]")
58    format_num = _get_num_formatter(ctx, "{:#04x}", "{:2d}")
59
60    ctype = "unknown type"
61    is_union = False
62    is_class = False
63    union_size = None
64    sym_size = symbol.GetByteSize()
65
66    if symbol.GetTypeClass() == lldb.eTypeClassUnion:
67        ctype = "union"
68        is_union = True
69        union_size = sym_size
70    if symbol.GetTypeClass() == lldb.eTypeClassStruct:
71        ctype = "struct"
72    if symbol.GetTypeClass() == lldb.eTypeClassClass:
73        ctype = "class"
74        is_class = True
75
76    if not outerSize or outerSize == sym_size:
77        outstr = format_offset(begin_offset, sym_size)
78    elif outerSize < sym_size: # happens with c++ inheritance
79        outstr = format_offset(begin_offset, outerSize)
80    else:
81        outstr = O.format("{:s}{VT.DarkRed}{{{:s}}}{VT.Default}",
82                format_offset(begin_offset, sym_size),
83                format_num(outerSize - sym_size))
84
85    if typedef:
86        outstr += O.format(" {0}", typedef)
87    if symbol.IsAnonymousType():
88        outstr += O.format(" ({VT.DarkMagenta}anonymous {0}{VT.Default})", ctype)
89    else:
90        outstr += O.format(" ({VT.DarkMagenta}{0} {1}{VT.Default})", ctype, symbol.GetName())
91    if memberName:
92        outstr += O.format(" {0} {{", memberName)
93    else:
94        outstr += ") {"
95
96    print(outstr)
97
98    with O.indent():
99        _previous_size = 0
100        _packed_bit_offset = 0
101        _nfields = symbol.GetNumberOfFields()
102
103        if is_class:
104            _next_offset_in_bits = 0
105            _nclasses = symbol.GetNumberOfDirectBaseClasses()
106
107            for i in range(_nclasses):
108                member = symbol.GetDirectBaseClassAtIndex(i)
109                if i < _nclasses - 1:
110                    m_size_bits = symbol.GetDirectBaseClassAtIndex(i + 1).GetOffsetInBits()
111                elif _nfields:
112                    m_size_bits = symbol.GetFieldAtIndex(0).GetOffsetInBits()
113                else:
114                    m_size_bits = symbol.GetByteSize() * 8
115
116                m_offset = member.GetOffsetInBytes() + begin_offset
117                m_type = member.GetType()
118                m_name = member.GetName()
119                m_size = m_size_bits // 8
120
121                _previous_size = m_size
122                _packed_bit_offset = member.GetOffsetInBits() + m_size_bits
123
124                _showStructPacking(ctx, m_type, m_offset, str(m_type), outerSize=m_size, memberName=m_name)
125
126        for i in range(_nfields):
127            member = symbol.GetFieldAtIndex(i)
128            m_offset = member.GetOffsetInBytes() + begin_offset
129            m_offset_bits = member.GetOffsetInBits()
130
131            m_type = member.GetType()
132            m_name = member.GetName()
133            m_size = m_type.GetByteSize()
134
135            if member.IsBitfield():
136                m_is_bitfield = True
137                m_size_bits = member.GetBitfieldSizeInBits()
138            else:
139                m_is_bitfield = False
140                m_size_bits = m_size * 8
141
142            if not is_union and _packed_bit_offset < m_offset_bits:
143                m_previous_offset = begin_offset + (_packed_bit_offset // 8)
144                m_hole_bits = m_offset_bits - _packed_bit_offset
145                if _packed_bit_offset % 8 == 0:
146                    print(O.format("{:s} ({VT.DarkRed}*** padding ***{VT.Default})",
147                           format_offset(m_previous_offset, (m_hole_bits // 8))))
148                else:
149                    print(O.format("{:s} ({VT.Brown}*** padding : {:s} ***{VT.Default})",
150                            format_offset(m_previous_offset, _previous_size),
151                            format_num(m_hole_bits)))
152
153            _previous_size = m_size
154            _packed_bit_offset = m_offset_bits + m_size_bits
155
156            _type_class = m_type.GetTypeClass()
157            _canonical_type = m_type.GetCanonicalType()
158            _canonical_type_class = m_type.GetCanonicalType().GetTypeClass()
159
160            if _type_class == lldb.eTypeClassTypedef and _canonical_type_class in _UnionStructClass:
161                _showStructPacking(ctx, _canonical_type, m_offset, str(m_type), outerSize=union_size, memberName=m_name)
162            elif _type_class in _UnionStructClass:
163                _showStructPacking(ctx, m_type, m_offset, outerSize=union_size, memberName=m_name)
164            else:
165                outstr = format_offset(m_offset, m_size)
166                if is_union and union_size != (m_size_bits // 8):
167                    outstr += O.format("{VT.DarkRed}{{{:s}}}{VT.Default}",
168                            format_num(union_size - (m_size_bits // 8)))
169                if m_is_bitfield:
170                    outstr += O.format(" ({VT.DarkGreen}{:s} : {:s}{VT.Default}) {:s}",
171                            m_type.GetName(),
172                            format_num(m_size_bits),
173                            m_name)
174                else:
175                    outstr += O.format(" ({VT.DarkGreen}{:s}{VT.Default}) {:s}",
176                            m_type.GetName(), m_name)
177                print(outstr)
178
179        referenceSize = sym_size
180        if outerSize:
181            referenceSize = min(outerSize, sym_size)
182
183        if not is_union and _packed_bit_offset < referenceSize * 8:
184            m_previous_offset = begin_offset + (_packed_bit_offset // 8)
185            m_hole_bits = referenceSize * 8 - _packed_bit_offset
186            if _packed_bit_offset % 8 == 0:
187                print(O.format("{:s} ({VT.DarkRed}*** padding ***{VT.Default})",
188                        format_offset(m_previous_offset, m_hole_bits // 8)))
189            else:
190                print(O.format("{:s} ({VT.Brown}padding : {:s}{VT.Default})\n",
191                        format_offset(m_previous_offset, _previous_size),
192                        format_num(m_hole_bits)))
193
194    print("}")
195
196@lldb_command('showstructpacking', "X" , fancy=True)
197def showStructInfo(cmd_args=None, cmd_options={}, O=None):
198    """ Show how a structure is packed in the binary.
199
200        Usage: showstructpacking [-X] <type name>
201            -X : prints struct members offsets and sizes in a hexadecimal format (decimal is default)
202
203        The format is:
204            <offset>, [<size_of_member>] (<type>) <name>
205
206        Example:
207            (lldb) showstructpacking pollfd
208                0,[   8] struct pollfd {
209                    0,[   4] (int) fd
210                    4,[   2] (short) events
211                    6,[   2] (short) revents
212                }
213    """
214    if not cmd_args:
215        raise ArgumentError("Please provide a type name.")
216
217    ty_name = cmd_args[0]
218    try:
219        sym = gettype(ty_name)
220    except NameError:
221        return O.error("Cannot find type named {0}", ty_name)
222
223    if sym.GetTypeClass() == lldb.eTypeClassTypedef:
224        sym = sym.GetCanonicalType()
225
226    if sym.GetTypeClass() not in _UnionStructClass:
227        return O.error("{0} is not a structure/union/class type", ty_name)
228
229    ctx = (O, "-X" in cmd_options)
230
231    _showStructPacking(ctx, sym, 0)
232
233# EndMacro: showstructinto
234