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