]> git.saurik.com Git - apple/xnu.git/blame - tools/lldbmacros/core/kernelcore.py
xnu-3789.51.2.tar.gz
[apple/xnu.git] / tools / lldbmacros / core / kernelcore.py
CommitLineData
39236c6e
A
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"""
5
6from cvalue import *
7from lazytarget import *
8from configuration import *
9import caching
10import lldb
11
12def IterateTAILQ_HEAD(headval, element_name):
13 """ iterate over a TAILQ_HEAD in kernel. refer to bsd/sys/queue.h
14 params:
15 headval - value : value object representing the head of the list
16 element_name- str : string name of the field which holds the list links.
17 returns:
18 A generator does not return. It is used for iterating.
19 value : an object that is of type as headval->tqh_first. Always a pointer object
20 example usage:
21 list_head = kern.GetGlobalVariable('mountlist')
22 for entryobj in IterateTAILQ_HEAD(list_head, 'mnt_list'):
23 print GetEntrySummary(entryobj)
24 """
25 iter_val = headval.tqh_first
26 while unsigned(iter_val) != 0 :
27 yield iter_val
28 iter_val = iter_val.__getattr__(element_name).tqe_next
29 #end of yield loop
30
31def IterateLinkedList(element, field_name):
32 """ iterate over a linked list.
33 This is equivalent to elt = element; while(elt) { do_work(elt); elt = elt-><field_name>; }
34 params:
35 element - value : value object representing element in the list.
36 field_name - str : name of field that holds pointer to next element
37 returns: Nothing. This is used as iterable
38 example usage:
39 first_zone = kern.GetGlobalVariable('first_zone')
40 for zone in IterateLinkedList(first_zone, 'next_zone'):
41 print GetZoneSummary(zone)
42 """
43 elt = element
44 while unsigned(elt) != 0:
45 yield elt
46 elt = elt.__getattr__(field_name)
47 #end of while loop
48
49def IterateListEntry(element, element_type, field_name):
50 """ iterate over a list as defined with LIST_HEAD in bsd/sys/queue.h
51 params:
52 element - value : Value object for lh_first
53 element_type - str : Type of the next element
54 field_name - str : Name of the field in next element's structure
55 returns:
56 A generator does not return. It is used for iterating
57 value : an object thats of type (element_type) head->le_next. Always a pointer object
58 example usage:
59 headp = kern.globals.initproc.p_children
60 for pp in IterateListEntry(headp, 'struct proc *', 'p_sibling'):
61 print GetProcInfo(pp)
62 """
63 elt = element.lh_first
64 if type(element_type) == str:
65 element_type = gettype(element_type)
66 while unsigned(elt) != 0:
67 yield elt
68 next_el = elt.__getattr__(field_name).le_next
69 elt = cast(next_el, element_type)
70
3e170ce0
A
71def IterateLinkageChain(queue_head, element_type, field_name, field_ofst=0):
72 """ Iterate over a Linkage Chain queue in kernel of type queue_head_t. (osfmk/kern/queue.h method 1)
73 This is equivalent to the qe_foreach_element() macro
74 params:
75 queue_head - value : Value object for queue_head.
76 element_type - lldb.SBType : pointer type of the element which contains the queue_chain_t. Typically its structs like thread, task etc..
77 - str : OR a string describing the type. ex. 'task *'
78 field_name - str : Name of the field (in element) which holds a queue_chain_t
79 field_ofst - int : offset from the 'field_name' (in element) which holds a queue_chain_t
80 This is mostly useful if a particular element contains an array of queue_chain_t
81 returns:
82 A generator does not return. It is used for iterating.
83 value : An object thats of type (element_type). Always a pointer object
84 example usage:
85 coalq = kern.GetGlobalVariable('coalitions_q')
86 for coal in IterateLinkageChain(coalq, 'struct coalition *', 'coalitions'):
87 print GetCoalitionInfo(coal)
88 """
89 global kern
90 if type(element_type) == str:
91 element_type = gettype(element_type)
92
93 if unsigned(queue_head) == 0:
94 return
95
96 if element_type.IsPointerType():
97 elem_ofst = getfieldoffset(element_type.GetPointeeType(), field_name) + field_ofst
98 else:
99 elem_ofst = getfieldoffset(element_type, field_name) + field_ofst
100
101 link = queue_head.next
102 while (unsigned(link) != unsigned(queue_head)):
103 addr = unsigned(link) - elem_ofst;
104 # I can't use the GetValueFromAddress function of the kernel class
105 # because I have no instance of that class!
106 obj = value(link.GetSBValue().CreateValueFromExpression(None,'(void *)'+str(addr)))
107 obj = cast(obj, element_type)
108 yield obj
109 link = link.next
110
111
39037602 112def IterateQueue(queue_head, element_ptr_type, element_field_name, backwards=False, unpack_ptr_fn=None):
3e170ce0 113 """ Iterate over an Element Chain queue in kernel of type queue_head_t. (osfmk/kern/queue.h method 2)
39236c6e
A
114 params:
115 queue_head - value : Value object for queue_head.
3e170ce0 116 element_ptr_type - lldb.SBType : a pointer type of the element 'next' points to. Typically its structs like thread, task etc..
39236c6e
A
117 - str : OR a string describing the type. ex. 'task *'
118 element_field_name - str : name of the field in target struct.
3e170ce0 119 backwards - backwards : traverse the queue backwards
39037602 120 unpack_ptr_fn - function : a function ptr of signature def unpack_ptr(long v) which returns long.
39236c6e
A
121 returns:
122 A generator does not return. It is used for iterating.
123 value : an object thats of type (element_type) queue_head->next. Always a pointer object
fe8ab488
A
124 example usage:
125 for page_meta in IterateQueue(kern.globals.first_zone.pages.all_free, 'struct zone_page_metadata *', 'pages'):
126 print page_meta
39236c6e
A
127 """
128 if type(element_ptr_type) == str :
129 element_ptr_type = gettype(element_ptr_type)
130
131 queue_head = queue_head.GetSBValue()
132 queue_head_addr = 0x0
133 if queue_head.TypeIsPointerType():
134 queue_head_addr = queue_head.GetValueAsUnsigned()
135 else:
136 queue_head_addr = queue_head.GetAddress().GetLoadAddress(LazyTarget.GetTarget())
39037602
A
137
138 def unpack_ptr_and_recast(v):
139 if unpack_ptr_fn is None:
140 return v
141 v_unpacked = unpack_ptr_fn(v.GetValueAsUnsigned())
142 obj = v.CreateValueFromExpression(None,'(void *)'+str(v_unpacked))
143 obj.Cast(element_ptr_type)
144 return obj
145
3e170ce0 146 if backwards:
39037602 147 cur_elt = unpack_ptr_and_recast(queue_head.GetChildMemberWithName('prev'))
3e170ce0 148 else:
39037602 149 cur_elt = unpack_ptr_and_recast(queue_head.GetChildMemberWithName('next'))
3e170ce0 150
39236c6e
A
151 while True:
152
153 if not cur_elt.IsValid() or cur_elt.GetValueAsUnsigned() == 0 or cur_elt.GetValueAsUnsigned() == queue_head_addr:
154 break
155 elt = cur_elt.Cast(element_ptr_type)
156 yield value(elt)
3e170ce0 157 if backwards:
39037602 158 cur_elt = unpack_ptr_and_recast(elt.GetChildMemberWithName(element_field_name).GetChildMemberWithName('prev'))
3e170ce0 159 else:
39037602
A
160 cur_elt = unpack_ptr_and_recast(elt.GetChildMemberWithName(element_field_name).GetChildMemberWithName('next'))
161
39236c6e
A
162
163class KernelTarget(object):
164 """ A common kernel object that provides access to kernel objects and information.
165 The class holds global lists for task, terminated_tasks, procs, zones, zombroc etc.
166 It also provides a way to symbolicate an address or create a value from an address.
167 """
168 def __init__(self, debugger):
169 """ Initialize the kernel debugging environment.
170 Target properties like architecture and connectedness are lazy-evaluted.
171 """
172 self._debugger = debugger # This holds an lldb.SBDebugger object for debugger state
173 self._threads_list = []
174 self._tasks_list = []
fe8ab488 175 self._coalitions_list = []
39236c6e
A
176 self._allproc = []
177 self._terminated_tasks_list = []
178 self._zones_list = []
179 self._zombproc_list = []
180 self._kernel_types_cache = {} #this will cache the Type objects as and when requested.
181 self._version = None
182 self._arch = None
183 self._ptrsize = None # pointer size of kernel, not userspace
184 self.symbolicator = None
185 class _GlobalVariableFind(object):
186 def __init__(self, kern):
187 self._xnu_kernobj_12obscure12 = kern
188 def __getattr__(self, name):
189 v = self._xnu_kernobj_12obscure12.GetGlobalVariable(name)
190 if not v.GetSBValue().IsValid():
3e170ce0 191 raise ValueError('No such global variable by name: %s '%str(name))
39236c6e
A
192 return v
193 self.globals = _GlobalVariableFind(self)
194 LazyTarget.Initialize(debugger)
195
196 def _GetSymbolicator(self):
197 """ Internal function: To initialize the symbolication from lldb.utils
198 """
199 if not self.symbolicator is None:
200 return self.symbolicator
201
202 from lldb.utils import symbolication
203 symbolicator = symbolication.Symbolicator()
204 symbolicator.target = LazyTarget.GetTarget()
205 self.symbolicator = symbolicator
206 return self.symbolicator
207
208 def Symbolicate(self, addr):
209 """ simple method to get name of function/variable from an address. this is equivalent of gdb 'output /a 0xaddress'
210 params:
211 addr - int : typically hex value like 0xffffff80002c0df0
212 returns:
213 str - '' if no symbol found else the symbol name.
214 Note: this function only finds the first symbol. If you expect multiple symbol conflict please use SymbolicateFromAddress()
215 """
216 ret_str = ''
217 syms = self.SymbolicateFromAddress(addr)
218 if len(syms) > 0:
219 ret_str +=syms[0].GetName()
220 return ret_str
221
222 def SymbolicateFromAddress(self, addr):
223 """ symbolicates any given address based on modules loaded in the target.
224 params:
225 addr - int : typically hex value like 0xffffff80002c0df0
226 returns:
227 [] of SBSymbol: In case we don't find anything than empty array is returned.
228 Note: a type of symbol can be figured out by gettype() function of SBSymbol.
229 example usage:
230 syms = kern.Symbolicate(0xffffff80002c0df0)
231 for s in syms:
232 if s.GetType() == lldb.eSymbolTypeCode:
233 print "Function", s.GetName()
234 if s.GetType() == lldb.eSymbolTypeData:
235 print "Variable", s.GetName()
236 """
237 if type(int(1)) != type(addr):
238 if str(addr).strip().find("0x") == 0 :
239 addr = int(addr, 16)
240 else:
241 addr = int(addr)
242 ret_array = []
243 symbolicator = self._GetSymbolicator()
244 syms = symbolicator.symbolicate(addr)
245 if not syms:
246 return ret_array
247 for s in syms:
248 ret_array.append(s.get_symbol_context().symbol)
249 return ret_array
250
251 def IsDebuggerConnected(self):
252 proc_state = LazyTarget.GetProcess().state
253 if proc_state == lldb.eStateInvalid : return False
254 if proc_state in [lldb.eStateStopped, lldb.eStateSuspended] : return True
255
256 def GetGlobalVariable(self, name):
257 """ Get the value object representation for a kernel global variable
258 params:
259 name : str - name of the variable. ex. version
260 returns: value - python object representing global variable.
261 raises : Exception in case the variable is not found.
262 """
3e170ce0
A
263 self._globals_cache_dict = caching.GetDynamicCacheData("kern._globals_cache_dict", {})
264 if name not in self._globals_cache_dict:
265 self._globals_cache_dict[name] = value(LazyTarget.GetTarget().FindGlobalVariables(name, 1).GetValueAtIndex(0))
266 return self._globals_cache_dict[name]
39236c6e
A
267
268 def GetLoadAddressForSymbol(self, name):
269 """ Get the load address of a symbol in the kernel.
270 params:
271 name : str - name of the symbol to lookup
272 returns: int - the load address as an integer. Use GetValueFromAddress to cast to a value.
273 raises : LookupError - if the symbol is not found.
274 """
275 name = str(name)
276 target = LazyTarget.GetTarget()
277 syms_arr = target.FindSymbols(name)
278 if syms_arr.IsValid() and len(syms_arr) > 0:
279 symbol = syms_arr[0].GetSymbol()
280 if symbol.IsValid():
281 return int(symbol.GetStartAddress().GetLoadAddress(target))
282
283 raise LookupError("Symbol not found: " + name)
284
285 def GetValueFromAddress(self, addr, type_str = 'void *'):
286 """ convert a address to value
287 params:
288 addr - int : typically hex value like 0xffffff80008dc390
289 type_str - str: type to cast to. Default type will be void *
290 returns:
291 value : a value object which has address as addr and type is type_str
292 """
293 obj = value(self.globals.version.GetSBValue().CreateValueFromExpression(None,'(void *)'+str(addr)))
294 obj = cast(obj, type_str)
295 return obj
296
297 def GetValueAsType(self, v, t):
298 """ Retrieves a global variable 'v' of type 't' wrapped in a vue object.
299 If 'v' is an address, creates a vue object of the appropriate type.
300 If 'v' is a name, looks for the global variable and asserts its type.
301 Throws:
302 NameError - If 'v' cannot be found
303 TypeError - If 'v' is of the wrong type
304 """
305 if islong(v):
306 return self.GetValueFromAddress(v, t)
307 else:
308 var = LazyTarget.GetTarget().FindGlobalVariables(v, 1)[0]
309 if not var:
310 raise NameError("Failed to find global variable '{0}'".format(v))
311 if var.GetTypeName() != t:
312 raise TypeError("{0} must be of type '{1}', not '{2}'".format(v, t, var.GetTypeName()))
313 return value(var)
314
315 def _GetIterator(self, iter_head_name, next_element_name='next', iter_head_type=None):
316 """ returns an iterator for a collection in kernel memory.
317 params:
318 iter_head_name - str : name of queue_head or list head variable.
319 next_element_name - str : name of the element that leads to next element.
320 for ex. in struct zone list 'next_zone' is the linking element.
321 returns:
322 iterable : typically used in conjunction with "for varname in iterable:"
323 """
324 head_element = self.GetGlobalVariable(iter_head_name)
325 return head_element.GetSBValue().linked_list_iter(next_element_name)
326
327 def TruncPage(self, addr):
328 return (addr & ~(unsigned(self.GetGlobalVariable("page_size")) - 1))
329
330 def RoundPage(self, addr):
331 return trunc_page(addr + unsigned(self.GetGlobalVariable("page_size")) - 1)
332
333 def StraddlesPage(self, addr, size):
334 if size > unsigned(self.GetGlobalVariable("page_size")):
335 return True
fe8ab488
A
336 val = ((addr + size) & (unsigned(self.GetGlobalVariable("page_size"))-1))
337 return (val < size and val > 0)
39236c6e
A
338
339 def PhysToKernelVirt(self, addr):
340 if self.arch == 'x86_64':
341 return (addr + unsigned(self.GetGlobalVariable('physmap_base')))
fe8ab488 342 elif self.arch == 'arm' or self.arch == 'arm64':
39236c6e
A
343 return (addr - unsigned(self.GetGlobalVariable("gPhysBase")) + unsigned(self.GetGlobalVariable("gVirtBase")))
344 else:
345 raise ValueError("PhysToVirt does not support {0}".format(arch))
346
3e170ce0
A
347 def GetNanotimeFromAbstime(self, abstime):
348 """ convert absolute time (which is in MATUs) to nano seconds.
349 Since based on architecture the conversion may differ.
350 params:
351 abstime - int absolute time as shown by mach_absolute_time
352 returns:
353 int - nanosecs of time
354 """
355 usec_divisor = caching.GetStaticCacheData("kern.rtc_usec_divisor", None)
356 if not usec_divisor:
357 if self.arch == 'x86_64':
358 usec_divisor = 1000
359 else:
360 rtclockdata_addr = self.GetLoadAddressForSymbol('RTClockData')
361 rtc = self.GetValueFromAddress(rtclockdata_addr, 'struct _rtclock_data_ *')
362 usec_divisor = unsigned(rtc.rtc_usec_divisor)
363 usec_divisor = int(usec_divisor)
364 caching.SaveStaticCacheData('kern.rtc_usec_divisor', usec_divisor)
365 nsecs = (abstime * 1000)/usec_divisor
366 return nsecs
367
39236c6e
A
368 def __getattribute__(self, name):
369 if name == 'zones' :
370 self._zones_list = caching.GetDynamicCacheData("kern._zones_list", [])
371 if len(self._zones_list) > 0: return self._zones_list
39037602
A
372 zone_array = self.GetGlobalVariable('zone_array')
373 for i in range(0, self.GetGlobalVariable('num_zones')):
374 self._zones_list.append(addressof(zone_array[i]))
39236c6e
A
375 caching.SaveDynamicCacheData("kern._zones_list", self._zones_list)
376 return self._zones_list
377
378 if name == 'threads' :
379 self._threads_list = caching.GetDynamicCacheData("kern._threads_list", [])
380 if len(self._threads_list) > 0 : return self._threads_list
381 thread_queue_head = self.GetGlobalVariable('threads')
382 thread_type = LazyTarget.GetTarget().FindFirstType('thread')
383 thread_ptr_type = thread_type.GetPointerType()
384 for th in IterateQueue(thread_queue_head, thread_ptr_type, 'threads'):
385 self._threads_list.append(th)
386 caching.SaveDynamicCacheData("kern._threads_list", self._threads_list)
387 return self._threads_list
388
389 if name == 'tasks' :
390 self._tasks_list = caching.GetDynamicCacheData("kern._tasks_list", [])
391 if len(self._tasks_list) > 0 : return self._tasks_list
392 task_queue_head = self.GetGlobalVariable('tasks')
393 task_type = LazyTarget.GetTarget().FindFirstType('task')
394 task_ptr_type = task_type.GetPointerType()
395 for tsk in IterateQueue(task_queue_head, task_ptr_type, 'tasks'):
396 self._tasks_list.append(tsk)
397 caching.SaveDynamicCacheData("kern._tasks_list", self._tasks_list)
398 return self._tasks_list
399
fe8ab488
A
400 if name == 'coalitions' :
401 self._coalitions_list = caching.GetDynamicCacheData("kern._coalitions_list", [])
402 if len(self._coalitions_list) > 0 : return self._coalitions_list
3e170ce0 403 coalition_queue_head = self.GetGlobalVariable('coalitions_q')
fe8ab488
A
404 coalition_type = LazyTarget.GetTarget().FindFirstType('coalition')
405 coalition_ptr_type = coalition_type.GetPointerType()
3e170ce0
A
406 for coal in IterateLinkageChain(addressof(coalition_queue_head), coalition_ptr_type, 'coalitions'):
407 self._coalitions_list.append(coal)
fe8ab488
A
408 caching.SaveDynamicCacheData("kern._coalitions_list", self._coalitions_list)
409 return self._coalitions_list
410
39236c6e
A
411 if name == 'terminated_tasks' :
412 self._terminated_tasks_list = caching.GetDynamicCacheData("kern._terminated_tasks_list", [])
413 if len(self._terminated_tasks_list) > 0 : return self._terminated_tasks_list
414 task_queue_head = self.GetGlobalVariable('terminated_tasks')
415 task_type = LazyTarget.GetTarget().FindFirstType('task')
416 task_ptr_type = task_type.GetPointerType()
417 for tsk in IterateQueue(task_queue_head, task_ptr_type, 'tasks'):
418 self._terminated_tasks_list.append(tsk)
419 caching.SaveDynamicCacheData("kern._terminated_tasks_list", self._terminated_tasks_list)
420 return self._terminated_tasks_list
421
422 if name == 'procs' :
423 self._allproc = caching.GetDynamicCacheData("kern._allproc", [])
424 if len(self._allproc) > 0 : return self._allproc
425 all_proc_head = self.GetGlobalVariable('allproc')
426 proc_val = cast(all_proc_head.lh_first, 'proc *')
427 while proc_val != 0:
428 self._allproc.append(proc_val)
429 proc_val = cast(proc_val.p_list.le_next, 'proc *')
430 caching.SaveDynamicCacheData("kern._allproc", self._allproc)
431 return self._allproc
432
fe8ab488
A
433 if name == 'interrupt_stats' :
434 self._interrupt_stats_list = caching.GetDynamicCacheData("kern._interrupt_stats_list", [])
435 if len(self._interrupt_stats_list) > 0 : return self._interrupt_stats_list
436 interrupt_stats_head = self.GetGlobalVariable('gInterruptAccountingDataList')
437 interrupt_stats_type = LazyTarget.GetTarget().FindFirstType('IOInterruptAccountingData')
438 interrupt_stats_ptr_type = interrupt_stats_type.GetPointerType()
439 for interrupt_stats_obj in IterateQueue(interrupt_stats_head, interrupt_stats_ptr_type, 'chain'):
440 self._interrupt_stats_list.append(interrupt_stats_obj)
441 caching.SaveDynamicCacheData("kern._interrupt_stats", self._interrupt_stats_list)
442 return self._interrupt_stats_list
443
39236c6e
A
444 if name == 'zombprocs' :
445 self._zombproc_list = caching.GetDynamicCacheData("kern._zombproc_list", [])
446 if len(self._zombproc_list) > 0 : return self._zombproc_list
447 zproc_head = self.GetGlobalVariable('zombproc')
448 proc_val = cast(zproc_head.lh_first, 'proc *')
449 while proc_val != 0:
450 self._zombproc_list.append(proc_val)
451 proc_val = cast(proc_val.p_list.le_next, 'proc *')
452 caching.SaveDynamicCacheData("kern._zombproc_list", self._zombproc_list)
453 return self._zombproc_list
454
455 if name == 'version' :
456 self._version = caching.GetStaticCacheData("kern.version", None)
457 if self._version != None : return self._version
458 self._version = str(self.GetGlobalVariable('version'))
459 caching.SaveStaticCacheData("kern.version", self._version)
460 return self._version
461
462 if name == 'arch' :
463 self._arch = caching.GetStaticCacheData("kern.arch", None)
464 if self._arch != None : return self._arch
465 arch = LazyTarget.GetTarget().triple.split('-')[0]
fe8ab488 466 if arch in ('armv7', 'armv7s', 'armv7k'):
39236c6e
A
467 self._arch = 'arm'
468 else:
469 self._arch = arch
470 caching.SaveStaticCacheData("kern.arch", self._arch)
471 return self._arch
472
473 if name == 'ptrsize' :
474 self._ptrsize = caching.GetStaticCacheData("kern.ptrsize", None)
475 if self._ptrsize != None : return self._ptrsize
476 arch = LazyTarget.GetTarget().triple.split('-')[0]
fe8ab488 477 if arch in ('x86_64', 'arm64'):
39236c6e
A
478 self._ptrsize = 8
479 else:
480 self._ptrsize = 4
481 caching.SaveStaticCacheData("kern.ptrsize", self._ptrsize)
482 return self._ptrsize
483
484 return object.__getattribute__(self, name)