1#!/usr/bin/python 2 3# GDB debugging support 4# 5# Copyright 2012 Red Hat, Inc. and/or its affiliates 6# 7# Authors: 8# Avi Kivity <avi@redhat.com> 9# 10# This work is licensed under the terms of the GNU GPL, version 2 11# or later. See the COPYING file in the top-level directory. 12 13import gdb 14 15VOID_PTR = gdb.lookup_type('void').pointer() 16 17def get_fs_base(): 18 '''Fetch %fs base value using arch_prctl(ARCH_GET_FS). This is 19 pthread_self().''' 20 # %rsp - 120 is scratch space according to the SystemV ABI 21 old = gdb.parse_and_eval('*(uint64_t*)($rsp - 120)') 22 gdb.execute('call (int)arch_prctl(0x1003, $rsp - 120)', False, True) 23 fs_base = gdb.parse_and_eval('*(uint64_t*)($rsp - 120)') 24 gdb.execute('set *(uint64_t*)($rsp - 120) = %s' % old, False, True) 25 return fs_base 26 27def pthread_self(): 28 '''Fetch pthread_self() from the glibc start_thread function.''' 29 f = gdb.newest_frame() 30 while f.name() != 'start_thread': 31 f = f.older() 32 if f is None: 33 return get_fs_base() 34 35 try: 36 return f.read_var("arg") 37 except ValueError: 38 return get_fs_base() 39 40def get_glibc_pointer_guard(): 41 '''Fetch glibc pointer guard value''' 42 fs_base = pthread_self() 43 return gdb.parse_and_eval('*(uint64_t*)((uint64_t)%s + 0x30)' % fs_base) 44 45def glibc_ptr_demangle(val, pointer_guard): 46 '''Undo effect of glibc's PTR_MANGLE()''' 47 return gdb.parse_and_eval('(((uint64_t)%s >> 0x11) | ((uint64_t)%s << (64 - 0x11))) ^ (uint64_t)%s' % (val, val, pointer_guard)) 48 49def get_jmpbuf_regs(jmpbuf): 50 JB_RBX = 0 51 JB_RBP = 1 52 JB_R12 = 2 53 JB_R13 = 3 54 JB_R14 = 4 55 JB_R15 = 5 56 JB_RSP = 6 57 JB_PC = 7 58 59 pointer_guard = get_glibc_pointer_guard() 60 return {'rbx': jmpbuf[JB_RBX], 61 'rbp': glibc_ptr_demangle(jmpbuf[JB_RBP], pointer_guard), 62 'rsp': glibc_ptr_demangle(jmpbuf[JB_RSP], pointer_guard), 63 'r12': jmpbuf[JB_R12], 64 'r13': jmpbuf[JB_R13], 65 'r14': jmpbuf[JB_R14], 66 'r15': jmpbuf[JB_R15], 67 'rip': glibc_ptr_demangle(jmpbuf[JB_PC], pointer_guard) } 68 69def bt_jmpbuf(jmpbuf): 70 '''Backtrace a jmpbuf''' 71 regs = get_jmpbuf_regs(jmpbuf) 72 old = dict() 73 74 for i in regs: 75 old[i] = gdb.parse_and_eval('(uint64_t)$%s' % i) 76 77 for i in regs: 78 gdb.execute('set $%s = %s' % (i, regs[i])) 79 80 gdb.execute('bt') 81 82 for i in regs: 83 gdb.execute('set $%s = %s' % (i, old[i])) 84 85def coroutine_to_jmpbuf(co): 86 coroutine_pointer = co.cast(gdb.lookup_type('CoroutineUContext').pointer()) 87 return coroutine_pointer['env']['__jmpbuf'] 88 89 90class CoroutineCommand(gdb.Command): 91 '''Display coroutine backtrace''' 92 def __init__(self): 93 gdb.Command.__init__(self, 'qemu coroutine', gdb.COMMAND_DATA, 94 gdb.COMPLETE_NONE) 95 96 def invoke(self, arg, from_tty): 97 argv = gdb.string_to_argv(arg) 98 if len(argv) != 1: 99 gdb.write('usage: qemu coroutine <coroutine-pointer>\n') 100 return 101 102 bt_jmpbuf(coroutine_to_jmpbuf(gdb.parse_and_eval(argv[0]))) 103 104class CoroutineSPFunction(gdb.Function): 105 def __init__(self): 106 gdb.Function.__init__(self, 'qemu_coroutine_sp') 107 108 def invoke(self, addr): 109 return get_jmpbuf_regs(coroutine_to_jmpbuf(addr))['rsp'].cast(VOID_PTR) 110 111class CoroutinePCFunction(gdb.Function): 112 def __init__(self): 113 gdb.Function.__init__(self, 'qemu_coroutine_pc') 114 115 def invoke(self, addr): 116 return get_jmpbuf_regs(coroutine_to_jmpbuf(addr))['rip'].cast(VOID_PTR) 117