]> git.saurik.com Git - apple/xnu.git/blob - tools/lldbmacros/core/kernelcore.py
26a4dcf7a0a53fd981f3413cc997c7acf086ce26
[apple/xnu.git] / tools / lldbmacros / core / kernelcore.py
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
6 from cvalue import *
7 from lazytarget import *
8 from configuration import *
9 import caching
10 import lldb
11
12 def 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
31 def 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
49 def 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
71 def IterateQueue(queue_head, element_ptr_type, element_field_name):
72 """ iterate over a queue in kernel of type queue_head_t. refer to osfmk/kern/queue.h
73 params:
74 queue_head - value : Value object for queue_head.
75 element_ptr_type - lldb.SBType : a pointer type of the element 'next' points to. Typically its structs like thread, task etc..
76 - str : OR a string describing the type. ex. 'task *'
77 element_field_name - str : name of the field in target struct.
78 returns:
79 A generator does not return. It is used for iterating.
80 value : an object thats of type (element_type) queue_head->next. Always a pointer object
81 example usage:
82 for page_meta in IterateQueue(kern.globals.first_zone.pages.all_free, 'struct zone_page_metadata *', 'pages'):
83 print page_meta
84 """
85 if type(element_ptr_type) == str :
86 element_ptr_type = gettype(element_ptr_type)
87
88 queue_head = queue_head.GetSBValue()
89 queue_head_addr = 0x0
90 if queue_head.TypeIsPointerType():
91 queue_head_addr = queue_head.GetValueAsUnsigned()
92 else:
93 queue_head_addr = queue_head.GetAddress().GetLoadAddress(LazyTarget.GetTarget())
94 cur_elt = queue_head.GetChildMemberWithName('next')
95 while True:
96
97 if not cur_elt.IsValid() or cur_elt.GetValueAsUnsigned() == 0 or cur_elt.GetValueAsUnsigned() == queue_head_addr:
98 break
99 elt = cur_elt.Cast(element_ptr_type)
100 yield value(elt)
101 cur_elt = elt.GetChildMemberWithName(element_field_name).GetChildMemberWithName('next')
102
103
104
105 class KernelTarget(object):
106 """ A common kernel object that provides access to kernel objects and information.
107 The class holds global lists for task, terminated_tasks, procs, zones, zombroc etc.
108 It also provides a way to symbolicate an address or create a value from an address.
109 """
110 def __init__(self, debugger):
111 """ Initialize the kernel debugging environment.
112 Target properties like architecture and connectedness are lazy-evaluted.
113 """
114 self._debugger = debugger # This holds an lldb.SBDebugger object for debugger state
115 self._threads_list = []
116 self._tasks_list = []
117 self._coalitions_list = []
118 self._allproc = []
119 self._terminated_tasks_list = []
120 self._zones_list = []
121 self._zombproc_list = []
122 self._kernel_types_cache = {} #this will cache the Type objects as and when requested.
123 self._version = None
124 self._arch = None
125 self._ptrsize = None # pointer size of kernel, not userspace
126 self.symbolicator = None
127 class _GlobalVariableFind(object):
128 def __init__(self, kern):
129 self._xnu_kernobj_12obscure12 = kern
130 def __getattr__(self, name):
131 v = self._xnu_kernobj_12obscure12.GetGlobalVariable(name)
132 if not v.GetSBValue().IsValid():
133 raise ValueError('no such global variable by name: %s '%str(name))
134 return v
135 self.globals = _GlobalVariableFind(self)
136 LazyTarget.Initialize(debugger)
137
138 def _GetSymbolicator(self):
139 """ Internal function: To initialize the symbolication from lldb.utils
140 """
141 if not self.symbolicator is None:
142 return self.symbolicator
143
144 from lldb.utils import symbolication
145 symbolicator = symbolication.Symbolicator()
146 symbolicator.target = LazyTarget.GetTarget()
147 self.symbolicator = symbolicator
148 return self.symbolicator
149
150 def Symbolicate(self, addr):
151 """ simple method to get name of function/variable from an address. this is equivalent of gdb 'output /a 0xaddress'
152 params:
153 addr - int : typically hex value like 0xffffff80002c0df0
154 returns:
155 str - '' if no symbol found else the symbol name.
156 Note: this function only finds the first symbol. If you expect multiple symbol conflict please use SymbolicateFromAddress()
157 """
158 ret_str = ''
159 syms = self.SymbolicateFromAddress(addr)
160 if len(syms) > 0:
161 ret_str +=syms[0].GetName()
162 return ret_str
163
164 def SymbolicateFromAddress(self, addr):
165 """ symbolicates any given address based on modules loaded in the target.
166 params:
167 addr - int : typically hex value like 0xffffff80002c0df0
168 returns:
169 [] of SBSymbol: In case we don't find anything than empty array is returned.
170 Note: a type of symbol can be figured out by gettype() function of SBSymbol.
171 example usage:
172 syms = kern.Symbolicate(0xffffff80002c0df0)
173 for s in syms:
174 if s.GetType() == lldb.eSymbolTypeCode:
175 print "Function", s.GetName()
176 if s.GetType() == lldb.eSymbolTypeData:
177 print "Variable", s.GetName()
178 """
179 if type(int(1)) != type(addr):
180 if str(addr).strip().find("0x") == 0 :
181 addr = int(addr, 16)
182 else:
183 addr = int(addr)
184 ret_array = []
185 symbolicator = self._GetSymbolicator()
186 syms = symbolicator.symbolicate(addr)
187 if not syms:
188 return ret_array
189 for s in syms:
190 ret_array.append(s.get_symbol_context().symbol)
191 return ret_array
192
193 def IsDebuggerConnected(self):
194 proc_state = LazyTarget.GetProcess().state
195 if proc_state == lldb.eStateInvalid : return False
196 if proc_state in [lldb.eStateStopped, lldb.eStateSuspended] : return True
197
198 def GetGlobalVariable(self, name):
199 """ Get the value object representation for a kernel global variable
200 params:
201 name : str - name of the variable. ex. version
202 returns: value - python object representing global variable.
203 raises : Exception in case the variable is not found.
204 """
205 return value(LazyTarget.GetTarget().FindGlobalVariables(name, 0).GetValueAtIndex(0))
206
207 def GetLoadAddressForSymbol(self, name):
208 """ Get the load address of a symbol in the kernel.
209 params:
210 name : str - name of the symbol to lookup
211 returns: int - the load address as an integer. Use GetValueFromAddress to cast to a value.
212 raises : LookupError - if the symbol is not found.
213 """
214 name = str(name)
215 target = LazyTarget.GetTarget()
216 syms_arr = target.FindSymbols(name)
217 if syms_arr.IsValid() and len(syms_arr) > 0:
218 symbol = syms_arr[0].GetSymbol()
219 if symbol.IsValid():
220 return int(symbol.GetStartAddress().GetLoadAddress(target))
221
222 raise LookupError("Symbol not found: " + name)
223
224 def GetValueFromAddress(self, addr, type_str = 'void *'):
225 """ convert a address to value
226 params:
227 addr - int : typically hex value like 0xffffff80008dc390
228 type_str - str: type to cast to. Default type will be void *
229 returns:
230 value : a value object which has address as addr and type is type_str
231 """
232 obj = value(self.globals.version.GetSBValue().CreateValueFromExpression(None,'(void *)'+str(addr)))
233 obj = cast(obj, type_str)
234 return obj
235
236 def GetValueAsType(self, v, t):
237 """ Retrieves a global variable 'v' of type 't' wrapped in a vue object.
238 If 'v' is an address, creates a vue object of the appropriate type.
239 If 'v' is a name, looks for the global variable and asserts its type.
240 Throws:
241 NameError - If 'v' cannot be found
242 TypeError - If 'v' is of the wrong type
243 """
244 if islong(v):
245 return self.GetValueFromAddress(v, t)
246 else:
247 var = LazyTarget.GetTarget().FindGlobalVariables(v, 1)[0]
248 if not var:
249 raise NameError("Failed to find global variable '{0}'".format(v))
250 if var.GetTypeName() != t:
251 raise TypeError("{0} must be of type '{1}', not '{2}'".format(v, t, var.GetTypeName()))
252 return value(var)
253
254 def _GetIterator(self, iter_head_name, next_element_name='next', iter_head_type=None):
255 """ returns an iterator for a collection in kernel memory.
256 params:
257 iter_head_name - str : name of queue_head or list head variable.
258 next_element_name - str : name of the element that leads to next element.
259 for ex. in struct zone list 'next_zone' is the linking element.
260 returns:
261 iterable : typically used in conjunction with "for varname in iterable:"
262 """
263 head_element = self.GetGlobalVariable(iter_head_name)
264 return head_element.GetSBValue().linked_list_iter(next_element_name)
265
266 def TruncPage(self, addr):
267 return (addr & ~(unsigned(self.GetGlobalVariable("page_size")) - 1))
268
269 def RoundPage(self, addr):
270 return trunc_page(addr + unsigned(self.GetGlobalVariable("page_size")) - 1)
271
272 def StraddlesPage(self, addr, size):
273 if size > unsigned(self.GetGlobalVariable("page_size")):
274 return True
275 val = ((addr + size) & (unsigned(self.GetGlobalVariable("page_size"))-1))
276 return (val < size and val > 0)
277
278 def PhysToKernelVirt(self, addr):
279 if self.arch == 'x86_64':
280 return (addr + unsigned(self.GetGlobalVariable('physmap_base')))
281 elif self.arch == 'arm' or self.arch == 'arm64':
282 return (addr - unsigned(self.GetGlobalVariable("gPhysBase")) + unsigned(self.GetGlobalVariable("gVirtBase")))
283 else:
284 raise ValueError("PhysToVirt does not support {0}".format(arch))
285
286 def __getattribute__(self, name):
287 if name == 'zones' :
288 self._zones_list = caching.GetDynamicCacheData("kern._zones_list", [])
289 if len(self._zones_list) > 0: return self._zones_list
290 first_zone = self.GetGlobalVariable('first_zone')
291 for z in IterateLinkedList(first_zone, 'next_zone'):
292 self._zones_list.append(z)
293 caching.SaveDynamicCacheData("kern._zones_list", self._zones_list)
294 return self._zones_list
295
296 if name == 'threads' :
297 self._threads_list = caching.GetDynamicCacheData("kern._threads_list", [])
298 if len(self._threads_list) > 0 : return self._threads_list
299 thread_queue_head = self.GetGlobalVariable('threads')
300 thread_type = LazyTarget.GetTarget().FindFirstType('thread')
301 thread_ptr_type = thread_type.GetPointerType()
302 for th in IterateQueue(thread_queue_head, thread_ptr_type, 'threads'):
303 self._threads_list.append(th)
304 caching.SaveDynamicCacheData("kern._threads_list", self._threads_list)
305 return self._threads_list
306
307 if name == 'tasks' :
308 self._tasks_list = caching.GetDynamicCacheData("kern._tasks_list", [])
309 if len(self._tasks_list) > 0 : return self._tasks_list
310 task_queue_head = self.GetGlobalVariable('tasks')
311 task_type = LazyTarget.GetTarget().FindFirstType('task')
312 task_ptr_type = task_type.GetPointerType()
313 for tsk in IterateQueue(task_queue_head, task_ptr_type, 'tasks'):
314 self._tasks_list.append(tsk)
315 caching.SaveDynamicCacheData("kern._tasks_list", self._tasks_list)
316 return self._tasks_list
317
318 if name == 'coalitions' :
319 self._coalitions_list = caching.GetDynamicCacheData("kern._coalitions_list", [])
320 if len(self._coalitions_list) > 0 : return self._coalitions_list
321 coalition_queue_head = self.GetGlobalVariable('coalitions')
322 coalition_type = LazyTarget.GetTarget().FindFirstType('coalition')
323 coalition_ptr_type = coalition_type.GetPointerType()
324 for tsk in IterateQueue(coalition_queue_head, coalition_ptr_type, 'coalitions'):
325 self._coalitions_list.append(tsk)
326 caching.SaveDynamicCacheData("kern._coalitions_list", self._coalitions_list)
327 return self._coalitions_list
328
329 if name == 'terminated_tasks' :
330 self._terminated_tasks_list = caching.GetDynamicCacheData("kern._terminated_tasks_list", [])
331 if len(self._terminated_tasks_list) > 0 : return self._terminated_tasks_list
332 task_queue_head = self.GetGlobalVariable('terminated_tasks')
333 task_type = LazyTarget.GetTarget().FindFirstType('task')
334 task_ptr_type = task_type.GetPointerType()
335 for tsk in IterateQueue(task_queue_head, task_ptr_type, 'tasks'):
336 self._terminated_tasks_list.append(tsk)
337 caching.SaveDynamicCacheData("kern._terminated_tasks_list", self._terminated_tasks_list)
338 return self._terminated_tasks_list
339
340 if name == 'procs' :
341 self._allproc = caching.GetDynamicCacheData("kern._allproc", [])
342 if len(self._allproc) > 0 : return self._allproc
343 all_proc_head = self.GetGlobalVariable('allproc')
344 proc_val = cast(all_proc_head.lh_first, 'proc *')
345 while proc_val != 0:
346 self._allproc.append(proc_val)
347 proc_val = cast(proc_val.p_list.le_next, 'proc *')
348 caching.SaveDynamicCacheData("kern._allproc", self._allproc)
349 return self._allproc
350
351 if name == 'interrupt_stats' :
352 self._interrupt_stats_list = caching.GetDynamicCacheData("kern._interrupt_stats_list", [])
353 if len(self._interrupt_stats_list) > 0 : return self._interrupt_stats_list
354 interrupt_stats_head = self.GetGlobalVariable('gInterruptAccountingDataList')
355 interrupt_stats_type = LazyTarget.GetTarget().FindFirstType('IOInterruptAccountingData')
356 interrupt_stats_ptr_type = interrupt_stats_type.GetPointerType()
357 for interrupt_stats_obj in IterateQueue(interrupt_stats_head, interrupt_stats_ptr_type, 'chain'):
358 self._interrupt_stats_list.append(interrupt_stats_obj)
359 caching.SaveDynamicCacheData("kern._interrupt_stats", self._interrupt_stats_list)
360 return self._interrupt_stats_list
361
362 if name == 'zombprocs' :
363 self._zombproc_list = caching.GetDynamicCacheData("kern._zombproc_list", [])
364 if len(self._zombproc_list) > 0 : return self._zombproc_list
365 zproc_head = self.GetGlobalVariable('zombproc')
366 proc_val = cast(zproc_head.lh_first, 'proc *')
367 while proc_val != 0:
368 self._zombproc_list.append(proc_val)
369 proc_val = cast(proc_val.p_list.le_next, 'proc *')
370 caching.SaveDynamicCacheData("kern._zombproc_list", self._zombproc_list)
371 return self._zombproc_list
372
373 if name == 'version' :
374 self._version = caching.GetStaticCacheData("kern.version", None)
375 if self._version != None : return self._version
376 self._version = str(self.GetGlobalVariable('version'))
377 caching.SaveStaticCacheData("kern.version", self._version)
378 return self._version
379
380 if name == 'arch' :
381 self._arch = caching.GetStaticCacheData("kern.arch", None)
382 if self._arch != None : return self._arch
383 arch = LazyTarget.GetTarget().triple.split('-')[0]
384 if arch in ('armv7', 'armv7s', 'armv7k'):
385 self._arch = 'arm'
386 else:
387 self._arch = arch
388 caching.SaveStaticCacheData("kern.arch", self._arch)
389 return self._arch
390
391 if name == 'ptrsize' :
392 self._ptrsize = caching.GetStaticCacheData("kern.ptrsize", None)
393 if self._ptrsize != None : return self._ptrsize
394 arch = LazyTarget.GetTarget().triple.split('-')[0]
395 if arch in ('x86_64', 'arm64'):
396 self._ptrsize = 8
397 else:
398 self._ptrsize = 4
399 caching.SaveStaticCacheData("kern.ptrsize", self._ptrsize)
400 return self._ptrsize
401
402 return object.__getattribute__(self, name)
403