#!/usr/bin/env python # DOF disassembler / Dumper # See bsd/sys/dtrace.h for details on the DOF format import lldb import shlex # DTrace Object Format (DOF) (from bsd/sys/dtrace.h) DOF_ID_SIZE = 16 DOF_ID_MODEL = 4 DOF_ID_ENCODING = 5 DOF_ID_VERSION = 6 DOF_ID_DIFVERS = 7 DOF_ID_DIFIREG = 8 DOF_ID_DIFTREG = 9 dof_magic = [0x7f, ord('D'), ord('O'), ord('F')] DOF_SECF_LOAD = 1 section_names = ["NONE", "COMMENTS", "SOURCE", "ECBDESC", "PROBEDESC", "ACTDESC", "DIFOHDR", "DIF", "STRTAB", "VARTAB", "RELTAB", "TYPTAB", "URELHDR", "KRELHDR", "OPTDESC", "PROVIDER", "PROBES", "PRARGS", "PROFFS", "INTTAB", "UTSNAME", "XLTAB", "XLMEMBERS", "XLIMPORT", "XLEXPORT", "PREXPORT", "PRENOFFS"] # DTrace DIF instructions (from bsd/sys/dtrace.h) def DIF_INSTR_OP(i): return (((i) >> 24) & 0xff) def DIF_INSTR_R1(i): return (((i) >> 16) & 0xff) def DIF_INSTR_R2(i): return (((i) >> 8) & 0xff) def DIF_INSTR_RD(i): return ((i) & 0xff) def DIF_INSTR_RS(i): return ((i) & 0xff) def DIF_INSTR_LABEL(i): return ((i) & 0xffffff) def DIF_INSTR_VAR(i): return (((i) >> 8) & 0xffff) def DIF_INSTR_INTEGER(i): return (((i) >> 8) & 0xffff) def DIF_INSTR_STRING(i): return (((i) >> 8) & 0xffff) def DIF_INSTR_SUBR(i): return (((i) >> 8) & 0xffff) def DIF_INSTR_TYPE(i): return (((i) >> 16) & 0xff) def DIF_INSTR_XLREF(i): return (((i) >> 8) & 0xffff) # DTrace DIF variables DIFV_SCOPE_GLOBAL = 0 DIFV_SCOPE_THREAD = 1 DIFV_SCOPE_LOCAL = 2 # DTrace subroutines (from bsd/sys/dtrace.h) subroutine_strings = { 0: "rand", 1: "mutex_owned", 2: "mutex_owner", 3: "mutex_type_adaptive", 4: "mutex_type_spin", 5: "rw_read_held", 6: "rw_write_held", 7: "rw_iswriter", 8: "copyin", 9: "copyinstr", 10: "speculation", 11: "progenyof", 12: "strlen", 13: "copyout", 14: "copyoutstr", 15: "alloca", 16: "bcopy", 17: "copyinto", 18: "msgdsize", 19: "msgsize", 20: "getmajor", 21: "getminor", 22: "ddi_pathname", 23: "strjoin", 24: "lltostr", 25: "basename", 26: "dirname", 27: "cleanpath", 28: "strchr", 29: "strrchr", 30: "strstr", 31: "strtok", 32: "substr", 33: "index", 34: "rindex", 35: "htons", 36: "htonl", 37: "htonll", 38: "ntohs", 39: "ntohl", 40: "ntohll", 41: "inet_ntop", 42: "inet_nota", 43: "inet_nota6", 44: "toupper", 45: "tolower", # Apple specific subroutines 200: "vm_kernel_addrperm", 201: "kdebug_trace", 202: "kdebug_trace_string" } # DTrace actions (from bsd/sys/dtrace.h) DTRACEACT_NONE = 0 DTRACEACT_DIFEXPR = 1 DTRACEACT_EXIT = 2 DTRACEACT_PRINTF = 3 DTRACEACT_PRINTA = 4 DTRACEACT_LIBACT = 5 DTRACEACT_TRACEMEM = 6 DTRACEACT_TRACEMEM_DYNSIZE = 7 DTRACEACT_PROC = 0x0100 DTRACEACT_USTACK = DTRACEACT_PROC + 1 DTRACEACT_JSTACK = DTRACEACT_PROC + 2 DTRACEACT_USYM = DTRACEACT_PROC + 3 DTRACEACT_UMOD = DTRACEACT_PROC + 3 DTRACEACT_UADDR = DTRACEACT_PROC + 3 DTRACEACT_PROC_DESTRUCTIVE = 0x0200 DTRACEACT_STOP = DTRACEACT_PROC_DESTRUCTIVE + 1 DTRACEACT_RAISE = DTRACEACT_PROC_DESTRUCTIVE + 2 DTRACEACT_SYSTEM = DTRACEACT_PROC_DESTRUCTIVE + 3 DTRACEACT_FREOPEN = DTRACEACT_PROC_DESTRUCTIVE + 4 DTRACEACT_KERNEL = 0x0400 DTRACEACT_STACK = DTRACEACT_KERNEL + 1 DTRACEACT_SYM = DTRACEACT_KERNEL + 2 DTRACEACT_MOD = DTRACEACT_KERNEL + 3 DTRACEACT_KERNEL_DESTRUCTIVE = 0x0500 DTRACEACT_BREAKPOINT = DTRACEACT_KERNEL_DESTRUCTIVE + 1 DTRACEACT_PANIC = DTRACEACT_KERNEL_DESTRUCTIVE + 2 DTRACEACT_CHILL = DTRACEACT_KERNEL_DESTRUCTIVE + 3 DTRACEACT_SPECULATIVE = 0x0600 DTRACEACT_SPECULATE = DTRACEACT_SPECULATIVE + 1 DTRACEACT_COMMIT = DTRACEACT_SPECULATIVE + 2 DTRACEACT_DISCARD = DTRACEACT_SPECULATIVE + 3 DTRACEACT_AGGREGATION = 0x0700 DTRACEAGG_COUNT = DTRACEACT_AGGREGATION + 1 DTRACEAGG_MIN = DTRACEACT_AGGREGATION + 2 DTRACEAGG_MAX = DTRACEACT_AGGREGATION + 3 DTRACEAGG_AVG = DTRACEACT_AGGREGATION + 4 DTRACEAGG_SUM = DTRACEACT_AGGREGATION + 5 DTRACEAGG_STDDEV = DTRACEACT_AGGREGATION + 6 DTRACEAGG_QUANTIZE = DTRACEACT_AGGREGATION + 7 DTRACEAGG_LQUANTIZE = DTRACEACT_AGGREGATION + 8 DTRACEAGG_LLQUANTIZE = DTRACEACT_AGGREGATION + 9 err = lldb.SBError() action_strings = { DTRACEACT_NONE: 'NONE', DTRACEACT_DIFEXPR: 'DIFEXPR', DTRACEACT_EXIT: 'EXIT', DTRACEACT_PRINTF: 'PRINTF', DTRACEACT_PRINTA: 'PRINTA', DTRACEACT_LIBACT: 'LIBACT', DTRACEACT_TRACEMEM: 'TRACEMEM', DTRACEACT_TRACEMEM_DYNSIZE: 'TRACEMEM_DYNSIZE', DTRACEACT_USTACK: 'USTACK', DTRACEACT_JSTACK: 'JSTACK', DTRACEACT_USYM: 'USYM', DTRACEACT_UMOD: 'UMOD', DTRACEACT_UADDR: 'UADDR', DTRACEACT_STOP: 'STOP', DTRACEACT_RAISE: 'RAISE', DTRACEACT_SYSTEM: 'SYSTEM', DTRACEACT_FREOPEN: 'FREOPEN', DTRACEACT_STACK: 'STACK', DTRACEACT_SYM: 'SYM', DTRACEACT_MOD: 'MOD', DTRACEACT_BREAKPOINT: 'BREAKPOINT', DTRACEACT_PANIC: 'PANIC', DTRACEACT_CHILL: 'CHILL', DTRACEACT_COMMIT: 'COMMIT', DTRACEACT_DISCARD: 'DISCARD', DTRACEAGG_COUNT: 'COUNT', DTRACEAGG_MIN: 'MIN', DTRACEAGG_MAX: 'MAX', DTRACEAGG_AVG: 'AVG', DTRACEAGG_SUM: 'SUM', DTRACEAGG_STDDEV: 'STDDEV', DTRACEAGG_QUANTIZE: 'QUANTIZE', DTRACEAGG_LQUANTIZE: 'LQUANTIZE', DTRACEAGG_LLQUANTIZE: 'LLQUANTIZE' } def ActionString(kind): if kind in action_strings: return action_strings[kind] return '??? ({})'.format(kind) def DOFModelString(model): if model == 0: return 'NONE' elif model == 1: return 'ILP32' elif model == 2: return 'LP64' def DOFEncodingString(encoding): if encoding == 0: return 'NONE' elif encoding == 1: return 'LSB' elif encoding == 2: return 'MSB' def DOFFlagsString(flag): if flag != DOF_SECF_LOAD: return '(Non-Loadable)' return '' def DOFSecIdxString(val): if val == -1: return 'NONE' return val def GetChildAtIndexAsUnsigned(source, i): return source.GetChildAtIndex(i).GetValueAsUnsigned() def GetMemberAsSigned(source, field): return source.GetChildMemberWithName(field, True).GetValueAsSigned() def GetMemberAsUnsigned(source, field): return source.GetChildMemberWithName(field, True).GetValueAsUnsigned() class DIFODisassembler: """ Disassembler for DIFO given a DIF section, a strtab section, a inttab section and a vartab section """ def __init__(self, dif, strtab, inttab, vartab): self.dif = dif self.strtab = strtab self.inttab = inttab self.vartab = vartab self.ops = [ ["(illegal opcode)", self.DisStr], ["or", self.DisLog], # DIF_OP_OR ["xor", self.DisLog], # DIF_OP_XOR ["and", self.DisLog], # DIF_OP_AND ["sll", self.DisLog], # DIF_OP_SLL ["srl", self.DisLog], # DIF_OP_SRL ["sub", self.DisLog], # DIF_OP_SUB ["add", self.DisLog], # DIF_OP_ADD ["mul", self.DisLog], # DIF_OP_MUL ["sdiv", self.DisLog], # DIF_OP_SDIV ["udiv", self.DisLog], # DIF_OP_UDIV ["srem", self.DisLog], # DIF_OP_SREM ["urem", self.DisLog], # DIF_OP_UREM ["not", self.DisR1Rd], # DIF_OP_NOT ["mov", self.DisR1Rd], # DIF_OP_MOV ["cmp", self.DisCmp], # DIF_OP_CMP ["tst", self.DisTst], # DIF_OP_TST ["ba", self.DisBranch], # DIF_OP_BA ["be", self.DisBranch], # DIF_OP_BE ["bne", self.DisBranch], # DIF_OP_BNE ["bg", self.DisBranch], # DIF_OP_BG ["bgu", self.DisBranch], # DIF_OP_BGU ["bge", self.DisBranch], # DIF_OP_BGE ["bgeu", self.DisBranch], # DIF_OP_BGEU ["bl", self.DisBranch], # DIF_OP_BL ["blu", self.DisBranch], # DIF_OP_BLU ["ble", self.DisBranch], # DIF_OP_BLE ["bleu", self.DisBranch], # DIF_OP_BLEU ["ldsb", self.DisLoad], # DIF_OP_LDSB ["ldsh", self.DisLoad], # DIF_OP_LDSH ["ldsw", self.DisLoad], # DIF_OP_LDSW ["ldub", self.DisLoad], # DIF_OP_LDUB ["lduh", self.DisLoad], # DIF_OP_LDUH ["lduw", self.DisLoad], # DIF_OP_LDUW ["ldx", self.DisLoad], # DIF_OP_LDX ["ret", self.DisRet], # DIF_OP_RET ["nop", self.DisStr], # DIF_OP_NOP ["setx", self.DisSetX], # DIF_OP_SETX ["sets", self.DisSetS], # DIF_OP_SETS ["scmp", self.DisCmp], # DIF_OP_SCMP ["ldga", self.DisLda], # DIF_OP_LDGA ["ldgs", self.DisLdv], # DIF_OP_LDGS ["stgs", self.DisStv], # DIF_OP_STGS ["ldta", self.DisLda], # DIF_OP_LDTA ["ldts", self.DisLdv], # DIF_OP_LDTS ["stts", self.DisStv], # DIF_OP_STTS ["sra", self.DisLog], # DIF_OP_SRA ["call", self.DisCall], # DIF_OP_CALL ["pushtr", self.DisPushTs], # DIF_OP_PUSHTR ["pushtv", self.DisPushTs], # DIF_OP_PUSHTS ["popts", self.DisStr], # DIF_OP_POPTS ["flushts", self.DisStr], # DIF_OP_FLUSHTS ["ldgaa", self.DisLdv], # DIF_OP_LDGAA ["ldtaa", self.DisLdv], # DIF_OP_LDTAA ["stgaa", self.DisStv], # DIF_OP_STDGAA ["sttaa", self.DisStv], # DIF_OP_STTAA ["ldls", self.DisLdv], # DIF_OP_LDLS ["stls", self.DisStv], # DIF_OP_STLS ["allocs", self.DisR1Rd], # DIF_OP_ALLOCS ["copys", self.DisLog], # DIF_OP_COPYS ["stb", self.DisStore], # DIF_OP_STB ["sth", self.DisStore], # DIF_OP_STH ["stw", self.DisStore], # DIF_OP_STW ["stx", self.DisStore], # DIF_OP_STX ["uldsb", self.DisLoad], # DIF_OP_ULDSB ["uldsh", self.DisLoad], # DIF_OP_ULDSH ["uldsw", self.DisLoad], # DIF_OP_ULDSW ["uldub", self.DisLoad], # DIF_OP_ULDUB ["ulduh", self.DisLoad], # DIF_OP_ULDUH ["ulduw", self.DisLoad], # DIF_OP_ULDUW ["uldx", self.DisLoad], # DIF_OP_ULDX ["rldsb", self.DisLoad], # DIF_OP_RLDSB ["rldsh", self.DisLoad], # DIF_OP_RLDSH ["rldsw", self.DisLoad], # DIF_OP_RLDSW ["rldub", self.DisLoad], # DIF_OP_RLDUB ["rlduh", self.DisLoad], # DIF_OP_RLDUH ["rlduw", self.DisLoad], # DIF_OP_RLDUW ["rldx", self.DisLoad], # DIF_OP_RLDX ["xlate", self.DisXlate], # DIF_OP_XLATE ["xlarg", self.DisXlate], # DIF_OP_XLARG ] def Disassemble(self): dif = self.dif print '\t%-3s %-8s %s' % ('OFF', 'OPCODE', 'INSTRUCTION') for i in range(dif.dif_len): instr = dif.GetInstr(i) op = DIF_INSTR_OP(instr) print '\t%02lu: %08x ' % (i, instr), if op < len(self.ops): self.ops[op][1](self.ops[op][0], instr) else: print '' def DisStr(self, name, ins): print name def DisLog(self, name, ins): print "%-4s %%r%u, %%r%u, %%r%u" % (name, DIF_INSTR_R1(ins), DIF_INSTR_R2(ins), DIF_INSTR_RD(ins)) def DisR1Rd(self, name, ins): print "%-4s %%r%u, %%r%u" % (name, DIF_INSTR_R1(ins), DIF_INSTR_RD(ins)) def DisCmp(self, name, ins): print "%-4s %%r%u, %%r%u" % (name, DIF_INSTR_R1(ins), DIF_INSTR_R2(ins)) def DisTst(self, name, ins): print "%-4s %%r%u" % (name, DIF_INSTR_R1(ins)) def DisBranch(self, name, ins): print "%-4s %u" % (name, DIF_INSTR_LABEL(ins)) def DisLoad(self, name, ins): print "%-4s [%%r%u], %%r%u" % (name, DIF_INSTR_R1(ins), DIF_INSTR_RD(ins)) def DisRet(self, name, ins): print "%-4s %%r%u" % (name, DIF_INSTR_RD(ins)) def DisSetX(self, name, ins): intptr = DIF_INSTR_INTEGER(ins) print "%-4s DT_INTEGER[%u], %%r%u" % (name, intptr, DIF_INSTR_RD(ins)), if intptr < self.inttab.int_len: print "\t\t! 0x%dx" % (self.inttab.GetInt(intptr)) else: print '' def DisSetS(self, name, ins): strptr = DIF_INSTR_STRING(ins) print "%-4s DT_STRING[%u], %%r%u" % (name, strptr, DIF_INSTR_RD(ins)), if strptr < self.strtab.data_size: print "\t\t! \"%s\"" % self.strtab.GetString(strptr) else: print '' def DisLda(self, name, ins): var = DIF_INSTR_R1(ins) print '%-4s DT_VAR(%u), %%r%u, %%r%u' % (name, var, DIF_INSTR_R2(ins), DIF_INSTR_RD(ins)), vname = self.VarName(var, self.Scope(name)) if vname is not(None): print "\t\t! DT_VAR(%u) = \"%s\"" % (var, vname) else: print '' def DisLdv(self, name, ins): var = DIF_INSTR_VAR(ins) print '%-4s DT_VAR(%u), %%r%u' % (name, var, DIF_INSTR_RD(ins)), vname = self.VarName(var, self.Scope(name)) if vname is not(None): print "\t\t! DT_VAR(%u) = \"%s\"" % (var, vname) else: print '' def DisStv(self, name, ins): var = DIF_INSTR_VAR(ins) print '%-4s %%r%u, DT_VAR(%u)' % (name, DIF_INSTR_RS(ins), var), vname = self.VarName(var, self.Scope(name)) if vname is not(None): print "\t\t! DT_VAR(%u) = \"%s\"" % (var, vname) else: print '' def DisLog(self, name, ins): print "%-4s %%r%u, %%r%u, %%r%u" % (name, DIF_INSTR_R1(ins), DIF_INSTR_R2(ins), DIF_INSTR_RD(ins)) def DisCall(self, name, ins): subr = DIF_INSTR_SUBR(ins) subr_string = subroutine_strings[subr] if subr in subroutine_strings else '' print '%-4s DIF_SUBR(%u), %%r%u\t\t! %s' % (name, subr, DIF_INSTR_RD(ins), subr_string) def DisPushTs(self, name, ins): tnames = ["D type", "string"] itype = DIF_INSTR_TYPE(ins) print "%-4s DT_TYPE(%u), %%r%u, %%r%u" % (name, itype, DIF_INSTR_R2(ins), DIF_INSTR_RS(ins)), if itype <= 1: print "\t! DT_TYPE(%u) = %s" % (itype, tnames[itype]) else: print '' def DisStore(self, name, ins): print '%-4s %%r%u, [%%r%u]' % (DIF_INSTR_R1(ins), DIF_INSTR_RD(ins)) def DisLoad(self, name, ins): print '%-4s [%%r%u], %%r%u' % (name, DIF_INSTR_R1(ins), DIF_INSTR_RD(ins)) def DisXlate(self, name, ins): xlr = DIF_INSTR_XLREF(ins); print "%-4s DT_XLREF[%u], %%r%u" % (name, xlr, DIF_INSTR_RD(ins)) def VarName(self, vid, scope): for i in range(self.vartab.var_len): var = self.vartab.GetVar(i) tested_vid = GetMemberAsSigned(var, "dtdv_id") tested_scope = GetMemberAsSigned(var, "dtdv_scope") if vid == tested_vid and scope == tested_scope: name = GetMemberAsUnsigned(var, "dtdv_name") return self.strtab.GetString(name) return None def Scope(self, name): scope = name[2] if scope == 'l': return DIFV_SCOPE_LOCAL elif scope == 't': return DIFV_SCOPE_THREAD elif scope == 'g': return DIFV_SCOPE_GLOBAL else: return -1 class AddrData: """DOF data coming from a specific address""" def __init__(self, target, addr): self.target = target self.addr = addr def GetAddr(self): return self.addr def GetValue(self, name, offset, vtype): address = lldb.SBAddress(self.addr + offset, self.target) return self.target.CreateValueFromAddress(name, address, vtype) class SectionData: """ DOF data coming from a section. We can't use addresses directly here because lldb won't let us inspect DOF sections in libraries not loaded in a specific process otherwise """ def __init__(self, target, section): self.section = section self.target = target def GetAddr(self): return self.section.GetLoadAddress(self.target) def GetData(self, offset, size): return self.section.GetSectionData(offset, size) def GetValue(self, name, offset, vtype): size = vtype.GetByteSize() return self.target.CreateValueFromData(name, self.GetData(offset, size), vtype) class DOFHeader: def __init__(self, dof, target, data): self.dof = dof # Find the required DOF types dof_hdr_type = target.FindFirstType("dof_hdr_t") if not(dof_hdr_type.IsValid()): print 'could not find dof_hdr_t type' return header = data.GetValue("$dof_hdr", 0, dof_hdr_type) self.secoff = GetMemberAsUnsigned(header, "dofh_secoff") self.secsize = GetMemberAsUnsigned(header,"dofh_secsize") self.nsecs = GetMemberAsUnsigned(header, "dofh_secnum") dofh_ident = header.GetChildMemberWithName("dofh_ident", True) for i in range(3): mag = dofh_ident.GetChildAtIndex(i).GetValueAsUnsigned() if mag != dof_magic[i]: raise LookupError("Invalid magic number, is this a DOF ?") self.model = DOFModelString(GetChildAtIndexAsUnsigned(dofh_ident, DOF_ID_MODEL)) self.encoding = DOFEncodingString(GetChildAtIndexAsUnsigned(dofh_ident, DOF_ID_ENCODING)) self.format_version = GetChildAtIndexAsUnsigned(dofh_ident, DOF_ID_VERSION) self.difvers = GetChildAtIndexAsUnsigned(dofh_ident, DOF_ID_DIFVERS) self.difireg = GetChildAtIndexAsUnsigned(dofh_ident, DOF_ID_DIFIREG) self.diftreg = GetChildAtIndexAsUnsigned(dofh_ident, DOF_ID_DIFTREG) def Show(self): print 'DOF data model: {}'.format(self.model) print 'DOF encoding: {}'.format(self.encoding) print 'DOF format version: {}'.format(self.format_version) print 'DOF instruction set version: {}'.format(self.difvers) print 'DOF integer register count: {}'.format(self.difireg) print 'DOF tuple register count: {}'.format(self.diftreg) class DOFSection(object): def __init__(self, dof, target, data, i): self.hdr_offset = dof.header.secoff + dof.header.secsize * i sec = data.GetValue("$sec", self.hdr_offset, dof.dof_sec_type) self.i = i self.dof = dof self.stype = section_names[GetMemberAsUnsigned(sec, "dofs_type")] self.flags = DOFFlagsString(GetMemberAsUnsigned(sec, "dofs_flags")) self.ent_size = GetMemberAsUnsigned(sec, "dofs_entsize") self.offset = GetMemberAsUnsigned(sec, "dofs_offset") self.data_size = GetMemberAsUnsigned(sec, "dofs_size") def Show(self): print 'Section {} type {} data size {} ent size {} {} header {} data {}'.format( self.i, self.stype, self.data_size, self.ent_size, self.flags, hex(self.dof.addr + self.hdr_offset), hex(self.dof.addr + self.offset)) class DOFDIFSection(DOFSection): def __init__(self, dof, target, data, i): super(DOFDIFSection, self).__init__(dof, target, data, i) dif_instr_type = target.FindFirstType("dif_instr_t") if not(dif_instr_type): raise TypeError('Could not find dif_instr_t. Do you have a kernel binary loaded ?)') self.dif_len = self.data_size / dif_instr_type.GetByteSize() # Ridiculous hack here : We don't have SBType::GetArrayType because # our lldb is too old, so we declare a variable of type # pointer to dif_instr_t[number_of_elements] and we grab its Pointee type # (Note that we can't directly declare a variable of type # dif_instr_t[number_of_elements] # since lldb tends to truncate the array size of conveniance variables # when debugging remote kernels target.EvaluateExpression("dif_instr_t (*$pi{})[{}]".format(self.dif_len, self.dif_len)) dift_type = target.EvaluateExpression("$pi{}".format(self.dif_len)).GetType().GetPointeeType() self.dif = data.GetValue("$dif_buf", self.offset, dift_type) def GetInstr(self, i): return GetChildAtIndexAsUnsigned(self.dif, i) class DOFStrTabSection(DOFSection): def __init__(self, dof, target, data, i): super(DOFStrTabSection, self).__init__(dof, target, data, i) # See note in DOFDIFSection.__init__ target.EvaluateExpression("char (*$p{})[{}]".format(self.data_size, self.data_size)) char_type = target.EvaluateExpression("$p{}".format(self.data_size)).GetType().GetPointeeType() self.str_sec_data = data.GetValue("$strtab", self.offset, char_type) def GetString(self, index): return self.str_sec_data.GetData().GetString(err, index) class DOFVarTabSection(DOFSection): def __init__(self, dof, target, data, i): super(DOFVarTabSection, self).__init__(dof, target, data, i) dtrace_difv_type = target.FindFirstType("dtrace_difv_t") if not(dtrace_difv_type): raise TypeError('Could not find dtrace_difv_t. Do you have a kernel binary loaded ?)') self.var_len = self.data_size / dtrace_difv_type.GetByteSize() # See note in DOFDIFSection.__init__ target.EvaluateExpression("dtrace_difv_t (*$difva{})[{}]".format(self.var_len, self.var_len)) difva_type = target.EvaluateExpression("$difva{}".format(self.var_len)).GetType().GetPointeeType() self.vartab = data.GetValue("$vartab", self.offset, difva_type) def GetVar(self, i): return self.vartab.GetChildAtIndex(i) class DOFIntTabSection(DOFSection): def __init__(self, dof, target, data, i): super(DOFIntTabSection, self).__init__(dof, target, data, i) uint64_type = target.FindFirstType("uint64_t") if not(uint64_type): raise TypeError('Could not find uint64_t. Do you have a kernel binary loaded ?)') self.int_len = self.data_size / uint64_type.GetByteSize() # Ridiculous hack here : We don't have SBType::GetArrayType because # our lldb is too old, so we declare a variable of type # pointer to char[size_of_strtab] and we grab its Pointee type # (Note that we can't directly declare a variable of type char[size] # since lldb tends to truncate the array size of locals when debugging # remote kernels target.EvaluateExpression("uint64_t (*$intat)[{}]".format(self.int_len)) inta_type = target.EvaluateExpression("$intat").GetType().GetPointeeType() self.inttab = data.GetValue("$inttab", self.offset, inta_type) def GetInt(self, i): return GetChildAtIndexAsUnsigned(self.inttab, i) class DOFProbe(object): def __init__(self, func, name, nargv, xargv): self.func = func self.name = name self.nargv = nargv self.xargv = xargv def Show(self): if self.nargv != self.xargv: print '\t{}::{}({} | {})'.format(self.func, self.name, self.nargv, self.xargv) else: print '\t{}::{}({})'.format(self.func, self.name, self.nargv) class DOFProviderSection(DOFSection): """ Helper (USDT) provider section, containing the definition of a provider and a link to a probe section containing the definition of probes for this provider. (usually) only contained in DOFs in files """ def __init__(self, dof, target, data, i): super(DOFProviderSection, self).__init__(dof, target, data, i) # Find the dof_provider_t/dof_probe_t type dof_provider_type = target.FindFirstType("dof_provider_t") if not(dof_provider_type): raise TypeError('Could not find dof_provider_t. Do you have a kernel binary loaded ?)') dof_probe_type = target.FindFirstType("dof_probe_t") if not(dof_probe_type): raise TypeError('Could not find dof_probe_t. Do you have a kernel binary loaded ?)') # Retrieve the provider data from the section provider = data.GetValue("$provider", self.offset, dof_provider_type) self.strtab_secid = GetMemberAsSigned(provider, "dofpv_strtab") self.probes_secid = GetMemberAsSigned(provider, "dofpv_probes") self.prargs_secid = GetMemberAsSigned(provider, "dofpv_prargs") self.proffs_secid = GetMemberAsSigned(provider, "dofpv_proffs") self.name_idx = GetMemberAsUnsigned(provider, "dofpv_name") # Retrieve the strtab section to get strings str_sec = dof.GetSection(self.strtab_secid) # Retrieve the provider name self.provider_name = str_sec.GetString(self.name_idx) # Retrieve the probes associated with this provider # Since those depend on the location of the string table they can't be # retrieved from inside the PROBE section prb_sec = dof.GetSection(self.probes_secid) arg_sec = dof.GetSection(self.prargs_secid) off_sec = dof.GetSection(self.proffs_secid) self.nprobes = prb_sec.data_size / prb_sec.ent_size self.probes = [] for i in range(self.nprobes): probe = data.GetValue("$probe", prb_sec.offset + i * prb_sec.ent_size, dof_probe_type) probe_name_idx = GetMemberAsUnsigned(probe, "dofpr_name") probe_name = str_sec.GetString(probe_name_idx) probe_func_idx = GetMemberAsUnsigned(probe, "dofpr_func") probe_func = str_sec.GetString(probe_func_idx) probe_nargv_idx = GetMemberAsUnsigned(probe, "dofpr_nargv") # When nargv_idx / xargv_idx == probe_func_idx, the probe has no # arguments probe_nargv = str_sec.GetString(probe_nargv_idx) if probe_nargv_idx != probe_func_idx else "void" probe_xargv_idx = GetMemberAsUnsigned(probe, "dofpr_xargv") probe_xargv = str_sec.GetString(probe_xargv_idx) if probe_xargv_idx != probe_func_idx else "void" self.probes.append(DOFProbe(probe_func, probe_name, probe_nargv, probe_xargv)) def Show(self): super(DOFProviderSection, self).Show() print 'Provider name: {} ({} probes)'.format(self.provider_name, self.nprobes) for i in range(self.nprobes): self.probes[i].Show() class DOFEcbDescSection(DOFSection): """ ECB (Enabling control block) description section """ def __init__(self, dof, target, data, i): super(DOFEcbDescSection, self).__init__(dof, target, data, i) # Find the dof_ecbdesc_t type dof_ecbdesc_type = target.FindFirstType("dof_ecbdesc_t") if not(dof_ecbdesc_type): raise TypeError('Could not find dof_ecbdesc_t. Do you have a kernel binary loaded ?') ecbdesc = data.GetValue("$ecbdesc", self.offset, dof_ecbdesc_type) self.probes_secid = GetMemberAsSigned(ecbdesc, "dofe_probes") self.pred_secid = GetMemberAsSigned(ecbdesc, "dofe_pred") self.actions_secid = GetMemberAsSigned(ecbdesc, "dofe_actions") def Show(self): super(DOFEcbDescSection, self).Show() print('\tprobe {} pred {} actions {}'.format( DOFSecIdxString(self.probes_secid), DOFSecIdxString(self.pred_secid), DOFSecIdxString(self.actions_secid) )) class DOFProbeDescSection(DOFSection): """ Probe description section """ def __init__(self, dof, target, data, i): super(DOFProbeDescSection, self).__init__(dof, target, data, i) # Find the dof_probedesc_t type dof_probedesc_type = target.FindFirstType("dof_probedesc_t") if not(dof_probedesc_type): raise TypeError('Could not find dof_probedesc_t. Do you have a kernel binary loaded ?') probe = data.GetValue("$probe", self.offset, dof_probedesc_type) str_sec_secid = GetMemberAsSigned(probe, "dofp_strtab") str_sec = dof.GetSection(str_sec_secid) provider_idx = GetMemberAsUnsigned(probe, "dofp_provider") mod_idx = GetMemberAsUnsigned(probe, "dofp_mod") func_idx = GetMemberAsUnsigned(probe, "dofp_func") name_idx = GetMemberAsUnsigned(probe, "dofp_name") self.provider = str_sec.GetString(provider_idx) self.mod = str_sec.GetString(mod_idx) self.func = str_sec.GetString(func_idx) self.name = str_sec.GetString(name_idx) def Show(self): super(DOFProbeDescSection, self).Show() print '\tProbe desc {}:{}:{}:{}'.format(self.provider, self.mod, self.func, self.name) class DOFAction: def __init__(self, i, kind, difo, arg, arg_string): self.i = i self.kind = kind self.difo = difo self.arg = arg self.arg_string = arg_string class DOFActDescSection(DOFSection): """ Probe action description. The section contains multiple actions """ def __init__(self, dof, target, data, i): super(DOFActDescSection, self).__init__(dof, target, data, i) # Find the DOF types dof_actdesc_type = target.FindFirstType("dof_actdesc_t") if not(dof_actdesc_type.IsValid()): raise TypeError('Could not find dof_actdesc_t type. Do you have a kenrel binary loaded ?') # Retrieve the number of actions nactions = self.data_size / self.ent_size self.actions = [] for i in range(nactions): act = data.GetValue("$act", self.ent_size * i + self.offset, dof_actdesc_type) kind = GetMemberAsUnsigned(act, "dofa_kind") difo = GetMemberAsSigned(act, "dofa_difo") strtab_secid = GetMemberAsSigned(act, "dofa_strtab") arg = GetMemberAsUnsigned(act, "dofa_arg") arg_string = None if (kind in [DTRACEACT_PRINTF, DTRACEACT_PRINTA, DTRACEACT_SYSTEM, DTRACEACT_FREOPEN]) and ((kind != DTRACEACT_PRINTA or strtab_secid != -1) or (kind == DTRACEACT_DIFEXPR and strtab_secid != -1)): strtab = dof.GetSection(strtab_secid) arg_string = strtab.GetString(arg) # TODO : Show the argument for print/printf/... actions self.actions.append(DOFAction(i, kind, difo, arg, arg_string)) def Show(self): super(DOFActDescSection, self).Show() for a in self.actions: print '\tAction {} type {} difo in {}'.format( a.i, ActionString(a.kind), a.difo, ), if a.arg_string is not(None): print 'arg "{}"'.format(a.arg_string) else: print 'arg {}'.format(a.arg) class DOFDifoHdrSection(DOFSection): """ DIFO header section. Contains references to DIF/INTTAB/STRTAB/VARTAB sections """ def __init__(self, dof, target, data, i): super(DOFDifoHdrSection, self).__init__(dof, target, data, i) # Retrieve required types dof_difohdr_type = target.FindFirstType("dof_difohdr_t") if not(dof_difohdr_type.IsValid()): raise TypeError('Could not find dof_difohdr_t type. Do you have a kernel binary loaded ?') dof_secidx_type = target.FindFirstType("dof_secidx_t") if not(dof_secidx_type.IsValid()): raise TypeError('Could not find dof_secidx_t type. Do you have a kernel binary loaded ?') self.nsections = (self.data_size - dof_difohdr_type.GetByteSize()) / dof_secidx_type.GetByteSize() + 1 self.secs = [] (self.dif_idx, self.dif) = (0, None) (self.inttab_idx, self.inttab) = (0, None) self.strtab_idx, self.strtab = (0, None) (self.vartab_idx, self.vartab) = (0, None) for i in range(self.nsections): sec_idx = data.GetValue("$idx", self.offset + dof_difohdr_type.GetByteSize() + (i - 1) * dof_secidx_type.GetByteSize(), dof_secidx_type).GetValueAsSigned() sec = dof.GetSection(sec_idx) if sec.stype == "DIF": self.dif_idx = sec_idx self.dif = sec elif sec.stype == "STRTAB": self.strtab_idx = sec_idx self.strtab = sec elif sec.stype == "INTTAB": self.inttab_idx = sec_idx self.inttab = sec elif sec.stype == "VARTAB": self.vartab_idx = sec_idx self.vartab = sec self.secs.append(sec_idx) self.disassembler = DIFODisassembler(self.dif, self.strtab, self.inttab, self.vartab) def Show(self): super(DOFDifoHdrSection, self).Show() print 'DIF {} STRTAB {} INTTAB {} VARTAB {}'.format( DOFSecIdxString(self.dif_idx), DOFSecIdxString(self.strtab_idx), DOFSecIdxString(self.inttab_idx), DOFSecIdxString(self.vartab_idx) ) self.disassembler.Disassemble() # Section classes, ordered by type number section_classes = { 'ECBDESC': DOFEcbDescSection, 'PROBEDESC': DOFProbeDescSection, 'ACTDESC': DOFActDescSection, 'DIFOHDR': DOFDifoHdrSection, 'DIF': DOFDIFSection, 'STRTAB': DOFStrTabSection, 'VARTAB': DOFVarTabSection, 'PROVIDER': DOFProviderSection, 'INTTAB': DOFIntTabSection, } class DOF: def __init__(self, target, data): self.target = target self.data = data self.addr = data.GetAddr() # Find the DOF types self.dof_sec_type = target.FindFirstType("dof_sec_t") if not(self.dof_sec_type.IsValid()): raise TypeError('Could not find dof_sec_t type. Do you have a kernel binary loaded ?') return # Retrieve the header self.header = DOFHeader(self, target, data) # Populate the section dictionary self.sections = {} for i in range(self.header.nsecs): section = self.GetSection(i) def GetSection(self, index): """Retrieves a section from the DOF, parsing it if it hasn't been already""" if index not in self.sections: stype = self.GetSectionType(index) # Create a different child of DOFSection depending on the section # type if stype in section_classes: SectionType = section_classes[stype] else: # If we have no specific class for this section type, just # retrieve the section header SectionType = DOFSection self.sections[index] = SectionType(self, self.target, self.data, index) return self.sections[index] def Show(self): self.header.Show() print 'Sections:' for i in range(len(self.sections)): self.sections[i].Show() def GetSectionType(self, i): sec = self.data.GetValue("$sec", self.header.secoff + self.header.secsize * i, self.dof_sec_type) return section_names[GetMemberAsUnsigned(sec, "dofs_type")] def __lldb_init_module(debugger, internal_dict): debugger.HandleCommand('command script add -f dof.dof_command dof') def show_dof_module(target, module): print 'Printing DOFs for module {}'.format(module.GetFileSpec().GetFilename()) text_section = module.FindSection("__TEXT") # Iterate over the sections of TEXT to find the dof ones for i in range(text_section.GetNumSubSections()): section = text_section.GetSubSectionAtIndex(i) if section.GetName().find("dof_") != -1: section_data = SectionData(target, section) dof = DOF(target, section_data) print 'Section {}'.format(section.GetName()) dof.Show() def dof_command_section(debugger, args, result, internal_dict): """ Retrieves DOF data from DOF sections inside a specific module """ target = debugger.GetSelectedTarget() # If we got no arguments, just assume we are looking at the first module if len(args) == 1: return show_dof_module(target, target.GetModuleAtIndex(0)) subsubcommand = args[1] if subsubcommand == 'index': i = int(args[2]) return show_dof_module(target, target.GetModuleAtIndex(i)) elif subsubcommand == 'file': filename = args[2] filespec = lldb.SBFileSpec(filename) return show_dof_module(target, target.FindModule(filespec)) elif subsubcommand == 'all': for i in range(target.GetNumModules()): show_dof_module(target, target.GetModuleAtIndex(i)) def dof_command_addr(debugger, args, result, internal_dict): """ Parses DOF from an address """ target = debugger.GetSelectedTarget() dof_hdr_type = target.FindFirstType("dof_hdr_t") if len(args) == 1: return addr = int(args[1], 0) addr_data = AddrData(target, addr) dof = DOF(target, addr_data) dof.Show() def dof_command(debugger, command, result, internal_dict): args = shlex.split(command) if len(args) == 0: return subcommand = args[0] if subcommand == "section": dof_command_section(debugger, args, result, internal_dict) elif subcommand == "addr": dof_command_addr(debugger, args, result, internal_dict)