xref: /xnu-8792.61.2/tools/lldbmacros/usertaskdebugging/userprocess.py (revision 42e220869062b56f8d7d0726fd4c88954f87902c)
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):
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                self.saved_state = self.thread.machine.contextData.ss.uss.ss_32
61            if cputype == CPU_TYPE_ARM64_32:
62                self.reg_type = "arm64"
63                self.saved_state = self.thread.machine.upcb.uss.ss_64
64
65        logging.debug("created thread id 0x%x of type %s, cputype 0x%x"
66                      % (self.thread_id, self.reg_type, cputype))
67
68    def getRegisterValueByName(self, name):
69        if self.reg_type == 'arm64':
70            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'):
71                return unsigned(getattr(self.saved_state, 'x')[int(name.strip('x'))])
72
73            return unsigned(getattr(self.saved_state, name))
74
75        if self.reg_type == "x86_64":
76            if name in ('rip', 'rflags', 'cs', 'rsp', 'cpu'):
77                return unsigned(getattr(self.saved_state.isf, name))
78            return unsigned(getattr(self.saved_state, name))
79
80        if self.reg_type == "arm":
81            if name in ('r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9', 'r10', 'r11', 'r12'):
82                retval = unsigned(getattr(self.saved_state, 'r')[int(name.strip('r'))])
83            else:
84                retval = unsigned(getattr(self.saved_state, name))
85            return retval
86
87        #TODO for i386
88
89    def getName(self):
90        return str(self.thread_id)
91
92    def getRegisterData(self, reg_num):
93        """ returns None if there is error """
94        if reg_num < 0 or reg_num >= len(self.registerset):
95            logging.warning("regnum %d is not defined for thread_id 0x%x" % (reg_num, self.thread_id))
96            return None
97        return self.getRegisterValueByName(self.registerset[reg_num]['name'])
98
99
100class UserProcess(target.Process):
101    """ Represent a user process and thread states """
102    def __init__(self, task):
103        self.task = task
104        self.proc = GetProcFromTask(task)
105        if not self.proc:
106            raise ValueError("Task has no associated BSD process.")
107        dataregisters64bit = False
108        ptrsize = 4
109
110        if task.t_flags & 0x1:
111            ptrsize = 8
112        if task.t_flags & 0x2:
113            dataregisters64bit = True
114
115        self.cputype = unsigned(self.proc.p_cputype)
116        self.cpusubtype = unsigned(self.proc.p_cpusubtype)
117
118        super(UserProcess, self).__init__(self.cputype, self.cpusubtype, ptrsize)
119        dbg_message = "process:%s is64bit:%d ptrsize:%d cputype:0x%x cpusubtype:0x%x" % (hex(self.proc), int(dataregisters64bit), ptrsize, self.cputype, self.cpusubtype)
120        self.proc_platform = int(GetProcPlatform(self.proc))
121        if self.proc_platform == xnudefines.P_PLATFORM_MACOS:
122            self.hinfo['ostype'] = 'macosx'
123        elif self.proc_platform == xnudefines.P_PLATFORM_WATCHOS:
124            self.hinfo['ostype'] = 'watchos'
125        elif self.proc_platform == xnudefines.P_PLATFORM_TVOS:
126            self.hinfo['ostype'] = 'tvos'
127        else:
128            self.hinfo['ostype'] = 'ios'
129        dbg_message += " ostype:%s" % self.hinfo['ostype']
130
131        if str(kern.arch).lower().startswith('arm'):
132            addressing_bits = 64 - int(kern.globals.gT1Sz)
133            self.hinfo['addressing_bits'] = addressing_bits
134            dbg_message += " addressing_bits:%d" % addressing_bits
135
136        self.registerset = GetRegisterSetForCPU(self.cputype, self.cpusubtype)
137        logging.info(dbg_message)
138        self.threads = {}
139        self.threads_ids_list = []
140        logging.debug("iterating over threads in process")
141        for thval in IterateQueue(task.threads, 'thread *', 'task_threads'):
142            self.threads[unsigned(thval.thread_id)] = UserThreadObject(thval, self.cputype, self.cpusubtype)
143            self.threads_ids_list.append(unsigned(thval.thread_id))
144
145    def getRegisterDataForThread(self, th_id, reg_num):
146        if th_id not in self.threads:
147            logging.critical("0x%x thread id is not found in this task")
148            return ''
149        if reg_num < 0 or reg_num >= len(self.registerset):
150            logging.warning("regnum %d is not defined for thread_id 0x%x" % (reg_num, th_id))
151            return ''
152        value = self.threads[th_id].getRegisterData(reg_num)
153        return self.encodeRegisterData(value, bytesize=(self.registerset[reg_num]['bitsize'] // 8))
154
155    def getRegisterCombinedDataForThread(self, th_id):
156        if th_id not in self.threads:
157            logging.critical("0x%x thread id is not found in this task" % th_id)
158            return ''
159        cur_thread = self.threads[th_id]
160        retval = 'thread:%s;name:%s;' % (self.encodeThreadID(th_id), cur_thread.getName())
161        pos = 0
162        for rinfo in self.registerset:
163            name = rinfo['name']
164            format = "%02x:%s;"
165            value = cur_thread.getRegisterValueByName(name)
166            value_endian_correct_str = self.encodeRegisterData(value, bytesize=(rinfo['bitsize'] // 8))
167            retval += format % (pos, value_endian_correct_str)
168            pos += 1
169        return retval
170
171    def getThreadStopInfo(self, th_id):
172        if th_id not in self.threads:
173            logging.critical("0x%x thread id is not found in this task")
174            return ''
175        return 'T02' + self.getRegisterCombinedDataForThread(th_id) + 'threads:' + self.getThreadsInfo()+';'
176
177    def getRegisterInfo(self, regnum):
178        #something similar to
179        #"name:x1;bitsize:64;offset:8;encoding:uint;format:hex;gcc:1;dwarf:1;set:General Purpose Registers;"
180        if regnum >= len(self.registerset):
181            logging.debug("No register_info for number %d." % regnum)
182            return 'E45'
183
184        rinfo = self.registerset[regnum]
185        retval = ''
186        for i in list(rinfo.keys()):
187            i_val = str(rinfo[i])
188            if i == 'set':
189                i_val = 'General Purpose Registers'
190            retval += '%s:%s;' % (str(i), i_val)
191
192        return retval
193
194    def getProcessInfo(self):
195        retval = ''
196        #pid:d22c;parent-pid:d34d;real-uid:ecf;real-gid:b;effective-uid:ecf;effective-gid:b;cputype:1000007;cpusubtype:3;
197        #ostype:macosx;vendor:apple;endian:little;ptrsize:8;
198        pinfo = {'effective-uid': 'ecf', 'effective-gid': 'b', 'endian': 'little', 'vendor': 'apple'}
199        pinfo['pid'] = "%x" % (GetProcPIDForTask(self.task))
200        pinfo['parent-pid'] = "%x" % (unsigned(self.proc.p_ppid))
201        pinfo['ptrsize'] = str(self.ptrsize)
202        pinfo['ostype'] = 'macosx'
203        pinfo['cputype'] = "%x" % self.cputype
204        pinfo['cpusubtype'] = "%x" % self.cpusubtype
205        pinfo['real-uid'] = "%x" % (unsigned(self.proc.p_ruid))
206        pinfo['real-gid'] = "%x" % (unsigned(self.proc.p_rgid))
207        if str(kern.arch).find('arm') >= 0:
208            pinfo['ostype'] = 'ios'
209        for i in list(pinfo.keys()):
210            i_val = str(pinfo[i])
211            retval += '%s:%s;' % (str(i), i_val)
212        return retval
213
214    def readMemory(self, address, size):
215        cache_key = "{}-{}-{}".format(hex(self.task), hex(address), size)
216        cache_data = caching.GetDynamicCacheData(cache_key)
217        if cache_data:
218            return self.encodeByteString(cache_data)
219        data = GetUserDataAsString(self.task, address, size)
220        if not data:
221            logging.error("Failed to read memory task:{: <#018x} {: <#018x} {:d}".format(self.task, address, size))
222        else:
223            caching.SaveDynamicCacheData(cache_key, data)
224        return self.encodeByteString(data)
225
226    def getSharedLibInfoAddress(self):
227        return unsigned(self.task.all_image_info_addr)
228