xref: /aosp_15_r20/external/bcc/src/python/bcc/disassembler.py (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
1# Copyright 2019 Clevernet
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15from os import linesep
16import ctypes as ct
17from .table import get_table_type_name
18
19class OffsetUnion(ct.Union):
20    _fields_ = [('offsetu', ct.c_uint16), ('offset', ct.c_int16)]
21
22class ImmUnion(ct.Union):
23    _fields_ = [('immu', ct.c_uint32), ('imm', ct.c_int32)]
24
25class BPFInstrFields(ct.Structure):
26    _pack_ = 1
27    _anonymous_ = ('o', 'i')
28    _fields_ = [('opcode', ct.c_uint8),
29                ('dst', ct.c_uint8, 4),
30                ('src', ct.c_uint8, 4),
31                ('o', OffsetUnion),
32                ('i', ImmUnion)]
33
34class BPFInstr(ct.Union):
35    _pack_ = 1
36    _anonymous_ = ('s')
37    _fields_ = [('s', BPFInstrFields), ('instr', ct.c_uint64)]
38
39class BPFDecoder():
40    BPF_PSEUDO_CALL = 1
41    bpf_helpers = ['unspec',
42                   'map_lookup_elem',
43                   'map_update_elem',
44                   'map_delete_elem',
45                   'probe_read',
46                   'ktime_get_ns',
47                   'trace_printk',
48                   'get_prandom_u32',
49                   'get_smp_processor_id',
50                   'skb_store_bytes',
51                   'l3_csum_replace',
52                   'l4_csum_replace',
53                   'tail_call',
54                   'clone_redirect',
55                   'get_current_pid_tgid',
56                   'get_current_uid_gid',
57                   'get_current_comm',
58                   'get_cgroup_classid',
59                   'skb_vlan_push',
60                   'skb_vlan_pop',
61                   'skb_get_tunnel_key',
62                   'skb_set_tunnel_key',
63                   'perf_event_read',
64                   'redirect',
65                   'get_route_realm',
66                   'perf_event_output',
67                   'skb_load_bytes',
68                   'get_stackid',
69                   'csum_diff',
70                   'skb_get_tunnel_opt',
71                   'skb_set_tunnel_opt',
72                   'skb_change_proto',
73                   'skb_change_type',
74                   'skb_under_cgroup',
75                   'get_hash_recalc',
76                   'get_current_task',
77                   'probe_write_user',
78                   'current_task_under_cgroup',
79                   'skb_change_tail',
80                   'skb_pull_data',
81                   'csum_update',
82                   'set_hash_invalid',
83                   'get_numa_node_id',
84                   'skb_change_head',
85                   'xdp_adjust_head',
86                   'probe_read_str',
87                   'get_socket_cookie',
88                   'get_socket_uid',
89                   'set_hash',
90                   'setsockopt',
91                   'skb_adjust_room',
92                   'redirect_map',
93                   'sk_redirect_map',
94                   'sock_map_update',
95                   'xdp_adjust_meta',
96                   'perf_event_read_value',
97                   'perf_prog_read_value',
98                   'getsockopt',
99                   'override_return',
100                   'sock_ops_cb_flags_set',
101                   'msg_redirect_map',
102                   'msg_apply_bytes',
103                   'msg_cork_bytes',
104                   'msg_pull_data',
105                   'bind',
106                   'xdp_adjust_tail',
107                   'skb_get_xfrm_state',
108                   'get_stack',
109                   'skb_load_bytes_relative',
110                   'fib_lookup',
111                   'sock_hash_update',
112                   'msg_redirect_hash',
113                   'sk_redirect_hash',
114                   'lwt_push_encap',
115                   'lwt_seg6_store_bytes',
116                   'lwt_seg6_adjust_srh',
117                   'lwt_seg6_action',
118                   'rc_repeat',
119                   'rc_keydown',
120                   'skb_cgroup_id',
121                   'get_current_cgroup_id',
122                   'get_local_storage',
123                   'sk_select_reuseport',
124                   'skb_ancestor_cgroup_id',
125                   'sk_lookup_tcp',
126                   'sk_lookup_udp',
127                   'sk_release',
128                   'map_push_elem',
129                   'map_pop_elem',
130                   'map_peek_elem',
131                   'msg_push_data',
132                   'msg_pop_data',
133                   'rc_pointer_rel']
134
135    opcodes = {0x04: ('add32',    'dstimm',     '+=',     32),
136               0x05: ('ja',       'joff',       None,     64),
137               0x07: ('add',      'dstimm',     '+=',     64),
138               0x0c: ('add32',    'dstsrc',     '+=',     32),
139               0x0f: ('add',      'dstsrc',     '+=',     64),
140               0x14: ('sub32',    'dstimm',     '-=',     32),
141               0x15: ('jeq',      'jdstimmoff', '==',     64),
142               0x17: ('sub',      'dstimm',     '-=',     64),
143               0x18: ('lddw',     'lddw',       None,     64),
144               0x1c: ('sub32',    'dstsrc',     '-=',     32),
145               0x1d: ('jeq',      'jdstsrcoff', '==',     64),
146               0x1f: ('sub',      'dstsrc',     '-=',     64),
147               0x20: ('ldabsw',   'ldabs',      None,     32),
148               0x24: ('mul32',    'dstimm',     '*=',     32),
149               0x25: ('jgt',      'jdstimmoff', '>',      64),
150               0x27: ('mul',      'dstimm',     '*=',     64),
151               0x28: ('ldabsh',   'ldabs',      None,     16),
152               0x2c: ('mul32',    'dstsrc',     '*=',     32),
153               0x2d: ('jgt',      'jdstsrcoff', '>',      64),
154               0x2f: ('mul',      'dstsrc',     '*=',     64),
155               0x30: ('ldabsb',   'ldabs',      None,      8),
156               0x34: ('div32',    'dstimm',     '/=',     32),
157               0x35: ('jge',      'jdstimmoff', '>=',     64),
158               0x37: ('div',      'dstimm',     '/=',     64),
159               0x38: ('ldabsdw',  'ldabs',      None,     64),
160               0x3c: ('div32',    'dstsrc',     '/=',     32),
161               0x3d: ('jge',      'jdstsrcoff', '>=',     64),
162               0x3f: ('div',      'dstsrc',     '/=',     64),
163               0x40: ('ldindw',   'ldind',      None,     32),
164               0x44: ('or32',     'dstimm_bw',  '|=',     32),
165               0x45: ('jset',     'jdstimmoff', '&',      64),
166               0x47: ('or',       'dstimm_bw',  '|=',     64),
167               0x48: ('ldindh',   'ldind',      None,     16),
168               0x4c: ('or32',     'dstsrc',     '|=',     32),
169               0x4d: ('jset',     'jdstsrcoff', '&',      64),
170               0x4f: ('or',       'dstsrc',     '|=',     64),
171               0x50: ('ldindb',   'ldind',      None,      8),
172               0x54: ('and32',    'dstimm_bw',  '&=',     32),
173               0x55: ('jne',      'jdstimmoff', '!=',     64),
174               0x57: ('and',      'dstimm_bw',  '&=',     64),
175               0x58: ('ldinddw',  'ldind',      None,     64),
176               0x5c: ('and32',    'dstsrc',     '&=',     32),
177               0x5d: ('jne',      'jdstsrcoff', '!=',     64),
178               0x5f: ('and',      'dstsrc',     '&=',     64),
179               0x61: ('ldxw',     'ldstsrcoff', None,     32),
180               0x62: ('stw',      'sdstoffimm', None,     32),
181               0x63: ('stxw',     'sdstoffsrc', None,     32),
182               0x64: ('lsh32',    'dstimm',     '<<=',    32),
183               0x65: ('jsgt',     'jdstimmoff', 's>',     64),
184               0x67: ('lsh',      'dstimm',     '<<=',    64),
185               0x69: ('ldxh',     'ldstsrcoff', None,     16),
186               0x6a: ('sth',      'sdstoffimm', None,     16),
187               0x6b: ('stxh',     'sdstoffsrc', None,     16),
188               0x6c: ('lsh32',    'dstsrc',     '<<=',    32),
189               0x6d: ('jsgt',     'jdstsrcoff', 's>',     64),
190               0x6f: ('lsh',      'dstsrc',     '<<=',    64),
191               0x71: ('ldxb',     'ldstsrcoff', None,      8),
192               0x72: ('stb',      'sdstoffimm', None,      8),
193               0x73: ('stxb',     'sdstoffsrc', None,      8),
194               0x74: ('rsh32',    'dstimm',     '>>=',    32),
195               0x75: ('jsge',     'jdstimmoff', 's>=',    64),
196               0x77: ('rsh',      'dstimm',     '>>=',    64),
197               0x79: ('ldxdw',    'ldstsrcoff', None,     64),
198               0x7a: ('stdw',     'sdstoffimm', None,     64),
199               0x7b: ('stxdw',    'sdstoffsrc', None,     64),
200               0x7c: ('rsh32',    'dstsrc',     '>>=',    32),
201               0x7d: ('jsge',     'jdstsrcoff', 's>=',    64),
202               0x7f: ('rsh',      'dstsrc',     '>>=',    64),
203               0x84: ('neg32',    'dst',        '~',      32),
204               0x85: ('call',     'call',       None,     64),
205               0x87: ('neg',      'dst',        '~',      64),
206               0x94: ('mod32',    'dstimm',     '%=',     32),
207               0x95: ('exit',     'exit',       None,     64),
208               0x97: ('mod',      'dstimm',     '%=',     64),
209               0x9c: ('mod32',    'dstsrc',     '%=',     32),
210               0x9f: ('mod',      'dstsrc',     '%=',     64),
211               0xa4: ('xor32',    'dstimm_bw',  '^=',     32),
212               0xa5: ('jlt',      'jdstimmoff', '<',      64),
213               0xa7: ('xor',      'dstimm_bw',  '^=',     64),
214               0xac: ('xor32',    'dstsrc',     '^=',     32),
215               0xad: ('jlt',      'jdstsrcoff', '<',      64),
216               0xaf: ('xor',      'dstsrc',     '^=',     64),
217               0xb4: ('mov32',    'dstimm',     '=',      32),
218               0xb5: ('jle',      'jdstimmoff', '<=',     64),
219               0xb7: ('mov',      'dstimm',     '=',      64),
220               0xbc: ('mov32',    'dstsrc',     '=',      32),
221               0xbd: ('jle',      'jdstsrcoff', '<=',     64),
222               0xbf: ('mov',      'dstsrc',     '=',      64),
223               0xc4: ('arsh32',   'dstimm',     's>>=',   32),
224               0xc5: ('jslt',     'jdstimmoff', 's<',     64),
225               0xc7: ('arsh',     'dstimm',     's>>=',   64),
226               0xcc: ('arsh32',   'dstsrc',     's>>=',   32),
227               0xcd: ('jslt',     'jdstsrcoff', 's<',     64),
228               0xcf: ('arsh',     'dstsrc',     's>>=',   64),
229               0xd5: ('jsle',     'jdstimmoff', 's<=',    64),
230               0xdc: ('endian32', 'dstsrc',     'endian', 32),
231               0xdd: ('jsle',     'jdstimmoff', 's<=',    64),}
232
233    @classmethod
234    def decode(cls, i, w, w1):
235        try:
236            name, opclass, op, bits = cls.opcodes[w.opcode]
237            if opclass == 'dstimm':
238                return 'r%d %s %d' % (w.dst, op, w.imm), 0
239
240            elif opclass == 'dstimm_bw':
241                return 'r%d %s 0x%x' % (w.dst, op, w.immu), 0
242
243            elif opclass == 'joff':
244                return 'goto %s <%d>' % ('%+d' % (w.offset),
245                                         i + w.offset + 1), 0
246
247            elif opclass == 'dstsrc':
248                return 'r%d %s r%d' % (w.dst, op, w.src), 0
249
250            elif opclass == 'jdstimmoff':
251                return 'if r%d %s %d goto pc%s <%d>' % (w.dst, op, w.imm,
252                                                      '%+d' % (w.offset),
253                                                      i + w.offset + 1), 0
254
255            elif opclass == 'jdstsrcoff':
256                return 'if r%d %s r%d goto pc%s <%d>' % (w.dst, op, w.src,
257                                                       '%+d' % (w.offset),
258                                                       i + w.offset + 1), 0
259
260            elif opclass == 'lddw':
261                # imm contains the file descriptor (FD) of the map being loaded;
262                # the kernel will translate this into the proper address
263                if w1 is None:
264                    raise Exception("lddw requires two instructions to be disassembled")
265                if w1.imm == 0:
266                    return 'r%d = <map at fd #%d>' % (w.dst, w.imm), 1
267                imm = (w1.imm << 32) | w.imm
268                return 'r%d = 0x%x' % (w.dst, imm), 1
269
270            elif opclass == 'ldabs':
271                return 'r0 = *(u%s*)skb[%s]' % (bits, w.imm), 0
272
273            elif opclass == 'ldind':
274                return 'r0 = *(u%d*)skb[r%d %s]' % (bits, w.src,
275                                                    '%+d' % (w.imm)), 0
276
277            elif opclass == 'ldstsrcoff':
278                return 'r%d = *(u%d*)(r%d %s)' % (w.dst, bits, w.src,
279                                                  '%+d' % (w.offset)), 0
280
281            elif opclass == 'sdstoffimm':
282                return '*(u%d*)(r%d %s) = %d' % (bits, w.dst,
283                                                 '%+d' % (w.offset), w.imm), 0
284
285            elif opclass == 'sdstoffsrc':
286                return '*(u%d*)(r%d %s) = r%d' % (bits, w.dst,
287                                                  '%+d' % (w.offset), w.src), 0
288
289            elif opclass == 'dst':
290                return 'r%d = %s (u%s)r%d' % (w.dst, op, bits, w.dst), 0
291
292            elif opclass == 'call':
293                if w.src != cls.BPF_PSEUDO_CALL:
294                    try:
295                        return '%s bpf_%s#%d' % (name, cls.bpf_helpers[w.immu], w.immu), 0
296                    except IndexError:
297                        return '%s <unknown helper #%d>' % (op, w.immu), 0
298                return '%s %s' % (name, '%+d' % (w.imm)), 0
299            elif opclass == 'exit':
300                return name, 0
301            else:
302                raise Exception('unknown opcode class')
303
304        except KeyError:
305            return 'unknown <0x%x>' % (w.opcode)
306
307def disassemble_instruction(i, w0, w1=None):
308    instr, skip = BPFDecoder.decode(i, w0, w1)
309    return "%4d: (%02x) %s" % (i, w0.opcode, instr), skip
310
311def disassemble_str(bpfstr):
312    ptr = ct.cast(ct.c_char_p(bpfstr), ct.POINTER(BPFInstr))
313    numinstr = int(len(bpfstr) / 8)
314    w0 = ptr[0]
315    skip = 0
316    instr_list = []
317    for i in range(1, numinstr):
318        w1 = ptr[i]
319        if skip:
320            skip -= 1
321            instr_str = "%4d:      (64-bit upper word)" % (i)
322        else:
323            instr_str, skip = disassemble_instruction(i - 1, w0, w1)
324        instr_list.append(instr_str)
325        w0 = w1
326    instr_str, skip = disassemble_instruction(numinstr - 1, w0, None)
327    instr_list.append(instr_str)
328    return instr_list
329
330def disassemble_prog(func_name, bpfstr):
331    instr_list = ["Disassemble of BPF program %s:" % (func_name)]
332    instr_list += disassemble_str(bpfstr)
333    return linesep.join(instr_list)
334
335class MapDecoder ():
336    ctype2str = {ct.c_bool: u"_Bool",
337                 ct.c_char: u"char",
338                 ct.c_wchar: u"wchar_t",
339                 ct.c_ubyte: u"unsigned char",
340                 ct.c_short: u"short",
341                 ct.c_ushort: u"unsigned short",
342                 ct.c_int: u"int",
343                 ct.c_uint: u"unsigned int",
344                 ct.c_long: u"long",
345                 ct.c_ulong: u"unsigned long",
346                 ct.c_longlong: u"long long",
347                 ct.c_ulonglong: u"unsigned long long",
348                 ct.c_float: u"float",
349                 ct.c_double: u"double",
350                 ct.c_longdouble: u"long double",
351                 ct.c_int64 * 2: u"__int128",
352                 ct.c_uint64 * 2: u"unsigned __int128",}
353
354    @classmethod
355    def get_ct_name(cls, t):
356        try:
357            if issubclass(t, ct.Structure):
358                field_type_name = "struct"
359            elif issubclass(t, ct.Union):
360                field_type_name = "union"
361            elif issubclass(t, ct.Array):
362                field_type_name = cls.ctype2str[t._type_] + "[" + str(t._length_) + "]"
363            else:
364                field_type_name = cls.ctype2str[t]
365        except KeyError:
366            field_type_name = str(t)
367        return field_type_name
368
369    @classmethod
370    def format_size_info(cls, offset, size, enabled=False, bitoffset=None):
371        if not enabled:
372            return ""
373        if bitoffset is not None:
374            return "[%d,%d +%d bit]" % (offset, bitoffset, size)
375        return "[%d +%d] " % (offset, size)
376
377    @classmethod
378    def print_ct_map(cls, t, indent="", offset=0, sizeinfo=False):
379        map_lines = []
380        try:
381            for field_name, field_type in t._fields_:
382                is_structured = (issubclass(field_type, ct.Structure) or
383                                 issubclass(field_type, ct.Union))
384                field_type_name = cls.get_ct_name(field_type)
385                field_offset = getattr(t, field_name).offset
386                field_size = ct.sizeof(field_type)
387                sizedesc = cls.format_size_info(offset + field_offset,
388                                                field_size, sizeinfo)
389                if is_structured:
390                    map_lines.append("%s%s%s {" % (indent, sizedesc, field_type_name))
391                    map_lines += cls.print_ct_map(field_type,
392                                                  indent + "  ",
393                                                  offset + field_offset)
394                    map_lines.append("%s} %s;" % (indent, field_name))
395                else:
396                    map_lines.append("%s%s%s %s;" % (indent, sizedesc,
397                                                     field_type_name,
398                                                     field_name))
399        except ValueError:
400            # is a bit field
401            offset_bits = 0
402            for field in t._fields_:
403                if len(field) == 3:
404                    field_name, field_type, field_bits = field
405                    field_type_name = cls.get_ct_name(field_type)
406                    sizedesc = cls.format_size_info(offset, offset_bits,
407                                                    sizeinfo, field_bits)
408                    map_lines.append("%s%s%s %s:%d;" % (indent, sizedesc,
409                                                        field_type_name,
410                                                        field_name,
411                                                        field_bits))
412                else:
413                    # end of previous bit field
414                    field_name, field_type = field
415                    field_type_name = cls.get_ct_name(field_type)
416                    field_offset = getattr(t, field_name).offset
417                    field_size = ct.sizeof(field_type)
418                    field_bits = 0
419                    offset_bits = 0
420                    sizedesc = cls.format_size_info(offset + field_offset,
421                                                    field_size, sizeinfo)
422                    map_lines.append("%s%s%s %s;" % (indent, sizedesc,
423                                                     field_type_name,
424                                                     field_name))
425                    offset += field_offset
426                offset_bits += field_bits
427        return map_lines
428
429    @classmethod
430    def print_map_ctype(cls, t, field_name, sizeinfo):
431        is_structured = (issubclass(t, ct.Structure) or
432                         issubclass(t, ct.Union))
433        type_name = cls.get_ct_name(t)
434        if is_structured:
435            map_lines = ["  %s {" % (type_name)]
436            map_lines += cls.print_ct_map(t, "    ", sizeinfo=sizeinfo)
437            map_lines.append("  } %s;" % (field_name))
438        else:
439            map_lines = ["  %s %s;" % (type_name, field_name)]
440        return map_lines
441
442    @classmethod
443    def decode_map(cls, map_name, map_obj, map_type, sizeinfo=False):
444        map_lines = ['Layout of BPF map %s (type %s, FD %d, ID %d):' % (map_name,
445                                                                        map_type,
446                                                                        map_obj.map_fd,
447                                                                        map_obj.map_id)]
448        map_lines += cls.print_map_ctype(map_obj.Key, 'key', sizeinfo=sizeinfo)
449        map_lines += cls.print_map_ctype(map_obj.Leaf, 'value', sizeinfo=sizeinfo)
450        return linesep.join(map_lines)
451
452def decode_map(map_name, map_obj, map_type, sizeinfo=False):
453    map_type_name = get_table_type_name(map_type)
454    return MapDecoder.decode_map(map_name, map_obj, map_type_name, sizeinfo=sizeinfo)
455