Source code for crash.target.x86_64

# -*- coding: utf-8 -*-
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:

from typing import Optional
import re

import gdb
import crash.target
from crash.target import IncorrectTargetError, register_target
from crash.target import KernelFrameFilter
from crash.util.symbols import Types, MinimalSymvals
from crash.util.symbols import TypeCallbacks, MinimalSymbolCallbacks

types = Types(['struct inactive_task_frame *', 'struct thread_info *',
               'unsigned long *'])
msymvals = MinimalSymvals(['thread_return'])

# pylint: disable=abstract-method
class _FetchRegistersBase(crash.target.TargetFetchRegistersBase):
    def __init__(self) -> None:
        super().__init__()
        self.filter: KernelFrameFilter

    def fetch_active(self, thread: gdb.InferiorThread,
                     register: Optional[gdb.RegisterDescriptor]) -> gdb.RegisterCollectionType:
        regmap = {
            "rflags" : "eflags"
        }
        registers = {}
        task = thread.info
        for reg in task.regs:
            if (reg == "rip" and register is not None and
                    register.name != "rip"):
                continue
            try:
                # vmcore uses rflags, gdb uses eflags
                if reg in regmap:
                    reg = regmap[reg]
                registers[reg] = task.regs[reg]
            except KeyError:
                pass

        return registers

class _FetchRegistersInactiveFrame(_FetchRegistersBase):
    def __init__(self) -> None:
        super().__init__()

        self._scheduled_rip: int = 0
        if not self._enabled:
            raise IncorrectTargetError("Missing struct inactive_task_frame type")

    # We don't have CFI for __switch_to_asm but we do know what it looks like.
    # We push 6 registers and then swap rsp, so we can just rewind back
    # to __switch_to_asm getting called and then populate the registers that
    # were saved on the stack.
    def find_scheduled_rip(self, task: gdb.Value) -> None:
        top = int(task['stack']) + 16*1024
        callq = re.compile(r"callq?.*<(\w+)>")

        rsp = task['thread']['sp'].cast(types.unsigned_long_p_type)

        count = 0
        while int(rsp) < top:
            val = int(rsp.dereference()) - 5
            if val > self.filter.address:
                try:
                    insn = gdb.execute(f"x/i {val:#x}", to_string=True)
                except gdb.error:
                    insn = None

                if insn:
                    m = callq.search(insn)
                    if m and m.group(1) == "__switch_to_asm":
                        print("Set scheduled RIP")
                        self._scheduled_rip = val
                        return

            rsp += 1
            count += 1

        raise RuntimeError("Cannot locate stack frame offset for __schedule")

    def get_scheduled_rip(self, task: gdb.Value) -> int:
        if self._scheduled_rip == 0:
            self.find_scheduled_rip(task)

        return self._scheduled_rip

    def fetch_scheduled(self, thread: gdb.InferiorThread,
                        register: Optional[gdb.RegisterDescriptor]) -> gdb.RegisterCollectionType:
        registers: gdb.RegisterCollectionType = {}
        task = thread.info.task_struct

        rsp = task['thread']['sp'].cast(types.unsigned_long_p_type)
        registers['rsp'] = rsp

        frame = rsp.cast(types.inactive_task_frame_p_type).dereference()

        registers['rip'] = self.get_scheduled_rip(task)
        registers['rbp'] = frame['bp']
        registers['rbx'] = frame['bx']
        registers['r12'] = frame['r12']
        registers['r13'] = frame['r13']
        registers['r14'] = frame['r14']
        registers['r15'] = frame['r15']
        registers['cs'] = 2*8
        registers['ss'] = 3*8

        thread.info.stack_pointer = rsp
        thread.info.valid_stack = True

        return registers

class _FetchRegistersThreadReturn(_FetchRegistersBase):
    def fetch_scheduled(self, thread: gdb.InferiorThread,
                        register: Optional[gdb.RegisterDescriptor]) -> gdb.RegisterCollectionType:
        registers: gdb.RegisterCollectionType = {}
        task = thread.info.task_struct

        rsp = task['thread']['sp'].cast(types.unsigned_long_p_type)
        rbp = rsp.dereference().cast(types.unsigned_long_p_type)
        rbx = (rbp - 1).dereference()
        r12 = (rbp - 2).dereference()
        r13 = (rbp - 3).dereference()
        r14 = (rbp - 4).dereference()
        r15 = (rbp - 5).dereference()

        # The two pushes that don't have CFI info
        # rsp += 2

        # ex = in_exception_stack(rsp)
        # if ex:
        #     print("EXCEPTION STACK: pid {:d}".format(task['pid']))

        registers['rip'] = msymvals.thread_return
        registers['rsp'] = rsp
        registers['rbp'] = rbp
        registers['rbx'] = rbx
        registers['r12'] = r12
        registers['r13'] = r13
        registers['r14'] = r14
        registers['r15'] = r15
        registers['cs'] = 2*8
        registers['ss'] = 3*8

        thread.info.stack_pointer = rsp
        thread.info.valid_stack = True

        return registers

[docs] class X8664TargetBase(crash.target.TargetBase): ident = "i386:x86-64" aliases = ["x86_64"] def __init__(self) -> None: super().__init__() # Stop stack traces with addresses below this self.filter = KernelFrameFilter(0xffff000000000000)
[docs] def arch_setup_thread(self, thread: gdb.InferiorThread) -> None: task = thread.info.task_struct thread_info = task['stack'].cast(types.thread_info_p_type) thread.info.set_thread_info(thread_info) thread.info.set_thread_struct(task['thread'])
[docs] def get_stack_pointer(self, thread: gdb.InferiorThread) -> int: return int(thread.info.thread_struct['sp'])
[docs] class X8664ThreadReturnTarget(_FetchRegistersThreadReturn, X8664TargetBase): pass
[docs] class X8664InactiveFrameTarget(_FetchRegistersInactiveFrame, X8664TargetBase): pass
type_cbs = TypeCallbacks([('struct inactive_task_frame', _FetchRegistersInactiveFrame.enable)], wait_for_target=False) msymbol_cbs = MinimalSymbolCallbacks([('thread_return', _FetchRegistersThreadReturn.enable)], wait_for_target=False) register_target(X8664ThreadReturnTarget) register_target(X8664InactiveFrameTarget)