1# Copyright 2016 The Fuchsia Authors
2#
3# Use of this source code is governed by a MIT-style
4# license that can be found in the LICENSE file or at
5# https://opensource.org/licenses/MIT
6
7# GDB support for zircon kernel.
8
9# TODO(dje): gdb should let us use a better command class than COMMAND_DATA.
10# TODO(dje): Add arm64 support.
11
12from __future__ import absolute_import
13from __future__ import division
14from __future__ import print_function
15
16import sys
17import gdb
18import gdb.printing
19import re
20from gdb.unwinder import Unwinder
21
22if sys.version_info > (3,):
23  long = int
24
25# The command prefix, passed in from gdbinit.py.
26_ZIRCON_COMMAND_PREFIX = "zircon"
27_KERNEL_EXCEPTION_UNWINDER_PARAMETER = "kernel-exception-unwinder"
28
29_THREAD_MAGIC = 0x74687264
30_BOOT_MAGIC = 0x544f4f42
31_KERNEL_BASE_ADDRESS = "KERNEL_BASE_ADDRESS"
32
33print("Loading zircon.elf-gdb.py ...")
34
35def _get_architecture():
36  # TODO(dje): gdb doesn't provide us with a simple way to do this.
37  return gdb.execute("show architecture", to_string=True)
38
39def _is_x86_64():
40  """Return True if we're on an x86-64 platform."""
41  return re.search(r"x86-64", _get_architecture())
42
43def _is_arm64():
44  """Return True if we're on an aarch64 platform."""
45  return re.search(r"aarch64", _get_architecture())
46
47
48# The default is 2 seconds which is too low.
49# But don't override something the user set.
50# [If the user set it to the default, too bad. :-)]
51# TODO(dje): Alas this trips over upstream PR 20084.
52#_DEFAULT_GDB_REMOTETIMEOUT = 2
53#if int(gdb.parameter("remotetimeout")) == _DEFAULT_GDB_REMOTETIMEOUT:
54#  gdb.execute("set remotetimeout 10")
55
56
57class _ZirconPrefix(gdb.Command):
58  """zircon command prefix"""
59
60  def __init__(self):
61    super(_ZirconPrefix, self).__init__("%s" % (_ZIRCON_COMMAND_PREFIX),
62                                        gdb.COMMAND_DATA,
63                                        prefix=True)
64
65
66class _InfoZircon(gdb.Command):
67  """info zircon command prefix"""
68
69  def __init__(self):
70    super(_InfoZircon, self).__init__("info %s" % (_ZIRCON_COMMAND_PREFIX),
71                                       gdb.COMMAND_DATA,
72                                       prefix=True)
73
74
75class _SetZircon(gdb.Command):
76  """set zircon command prefix"""
77
78  def __init__(self):
79    super(_SetZircon, self).__init__("set %s" % (_ZIRCON_COMMAND_PREFIX),
80                                      gdb.COMMAND_DATA,
81                                      prefix=True)
82
83
84class _ShowZircon(gdb.Command):
85  """show zircon command prefix"""
86
87  def __init__(self):
88    super(_ShowZircon, self).__init__("show %s" % (_ZIRCON_COMMAND_PREFIX),
89                                       gdb.COMMAND_DATA,
90                                       prefix=True)
91
92
93class _ZirconMaxInfoThreads(gdb.Parameter):
94  """Parameter to limit output of "info zircon threads" command.
95
96  This parameter is an escape hatch to catch corrupted lists.
97  We don't want "info zircon threads" to loop forever.
98
99  The value is the maximum number of threads that will be printed.
100  """
101
102  set_doc = "Set the maximum number of zircon threads to print."
103  show_doc = "Show the maximum number of zircon threads to print."
104
105  def __init__(self):
106    super(_ZirconMaxInfoThreads, self).__init__(
107        "%s max-info-threads" % (_ZIRCON_COMMAND_PREFIX),
108        gdb.COMMAND_DATA, gdb.PARAM_UINTEGER)
109    self.value = 1000
110
111  def get_show_string(self, pvalue):
112    return "Maximum number of threads to print is " + pvalue + "."
113
114  def get_set_string(self):
115    # Ugh.  There doesn't seem to be a way to implement a gdb parameter in
116    # Python that will be silent when the user changes the value.
117    if self.value is None:
118      value = "unlimited"
119    else:
120      value = self.value
121    return "Maximum number of threads to print been set to %s." % (value)
122
123
124class _ZirconMaxInfoProcesses(gdb.Parameter):
125  """Parameter to limit output of "info zircon processes" command.
126
127  This parameter is an escape hatch to catch corrupted lists.
128  We don't want "info zircon processes" to loop forever.
129
130  The value is the maximum number of processes that will be printed.
131  """
132
133  set_doc = "Set the maximum number of zircon processes to print."
134  show_doc = "Show the maximum number of zircon processes to print."
135
136  def __init__(self):
137    super(_ZirconMaxInfoProcesses, self).__init__(
138        "%s max-info-processes" % (_ZIRCON_COMMAND_PREFIX),
139        gdb.COMMAND_DATA, gdb.PARAM_UINTEGER)
140    self.value = 1000
141
142  def get_show_string(self, pvalue):
143    return "Maximum number of processes to print is " + pvalue + "."
144
145  def get_set_string(self):
146    # Ugh.  There doesn't seem to be a way to implement a gdb parameter in
147    # Python that will be silent when the user changes the value.
148    if self.value is None:
149      value = "unlimited"
150    else:
151      value = self.value
152    return "Maximum number of processes to print been set to %s." % (value)
153
154
155class _ZirconMaxInfoHandles(gdb.Parameter):
156  """Parameter to limit output of "info zircon handles" command.
157
158  This parameter is an escape hatch to catch corrupted lists.
159  We don't want "info zircon handles" to loop forever.
160
161  The value is the maximum number of handles that will be printed.
162  """
163
164  set_doc = "Set the maximum number of zircon handles to print."
165  show_doc = "Show the maximum number of zircon handles to print."
166
167  def __init__(self):
168    super(_ZirconMaxInfoHandles, self).__init__(
169        "%s max-info-handles" % (_ZIRCON_COMMAND_PREFIX),
170        gdb.COMMAND_DATA, gdb.PARAM_UINTEGER)
171    self.value = 1000
172
173  def get_show_string(self, pvalue):
174    return "Maximum number of handles to print is " + pvalue + "."
175
176  def get_set_string(self):
177    # Ugh.  There doesn't seem to be a way to implement a gdb parameter in
178    # Python that will be silent when the user changes the value.
179    if self.value is None:
180      value = "unlimited"
181    else:
182      value = self.value
183    return "Maximum number of handles to print been set to %s." % (value)
184
185
186def containerof(node_ptr, type_name, member_name):
187  """Python version of zircon's containerof macro."""
188  # TODO(dje): This could only be computed once.
189  # For more popular types, compute all possible once.
190  char_ptr = gdb.lookup_type("char").pointer()
191  ptr = node_ptr.cast(char_ptr)
192  type_object_ptr = gdb.lookup_type(type_name).pointer()
193  offsetof = long(gdb.Value(0).cast(type_object_ptr)[member_name].address)
194  return (ptr - offsetof).cast(type_object_ptr)
195
196
197def _build_zircon_pretty_printers():
198  pp = gdb.printing.RegexpCollectionPrettyPrinter("zircon")
199  # Insert printer registration here.
200  #pp.add_printer("foo", "^foo$", _ZirconFooPrinter)
201  return pp
202
203
204def register_zircon_pretty_printers(obj):
205  if obj is None:
206    obj = gdb
207  gdb.printing.register_pretty_printer(obj, _build_zircon_pretty_printers(),
208                                       replace=True)
209
210
211def _get_thread_list():
212  """ Return a list of all thread_t threads.
213
214  The result is constrained by "zircon max-info-threads".
215  """
216  threads = []
217  head = gdb.parse_and_eval("&thread_list")
218  t = head["next"]
219  count = 0
220  max_threads = gdb.parameter("%s max-info-threads" % (_ZIRCON_COMMAND_PREFIX))
221  int_type = gdb.lookup_type("int")
222  ptr_size = int_type.pointer().sizeof
223  # Note: A "corrupted" list can happen for a short time while an
224  # element is being added/removed. And, in non-stop mode, the list can
225  # change while we're at it. This isn't a problem in all-stop mode, but
226  # non-stop mode is generally preferable. We'll see how this works in
227  # practice.
228  while t and t != head:
229    if max_threads is not None and count >= max_threads:
230      break
231    # Catch misaligned pointers.
232    # Casting to int shouldn't be necessary, but the python API doesn't
233    # support creating an int from a pointer.
234    # We assume the object is aligned at least as great as a pointer.
235    if (t.cast(int_type) & (ptr_size - 1)) != 0:
236      break
237    # TODO(dje): Do a range check?
238    thread_ptr = containerof(t, "thread", "thread_list_node")
239    if thread_ptr["magic"] != _THREAD_MAGIC:
240      break
241    # TODO(dje): Move this to a routine, more list printers will want this.
242    threads.append(thread_ptr)
243    t = t["next"]
244    count += 1
245  return threads
246
247
248def _get_process_list():
249  """Return list of all processes.
250
251  The result is constrained by "zircon max-info-processes".
252  """
253  processes = []
254  head = gdb.parse_and_eval("&process_list")
255  head = head["head_"]
256  p = head
257  count = 0
258  max_processes = gdb.parameter("%s max-info-processes" % (_ZIRCON_COMMAND_PREFIX))
259  int_type = gdb.lookup_type("int")
260  ptr_size = int_type.pointer().sizeof
261  # Note: A "corrupted" list can happen for a short time while an
262  # element is being added/removed. And, in non-stop mode, the list can
263  # change while we're at it. This isn't a problem in all-stop mode, but
264  # non-stop mode is generally preferable. We'll see how this works in
265  # practice.
266  while p:
267    if max_processes is not None and count >= max_processes:
268      break
269    # Catch misaligned pointers.
270    # Casting to int shouldn't be necessary, but the python API doesn't
271    # support creating an int from a pointer.
272    # We assume the object is aligned at least as great as a pointer.
273    if (p.cast(int_type) & (ptr_size - 1)) != 0:
274      break
275    # TODO(dje): Do a range check?
276    # TODO(dje): Move this to a routine, more list printers will want this.
277    processes.append(p)
278    p = p["next_"]
279    count += 1
280    if p == head:
281      break
282  return processes
283
284
285def _get_handle_list(process):
286  """Return list of all handles of process.
287
288  The result is constrained by "zircon max-info-handles".
289  """
290  handles = []
291  head = process["handles_"]
292  head = head["head_"]
293  h = head
294  count = 0
295  max_handles = gdb.parameter("%s max-info-handles" % (_ZIRCON_COMMAND_PREFIX))
296  int_type = gdb.lookup_type("int")
297  ptr_size = int_type.pointer().sizeof
298  # Note: A "corrupted" list can happen for a short time while an
299  # element is being added/removed. And, in non-stop mode, the list can
300  # change while we're at it. This isn't a problem in all-stop mode, but
301  # non-stop mode is generally preferable. We'll see how this works in
302  # practice.
303  while h:
304    if max_handles is not None and count >= max_handles:
305      break
306    # Catch misaligned pointers.
307    # Casting to int shouldn't be necessary, but the python API doesn't
308    # support creating an int from a pointer.
309    # We assume the object is aligned at least as great as a pointer.
310    if (h.cast(int_type) & (ptr_size - 1)) != 0:
311      break
312    # TODO(dje): Do a range check?
313    # TODO(dje): Move this to a routine, more list printers will want this.
314    handles.append(h)
315    h = h["next_"]
316    count += 1
317    if h == head:
318      break
319  return handles
320
321
322def _print_thread_summary(thread, number, tls_entry, user_thread_ptr_t):
323  user_thread = thread["tls"][tls_entry]
324  if user_thread:
325    user_thread_ptr = user_thread.cast(user_thread_ptr_t)
326    # TODO(dje): Why is str necessary here? Otherwise ->
327    # "Cannot convert value to int."
328    pid = str(user_thread_ptr["process_"]["id_"])
329  else:
330    pid = "kern"
331  name = str(thread["name"].lazy_string().value()).strip('"')
332  print("%3d %5s %#16x %-32s %s" % (
333      number, pid, thread.address, name, thread["state"]))
334
335
336class _InfoZirconThreads(gdb.Command):
337  """info zircon threads command
338
339  This command prints a list of all zircon threads.
340  TODO: Allow specifying which threads to print.
341  """
342
343  def __init__(self):
344    super(_InfoZirconThreads, self).__init__("info %s threads" % (_ZIRCON_COMMAND_PREFIX),
345                                              gdb.COMMAND_USER)
346
347  def invoke(self, arg, from_tty):
348    # Do this first to make sure the previous value gets cleared out.
349    # There's no way to unset a convenience var, so KISS.
350    gdb.execute("set $zx_threads = (thread_t*[1]) { 0 }")
351    tls_entry_lkuser = gdb.parse_and_eval("TLS_ENTRY_LKUSER")
352    threads = _get_thread_list()
353    num_threads = len(threads)
354    # The array is origin-1-indexed. Have a null first entry to KISS.
355    gdb.execute("set $zx_threads = (thread_t*[%d]) { 0 }" % (num_threads + 1))
356
357    # Populate the array first, before printing the summary, to make sure this
358    # gets done even if there's an error during printing.
359    num = 1
360    for thread_ptr in threads:
361      gdb.execute("set $zx_threads[%d] = (thread_t*) %u" % (num, thread_ptr))
362      num += 1
363
364    # Translating gdb values to python often trips over these. Heads up.
365    save_print_address = "yes" if gdb.parameter("print address") else "no"
366    save_print_symbol = "yes" if gdb.parameter("print symbol") else "no"
367    gdb.execute("set print address off")
368    gdb.execute("set print symbol off")
369
370    print("%3s %5s %-18s %-32s %s" % (
371        "Num", "Pid", "thread_t*", "Name", "State"))
372    # Make sure we restore these when we're done.
373    try:
374      user_thread_ptr_t = gdb.lookup_type("UserThread").pointer()
375      num = 1
376      for thread_ptr in threads:
377        # TODO(dje): remove dereference
378        _print_thread_summary(thread_ptr.dereference(), num, tls_entry_lkuser, user_thread_ptr_t)
379        num += 1
380    finally:
381      gdb.execute("set print address %s" % (save_print_address))
382      gdb.execute("set print symbol %s" % (save_print_symbol))
383    if num_threads:
384      print("Note: Each thread is now available in $zx_threads[num].")
385    else:
386      print("<no threads>")
387
388
389def _print_process_summary(process, number):
390  state = str(process["state_"])
391  state = state.replace("ProcessDispatcher::", "")
392  print("%3d %#16x %4u %s" % (
393      number, process.address, process["id_"], state))
394
395
396class _InfoZirconProcesses(gdb.Command):
397  """info zircon processes command
398
399  This command prints a list of all zircon processes.
400  TODO: Allow specifying which processes to print.
401  """
402
403  def __init__(self):
404    super(_InfoZirconProcesses, self).__init__("info %s processes" % (_ZIRCON_COMMAND_PREFIX),
405                                              gdb.COMMAND_USER)
406
407  def invoke(self, arg, from_tty):
408    # Do this first to make sure the previous value gets cleared out.
409    # There's no way to unset a convenience var, so KISS.
410    gdb.execute("set $zx_processes = (ProcessDispatcher*[1]) { 0 }")
411    tls_entry_lkuser = gdb.parse_and_eval("TLS_ENTRY_LKUSER")
412    processes = _get_process_list()
413    num_processes = len(processes)
414    # The array is origin-1-indexed. Have a null first entry to KISS.
415    gdb.execute("set $zx_processes = (ProcessDispatcher*[%d]) { 0 }" % (num_processes + 1))
416
417    # Populate the array first, before printing the summary, to make sure this
418    # gets done even if there's an error during printing.
419    num = 1
420    for process_ptr in processes:
421      gdb.execute("set $zx_processes[%d] = (ProcessDispatcher*) %u" % (num, process_ptr))
422      num += 1
423
424    # Translating gdb values to python often trips over these. Heads up.
425    save_print_address = "yes" if gdb.parameter("print address") else "no"
426    save_print_symbol = "yes" if gdb.parameter("print symbol") else "no"
427    gdb.execute("set print address off")
428    gdb.execute("set print symbol off")
429
430    print("%3s %-18s %4s %s" % (
431        "Num", "ProcessDispatcher*", "Pid", "State"))
432    # Make sure we restore these when we're done.
433    try:
434      num = 1
435      for process_ptr in processes:
436        _print_process_summary(process_ptr.dereference(), num)
437        num += 1
438    finally:
439      gdb.execute("set print address %s" % (save_print_address))
440      gdb.execute("set print symbol %s" % (save_print_symbol))
441    if num_processes:
442      print("Note: Each process is now available in $zx_processes[num].")
443    else:
444      print("<no processes>")
445
446
447def _print_handle_summary(handle, number):
448  process_id = handle["process_id_"]
449  rights = handle["rights_"]
450  dispatcher = handle["dispatcher_"]["ptr_"]
451  # TODO(dje): This is a hack to get the underlying type from the vtable.
452  # The python API should support this directly.
453  dispatcher_text = gdb.execute("output *(Dispatcher*) %s" % (dispatcher), to_string=True)
454  dispatcher_split_text = dispatcher_text.split(" ", 1)
455  if len(dispatcher_split_text) == 2:
456    dispatcher_type = dispatcher_split_text[0].strip("()")
457  else:
458    dispatcher_type = "Dispatcher"
459  dispatcher_text = "(%s*) %s" % (dispatcher_type, dispatcher)
460  print("  %3d %-18s %4u %#8x %s" % (
461      number, handle.address, process_id, rights, dispatcher_text))
462
463
464class _InfoZirconHandles(gdb.Command):
465  """info zircon handles command
466
467  This command prints a list of all zircon handles.
468  TODO: Allow specifying which handles to print.
469  """
470
471  def __init__(self):
472    super(_InfoZirconHandles, self).__init__("info %s handles" % (_ZIRCON_COMMAND_PREFIX),
473                                              gdb.COMMAND_USER)
474
475  def invoke(self, arg, from_tty):
476    processes = _get_process_list()
477
478    # Translating gdb values to python often trips over these. Heads up.
479    save_print_address = "yes" if gdb.parameter("print address") else "no"
480    save_print_symbol = "yes" if gdb.parameter("print symbol") else "no"
481    gdb.execute("set print address on")
482    gdb.execute("set print symbol off")
483
484    # Make sure we restore these when we're done.
485    try:
486      for p in processes:
487        handles = _get_handle_list(p)
488        num_handles = len(handles)
489
490        print("Process %u" % (p["id_"]))
491        print("  %3s %-18s %4s %8s %s" % (
492            "Num", "Handle*", "Pid", "Rights", "Dispatcher"))
493
494        num = 1
495        for handle_ptr in handles:
496          _print_handle_summary(handle_ptr.dereference(), num)
497          num += 1
498
499        if not num_handles:
500          print("  <no handles>")
501
502    finally:
503      gdb.execute("set print address %s" % (save_print_address))
504      gdb.execute("set print symbol %s" % (save_print_symbol))
505
506
507class _ZirconKernelExceptionUnwinder(gdb.Parameter):
508  """Parameter to enable zircon kernel exception unwinding.
509
510  This parameter is an escape hatch in case there are problems with the unwinder.
511
512  N.B. Perhaps this command should flush registers.
513  It doesn't now to avoid side-effects, and the user is responsible for typing
514  "flushregs" if s/he wants to reprint a recent backtrace.
515  """
516
517  set_doc = "Set whether the zircon kernel exception unwinder is enabled."
518  show_doc = "Show whether the zircon kernel exception unwinder is enabled."
519
520  def __init__(self):
521    super(_ZirconKernelExceptionUnwinder, self).__init__(
522        "%s %s" % (_ZIRCON_COMMAND_PREFIX, _KERNEL_EXCEPTION_UNWINDER_PARAMETER),
523        gdb.COMMAND_DATA, gdb.PARAM_BOOLEAN)
524    self.value = True
525
526  def get_show_string(self, pvalue):
527    value = "enabled" if self.value else "disabled"
528    return "The kernel exception unwinder is %s." % (value)
529
530  def get_set_string(self):
531    # Ugh.  There doesn't seem to be a way to implement a gdb parameter in
532    # Python that will be silent when the user changes the value.
533    value = "enabled" if self.value else "disabled"
534    return "The kernel exception unwinder is %s." % (value)
535
536
537class _Amd64KernelExceptionUnwinder(Unwinder):
538  # See arch/x86/64/exceptions.S.
539  AT_IFRAME_SETUP = "interrupt_common_iframe_set_up_for_debugger"
540  INTERRUPT_COMMON = "interrupt_common"
541
542  class FrameId(object):
543    def __init__(self, sp, pc):
544      self.sp = sp
545      self.pc = pc
546
547  @staticmethod
548  def lookup_minsym(minsym_name):
549    """Return the address of "minimal symbol" minsym_name."""
550    # TODO(dje): What's here now is a quick hack to get things going.
551    # GDB's python API doesn't yet provide the ability to look up minsyms.
552    try:
553      output = gdb.execute("output %s" % (minsym_name), to_string=True)
554    except (gdb.error):
555      return None
556    symbol_value = None
557    if not output.startswith("No symbol"):
558      symbol_match = re.search(r"0x[0-9a-f]+", output)
559      if symbol_match is not None:
560        symbol_value = long(symbol_match.group(0), 16)
561    return symbol_value
562
563  @staticmethod
564  def is_user_space(iframe):
565    """Return True if iframe is from user space."""
566    # See arch/x86/include/arch/x86/descriptor.h:SELECTOR_PL.
567    return (iframe["cs"] & 3) != 0
568
569  def __init__(self):
570    super(_Amd64KernelExceptionUnwinder, self).__init__("AMD64 kernel exception unwinder")
571    # We assume uintptr_t is present in the debug info.
572    # We *could* use unsigned long, but it's a bit of an obfuscation.
573    self.uintptr_t = gdb.lookup_type("uintptr_t")
574    # We assume "unsigned int" is 32 bits.
575    self.uint32_t = gdb.lookup_type("unsigned int")
576    self.iframe_ptr_t = gdb.lookup_type("x86_iframe_t").pointer()
577    # We need to know when the pc is at the point where it has called
578    # x86_exception_handler.
579    self.at_iframe_setup = self.lookup_minsym(_Amd64KernelExceptionUnwinder.AT_IFRAME_SETUP)
580    self.interrupt_common_begin_addr = self.lookup_minsym(_Amd64KernelExceptionUnwinder.INTERRUPT_COMMON)
581
582  def is_in_icommon(self, pc):
583    """Return True if pc is in the interrupt_common function."""
584    # First do the preferred test.
585    # If the pc is here then we've called into x86_exception_handler.
586    if self.at_iframe_setup is not None and pc == self.at_iframe_setup:
587      return True
588    # Fall back to this in case the special symbol doesn't exist.
589    if self.interrupt_common_begin_addr is None:
590      return False
591    end_addr = self.interrupt_common_begin_addr + 64
592    return pc >= self.interrupt_common_begin_addr and pc < end_addr
593
594  def __call__(self, pending_frame):
595    try:
596      # Punt if disabled.
597      if not gdb.parameter("%s %s" % (_ZIRCON_COMMAND_PREFIX, _KERNEL_EXCEPTION_UNWINDER_PARAMETER)):
598        return None
599      # Note: We use rip,rsp here instead of pc,sp to work around bug 20128
600      #pc = pending_frame.read_register("pc").cast(self.uintptr_t)
601      pc = pending_frame.read_register("rip").cast(self.uintptr_t)
602      #print "icommon unwinder, pc = %#x" % (long(str(pc))) # work around bug 20126
603      if not self.is_in_icommon(pc):
604        return None
605      sp = pending_frame.read_register("rsp").cast(self.uintptr_t)
606      #print "icommon unwinder, sp = %#x" % (long(str(sp)))
607      iframe = sp.cast(self.iframe_ptr_t)
608      # This is only for kernel faults, not user-space ones.
609      if self.is_user_space(iframe):
610        return None
611      frame_id = self.FrameId(sp, pc)
612      unwind_info = pending_frame.create_unwind_info(frame_id)
613      unwind_info.add_saved_register("rip", iframe["ip"])
614      unwind_info.add_saved_register("rsp", iframe["user_sp"])
615      unwind_info.add_saved_register("rax", iframe["rax"])
616      unwind_info.add_saved_register("rbx", iframe["rbx"])
617      unwind_info.add_saved_register("rcx", iframe["rcx"])
618      unwind_info.add_saved_register("rdx", iframe["rdx"])
619      unwind_info.add_saved_register("rbp", iframe["rbp"])
620      unwind_info.add_saved_register("rsi", iframe["rsi"])
621      unwind_info.add_saved_register("r8", iframe["r8"])
622      unwind_info.add_saved_register("r9", iframe["r9"])
623      unwind_info.add_saved_register("r10", iframe["r10"])
624      unwind_info.add_saved_register("r11", iframe["r11"])
625      unwind_info.add_saved_register("r12", iframe["r12"])
626      unwind_info.add_saved_register("r13", iframe["r13"])
627      unwind_info.add_saved_register("r14", iframe["r14"])
628      unwind_info.add_saved_register("r15", iframe["r15"])
629      # Flags is recorded as 64 bits, but gdb needs to see 32.
630      unwind_info.add_saved_register("eflags", iframe["flags"].cast(self.uint32_t))
631      #print "Unwind info:"
632      #print unwind_info
633      return unwind_info
634    except (gdb.error, RuntimeError):
635      return None
636
637
638_ZirconPrefix()
639_InfoZircon()
640_SetZircon()
641_ShowZircon()
642
643_ZirconMaxInfoThreads()
644_ZirconMaxInfoProcesses()
645_ZirconMaxInfoHandles()
646
647_InfoZirconThreads()
648_InfoZirconProcesses()
649_InfoZirconHandles()
650
651_ZirconKernelExceptionUnwinder()
652
653_ull = gdb.lookup_type("unsigned long long")
654_uint = gdb.lookup_type("unsigned int")
655
656
657def _cast(value, t, shift=64):
658  return int(value.cast(t)) & ((1 << shift)-1)
659
660
661def _cast_ull(value):
662  """ Cast a value to unsigned long long """
663  global _ull
664  return _cast(value, _ull)
665
666
667def _cast_uint(value):
668  """ Cast a value to unsigned int"""
669  global _uint
670  return _cast(value, _uint, 32)
671
672
673def _read_symbol_address(name):
674  """ Read the address of a symbol """
675  addr = gdb.parse_and_eval("&"+name)
676  try:
677    if addr is not None:
678      return _cast_ull(addr)
679  except gdb.MemoryError:
680    pass
681  print("Can't find %s to lookup KASLR relocation" % name)
682  return None
683
684
685def _read_pointer(addr):
686  """ Read a pointer in an address """
687  value = gdb.parse_and_eval("*(unsigned long*)0x%x" % addr)
688  try:
689    if value is not None:
690      return _cast_ull(value)
691  except gdb.MemoryError:
692    pass
693
694  print("Can't read 0x%x to lookup KASLR ptr value" % addr)
695  return None
696
697
698def _read_uint(addr):
699  """ Read a uint """
700  value = gdb.parse_and_eval("*(unsigned int*)0x%x" % addr)
701  try:
702    if value is not None:
703      return _cast_uint(value)
704  except gdb.MemoryError:
705    pass
706  print("Can't read 0x%x to lookup KASLR uint value" % addr)
707  return None
708
709
710def _offset_symbols_and_breakpoints(kernel_relocated_base=None):
711  """ Using the KASLR relocation address, reload symbols and breakpoints """
712  print("Update symbols and breakpoints for KASLR")
713
714  base_address = _read_symbol_address(_KERNEL_BASE_ADDRESS)
715  if not base_address:
716    return False
717
718  load_start = _read_symbol_address("IMAGE_LOAD_START")
719  if not load_start:
720    return False
721
722  if not kernel_relocated_base:
723    kernel_relocated_base = _read_symbol_address("kernel_relocated_base")
724    if not kernel_relocated_base:
725      return False
726
727  relocated = _read_pointer(kernel_relocated_base)
728  if not relocated:
729    print("Failed to fetch KASLR base address")
730    return False
731
732  # There is no api for symbol management.
733  # Everything has to be done by custom commands
734  sym = gdb.execute("info target", to_string=True)
735  m = re.match("^Symbols from \"([^\"]+)\"", sym)
736  if not m:
737    print("Error: Cannot find the target symbol")
738    return False
739  sym_path = m.group(1)
740
741  # Identify all section addresses
742  x = gdb.execute("info target", to_string=True)
743  m = re.findall("\s0x([a-f0-9]+) - 0x[a-f0-9]+ is (.*)", x)
744
745  offset = base_address - relocated
746  sections = dict([(name, int(addr, 16)) for addr,name in m])
747  if len(sections) == 0:
748    print("Error: Failed to find sections in binary")
749    return False
750
751  if ".text" not in sections:
752    print("Error: Failed to find .text section")
753    return False
754
755  # Do not prompt the user
756  confirm_was_enabled  = gdb.parameter("confirm")
757  if confirm_was_enabled:
758    gdb.execute("set confirm off", to_string=True)
759
760  # Disable auto loading to prevent the script to be reloaded
761  auto_loading_enabled = gdb.parameter("auto-load python-scripts")
762  if auto_loading_enabled:
763    gdb.execute("set auto-load python-scripts off", to_string=True)
764
765  # Remove all symbols
766  gdb.execute("symbol-file", to_string=True)
767
768  # Update all addresses to the relocated address
769  sections = dict([(name, addr - offset) for name,addr in sections.iteritems()])
770
771  text_addr = sections[".text"]
772  del sections[".text"]
773
774  args = ["-s %s 0x%x" % (name, addr) for name,addr in sections.iteritems()]
775
776  # Reload the ELF with all sections set
777  gdb.execute("add-symbol-file \"%s\" 0x%x -readnow %s" \
778              % (sym_path, text_addr, " ".join(args)), to_string=True)
779
780  if auto_loading_enabled:
781    gdb.execute("set auto-load python-scripts on", to_string=True)
782
783  if confirm_was_enabled:
784    gdb.execute("set confirm on", to_string=True)
785
786  # Verify it works as expected
787  code_start = _read_symbol_address("__code_start")
788  if not kernel_relocated_base:
789    return False
790
791  expected = relocated + (load_start - base_address)
792  if code_start != expected:
793    print("Error: Incorrect relocation for __code_start 0x%x vs 0x%x" \
794          % (expected, code_start))
795    return False
796
797  print("KASLR: Correctly reloaded kernel at 0x%x" % relocated)
798  return True
799
800class KASLRBootWatchpoint(gdb.Breakpoint):
801  """ Watchpoint to catch read access to KASLR relocated address
802
803  The assumption is the address was written before it is read. It is an
804  architecture independent way to start at the right moment if the relocated
805  KASLR symbol name is the same.
806  """
807  def __init__(self, pc):
808    self._is_valid = False
809
810    base_address = _read_symbol_address(_KERNEL_BASE_ADDRESS)
811    if not base_address:
812      return
813
814    kernel_relocated_base = _read_symbol_address("kernel_relocated_base")
815    if not kernel_relocated_base:
816      return
817
818    self._relocated_base_offset = kernel_relocated_base
819
820    if _is_x86_64():
821      # x86_64 uses the physical address
822      self._relocated_base_offset -= base_address
823    elif _is_arm64():
824      # Search from pc to find BOOT tag
825      found = False
826      for addr in range(pc, pc+0x100000, 0x1000):
827        data = _read_uint(addr)
828        if data == _BOOT_MAGIC:
829          self._relocated_base_offset -= base_address
830          self._relocated_base_offset += addr
831          found = True
832          break
833
834      if found == False:
835        print("Error: Could not found BOOT_MAGIC")
836        return
837
838    self._is_valid = True
839    super(KASLRBootWatchpoint, self).__init__("*0x%x" % self._relocated_base_offset,
840                                              gdb.BP_WATCHPOINT,
841                                              gdb.WP_READ,
842                                              internal=True)
843
844    self.silence = True
845
846
847  def is_valid(self):
848    if super(KASLRBootWatchpoint, self).is_valid() == False:
849      return False
850    return self._is_valid
851
852
853  def stop(self):
854    # A breakpoint cannot change anything so get callback on the following stop
855    gdb.events.stop.connect(self._stop_callback)
856    return True
857
858
859  # Callback through events so the state can be changed
860  def _stop_callback(self, event):
861    gdb.events.stop.disconnect(self._stop_callback)
862    self.delete() # Temporary watchpoint are not supported, delete it manually
863
864    # Load symbols using load_offset
865    _offset_symbols_and_breakpoints(self._relocated_base_offset)
866
867
868def _align(addr, shift):
869  b64 = (1 << 64) - 1
870  mask = (1 << shift) - 1
871  return addr & (~mask & b64)
872
873
874def _identify_offset_to_reload(pc):
875  """ From $pc, search the base address to the page size
876  Used when attaching to an existing debugging session
877  """
878  print("Search KASLR base address based on $pc")
879  if (pc >> 63) != 1:
880    print("Error: Didn't break into kernel code")
881    return False
882
883  base_address = _read_symbol_address(_KERNEL_BASE_ADDRESS)
884  if not base_address:
885    return False
886
887  end = _read_symbol_address("_end")
888  if not end:
889    return False
890
891  kernel_relocated_base = _read_symbol_address("kernel_relocated_base")
892  if not kernel_relocated_base:
893    return False
894
895  offset = kernel_relocated_base - base_address
896  max_size = end - base_address
897
898  # Search base+offset == base for each previous page until the max size
899  found = False
900  addr = pc
901  while addr > (pc - max_size):
902    addr = _align(addr - 1, 12)
903    target = addr + offset
904    value = _read_pointer(target)
905    if value == None:
906      break
907    if addr == value:
908      found = True
909      break
910
911  if found == False:
912    print("Error: Failed to find the KASLR relocation from the $pc")
913    return False
914
915  return _offset_symbols_and_breakpoints(target)
916
917
918def _is_earlyboot_pc(pc):
919  """ Early boot if the top 32-bit is zero """
920  return not (pc >> 32)
921
922
923def _KASLR_stop_event(event):
924  """ Called on first stop after debugger started """
925  gdb.events.stop.disconnect(_KASLR_stop_event)
926  pc = _cast_ull(gdb.parse_and_eval("$pc"))
927  if not _is_earlyboot_pc(pc):
928    # If not early boot, try to find the kernel base and adapt
929    _identify_offset_to_reload(pc)
930    return
931
932  x = KASLRBootWatchpoint(pc)
933  if not x.is_valid():
934    print("Error: Failed create KASLR boot watchpoint")
935    return
936
937  print("Watchpoint set on KASLR relocated base variable")
938  gdb.execute("continue")
939
940
941def _install():
942  current_objfile = gdb.current_objfile()
943  register_zircon_pretty_printers(current_objfile)
944  if current_objfile is not None and _is_x86_64():
945    gdb.unwinder.register_unwinder(current_objfile, _Amd64KernelExceptionUnwinder(), True)
946
947  if not _is_x86_64() and not _is_arm64():
948    print("Warning: Unsupported architecture, KASLR support will be experimental")
949  gdb.events.stop.connect(_KASLR_stop_event)
950
951_install()
952