def __call__(self, pending_frame): fp = pending_frame.read_register(self.regs['fp']) sp = pending_frame.read_register(self.regs['sp']) ip = pending_frame.read_register(self.regs['ip']) if not frame.is_jitted(fp, ip): return None # GDB wants a FrameId to be a pair of (stack pointer, starting PC) for # a given function frame. Unfortunately, we can't restore the stack # pointer correctly in the TC, and we don't know our starting IP. # # Instead, we just use the stack pointer value for our most # recent call into native code, along with the current PC. It # turns out that this is almost good enough. The problem is # that we can end up with two frames with the same sp and pc # in the same vm nesting (especially if we have interp ret # helpers as the return address), and if two frame ids are the # same, gdb bails out with an error. # # GDB expects stack pointers to be monotonically nondecreasing # as we unwind, so we can't use fp instead of sp, but FrameIds # are allowed to have a "special" field thats used to # differentiate otherwise identical ones; so use fp there # instead. unwind_info = pending_frame.create_unwind_info(FrameId(sp, ip, fp)) # Restore the saved frame pointer and instruction pointer. fp = fp.cast(T('uintptr_t').pointer()) unwind_info.add_saved_register(self.regs['fp'], fp[0]) unwind_info.add_saved_register(self.regs['ip'], fp[1]) if frame.is_jitted(fp[0], fp[1]): # Our parent frame is jitted. Again, we are unable to track %rsp # properly in the TC, so just preserve its value (just as we do in # the TC's custom .eh_frame section). unwind_info.add_saved_register(self.regs['sp'], sp) else: # Our parent frame is not jitted, so we're in g_ustubs.enterTCHelper, # which has a special restore sequence i = 0 for r in self.regs['cross_jit_save']: i -= 1 unwind_info.add_saved_register(r, fp[i]) unwind_info.add_saved_register(self.regs['sp'], fp + 2) return unwind_info
def try_unwinder_init(): """Try to register the custom unwinder if it hasn't been already, and return whether the unwinder has been successfully registered.""" global _did_init if _did_init: return True # If we can't successfully call is_jitted(), then gdb startup hasn't # proceeded to a point where it's safe to set up our unwinder yet, so just # bail out. try: frame.is_jitted(0, 0) except: return False register_unwinder(None, HHVMUnwinder()) _did_init = True return True
def invoke(self, args, from_tty): argv = parse_argv(args) if len(argv) > 2: print('Usage: walkfp [fp] [rip]') return fp_type = T('uintptr_t').pointer() try: arch = gdb.newest_frame().architecture().name() except: arch = 'i386:x86-64' frame_reg = '$x29' if arch == 'aarch64' else '$rbp' pc_reg = '$pc' if arch == 'aarch64' else '$rip' fp = (argv[0] if len(argv) >= 1 else gdb.parse_and_eval(frame_reg)).cast(fp_type) rip = (argv[1] if len(argv) == 2 else gdb.parse_and_eval(pc_reg)).cast( T('uintptr_t')) i = 0 fp = (fp, rip) while fp: rip = fp[1] fp = fp[0].cast(fp_type) try: if frame.is_jitted(fp, rip): ar_type = T('HPHP::ActRec').pointer() print( frame.stringify( frame.create_php(idx=i, ar=fp.cast(ar_type), rip=rip))) else: print( frame.stringify( frame.create_native(idx=i, fp=fp, rip=rip, name=_function_for(rip)))) except: print( frame.stringify(frame.create_native(idx=i, fp=fp, rip=rip))) i += 1
def __call__(self, pending_frame): fp = pending_frame.read_register('rbp') sp = pending_frame.read_register('rsp') ip = pending_frame.read_register('rip') if not frame.is_jitted(fp, ip): return None # GDB wants a FrameId to be a pair of (stack pointer, starting PC) for # a given function frame. Unfortunately, we can't restore the stack # pointer correctly in the TC, and we don't know our starting IP. # # Instead, we just use the stack pointer value for our most recent call # into native code, along with the current PC. It turns out that this # is good enough. # # GDB expects stack pointers to be monotonically nondecreasing as we # unwind, so we can't use, e.g., the frame pointer as part of the ID. unwind_info = pending_frame.create_unwind_info(FrameId(sp, ip)) # Restore the saved frame pointer and instruction pointer. fp = fp.cast(T('uintptr_t').pointer()) unwind_info.add_saved_register('rbp', fp[0]) unwind_info.add_saved_register('rip', fp[1]) if frame.is_jitted(fp[0], fp[1]): # Our parent frame is jitted. Again, we are unable to track %rsp # properly in the TC, so just preserve its value (just as we do in # the TC's custom .eh_frame section). unwind_info.add_saved_register('rsp', sp) else: # Our parent frame is not jitted, so we're in enterTCHelper, and we # can restore our parent's %rsp as usual. unwind_info.add_saved_register('rsp', fp + 16) return unwind_info
def invoke(self, args, from_tty): argv = parse_argv(args) if len(argv) > 2: print('Usage: walkfp [fp] [rip]') return fp_type = T('uintptr_t').pointer() fp = gdb.parse_and_eval('$rbp').cast(fp_type) rip = gdb.parse_and_eval('$rip').cast(T('uintptr_t')) if len(argv) >= 1: fp = argv[0].cast(fp_type) if len(argv) == 2: rip = argv[1].cast(T('uintptr_t')) i = 0 fp = (fp, rip) while fp: rip = fp[1] fp = fp[0].cast(fp_type) try: if frame.is_jitted(fp, rip): ar_type = T('HPHP::ActRec').pointer() print( frame.stringify( frame.create_php(idx=i, ar=fp.cast(ar_type), rip=rip))) else: print( frame.stringify( frame.create_native(idx=i, fp=fp, rip=rip, name=_function_for(rip)))) except: print( frame.stringify(frame.create_native(idx=i, fp=fp, rip=rip))) i += 1
def invoke(self, args, from_tty): argv = parse_argv(args) if len(argv) > 2: print('Usage: walkfp [fp] [rip]') return fp_type = T('uintptr_t').pointer() try: arch = gdb.newest_frame().architecture().name() except: arch = 'i386:x86-64' frame_reg = '$x29' if arch == 'aarch64' else '$rbp' pc_reg = '$pc' if arch == 'aarch64' else '$rip' fp = (argv[0] if len(argv) >= 1 else gdb.parse_and_eval(frame_reg)).cast(fp_type) rip = (argv[1] if len(argv) == 2 else gdb.parse_and_eval(pc_reg)).cast(T('uintptr_t')) i = 0 fp = (fp, rip) while fp: rip = fp[1] fp = fp[0].cast(fp_type) try: if frame.is_jitted(fp, rip): ar_type = T('HPHP::ActRec').pointer() print(frame.stringify(frame.create_php( idx=i, ar=fp.cast(ar_type), rip=rip))) else: print(frame.stringify(frame.create_native( idx=i, fp=fp, rip=rip, name=_function_for(rip)))) except: print(frame.stringify(frame.create_native(idx=i, fp=fp, rip=rip))) i += 1
def invoke(self, args, from_tty): argv = parse_argv(args) if len(argv) > 2: print('Usage: walkfp [fp] [rip]') return fp_type = T('uintptr_t').pointer() fp = gdb.parse_and_eval('$rbp').cast(fp_type) rip = gdb.parse_and_eval('$rip').cast(T('uintptr_t')) if len(argv) >= 1: fp = argv[0].cast(fp_type) if len(argv) == 2: rip = argv[1].cast(T('uintptr_t')) i = 0 fp = (fp, rip) while fp: rip = fp[1] fp = fp[0].cast(fp_type) try: if frame.is_jitted(fp, rip): ar_type = T('HPHP::ActRec').pointer() print(frame.stringify(frame.create_php( idx=i, ar=fp.cast(ar_type), rip=rip))) else: print(frame.stringify(frame.create_native( idx=i, fp=fp, rip=rip, name=_function_for(rip)))) except: print(frame.stringify(frame.create_native(idx=i, fp=fp, rip=rip))) i += 1
def invoke(self, args, from_tty): argv = parse_argv(args) if len(argv) > 1: print('Usage: walkstk [fp]') return # Bail early if the custom unwinder has not been set up. if not unwind.try_unwinder_init(): print('walkstk: Could not initialize the HHVM unwinder.') # Find the starting native frame. native_frame = gdb.newest_frame() if native_frame is None: print('walkstk: Cannot find any frames: corrupt stack?') return # Set fp = $rbp, rip = $rip. fp_type = T('uintptr_t').pointer() fp = native_frame.read_register('rbp').cast(fp_type) rip = native_frame.pc() if len(argv) == 1: # Start walking the stack from the user-provided `fp'. fp = argv[0].cast(fp_type)[0] rip = argv[0].cast(fp_type)[1] # Try to find a corresponding native frame. while (native_frame is not None and rip != native_frame.pc()): native_frame = native_frame.older() i = 0 # Make a fake frame for our `fp' and `rip'. This lets us pop a frame # at the top of the loop, which makes it easier to include the final # frame. fp = (fp, rip) while fp: rip = fp[1] fp = fp[0].cast(fp_type) # Try to get the PHP function name from the ActRec at `fp' if we're # executing in the TC. if frame.is_jitted(fp, rip): ar_type = T('HPHP::ActRec').pointer() try: print( frame.stringify( frame.create_php(idx=i, ar=fp.cast(ar_type), rip=rip))) except gdb.MemoryError: print( frame.stringify( frame.create_native(idx=i, fp=fp, rip=rip, native_frame=native_frame))) if native_frame is not None: native_frame = native_frame.older() i += 1 else: if native_frame is None: # If we couldn't find a native frame, then `walkstk' was # invoked with a non-native `fp' argument. Now that we # don't seem to be in the TC, try to find the corresponding # native frame. native_frame = gdb.newest_frame() while (native_frame is not None and rip != native_frame.pc()): native_frame = native_frame.older() if native_frame is not None: # Pop native frames until we hit our caller's rip. frames = [] while (native_frame is not None and (fp == 0x0 or fp[1] != native_frame.pc())): frames.append( frame.create_native(idx=i, fp='{inline frame}', rip=native_frame.pc(), native_frame=native_frame)) native_frame = native_frame.older() i += 1 if frames: # Associate the frame pointer with the un-inlined frame. frames[-1]['fp'] = str(fp) for f in frames: print(frame.stringify(f)) else: # We only hit this case if gdb undercounted the TC's # frames---which shouldn't happen unless the custom # unwinder (or gdb's unwinder API) is malfunctioning. # # Just guess that the name of the frame is the same as the # name of the block we're in. try: block = gdb.block_for_pc(int(rip)) name = block.function.name print( frame.stringify( frame.create_native(idx=i, fp=fp, rip=rip, name='? ' + name))) except: print( frame.stringify( frame.create_native(idx=i, fp=fp, rip=rip, native_frame=None))) i += 1
def invoke(self, args, from_tty): argv = parse_argv(args) if len(argv) > 1: print('Usage: walkstk [fp]') return # Bail early if the custom unwinder has not been set up. if not unwind.try_unwinder_init(): print('walkstk: Could not initialize the HHVM unwinder.') # Find the starting native frame. native_frame = gdb.newest_frame() if native_frame is None: print('walkstk: Cannot find any frames: corrupt stack?') return # Set fp = $rbp, rip = $rip. fp_type = T('uintptr_t').pointer() fp = native_frame.read_register('rbp').cast(fp_type) rip = native_frame.pc() if len(argv) == 1: # Start walking the stack from the user-provided `fp'. fp = argv[0].cast(fp_type)[0] rip = argv[0].cast(fp_type)[1] # Try to find a corresponding native frame. while (native_frame is not None and rip != native_frame.pc()): native_frame = native_frame.older() i = 0 # Make a fake frame for our `fp' and `rip'. This lets us pop a frame # at the top of the loop, which makes it easier to include the final # frame. fp = (fp, rip) while fp: rip = fp[1] fp = fp[0].cast(fp_type) # Try to get the PHP function name from the ActRec at `fp' if we're # executing in the TC. if frame.is_jitted(fp, rip): ar_type = T('HPHP::ActRec').pointer() try: print(frame.stringify(frame.create_php( idx=i, ar=fp.cast(ar_type), rip=rip))) except gdb.MemoryError: print(frame.stringify(frame.create_native( idx=i, fp=fp, rip=rip, native_frame=native_frame))) if native_frame is not None: native_frame = native_frame.older() i += 1 else: if native_frame is None: # If we couldn't find a native frame, then `walkstk' was # invoked with a non-native `fp' argument. Now that we # don't seem to be in the TC, try to find the corresponding # native frame. native_frame = gdb.newest_frame() while (native_frame is not None and rip != native_frame.pc()): native_frame = native_frame.older() if native_frame is not None: # Pop native frames until we hit our caller's rip. frames = [] while (native_frame is not None and (fp == 0x0 or fp[1] != native_frame.pc())): frames.append(frame.create_native( idx=i, fp='{inline frame}', rip=native_frame.pc(), native_frame=native_frame)) native_frame = native_frame.older() i += 1 if frames: # Associate the frame pointer with the un-inlined frame. frames[-1]['fp'] = str(fp) for f in frames: print(frame.stringify(f)) else: # We only hit this case if gdb undercounted the TC's # frames---which shouldn't happen unless the custom # unwinder (or gdb's unwinder API) is malfunctioning. # # Just guess that the name of the frame is the same as the # name of the block we're in. try: print(frame.stringify(frame.create_native( idx=i, fp=fp, rip=rip, name=_function_for(rip)))) except: print(frame.stringify(frame.create_native( idx=i, fp=fp, rip=rip, native_frame=None))) i += 1