xref: /xnu-10002.1.13/tools/lldbmacros/usertaskdebugging/userprocess.py (revision 1031c584a5e37aff177559b9f69dbd3c8c3fd30a)
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        self.cache = {}
106        if not self.proc:
107            raise ValueError("Task has no associated BSD process.")
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        self.cputype = unsigned(self.proc.p_cputype)
117        self.cpusubtype = unsigned(self.proc.p_cpusubtype)
118
119        super(UserProcess, self).__init__(self.cputype, self.cpusubtype, ptrsize)
120        dbg_message = "process:%s is64bit:%d ptrsize:%d cputype:0x%x cpusubtype:0x%x" % (hex(self.proc), int(dataregisters64bit), ptrsize, self.cputype, self.cpusubtype)
121        self.proc_platform = int(GetProcPlatform(self.proc))
122        if self.proc_platform == xnudefines.P_PLATFORM_MACOS:
123            self.hinfo['ostype'] = 'macosx'
124        elif self.proc_platform == xnudefines.P_PLATFORM_WATCHOS:
125            self.hinfo['ostype'] = 'watchos'
126        elif self.proc_platform == xnudefines.P_PLATFORM_TVOS:
127            self.hinfo['ostype'] = 'tvos'
128        else:
129            self.hinfo['ostype'] = 'ios'
130        dbg_message += " ostype:%s" % self.hinfo['ostype']
131
132        if str(kern.arch).lower().startswith('arm'):
133            addressing_bits = 64 - int(kern.globals.gT1Sz)
134            self.hinfo['addressing_bits'] = addressing_bits
135            dbg_message += " addressing_bits:%d" % addressing_bits
136
137        self.registerset = GetRegisterSetForCPU(self.cputype, self.cpusubtype)
138        logging.info(dbg_message)
139        self.threads = {}
140        self.threads_ids_list = []
141        logging.debug("iterating over threads in process")
142        for thval in IterateQueue(task.threads, 'thread *', 'task_threads'):
143            self.threads[unsigned(thval.thread_id)] = UserThreadObject(thval, self.cputype, self.cpusubtype)
144            self.threads_ids_list.append(unsigned(thval.thread_id))
145
146    def getRegisterDataForThread(self, th_id, reg_num):
147        if th_id not in self.threads:
148            logging.critical("0x%x thread id is not found in this task")
149            return ''
150        if reg_num < 0 or reg_num >= len(self.registerset):
151            logging.warning("regnum %d is not defined for thread_id 0x%x" % (reg_num, th_id))
152            return ''
153        value = self.threads[th_id].getRegisterData(reg_num)
154        return self.encodeRegisterData(value, bytesize=(self.registerset[reg_num]['bitsize'] // 8))
155
156    def getRegisterCombinedDataForThread(self, th_id):
157        if th_id not in self.threads:
158            logging.critical("0x%x thread id is not found in this task" % th_id)
159            return ''
160        cur_thread = self.threads[th_id]
161        retval = 'thread:%s;name:%s;' % (self.encodeThreadID(th_id), cur_thread.getName())
162        pos = 0
163        for rinfo in self.registerset:
164            name = rinfo['name']
165            format = "%02x:%s;"
166            value = cur_thread.getRegisterValueByName(name)
167            value_endian_correct_str = self.encodeRegisterData(value, bytesize=(rinfo['bitsize'] // 8))
168            retval += format % (pos, value_endian_correct_str)
169            pos += 1
170        return retval
171
172    def getThreadStopInfo(self, th_id):
173        if th_id not in self.threads:
174            logging.critical("0x%x thread id is not found in this task")
175            return ''
176        return 'T02' + self.getRegisterCombinedDataForThread(th_id) + 'threads:' + self.getThreadsInfo()+';'
177
178    def getRegisterInfo(self, regnum):
179        #something similar to
180        #"name:x1;bitsize:64;offset:8;encoding:uint;format:hex;gcc:1;dwarf:1;set:General Purpose Registers;"
181        if regnum >= len(self.registerset):
182            logging.debug("No register_info for number %d." % regnum)
183            return 'E45'
184
185        rinfo = self.registerset[regnum]
186        retval = ''
187        for i in list(rinfo.keys()):
188            i_val = str(rinfo[i])
189            if i == 'set':
190                i_val = 'General Purpose Registers'
191            retval += '%s:%s;' % (str(i), i_val)
192
193        return retval
194
195    def getProcessInfo(self):
196        retval = ''
197        #pid:d22c;parent-pid:d34d;real-uid:ecf;real-gid:b;effective-uid:ecf;effective-gid:b;cputype:1000007;cpusubtype:3;
198        #ostype:macosx;vendor:apple;endian:little;ptrsize:8;
199        pinfo = {'effective-uid': 'ecf', 'effective-gid': 'b', 'endian': 'little', 'vendor': 'apple'}
200        pinfo['pid'] = "%x" % (GetProcPIDForTask(self.task))
201        pinfo['parent-pid'] = "%x" % (unsigned(self.proc.p_ppid))
202        pinfo['ptrsize'] = str(self.ptrsize)
203        pinfo['ostype'] = 'macosx'
204        pinfo['cputype'] = "%x" % self.cputype
205        pinfo['cpusubtype'] = "%x" % self.cpusubtype
206        pinfo['real-uid'] = "%x" % (unsigned(self.proc.p_ruid))
207        pinfo['real-gid'] = "%x" % (unsigned(self.proc.p_rgid))
208        if str(kern.arch).find('arm') >= 0:
209            pinfo['ostype'] = 'ios'
210        for i in list(pinfo.keys()):
211            i_val = str(pinfo[i])
212            retval += '%s:%s;' % (str(i), i_val)
213        return retval
214
215    def readMemory(self, address, size):
216        cache_key = "{}-{}-{}".format(hex(self.task), hex(address), size)
217        cache_data = self.cache.get(cache_key, None)
218        if cache_data:
219            return self.encodeByteString(cache_data)
220        data = GetUserDataAsString(self.task, address, size)
221        if not data:
222            logging.error("Failed to read memory task:{: <#018x} {: <#018x} {:d}".format(self.task, address, size))
223        else:
224            self.cache[cache_key] = data
225        return self.encodeByteString(data)
226
227    def getSharedLibInfoAddress(self):
228        return unsigned(self.task.all_image_info_addr)
229