Exemple #1
0
    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
Exemple #2
0
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
Exemple #3
0
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
Exemple #4
0
    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
Exemple #5
0
    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
Exemple #6
0
    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
Exemple #7
0
    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
Exemple #8
0
    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
Exemple #9
0
    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
Exemple #10
0
    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
Exemple #11
0
    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