xref: /xnu-11417.121.6/tools/lldbmacros/core/kernelcore.py (revision a1e26a70f38d1d7daa7b49b258e2f8538ad81650)
1
2""" Please make sure you read the README COMPLETELY BEFORE reading anything below.
3    It is very critical that you read coding guidelines in Section E in README file.
4"""
5from .cvalue import value
6from . import iterators as ccol
7from .caching import (
8    LazyTarget,
9    dyn_cached_property,
10    cache_dynamically,
11    cache_statically,
12)
13from utils import *
14
15import lldb
16
17class UnsupportedArchitectureError(RuntimeError):
18    def __init__(self, arch, msg="Unsupported architecture"):
19        self._arch = arch
20        self._msg = msg
21        super().__init__(msg)
22
23    def __str__(self):
24        return '%s: %s' % (self._arch, self._msg)
25
26
27def IterateTAILQ_HEAD(headval, element_name, list_prefix=''):
28    """ iterate over a TAILQ_HEAD in kernel. refer to bsd/sys/queue.h
29        params:
30            headval      - value : value object representing the head of the list
31            element_name - str   :  string name of the field which holds the list links.
32            list_prefix  - str   : use 's' here to iterate STAILQ_HEAD instead
33        returns:
34            A generator does not return. It is used for iterating.
35            value : an object that is of type as headval->tqh_first. Always a pointer object
36        example usage:
37          list_head = kern.GetGlobalVariable('mountlist')
38          for entryobj in IterateTAILQ_HEAD(list_head, 'mnt_list'):
39            print GetEntrySummary(entryobj)
40    """
41
42    next_path = ".{}.{}tqe_next".format(element_name, list_prefix)
43    head = headval.GetSBValue()
44
45    return (value(e.AddressOf()) for e in ccol.iter_linked_list(
46        head.Dereference() if head.TypeIsPointerType() else head,
47        next_path,
48        list_prefix + 'tqh_first',
49    ))
50
51
52def IterateLinkedList(headval, field_name):
53    """ iterate over a linked list.
54        This is equivalent to elt = headval; while(elt) { do_work(elt); elt = elt-><field_name>; }
55        params:
56            headval - value : value object representing element in the list.
57            field_name - str       : name of field that holds pointer to next element
58        returns: Nothing. This is used as iterable
59        example usage:
60            first_zone = kern.GetGlobalVariable('first_zone')
61            for zone in IterateLinkedList(first_zone, 'next_zone'):
62                print GetZoneSummary(zone)
63    """
64
65    head = headval.GetSBValue()
66
67    return (value(e.AddressOf()) for e in ccol.iter_linked_list(head, field_name))
68
69
70def IterateListEntry(headval, field_name, list_prefix=''):
71    """ iterate over a list as defined with LIST_HEAD in bsd/sys/queue.h
72        params:
73            headval      - value : Value object for lh_first
74            field_name   - str   : Name of the field in next element's structure
75            list_prefix  - str   : use 's' here to iterate SLIST_HEAD instead
76        returns:
77            A generator does not return. It is used for iterating
78            value  : an object thats of type (element_type) head->le_next. Always a pointer object
79        example usage:
80            headp = kern.globals.initproc.p_children
81            for pp in IterateListEntry(headp, 'p_sibling'):
82                print GetProcInfo(pp)
83    """
84
85    next_path = ".{}.{}le_next".format(field_name, list_prefix)
86    head = headval.GetSBValue()
87
88    return (value(e.AddressOf()) for e in ccol.iter_linked_list(
89        head.Dereference() if head.TypeIsPointerType() else head,
90        next_path,
91        list_prefix + 'lh_first',
92    ))
93
94
95def IterateLinkageChain(queue_head, element_type, field_name):
96    """ Iterate over a Linkage Chain queue in kernel of type queue_head_t. (osfmk/kern/queue.h method 1)
97        This is equivalent to the qe_foreach_element() macro
98        params:
99            queue_head   - value       : Value object for queue_head.
100            element_type - lldb.SBType : pointer type of the element which contains the queue_chain_t. Typically its structs like thread, task etc..
101                         - str         : OR a string describing the type. ex. 'task *'
102            field_name   - str         : Name of the field (in element) which holds a queue_chain_t
103        returns:
104            A generator does not return. It is used for iterating.
105            value  : An object thats of type (element_type). Always a pointer object
106        example usage:
107            coalq = kern.GetGlobalVariable('coalitions_q')
108            for coal in IterateLinkageChain(coalq, 'struct coalition *', 'coalitions'):
109                print GetCoalitionInfo(coal)
110    """
111
112    if isinstance(element_type, str):
113        element_type = gettype(element_type)
114
115    head = queue_head.GetSBValue()
116
117    return (value(e.AddressOf()) for e in ccol.iter_queue_entries(
118        head.Dereference() if head.TypeIsPointerType() else head,
119        element_type.GetPointeeType(),
120        field_name,
121    ))
122
123
124def IterateCircleQueue(queue_head, element_type, field_name):
125    """ iterate over a circle queue in kernel of type circle_queue_head_t. refer to osfmk/kern/circle_queue.h
126        params:
127            queue_head    - lldb.SBValue : Value object for queue_head.
128            element_type  - lldb.SBType : a type of the element 'next' points to. Typically its structs like thread, task etc..
129            field_name    - str : name of the field in target struct.
130        returns:
131            A generator does not return. It is used for iterating.
132            SBValue  : an object thats of type (element_type) queue_head->next. Always a pointer object
133    """
134
135    if isinstance(element_type, str):
136        element_type = gettype(element_type)
137
138    head = queue_head.GetSBValue()
139
140    return (value(e.AddressOf()) for e in ccol.iter_circle_queue(
141        head.Dereference() if head.TypeIsPointerType() else head,
142        element_type,
143        field_name,
144    ))
145
146
147def IterateQueue(queue_head, element_ptr_type, element_field_name, backwards=False, unpack_ptr_fn=None):
148    """ Iterate over an Element Chain queue in kernel of type queue_head_t. (osfmk/kern/queue.h method 2)
149        params:
150            queue_head         - value : Value object for queue_head.
151            element_ptr_type   - lldb.SBType : a pointer type of the element 'next' points to. Typically its structs like thread, task etc..
152                               - str         : OR a string describing the type. ex. 'task *'
153            element_field_name - str : name of the field in target struct.
154            backwards          - backwards : traverse the queue backwards
155            unpack_ptr_fn      - function : a function ptr of signature def unpack_ptr(long v) which returns long.
156        returns:
157            A generator does not return. It is used for iterating.
158            value  : an object thats of type (element_type) queue_head->next. Always a pointer object
159        example usage:
160            for page_meta in IterateQueue(kern.globals.first_zone.pages.all_free, 'struct zone_page_metadata *', 'pages'):
161                print page_meta
162    """
163
164    if isinstance(element_ptr_type, str):
165        element_ptr_type = gettype(element_ptr_type)
166
167    head = queue_head.GetSBValue()
168
169    return (value(e.AddressOf()) for e in ccol.iter_queue(
170        head.Dereference() if head.TypeIsPointerType() else head,
171        element_ptr_type.GetPointeeType(),
172        element_field_name,
173        backwards=backwards,
174        unpack=unpack_ptr_fn,
175    ))
176
177
178def IterateRBTreeEntry(rootelt, field_name):
179    """ iterate over a rbtree as defined with RB_HEAD in libkern/tree.h
180            rootelt      - value : Value object for rbh_root
181            field_name   - str   : Name of the field in link element's structure
182        returns:
183            A generator does not return. It is used for iterating
184            value  : an object thats of type (element_type) head->sle_next. Always a pointer object
185    """
186
187    return (value(e.AddressOf()) for e in ccol.iter_RB_HEAD(rootelt.GetSBValue(), field_name))
188
189
190def IterateSchedPriorityQueue(root, element_type, field_name):
191    """ iterate over a priority queue as defined with struct priority_queue from osfmk/kern/priority_queue.h
192            root         - value : Value object for the priority queue
193            element_type - str   : Type of the link element
194            field_name   - str   : Name of the field in link element's structure
195        returns:
196            A generator does not return. It is used for iterating
197            value  : an object thats of type (element_type). Always a pointer object
198    """
199
200    if isinstance(element_type, str):
201        element_type = gettype(element_type)
202
203    root = root.GetSBValue()
204
205    return (value(e.AddressOf()) for e in ccol.iter_priority_queue(
206        root.Dereference() if root.TypeIsPointerType() else root,
207        element_type,
208        field_name,
209    ))
210
211
212def IterateMPSCQueue(root, element_type, field_name):
213    """ iterate over an MPSC queue as defined with struct mpsc_queue_head from osfmk/kern/mpsc_queue.h
214            root         - value : Value object for the mpsc queue
215            element_type - str   : Type of the link element
216            field_name   - str   : Name of the field in link element's structure
217        returns:
218            A generator does not return. It is used for iterating
219            value  : an object thats of type (element_type). Always a pointer object
220    """
221    if isinstance(element_type, str):
222        element_type = gettype(element_type)
223
224    return (value(e.AddressOf()) for e in ccol.iter_mpsc_queue(
225        root.GetSBValue(), element_type, field_name
226    ))
227
228function_counters = dict()
229
230class KernelTarget(object):
231    """ A common kernel object that provides access to kernel objects and information.
232        The class holds global lists for  task, terminated_tasks, procs, zones, zombroc etc.
233        It also provides a way to symbolicate an address or create a value from an address.
234    """
235    def __init__(self, debugger):
236        """ Initialize the kernel debugging environment.
237            Target properties like architecture and connectedness are lazy-evaluted.
238        """
239
240        self.symbolicator = None
241
242        class _GlobalVariableFind(object):
243            def __init__(self, kern):
244                self._xnu_kernobj_12obscure12 = kern
245
246            @cache_statically
247            def __getattr__(self, name, target=None):
248                v = self._xnu_kernobj_12obscure12.GetGlobalVariable(name)
249                if not v.GetSBValue().IsValid():
250                    # Python 2 swallows all exceptions in hasattr(). That makes it work
251                    # even when global variable is not found. Python 3 has fixed the behavior
252                    # and we can raise only AttributeError here to keep original behavior.
253                    raise AttributeError('No such global variable by name: %s '%str(name))
254                return v
255            def __contains__(self, name):
256                try:
257                    val = self.__getattr__(name)
258                    return True
259                except AttributeError:
260                    return False
261        self.globals = _GlobalVariableFind(self)
262
263
264    def _GetSymbolicator(self):
265        """ Internal function: To initialize the symbolication from lldb.utils
266        """
267        if not self.symbolicator is None:
268            return self.symbolicator
269
270        from lldb.utils import symbolication
271        symbolicator = symbolication.Symbolicator()
272        symbolicator.target = LazyTarget.GetTarget()
273        self.symbolicator = symbolicator
274        return self.symbolicator
275
276    def Symbolicate(self, addr):
277        """ simple method to get name of function/variable from an address. this is equivalent of gdb 'output /a 0xaddress'
278            params:
279                addr - int : typically hex value like 0xffffff80002c0df0
280            returns:
281                str - '' if no symbol found else the symbol name.
282            Note: this function only finds the first symbol. If you expect multiple symbol conflict please use SymbolicateFromAddress()
283        """
284        ret_str = ''
285        syms = self.SymbolicateFromAddress(addr)
286        if len(syms) > 0:
287            ret_str +=syms[0].GetName()
288        return ret_str
289
290    def SymbolicateFromAddress(self, addr, fullSymbol=False):
291        """ symbolicates any given address based on modules loaded in the target.
292            params:
293                addr - int : typically hex value like 0xffffff80002c0df0
294            returns:
295                [] of SBSymbol: In case we don't find anything than empty array is returned.
296                      Note: a type of symbol can be figured out by gettype() function of SBSymbol.
297            example usage:
298                syms = kern.Symbolicate(0xffffff80002c0df0)
299                for s in syms:
300                  if s.GetType() == lldb.eSymbolTypeCode:
301                    print "Function", s.GetName()
302                  if s.GetType() == lldb.eSymbolTypeData:
303                    print "Variable", s.GetName()
304        """
305        if type(int(1)) != type(addr):
306            if str(addr).strip().find("0x") == 0 :
307                addr = int(addr, 16)
308            else:
309                addr = int(addr)
310        addr = self.StripKernelPAC(addr)
311        ret_array = []
312        symbolicator = self._GetSymbolicator()
313        syms = symbolicator.symbolicate(addr)
314        if not syms:
315            return ret_array
316        for s in syms:
317            if fullSymbol:
318                ret_array.append(s)
319            else:
320                ret_array.append(s.get_symbol_context().symbol)
321        return ret_array
322
323    def IsDebuggerConnected(self):
324        proc_state = LazyTarget.GetProcess().state
325        if proc_state == lldb.eStateInvalid : return False
326        if proc_state in [lldb.eStateStopped, lldb.eStateSuspended] : return True
327
328    @staticmethod
329    @cache_statically
330    def GetGlobalVariable(name, target=None):
331        """ Get the value object representation for a kernel global variable
332            params:
333              name : str - name of the variable. ex. version
334            returns: value - python object representing global variable.
335            raises : Exception in case the variable is not found.
336        """
337
338        return value(target.FindGlobalVariables(name, 1).GetValueAtIndex(0))
339
340    @cache_statically
341    def PERCPU_BASE(self, cpu, target=None):
342        """ Get the PERCPU base for the given cpu number
343            params:
344              cpu  : int - the cpu# for this variable
345            returns: int - the base for PERCPU for this cpu index
346        """
347        if self.arch == 'x86_64':
348            return unsigned(self.globals.cpu_data_ptr[cpu].cpu_pcpu_base)
349        elif self.arch.startswith('arm'):
350            data_entries = self.GetGlobalVariable('CpuDataEntries')
351            BootCpuData = addressof(self.GetGlobalVariable('percpu_slot_cpu_data'))
352            return unsigned(data_entries[cpu].cpu_data_vaddr) - unsigned(BootCpuData)
353
354    def PERCPU_GET(self, name, cpu):
355        """ Get the value object representation for a kernel percpu global variable
356            params:
357              name : str - name of the variable. ex. version
358              cpu  : int - the cpu# for this variable
359            returns: value - python object representing global variable.
360            raises : Exception in case the variable is not found.
361        """
362        var = addressof(self.GetGlobalVariable('percpu_slot_' + name))
363        var_type = var.GetSBValue().GetType().name
364        addr = unsigned(var) + self.PERCPU_BASE(cpu)
365        return dereference(self.GetValueFromAddress(addr, var_type))
366
367    @cache_statically
368    def GetLoadAddressForSymbol(self, name, target=None):
369        """ Get the load address of a symbol in the kernel.
370            params:
371              name : str - name of the symbol to lookup
372            returns: int - the load address as an integer. Use GetValueFromAddress to cast to a value.
373            raises : LookupError - if the symbol is not found.
374        """
375        name = str(name)
376        syms_arr = target.FindSymbols(name)
377        if syms_arr.IsValid() and len(syms_arr) > 0:
378            symbol = syms_arr[0].GetSymbol()
379            if symbol.IsValid():
380                return int(symbol.GetStartAddress().GetLoadAddress(target))
381
382        raise LookupError("Symbol not found: " + name)
383
384    def GetValueFromAddress(self, addr: int, type_str: str = 'void *') -> value:
385        """ convert an address to a value
386            params:
387                addr - int : typically hex value like 0xffffff80008dc390
388                type_str - str: type to cast to. Default type will be void *
389            returns:
390                value : a value object which has address as addr and type is type_str
391        """
392        sbv = self.globals.version.GetSBValue().CreateValueFromExpression(None,f"({type_str}){str(addr)}")
393
394        wanted_type = gettype(type_str)
395        if sbv.GetType() != wanted_type:
396            sbv = sbv.Cast(wanted_type)
397
398        return value(sbv)
399
400    def CreateValueFromAddress(self, addr: int, type_str: str = 'void *') -> value:
401        """ convert an address to a value, using `GetValueFromAddress()`
402            params:
403                addr - int : typically hex value like 0xffffff80008dc390
404                type_str - str: type to cast to. Default type will be void *
405            returns:
406                value : a value object which has address as addr and type is type_str
407
408            There are 2 LLDB APIs to create SBValues for data in memory - `CreateValueFromExpression()` and `CreateValueFromAddress()`.
409            The former will parse an expression (like those used in an LLDB print command - `p/x *(vm_map_t)0xFOO_ADDR`).
410            The latter allows telling LLDB "Give me an SBValue that interprets the data begginning at FOO address as BAR type".
411
412            `CreateValueFromAddress()` is more performant, but can be clunkier to work with.
413            However, for simple use cases it can be just as convenient as `CreateValueFromExpression()`.
414            Just take heed that you probably don't want "an SBValue for a pointer to BAR type who's data is at address FOO",
415            rather "an SBValue for BAR type who's data is at address FOO".
416
417            Where performance matters or there's no usability tradeoff, you're encouraged to use `CreateValueFromAddress()` over `GetValueFromAddress()`.
418            The poor, confusing naming is legacy :/
419
420        """
421        sbv = self.globals.version.GetSBValue().xCreateValueFromAddress(None, addr, gettype(type_str))
422        return value(sbv)
423
424    def CreateTypedPointerFromAddress(self, addr, type_str = "char"):
425        """ convert a address to pointer value
426
427            Note: This is obsolete and here as a temporary solution
428                  for people to migrate to using references instead.
429
430            params:
431                addr - int : typically hex value like 0xffffff80008dc390
432                type_str - str: type to cast to, must not be a pointer type.
433            returns:
434                value : a value object which has address as addr
435                        and type is `type_str *`
436        """
437
438        target = LazyTarget.GetTarget()
439        sbv    = target.xCreateValueFromAddress(None, addr, gettype(type_str))
440        return value(sbv.AddressOf())
441
442
443    def GetValueAsType(self, v, t):
444        """ Retrieves a global variable 'v' of type 't' wrapped in a vue object.
445            If 'v' is an address, creates a vue object of the appropriate type.
446            If 'v' is a name, looks for the global variable and asserts its type.
447            Throws:
448                NameError - If 'v' cannot be found
449                TypeError - If 'v' is of the wrong type
450        """
451        if islong(v):
452            return self.GetValueFromAddress(v, t)
453        else:
454            var = LazyTarget.GetTarget().FindGlobalVariables(v, 1)[0]
455            if not var:
456                raise NameError("Failed to find global variable '{0}'".format(v))
457            if var.GetTypeName() != t:
458                raise TypeError("{0} must be of type '{1}', not '{2}'".format(v, t, var.GetTypeName()))
459            return value(var)
460
461    def _GetIterator(self, iter_head_name, next_element_name='next', iter_head_type=None):
462        """ returns an iterator for a collection in kernel memory.
463            params:
464                iter_head_name - str : name of queue_head or list head variable.
465                next_element_name - str : name of the element that leads to next element.
466                                          for ex. in struct zone list 'next_zone' is the linking element.
467            returns:
468                iterable : typically used in conjunction with "for varname in iterable:"
469        """
470        head_element = self.GetGlobalVariable(iter_head_name)
471        return head_element.GetSBValue().linked_list_iter(next_element_name)
472
473    def TruncPage(self, addr):
474        return (addr & ~(unsigned(self.GetGlobalVariable("page_size")) - 1))
475
476    def RoundPage(self, addr):
477        return trunc_page(addr + unsigned(self.GetGlobalVariable("page_size")) - 1)
478
479    def StraddlesPage(self, addr, size):
480        if size > unsigned(self.GetGlobalVariable("page_size")):
481            return True
482        val = ((addr + size) & (unsigned(self.GetGlobalVariable("page_size"))-1))
483        return (val < size and val > 0)
484
485    def StripUserPAC(self, addr):
486        if self.arch != 'arm64e':
487            return addr
488        T0Sz = self.GetGlobalVariable('gT0Sz')
489        return StripPAC(addr, T0Sz)
490
491    def StripKernelPAC(self, addr):
492        if self.arch != 'arm64e':
493            return addr
494        T1Sz = self.GetGlobalVariable('gT1Sz')
495        return StripPAC(addr, T1Sz)
496
497    PAGE_PROTECTION_TYPE_NONE = 0
498    PAGE_PROTECTION_TYPE_PPL = 1
499    PAGE_PROTECTION_TYPE_SPTM = 2
500
501    def PhysToKVARM64(self, addr):
502        if self.globals.page_protection_type <= self.PAGE_PROTECTION_TYPE_PPL:
503            ptov_table = self.globals.ptov_table
504            for i in range(0, self.globals.ptov_index):
505                if (addr >= int(unsigned(ptov_table[i].pa))) and (addr < (int(unsigned(ptov_table[i].pa)) + int(unsigned(ptov_table[i].len)))):
506                    return (addr - int(unsigned(ptov_table[i].pa)) + int(unsigned(ptov_table[i].va)))
507        else:
508            papt_table = self.globals.libsptm_papt_ranges
509            page_size = self.globals.page_size
510            for i in range(0, self.globals.libsptm_n_papt_ranges):
511                if (addr >= int(unsigned(papt_table[i].paddr_start))) and (addr < (int(unsigned(papt_table[i].paddr_start)) + int(unsigned(papt_table[i].num_mappings) * page_size))):
512                    return (addr - int(unsigned(papt_table[i].paddr_start)) + int(unsigned(papt_table[i].papt_start)))
513            raise ValueError("PA {:#x} not found in physical region lookup table".format(addr))
514        return (addr - unsigned(self.globals.gPhysBase) + unsigned(self.globals.gVirtBase))
515
516    def PhysToKernelVirt(self, addr):
517        if self.arch == 'x86_64':
518            return (addr + unsigned(self.GetGlobalVariable('physmap_base')))
519        elif self.arch.startswith('arm64'):
520            return self.PhysToKVARM64(addr)
521        elif self.arch.startswith('arm'):
522            return (addr - unsigned(self.GetGlobalVariable("gPhysBase")) + unsigned(self.GetGlobalVariable("gVirtBase")))
523        else:
524            raise ValueError("PhysToVirt does not support {0}".format(self.arch))
525
526    @cache_statically
527    def GetUsecDivisor(self, target=None):
528        if self.arch == 'x86_64':
529            return 1000
530
531        rtclockdata_addr = self.GetLoadAddressForSymbol('RTClockData')
532        rtc = self.GetValueFromAddress(rtclockdata_addr, 'struct _rtclock_data_ *')
533        return unsigned(rtc.rtc_usec_divisor)
534
535    def GetNanotimeFromAbstime(self, abstime):
536        """ convert absolute time (which is in MATUs) to nano seconds.
537            Since based on architecture the conversion may differ.
538            params:
539                abstime - int absolute time as shown by mach_absolute_time
540            returns:
541                int - nanosecs of time
542        """
543        return (abstime * 1000) // self.GetUsecDivisor()
544
545    @property
546    @cache_statically
547    def zones(self, target=None):
548        za = target.chkFindFirstGlobalVariable('zone_array')
549        zs = target.chkFindFirstGlobalVariable('zone_security_array')
550        n  = target.chkFindFirstGlobalVariable('num_zones').xGetValueAsInteger()
551
552        iter_za = za.chkGetChildAtIndex(0).xIterSiblings(0, n)
553        iter_zs = zs.chkGetChildAtIndex(0).xIterSiblings(0, n)
554
555        return [
556            (value(next(iter_za).AddressOf()), value(next(iter_zs).AddressOf()))
557            for i in range(n)
558        ]
559
560    @property
561    def threads(self):
562        target = LazyTarget.GetTarget()
563
564        return (value(t.AddressOf()) for t in ccol.iter_queue(
565            target.chkFindFirstGlobalVariable('threads'),
566            gettype('thread'),
567            'threads',
568        ))
569
570    @dyn_cached_property
571    def tasks(self, target=None):
572        return [value(t.AddressOf()) for t in ccol.iter_queue(
573            target.chkFindFirstGlobalVariable('tasks'),
574            gettype('task'),
575            'tasks',
576        )]
577
578    @property
579    def coalitions(self):
580        target = LazyTarget.GetTarget()
581
582        return (value(coal.AddressOf()) for coal in ccol.SMRHash(
583            target.chkFindFirstGlobalVariable('coalition_hash'),
584            target.chkFindFirstGlobalVariable('coal_hash_traits'),
585        ))
586
587    @property
588    def thread_groups(self):
589        target = LazyTarget.GetTarget()
590
591        return (value(tg.AddressOf()) for tg in ccol.iter_queue_entries(
592            target.chkFindFirstGlobalVariable('tg_queue'),
593            gettype('thread_group'),
594            'tg_queue_chain',
595        ))
596
597    @property
598    def terminated_tasks(self):
599        target = LazyTarget.GetTarget()
600
601        return (value(t.AddressOf()) for t in ccol.iter_queue(
602            target.chkFindFirstGlobalVariable('terminated_tasks'),
603            gettype('task'),
604            'tasks',
605        ))
606
607    @property
608    def terminated_threads(self):
609        target = LazyTarget.GetTarget()
610
611        return (value(t.AddressOf()) for t in ccol.iter_queue(
612            target.chkFindFirstGlobalVariable('terminated_threads'),
613            gettype('thread'),
614            'threads',
615        ))
616
617    @property
618    def procs(self):
619        target = LazyTarget.GetTarget()
620
621        return (value(p.AddressOf()) for p in ccol.iter_LIST_HEAD(
622            target.chkFindFirstGlobalVariable('allproc'),
623            'p_list',
624        ))
625
626    @property
627    def interrupt_stats(self):
628        target = LazyTarget.GetTarget()
629
630        return (value(stat.AddressOf()) for stat in ccol.iter_queue(
631            target.chkFindFirstGlobalVariable('gInterruptAccountingDataList'),
632            gettype('IOInterruptAccountingData'),
633            'chain',
634        ))
635
636    @property
637    def zombprocs(self):
638        target = LazyTarget.GetTarget()
639
640        return (value(p.AddressOf()) for p in ccol.iter_LIST_HEAD(
641            target.chkFindFirstGlobalVariable('zombproc'),
642            'p_list',
643        ))
644
645    @property
646    def version(self):
647        return str(self.globals.version)
648
649    @property
650    def arch(self):
651        return LazyTarget.GetTarget().triple.split('-', 1)[0]
652
653    @property
654    def ptrsize(self):
655        return LazyTarget.GetTarget().GetAddressByteSize()
656
657    @property
658    def VM_MIN_KERNEL_ADDRESS(self):
659        if self.arch == 'x86_64':
660            return 0xffffff8000000000
661        else:
662            return 0xffffffe00000000
663
664    @property
665    def VM_MIN_KERNEL_AND_KEXT_ADDRESS(self):
666        if self.arch == 'x86_64':
667            return 0xffffff8000000000 - 0x80000000
668        else:
669            return 0xffffffe00000000
670