xref: /xnu-8020.101.4/tools/lldbmacros/usertaskdebugging/userprocess.py (revision e7776783b89a353188416a9a346c6cdb4928faad)
1from __future__ import absolute_import, division
2
3from builtins import hex
4
5from builtins import object
6import logging
7from . import target
8import struct
9
10from xnu import *
11from core.operating_system import Armv8_RegisterSet, Armv7_RegisterSet, I386_RegisterSet, X86_64RegisterSet
12from core import caching
13
14""" these defines should come from an authoritative header file """
15CPU_TYPE_I386 = 0x00000007
16CPU_TYPE_X86_64 = 0x01000007
17CPU_TYPE_ARM = 0x0000000c
18CPU_TYPE_ARM64 = 0x0100000c
19CPU_TYPE_ARM64_32 = 0x0200000c
20
21def GetRegisterSetForCPU(cputype, subtype):
22    if cputype == CPU_TYPE_ARM64:
23        retval = Armv8_RegisterSet
24    elif cputype == CPU_TYPE_ARM64_32:
25        retval = Armv8_RegisterSet
26    elif cputype == CPU_TYPE_ARM:
27        retval = Armv7_RegisterSet
28    elif cputype == CPU_TYPE_I386:
29        retval = I386_RegisterSet
30    elif cputype == CPU_TYPE_X86_64:
31        retval = X86_64RegisterSet
32
33    """ crash if unknown cputype """
34
35    return retval.register_info['registers']
36
37
38class UserThreadObject(object):
39    """representation of userspace thread"""
40    def __init__(self, thr_obj, cputype, cpusubtype, is_kern_64bit):
41        super(UserThreadObject, self).__init__()
42        self.thread = thr_obj
43        self.registerset = GetRegisterSetForCPU(cputype, cpusubtype)
44        self.thread_id = unsigned(self.thread.thread_id)
45        self.is64Bit = bool(cputype & 0x01000000)
46
47        if self.is64Bit:
48            if cputype == CPU_TYPE_X86_64:
49                self.reg_type = "x86_64"
50                self.saved_state = Cast(self.thread.machine.iss, 'x86_saved_state_t *').uss.ss_64
51            if cputype == CPU_TYPE_ARM64:
52                self.reg_type = "arm64"
53                self.saved_state = self.thread.machine.upcb.uss.ss_64
54        else:
55            if cputype == CPU_TYPE_I386:
56                self.reg_type = "i386"
57                self.saved_state = Cast(self.thread.machine.iss, 'x86_saved_state_t *').uss.ss_32
58            if cputype == CPU_TYPE_ARM:
59                self.reg_type = "arm"
60                if not is_kern_64bit:
61                    self.saved_state = self.thread.machine.PcbData
62                else:
63                    self.saved_state = self.thread.machine.contextData.ss.uss.ss_32
64            if cputype == CPU_TYPE_ARM64_32:
65                self.reg_type = "arm64"
66                self.saved_state = self.thread.machine.upcb.uss.ss_64
67
68        logging.debug("created thread id 0x%x of type %s, is_kern_64bit 0x%x cputype 0x%x"
69                      % (self.thread_id, self.reg_type, is_kern_64bit, cputype))
70
71    def getRegisterValueByName(self, name):
72        if self.reg_type == 'arm64':
73            if name in ('x0', 'x1', 'x2', 'x3', 'x4', 'x5', 'x6', 'x7', 'x8', 'x9', 'x10', 'x11', 'x12', 'x13', 'x14', 'x15', 'x16', 'x17', 'x18', 'x19', 'x20', 'x21', 'x22', 'x23', 'x24', 'x25', 'x26', 'x27', 'x28'):
74                return unsigned(getattr(self.saved_state, 'x')[int(name.strip('x'))])
75
76            return unsigned(getattr(self.saved_state, name))
77
78        if self.reg_type == "x86_64":
79            if name in ('rip', 'rflags', 'cs', 'rsp', 'cpu'):
80                return unsigned(getattr(self.saved_state.isf, name))
81            return unsigned(getattr(self.saved_state, name))
82
83        if self.reg_type == "arm":
84            if name in ('r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9', 'r10', 'r11', 'r12'):
85                retval = unsigned(getattr(self.saved_state, 'r')[int(name.strip('r'))])
86            else:
87                retval = unsigned(getattr(self.saved_state, name))
88            return retval
89
90        #TODO for i386
91
92    def getName(self):
93        return str(self.thread_id)
94
95    def getRegisterData(self, reg_num):
96        """ returns None if there is error """
97        if reg_num < 0 or reg_num >= len(self.registerset):
98            logging.warning("regnum %d is not defined for thread_id 0x%x" % (reg_num, self.thread_id))
99            return None
100        return self.getRegisterValueByName(self.registerset[reg_num]['name'])
101
102
103class UserProcess(target.Process):
104    """ Represent a user process and thread states """
105    def __init__(self, task):
106        self.task = task
107        self.proc = Cast(task.bsd_info, 'proc_t')
108        dataregisters64bit = False
109        ptrsize = 4
110
111        if task.t_flags & 0x1:
112            ptrsize = 8
113        if task.t_flags & 0x2:
114            dataregisters64bit = True
115
116        is_kern_64bit = kern.arch in ['x86_64', 'x86_64h', 'arm64', 'arm64e']
117
118        self.cputype = unsigned(self.proc.p_cputype)
119        self.cpusubtype = unsigned(self.proc.p_cpusubtype)
120
121        super(UserProcess, self).__init__(self.cputype, self.cpusubtype, ptrsize)
122        dbg_message = "process:%s is64bit:%d ptrsize:%d cputype:0x%x cpusubtype:0x%x" % (hex(self.proc), int(dataregisters64bit), ptrsize, self.cputype, self.cpusubtype)
123        self.proc_platform = int(GetProcPlatform(self.proc))
124        if self.proc_platform == xnudefines.P_PLATFORM_MACOS:
125            self.hinfo['ostype'] = 'macosx'
126        elif self.proc_platform == xnudefines.P_PLATFORM_WATCHOS:
127            self.hinfo['ostype'] = 'watchos'
128        elif self.proc_platform == xnudefines.P_PLATFORM_TVOS:
129            self.hinfo['ostype'] = 'tvos'
130        else:
131            self.hinfo['ostype'] = 'ios'
132        dbg_message += " ostype:%s" % self.hinfo['ostype']
133
134        if is_kern_64bit and str(kern.arch).lower().startswith('arm'):
135            addressing_bits = 64 - int(kern.globals.gT1Sz)
136            self.hinfo['addressing_bits'] = addressing_bits
137            dbg_message += " addressing_bits:%d" % addressing_bits
138
139        self.registerset = GetRegisterSetForCPU(self.cputype, self.cpusubtype)
140        logging.info(dbg_message)
141        self.threads = {}
142        self.threads_ids_list = []
143        logging.debug("iterating over threads in process")
144        for thval in IterateQueue(task.threads, 'thread *', 'task_threads'):
145            self.threads[unsigned(thval.thread_id)] = UserThreadObject(thval, self.cputype, self.cpusubtype, is_kern_64bit)
146            self.threads_ids_list.append(unsigned(thval.thread_id))
147
148    def getRegisterDataForThread(self, th_id, reg_num):
149        if th_id not in self.threads:
150            logging.critical("0x%x thread id is not found in this task")
151            return ''
152        if reg_num < 0 or reg_num >= len(self.registerset):
153            logging.warning("regnum %d is not defined for thread_id 0x%x" % (reg_num, th_id))
154            return ''
155        value = self.threads[th_id].getRegisterData(reg_num)
156        return self.encodeRegisterData(value, bytesize=(self.registerset[reg_num]['bitsize'] // 8))
157
158    def getRegisterCombinedDataForThread(self, th_id):
159        if th_id not in self.threads:
160            logging.critical("0x%x thread id is not found in this task" % th_id)
161            return ''
162        cur_thread = self.threads[th_id]
163        retval = 'thread:%s;name:%s;' % (self.encodeThreadID(th_id), cur_thread.getName())
164        pos = 0
165        for rinfo in self.registerset:
166            name = rinfo['name']
167            format = "%02x:%s;"
168            value = cur_thread.getRegisterValueByName(name)
169            value_endian_correct_str = self.encodeRegisterData(value, bytesize=(rinfo['bitsize'] // 8))
170            retval += format % (pos, value_endian_correct_str)
171            pos += 1
172        return retval
173
174    def getThreadStopInfo(self, th_id):
175        if th_id not in self.threads:
176            logging.critical("0x%x thread id is not found in this task")
177            return ''
178        return 'T02' + self.getRegisterCombinedDataForThread(th_id) + 'threads:' + self.getThreadsInfo()+';'
179
180    def getRegisterInfo(self, regnum):
181        #something similar to
182        #"name:x1;bitsize:64;offset:8;encoding:uint;format:hex;gcc:1;dwarf:1;set:General Purpose Registers;"
183        if regnum >= len(self.registerset):
184            logging.debug("No register_info for number %d." % regnum)
185            return 'E45'
186
187        rinfo = self.registerset[regnum]
188        retval = ''
189        for i in list(rinfo.keys()):
190            i_val = str(rinfo[i])
191            if i == 'set':
192                i_val = 'General Purpose Registers'
193            retval += '%s:%s;' % (str(i), i_val)
194
195        return retval
196
197    def getProcessInfo(self):
198        retval = ''
199        #pid:d22c;parent-pid:d34d;real-uid:ecf;real-gid:b;effective-uid:ecf;effective-gid:b;cputype:1000007;cpusubtype:3;
200        #ostype:macosx;vendor:apple;endian:little;ptrsize:8;
201        pinfo = {'effective-uid': 'ecf', 'effective-gid': 'b', 'endian': 'little', 'vendor': 'apple'}
202        pinfo['pid'] = "%x" % (GetProcPIDForTask(self.task))
203        pinfo['parent-pid'] = "%x" % (unsigned(self.proc.p_ppid))
204        pinfo['ptrsize'] = str(self.ptrsize)
205        pinfo['ostype'] = 'macosx'
206        pinfo['cputype'] = "%x" % self.cputype
207        pinfo['cpusubtype'] = "%x" % self.cpusubtype
208        pinfo['real-uid'] = "%x" % (unsigned(self.proc.p_ruid))
209        pinfo['real-gid'] = "%x" % (unsigned(self.proc.p_rgid))
210        if str(kern.arch).find('arm') >= 0:
211            pinfo['ostype'] = 'ios'
212        for i in list(pinfo.keys()):
213            i_val = str(pinfo[i])
214            retval += '%s:%s;' % (str(i), i_val)
215        return retval
216
217    def readMemory(self, address, size):
218        cache_key = "{}-{}-{}".format(hex(self.task), hex(address), size)
219        cache_data = caching.GetDynamicCacheData(cache_key)
220        if cache_data:
221            return self.encodeByteString(cache_data)
222        data = GetUserDataAsString(self.task, address, size)
223        if not data:
224            logging.error("Failed to read memory task:{: <#018x} {: <#018x} {:d}".format(self.task, address, size))
225        else:
226            caching.SaveDynamicCacheData(cache_key, data)
227        return self.encodeByteString(data)
228
229    def getSharedLibInfoAddress(self):
230        return unsigned(self.task.all_image_info_addr)
231