xref: /xnu-8792.81.2/tools/lldbmacros/core/kernelcore.py (revision 19c3b8c28c31cb8130e034cfb5df6bf9ba342d90)
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 __future__ import absolute_import, division
6
7from builtins import range
8from builtins import object
9
10from .cvalue import value
11from .compat import valueint as int
12from .lazytarget import LazyTarget
13from utils import *
14from . import caching
15
16import lldb
17import six
18
19class UnsupportedArchitectureError(RuntimeError):
20    def __init__(self, arch, msg="Unsupported architecture"):
21        self._arch = arch
22        self._msg = msg
23        super().__init__(msg)
24
25    def __str__(self):
26        return '%s: %s' % (self._arch, self._msg)
27
28def IterateTAILQ_HEAD(headval, element_name, list_prefix=''):
29    """ iterate over a TAILQ_HEAD in kernel. refer to bsd/sys/queue.h
30        params:
31            headval      - value : value object representing the head of the list
32            element_name - str   :  string name of the field which holds the list links.
33            list_prefix  - str   : use 's' here to iterate STAILQ_HEAD instead
34        returns:
35            A generator does not return. It is used for iterating.
36            value : an object that is of type as headval->tqh_first. Always a pointer object
37        example usage:
38          list_head = kern.GetGlobalVariable('mountlist')
39          for entryobj in IterateTAILQ_HEAD(list_head, 'mnt_list'):
40            print GetEntrySummary(entryobj)
41    """
42    iter_val = headval.__getattr__(list_prefix + 'tqh_first')
43    while unsigned(iter_val) != 0 :
44        yield iter_val
45        iter_val = iter_val.__getattr__(element_name).__getattr__(list_prefix + 'tqe_next')
46    #end of yield loop
47
48def IterateLinkedList(element, field_name):
49    """ iterate over a linked list.
50        This is equivalent to elt = element; while(elt) { do_work(elt); elt = elt-><field_name>; }
51        params:
52            element - value : value object representing element in the list.
53            field_name - str       : name of field that holds pointer to next element
54        returns: Nothing. This is used as iterable
55        example usage:
56            first_zone = kern.GetGlobalVariable('first_zone')
57            for zone in IterateLinkedList(first_zone, 'next_zone'):
58                print GetZoneSummary(zone)
59    """
60    elt = element
61    while unsigned(elt) != 0:
62        yield elt
63        elt = elt.__getattr__(field_name)
64    #end of while loop
65
66def IterateListEntry(element, element_type, field_name, list_prefix=''):
67    """ iterate over a list as defined with LIST_HEAD in bsd/sys/queue.h
68        params:
69            element      - value : Value object for lh_first
70            element_type - str   : Type of the next element
71            field_name   - str   : Name of the field in next element's structure
72            list_prefix  - str   : use 's' here to iterate SLIST_HEAD instead
73        returns:
74            A generator does not return. It is used for iterating
75            value  : an object thats of type (element_type) head->le_next. Always a pointer object
76        example usage:
77            headp = kern.globals.initproc.p_children
78            for pp in IterateListEntry(headp, 'struct proc *', 'p_sibling'):
79                print GetProcInfo(pp)
80    """
81    elt = element.__getattr__(list_prefix + 'lh_first')
82    if isinstance(element_type, six.string_types):
83        element_type = gettype(element_type)
84    while unsigned(elt) != 0:
85        yield elt
86        next_el = elt.__getattr__(field_name).__getattr__(list_prefix + 'le_next')
87        elt = cast(next_el, element_type)
88
89def IterateLinkageChain(queue_head, element_type, field_name, field_ofst=0):
90    """ Iterate over a Linkage Chain queue in kernel of type queue_head_t. (osfmk/kern/queue.h method 1)
91        This is equivalent to the qe_foreach_element() macro
92        params:
93            queue_head   - value       : Value object for queue_head.
94            element_type - lldb.SBType : pointer type of the element which contains the queue_chain_t. Typically its structs like thread, task etc..
95                         - str         : OR a string describing the type. ex. 'task *'
96            field_name   - str         : Name of the field (in element) which holds a queue_chain_t
97            field_ofst   - int         : offset from the 'field_name' (in element) which holds a queue_chain_t
98                                         This is mostly useful if a particular element contains an array of queue_chain_t
99        returns:
100            A generator does not return. It is used for iterating.
101            value  : An object thats of type (element_type). Always a pointer object
102        example usage:
103            coalq = kern.GetGlobalVariable('coalitions_q')
104            for coal in IterateLinkageChain(coalq, 'struct coalition *', 'coalitions'):
105                print GetCoalitionInfo(coal)
106    """
107    global kern
108    if isinstance(element_type, six.string_types):
109        element_type = gettype(element_type)
110
111    if unsigned(queue_head) == 0:
112        return
113
114    if element_type.IsPointerType():
115        elem_ofst = getfieldoffset(element_type.GetPointeeType(), field_name) + field_ofst
116    else:
117        elem_ofst = getfieldoffset(element_type, field_name) + field_ofst
118
119    link = queue_head.next
120    while (unsigned(link) != unsigned(queue_head)):
121        addr = unsigned(link) - elem_ofst;
122        # I can't use the GetValueFromAddress function of the kernel class
123        # because I have no instance of that class!
124        obj = value(link.GetSBValue().CreateValueFromExpression(None,'(void *)'+str(addr)))
125        obj = cast(obj, element_type)
126        yield obj
127        link = link.next
128
129def IterateCircleQueue(queue_head, element_ptr_type, element_field_name):
130    """ iterate over a circle queue in kernel of type circle_queue_head_t. refer to osfmk/kern/circle_queue.h
131        params:
132            queue_head         - lldb.SBValue : Value object for queue_head.
133            element_type       - lldb.SBType : a pointer type of the element 'next' points to. Typically its structs like thread, task etc..
134            element_field_name - str : name of the field in target struct.
135        returns:
136            A generator does not return. It is used for iterating.
137            SBValue  : an object thats of type (element_type) queue_head->next. Always a pointer object
138    """
139    head = queue_head.head.GetSBValue()
140    queue_head_addr = 0x0
141    if head.TypeIsPointerType():
142        queue_head_addr = head.GetValueAsUnsigned()
143    else:
144        queue_head_addr = head.GetAddress().GetLoadAddress(osplugin_target_obj)
145    cur_elt = head
146    while True:
147        if not cur_elt.IsValid() or cur_elt.GetValueAsUnsigned() == 0:
148            break
149        yield containerof(value(cur_elt), element_ptr_type, element_field_name)
150        cur_elt = cur_elt.GetChildMemberWithName('next')
151        if cur_elt.GetValueAsUnsigned() == queue_head_addr:
152            break
153
154def IterateQueue(queue_head, element_ptr_type, element_field_name, backwards=False, unpack_ptr_fn=None):
155    """ Iterate over an Element Chain queue in kernel of type queue_head_t. (osfmk/kern/queue.h method 2)
156        params:
157            queue_head         - value : Value object for queue_head.
158            element_ptr_type   - lldb.SBType : a pointer type of the element 'next' points to. Typically its structs like thread, task etc..
159                               - str         : OR a string describing the type. ex. 'task *'
160            element_field_name - str : name of the field in target struct.
161            backwards          - backwards : traverse the queue backwards
162            unpack_ptr_fn      - function : a function ptr of signature def unpack_ptr(long v) which returns long.
163        returns:
164            A generator does not return. It is used for iterating.
165            value  : an object thats of type (element_type) queue_head->next. Always a pointer object
166        example usage:
167            for page_meta in IterateQueue(kern.globals.first_zone.pages.all_free, 'struct zone_page_metadata *', 'pages'):
168                print page_meta
169    """
170    if isinstance(element_ptr_type, six.string_types):
171        element_ptr_type = gettype(element_ptr_type)
172
173    next_direction = 'prev' if backwards else 'next'
174
175    def next_element(e):
176        next_elt = getattr(e, next_direction)
177        if not unpack_ptr_fn:
178            return next_elt
179        next_sbv = next_elt.GetSBValue()
180        addr = unpack_ptr_fn(next_sbv.GetValueAsUnsigned())
181        next_sbv = next_sbv.CreateValueFromExpression(None,'(void *)' + str(addr))
182        return value(next_sbv.Cast(element_ptr_type))
183
184    queue_head_sbv = queue_head.GetSBValue()
185
186    if queue_head_sbv.TypeIsPointerType():
187        queue_head_addr = queue_head_sbv.GetValueAsAddress()
188    else:
189        queue_head_addr = queue_head_sbv.GetAddress().GetLoadAddress(LazyTarget.GetTarget())
190
191    elt = next_element(queue_head)
192    while True:
193        sbv = elt.GetSBValue()
194        if not sbv.IsValid() or sbv.GetValueAsUnsigned() == 0 or sbv.GetValueAsAddress() == queue_head_addr:
195            break
196        elt = cast(elt, element_ptr_type)
197        yield elt
198        elt = next_element(getattr(elt, element_field_name))
199
200
201def IterateRBTreeEntry(element, element_type, field_name):
202    """ iterate over a rbtree as defined with RB_HEAD in libkern/tree.h
203            element      - value : Value object for rbh_root
204            element_type - str   : Type of the link element
205            field_name   - str   : Name of the field in link element's structure
206        returns:
207            A generator does not return. It is used for iterating
208            value  : an object thats of type (element_type) head->sle_next. Always a pointer object
209    """
210    elt = element.__getattr__('rbh_root')
211    if isinstance(element_type, six.string_types):
212        element_type = gettype(element_type)
213
214    # Walk to find min
215    parent = elt
216    while unsigned(elt) != 0:
217        parent = elt
218        elt = cast(elt.__getattr__(field_name).__getattr__('rbe_left'), element_type)
219    elt = parent
220
221    # Now elt is min
222    while unsigned(elt) != 0:
223        yield elt
224        # implementation cribbed from RB_NEXT in libkern/tree.h
225        right = cast(elt.__getattr__(field_name).__getattr__('rbe_right'), element_type)
226        if unsigned(right) != 0:
227            elt = right
228            left = cast(elt.__getattr__(field_name).__getattr__('rbe_left'), element_type)
229            while unsigned(left) != 0:
230                elt = left
231                left = cast(elt.__getattr__(field_name).__getattr__('rbe_left'), element_type)
232        else:
233
234            # avoid using GetValueFromAddress
235            addr = elt.__getattr__(field_name).__getattr__('rbe_parent')&~1
236            parent = value(elt.GetSBValue().CreateValueFromExpression(None,'(void *)'+str(addr)))
237            parent = cast(parent, element_type)
238
239            if unsigned(parent) != 0:
240                left = cast(parent.__getattr__(field_name).__getattr__('rbe_left'), element_type)
241            if (unsigned(parent) != 0) and (unsigned(elt) == unsigned(left)):
242                elt = parent
243            else:
244                if unsigned(parent) != 0:
245                    right = cast(parent.__getattr__(field_name).__getattr__('rbe_right'), element_type)
246                while unsigned(parent) != 0 and (unsigned(elt) == unsigned(right)):
247                    elt = parent
248
249                    # avoid using GetValueFromAddress
250                    addr = elt.__getattr__(field_name).__getattr__('rbe_parent')&~1
251                    parent = value(elt.GetSBValue().CreateValueFromExpression(None,'(void *)'+str(addr)))
252                    parent = cast(parent, element_type)
253
254                    right = cast(parent.__getattr__(field_name).__getattr__('rbe_right'), element_type)
255
256                # avoid using GetValueFromAddress
257                addr = elt.__getattr__(field_name).__getattr__('rbe_parent')&~1
258                elt = value(elt.GetSBValue().CreateValueFromExpression(None,'(void *)'+str(addr)))
259                elt = cast(elt, element_type)
260
261
262def IterateSchedPriorityQueue(root, element_type, field_name):
263    """ iterate over a priority queue as defined with struct priority_queue from osfmk/kern/priority_queue.h
264            root         - value : Value object for the priority queue
265            element_type - str   : Type of the link element
266            field_name   - str   : Name of the field in link element's structure
267        returns:
268            A generator does not return. It is used for iterating
269            value  : an object thats of type (element_type). Always a pointer object
270    """
271    def _make_pqe(addr):
272        return value(root.GetSBValue().CreateValueFromExpression(None,'(struct priority_queue_entry_sched *)'+str(addr)))
273
274    queue = [unsigned(root.pq_root)]
275
276    while len(queue):
277        elt = _make_pqe(queue.pop())
278
279        while elt:
280            yield containerof(elt, element_type, field_name)
281            addr = unsigned(elt.child)
282            if addr: queue.append(addr)
283            elt = elt.next
284
285def SchedPriorityStableQueueRootPri(root, element_type, field_name):
286    """ Return the root level priority of a priority queue as defined with struct priority_queue from osfmk/kern/priority_queue.h
287            root         - value : Value object for the priority queue
288            element_type - str   : Type of the link element
289            field_name   - str   : Name of the field in link element's structure
290        returns:
291            The sched pri of the root element.
292    """
293    def _make_pqe(addr):
294        return value(root.GetSBValue().CreateValueFromExpression(None,'(struct priority_queue_entry_stable *)'+str(addr)))
295
296    elt = _make_pqe(unsigned(root.pq_root))
297    return (elt.key >> 8);
298
299def IterateMPSCQueue(root, element_type, field_name):
300    """ iterate over an MPSC queue as defined with struct mpsc_queue_head from osfmk/kern/mpsc_queue.h
301            root         - value : Value object for the mpsc queue
302            element_type - str   : Type of the link element
303            field_name   - str   : Name of the field in link element's structure
304        returns:
305            A generator does not return. It is used for iterating
306            value  : an object thats of type (element_type). Always a pointer object
307    """
308    elt = root.mpqh_head.mpqc_next
309    while unsigned(elt):
310        yield containerof(elt, element_type, field_name)
311        elt = elt.mpqc_next
312
313class KernelTarget(object):
314    """ A common kernel object that provides access to kernel objects and information.
315        The class holds global lists for  task, terminated_tasks, procs, zones, zombroc etc.
316        It also provides a way to symbolicate an address or create a value from an address.
317    """
318    def __init__(self, debugger):
319        """ Initialize the kernel debugging environment.
320            Target properties like architecture and connectedness are lazy-evaluted.
321        """
322        self._debugger = debugger # This holds an lldb.SBDebugger object for debugger state
323        self._threads_list = []
324        self._tasks_list = []
325        self._coalitions_list = []
326        self._thread_groups = []
327        self._allproc = []
328        self._terminated_tasks_list = []
329        self._terminated_threads_list = []
330        self._zones_list = []
331        self._zombproc_list = []
332        self._kernel_types_cache = {} #this will cache the Type objects as and when requested.
333        self._version = None
334        self._arch = None
335        self._ptrsize = None # pointer size of kernel, not userspace
336        self.symbolicator = None
337        class _GlobalVariableFind(object):
338            def __init__(self, kern):
339                self._xnu_kernobj_12obscure12 = kern
340            def __getattr__(self, name):
341                v = self._xnu_kernobj_12obscure12.GetGlobalVariable(name)
342                if not v.GetSBValue().IsValid():
343                    # Python 2 swallows all exceptions in hasattr(). That makes it work
344                    # even when global variable is not found. Python 3 has fixed the behavior
345                    # and we can raise only AttributeError here to keep original behavior.
346                    raise AttributeError('No such global variable by name: %s '%str(name))
347                return v
348        self.globals = _GlobalVariableFind(self)
349        LazyTarget.Initialize(debugger)
350
351    def _GetSymbolicator(self):
352        """ Internal function: To initialize the symbolication from lldb.utils
353        """
354        if not self.symbolicator is None:
355            return self.symbolicator
356
357        from lldb.utils import symbolication
358        symbolicator = symbolication.Symbolicator()
359        symbolicator.target = LazyTarget.GetTarget()
360        self.symbolicator = symbolicator
361        return self.symbolicator
362
363    def Symbolicate(self, addr):
364        """ simple method to get name of function/variable from an address. this is equivalent of gdb 'output /a 0xaddress'
365            params:
366                addr - int : typically hex value like 0xffffff80002c0df0
367            returns:
368                str - '' if no symbol found else the symbol name.
369            Note: this function only finds the first symbol. If you expect multiple symbol conflict please use SymbolicateFromAddress()
370        """
371        ret_str = ''
372        syms = self.SymbolicateFromAddress(addr)
373        if len(syms) > 0:
374            ret_str +=syms[0].GetName()
375        return ret_str
376
377    def SymbolicateFromAddress(self, addr, fullSymbol=False):
378        """ symbolicates any given address based on modules loaded in the target.
379            params:
380                addr - int : typically hex value like 0xffffff80002c0df0
381            returns:
382                [] of SBSymbol: In case we don't find anything than empty array is returned.
383                      Note: a type of symbol can be figured out by gettype() function of SBSymbol.
384            example usage:
385                syms = kern.Symbolicate(0xffffff80002c0df0)
386                for s in syms:
387                  if s.GetType() == lldb.eSymbolTypeCode:
388                    print "Function", s.GetName()
389                  if s.GetType() == lldb.eSymbolTypeData:
390                    print "Variable", s.GetName()
391        """
392        if type(int(1)) != type(addr):
393            if str(addr).strip().find("0x") == 0 :
394                addr = int(addr, 16)
395            else:
396                addr = int(addr)
397        addr = self.StripKernelPAC(addr)
398        ret_array = []
399        symbolicator = self._GetSymbolicator()
400        syms = symbolicator.symbolicate(addr)
401        if not syms:
402            return ret_array
403        for s in syms:
404            if fullSymbol:
405                ret_array.append(s)
406            else:
407                ret_array.append(s.get_symbol_context().symbol)
408        return ret_array
409
410    def IsDebuggerConnected(self):
411        proc_state = LazyTarget.GetProcess().state
412        if proc_state == lldb.eStateInvalid : return False
413        if proc_state in [lldb.eStateStopped, lldb.eStateSuspended] : return True
414
415    def GetGlobalVariable(self, name):
416        """ Get the value object representation for a kernel global variable
417            params:
418              name : str - name of the variable. ex. version
419            returns: value - python object representing global variable.
420            raises : Exception in case the variable is not found.
421        """
422        self._globals_cache_dict = caching.GetDynamicCacheData("kern._globals_cache_dict", {})
423        if name not in self._globals_cache_dict:
424            self._globals_cache_dict[name] = value(LazyTarget.GetTarget().FindGlobalVariables(name, 1).GetValueAtIndex(0))
425        return self._globals_cache_dict[name]
426
427    def PERCPU_BASE(self, cpu):
428        """ Get the PERCPU base for the given cpu number
429            params:
430              cpu  : int - the cpu# for this variable
431            returns: int - the base for PERCPU for this cpu index
432        """
433        if self.arch == 'x86_64':
434            return unsigned(self.globals.cpu_data_ptr[cpu].cpu_pcpu_base)
435        elif self.arch.startswith('arm'):
436            data_entries = self.GetGlobalVariable('CpuDataEntries')
437            BootCpuData = addressof(self.GetGlobalVariable('percpu_slot_cpu_data'))
438            return unsigned(data_entries[cpu].cpu_data_vaddr) - unsigned(BootCpuData)
439
440    def PERCPU_GET(self, name, cpu):
441        """ Get the value object representation for a kernel percpu global variable
442            params:
443              name : str - name of the variable. ex. version
444              cpu  : int - the cpu# for this variable
445            returns: value - python object representing global variable.
446            raises : Exception in case the variable is not found.
447        """
448        var = addressof(self.GetGlobalVariable('percpu_slot_' + name))
449        addr = unsigned(var) + self.PERCPU_BASE(cpu)
450        return dereference(self.GetValueFromAddress(addr, var))
451
452    def GetLoadAddressForSymbol(self, name):
453        """ Get the load address of a symbol in the kernel.
454            params:
455              name : str - name of the symbol to lookup
456            returns: int - the load address as an integer. Use GetValueFromAddress to cast to a value.
457            raises : LookupError - if the symbol is not found.
458        """
459        name = str(name)
460        target = LazyTarget.GetTarget()
461        syms_arr = target.FindSymbols(name)
462        if syms_arr.IsValid() and len(syms_arr) > 0:
463            symbol = syms_arr[0].GetSymbol()
464            if symbol.IsValid():
465                return int(symbol.GetStartAddress().GetLoadAddress(target))
466
467        raise LookupError("Symbol not found: " + name)
468
469    def GetValueFromAddress(self, addr, type_str = 'void *'):
470        """ convert a address to value
471            params:
472                addr - int : typically hex value like 0xffffff80008dc390
473                type_str - str: type to cast to. Default type will be void *
474            returns:
475                value : a value object which has address as addr and type is type_str
476        """
477        obj = value(self.globals.version.GetSBValue().CreateValueFromExpression(None,'(void *)'+str(addr)))
478        obj = cast(obj, type_str)
479        return obj
480
481    def GetValueAsType(self, v, t):
482        """ Retrieves a global variable 'v' of type 't' wrapped in a vue object.
483            If 'v' is an address, creates a vue object of the appropriate type.
484            If 'v' is a name, looks for the global variable and asserts its type.
485            Throws:
486                NameError - If 'v' cannot be found
487                TypeError - If 'v' is of the wrong type
488        """
489        if islong(v):
490            return self.GetValueFromAddress(v, t)
491        else:
492            var = LazyTarget.GetTarget().FindGlobalVariables(v, 1)[0]
493            if not var:
494                raise NameError("Failed to find global variable '{0}'".format(v))
495            if var.GetTypeName() != t:
496                raise TypeError("{0} must be of type '{1}', not '{2}'".format(v, t, var.GetTypeName()))
497            return value(var)
498
499    def _GetIterator(self, iter_head_name, next_element_name='next', iter_head_type=None):
500        """ returns an iterator for a collection in kernel memory.
501            params:
502                iter_head_name - str : name of queue_head or list head variable.
503                next_element_name - str : name of the element that leads to next element.
504                                          for ex. in struct zone list 'next_zone' is the linking element.
505            returns:
506                iterable : typically used in conjunction with "for varname in iterable:"
507        """
508        head_element = self.GetGlobalVariable(iter_head_name)
509        return head_element.GetSBValue().linked_list_iter(next_element_name)
510
511    def TruncPage(self, addr):
512        return (addr & ~(unsigned(self.GetGlobalVariable("page_size")) - 1))
513
514    def RoundPage(self, addr):
515        return trunc_page(addr + unsigned(self.GetGlobalVariable("page_size")) - 1)
516
517    def StraddlesPage(self, addr, size):
518        if size > unsigned(self.GetGlobalVariable("page_size")):
519            return True
520        val = ((addr + size) & (unsigned(self.GetGlobalVariable("page_size"))-1))
521        return (val < size and val > 0)
522
523    def StripUserPAC(self, addr):
524        if self.arch != 'arm64e':
525            return addr
526        T0Sz = self.GetGlobalVariable('gT0Sz')
527        return StripPAC(addr, T0Sz)
528
529    def StripKernelPAC(self, addr):
530        if self.arch != 'arm64e':
531            return addr
532        T1Sz = self.GetGlobalVariable('gT1Sz')
533        return StripPAC(addr, T1Sz)
534
535    def PhysToKVARM64(self, addr):
536        ptov_table = self.GetGlobalVariable('ptov_table')
537        for i in range(0, self.GetGlobalVariable('ptov_index')):
538            if (addr >= int(unsigned(ptov_table[i].pa))) and (addr < (int(unsigned(ptov_table[i].pa)) + int(unsigned(ptov_table[i].len)))):
539                return (addr - int(unsigned(ptov_table[i].pa)) + int(unsigned(ptov_table[i].va)))
540        return (addr - unsigned(self.GetGlobalVariable("gPhysBase")) + unsigned(self.GetGlobalVariable("gVirtBase")))
541
542    def PhysToKernelVirt(self, addr):
543        if self.arch == 'x86_64':
544            return (addr + unsigned(self.GetGlobalVariable('physmap_base')))
545        elif self.arch.startswith('arm64'):
546            return self.PhysToKVARM64(addr)
547        elif self.arch.startswith('arm'):
548            return (addr - unsigned(self.GetGlobalVariable("gPhysBase")) + unsigned(self.GetGlobalVariable("gVirtBase")))
549        else:
550            raise ValueError("PhysToVirt does not support {0}".format(self.arch))
551
552    def GetUsecDivisor(self):
553        usec_divisor = caching.GetStaticCacheData("kern.rtc_usec_divisor", None)
554        if not usec_divisor:
555            if self.arch == 'x86_64':
556                usec_divisor = 1000
557            else:
558                rtclockdata_addr = self.GetLoadAddressForSymbol('RTClockData')
559                rtc = self.GetValueFromAddress(rtclockdata_addr, 'struct _rtclock_data_ *')
560                usec_divisor = unsigned(rtc.rtc_usec_divisor)
561            usec_divisor = int(usec_divisor)
562            caching.SaveStaticCacheData('kern.rtc_usec_divisor', usec_divisor)
563        return usec_divisor
564
565    def GetNanotimeFromAbstime(self, abstime):
566        """ convert absolute time (which is in MATUs) to nano seconds.
567            Since based on architecture the conversion may differ.
568            params:
569                abstime - int absolute time as shown by mach_absolute_time
570            returns:
571                int - nanosecs of time
572        """
573        return (abstime * 1000) // self.GetUsecDivisor()
574
575    def __getattribute__(self, name):
576        if name == 'zones' :
577            self._zones_list = caching.GetDynamicCacheData("kern._zones_list", [])
578            if len(self._zones_list) > 0: return self._zones_list
579            zone_array = self.GetGlobalVariable('zone_array')
580            zone_security_array = self.GetGlobalVariable('zone_security_array')
581            for i in range(0, self.GetGlobalVariable('num_zones')):
582                self._zones_list.append([addressof(zone_array[i]), addressof(zone_security_array[i])])
583            caching.SaveDynamicCacheData("kern._zones_list", self._zones_list)
584            return self._zones_list
585
586        if name == 'threads' :
587            self._threads_list = caching.GetDynamicCacheData("kern._threads_list", [])
588            if len(self._threads_list) > 0 : return self._threads_list
589            thread_queue_head = self.GetGlobalVariable('threads')
590            thread_type = LazyTarget.GetTarget().FindFirstType('thread')
591            thread_ptr_type = thread_type.GetPointerType()
592            for th in IterateQueue(thread_queue_head, thread_ptr_type, 'threads'):
593                self._threads_list.append(th)
594            caching.SaveDynamicCacheData("kern._threads_list", self._threads_list)
595            return self._threads_list
596
597        if name == 'tasks' :
598            self._tasks_list = caching.GetDynamicCacheData("kern._tasks_list", [])
599            if len(self._tasks_list) > 0 : return self._tasks_list
600            task_queue_head = self.GetGlobalVariable('tasks')
601            task_type = LazyTarget.GetTarget().FindFirstType('task')
602            task_ptr_type = task_type.GetPointerType()
603            for tsk in IterateQueue(task_queue_head, task_ptr_type, 'tasks'):
604                self._tasks_list.append(tsk)
605            caching.SaveDynamicCacheData("kern._tasks_list", self._tasks_list)
606            return self._tasks_list
607
608        if name == 'coalitions' :
609            self._coalitions_list = caching.GetDynamicCacheData("kern._coalitions_list", [])
610            if len(self._coalitions_list) > 0 : return self._coalitions_list
611            coalition_queue_head = self.GetGlobalVariable('coalitions_q')
612            coalition_type = LazyTarget.GetTarget().FindFirstType('coalition')
613            coalition_ptr_type = coalition_type.GetPointerType()
614            for coal in IterateLinkageChain(addressof(coalition_queue_head), coalition_ptr_type, 'coalitions'):
615                self._coalitions_list.append(coal)
616            caching.SaveDynamicCacheData("kern._coalitions_list", self._coalitions_list)
617            return self._coalitions_list
618
619        if name == 'thread_groups' :
620            self._thread_groups_list = caching.GetDynamicCacheData("kern._thread_groups_list", [])
621            if len(self._thread_groups_list) > 0 : return self._thread_groups_list
622            thread_groups_queue_head = self.GetGlobalVariable('tg_queue')
623            thread_group_type = LazyTarget.GetTarget().FindFirstType('thread_group')
624            thread_groups_ptr_type = thread_group_type.GetPointerType()
625            for coal in IterateLinkageChain(addressof(thread_groups_queue_head), thread_groups_ptr_type, 'tg_queue_chain'):
626                self._thread_groups_list.append(coal)
627            caching.SaveDynamicCacheData("kern._thread_groups_list", self._thread_groups_list)
628            return self._thread_groups_list
629
630        if name == 'terminated_tasks' :
631            self._terminated_tasks_list = caching.GetDynamicCacheData("kern._terminated_tasks_list", [])
632            if len(self._terminated_tasks_list) > 0 : return self._terminated_tasks_list
633            task_queue_head = self.GetGlobalVariable('terminated_tasks')
634            task_type = LazyTarget.GetTarget().FindFirstType('task')
635            task_ptr_type = task_type.GetPointerType()
636            for tsk in IterateQueue(task_queue_head, task_ptr_type, 'tasks'):
637                self._terminated_tasks_list.append(tsk)
638            caching.SaveDynamicCacheData("kern._terminated_tasks_list", self._terminated_tasks_list)
639            return self._terminated_tasks_list
640
641        if name == 'terminated_threads' :
642            self._terminated_threads_list = caching.GetDynamicCacheData("kern._terminated_threads_list", [])
643            if len(self._terminated_threads_list) > 0 : return self._terminated_threads_list
644            thread_queue_head = self.GetGlobalVariable('terminated_threads')
645            thread_type = LazyTarget.GetTarget().FindFirstType('thread')
646            thread_ptr_type = thread_type.GetPointerType()
647            for trd in IterateQueue(thread_queue_head, thread_ptr_type, 'threads'):
648                self._terminated_threads_list.append(trd)
649            caching.SaveDynamicCacheData("kern._terminated_threads_list", self._terminated_threads_list)
650            return self._terminated_threads_list
651
652        if name == 'procs' :
653            self._allproc = caching.GetDynamicCacheData("kern._allproc", [])
654            if len(self._allproc) > 0 : return self._allproc
655            all_proc_head = self.GetGlobalVariable('allproc')
656            proc_val = cast(all_proc_head.lh_first, 'proc *')
657            while proc_val != 0:
658                self._allproc.append(proc_val)
659                proc_val = cast(proc_val.p_list.le_next, 'proc *')
660            caching.SaveDynamicCacheData("kern._allproc", self._allproc)
661            return self._allproc
662
663        if name == 'interrupt_stats' :
664            self._interrupt_stats_list = caching.GetDynamicCacheData("kern._interrupt_stats_list", [])
665            if len(self._interrupt_stats_list) > 0 : return self._interrupt_stats_list
666            interrupt_stats_head = self.GetGlobalVariable('gInterruptAccountingDataList')
667            interrupt_stats_type = LazyTarget.GetTarget().FindFirstType('IOInterruptAccountingData')
668            interrupt_stats_ptr_type = interrupt_stats_type.GetPointerType()
669            for interrupt_stats_obj in IterateQueue(interrupt_stats_head, interrupt_stats_ptr_type, 'chain'):
670                self._interrupt_stats_list.append(interrupt_stats_obj)
671            caching.SaveDynamicCacheData("kern._interrupt_stats", self._interrupt_stats_list)
672            return self._interrupt_stats_list
673
674        if name == 'zombprocs' :
675            self._zombproc_list = caching.GetDynamicCacheData("kern._zombproc_list", [])
676            if len(self._zombproc_list) > 0 : return self._zombproc_list
677            zproc_head = self.GetGlobalVariable('zombproc')
678            proc_val = cast(zproc_head.lh_first, 'proc *')
679            while proc_val != 0:
680                self._zombproc_list.append(proc_val)
681                proc_val = cast(proc_val.p_list.le_next, 'proc *')
682            caching.SaveDynamicCacheData("kern._zombproc_list", self._zombproc_list)
683            return self._zombproc_list
684
685        if name == 'version' :
686            self._version = caching.GetStaticCacheData("kern.version", None)
687            if self._version != None : return self._version
688            self._version = str(self.GetGlobalVariable('version'))
689            caching.SaveStaticCacheData("kern.version", self._version)
690            return self._version
691
692        if name == 'arch' :
693            self._arch = caching.GetStaticCacheData("kern.arch", None)
694            if self._arch != None : return self._arch
695            arch = LazyTarget.GetTarget().triple.split('-')[0]
696            if arch in ('armv7', 'armv7s', 'armv7k'):
697                raise UnsupportedArchitectureError(arch)
698            else:
699                self._arch = arch
700            caching.SaveStaticCacheData("kern.arch", self._arch)
701            return self._arch
702
703        if name == 'ptrsize' :
704            self._ptrsize = caching.GetStaticCacheData("kern.ptrsize", None)
705            if self._ptrsize != None : return self._ptrsize
706            arch = LazyTarget.GetTarget().triple.split('-')[0]
707            if arch == 'x86_64' or arch.startswith('arm64'):
708                self._ptrsize = 8
709            else:
710                self._ptrsize = 4
711            caching.SaveStaticCacheData("kern.ptrsize", self._ptrsize)
712            return self._ptrsize
713
714        if name == 'VM_MIN_KERNEL_ADDRESS':
715            if self.arch == 'x86_64':
716                return unsigned(0xFFFFFF8000000000)
717            elif self.arch.startswith('arm64'):
718                return unsigned(0xffffffe000000000)
719            else:
720                return unsigned(0x80000000)
721
722        if name == 'VM_MIN_KERNEL_AND_KEXT_ADDRESS':
723            if self.arch == 'x86_64':
724                return self.VM_MIN_KERNEL_ADDRESS - 0x80000000
725            else:
726                return self.VM_MIN_KERNEL_ADDRESS
727
728        return object.__getattribute__(self, name)
729