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