bcc/python: remove unused imports, remove redundant semicolon
[platform/upstream/bcc.git] / src / python / bcc / disassembler.py
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
15 from os import linesep
16 import ctypes as ct
17 from .table import get_table_type_name
18
19 class OffsetUnion(ct.Union):
20     _fields_ = [('offsetu', ct.c_uint16), ('offset', ct.c_int16)]
21
22 class ImmUnion(ct.Union):
23     _fields_ = [('immu', ct.c_uint32), ('imm', ct.c_int32)]
24
25 class 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
34 class BPFInstr(ct.Union):
35     _pack_ = 1
36     _anonymous_ = ('s')
37     _fields_ = [('s', BPFInstrFields), ('instr', ct.c_uint64)]
38
39 class 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
307 def 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
311 def 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
330 def 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
335 class 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
452 def 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)