Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 60 additions & 24 deletions qiling/debugger/qdb/qdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@

import cmd

from typing import Optional, Tuple, Union
from typing import Optional, Tuple, Union, List
from contextlib import contextmanager

from qiling import Qiling
from qiling.const import QL_OS, QL_ARCH, QL_ENDIAN
from qiling.const import QL_OS, QL_ARCH, QL_ENDIAN, QL_STATE, QL_VERBOSE
from qiling.debugger import QlDebugger

from .utils import setup_context_render, setup_branch_predictor, setup_address_marker, SnapshotManager, run_qdb_script
Expand All @@ -25,7 +25,7 @@ class QlQdb(cmd.Cmd, QlDebugger):
The built-in debugger of Qiling Framework
"""

def __init__(self, ql: Qiling, init_hook: str = "", rr: bool = False, script: str = "") -> None:
def __init__(self, ql: Qiling, init_hook: List[str] = [], rr: bool = False, script: str = "") -> None:
"""
@init_hook: the entry to be paused at
@rr: record/replay debugging
Expand All @@ -45,9 +45,10 @@ def __init__(self, ql: Qiling, init_hook: str = "", rr: bool = False, script: st

super().__init__()

self.dbg_hook(init_hook)
# filter out entry_point of loader if presented
self.dbg_hook(list(filter(lambda d: int(d, 0) != self.ql.loader.entry_point, init_hook)))

def dbg_hook(self, init_hook: str):
def dbg_hook(self, init_hook: List[str]):
"""
initial hook to prepare everything we need
"""
Expand All @@ -67,27 +68,54 @@ def bp_handler(ql, address, size, bp_list):
if bp.hitted:
return

qdb_print(QDB_MSG.INFO, f"hit breakpoint at 0x{self.cur_addr:08x}")
qdb_print(QDB_MSG.INFO, f"hit breakpoint at {self.cur_addr:#x}")
bp.hitted = True

ql.stop()
self.do_context()

self.ql.hook_code(bp_handler, self.bp_list)

if self.ql.os.type == QL_OS.BLOB:
self.ql.loader.entry_point = self.ql.loader.load_address

elif init_hook and self.ql.loader.entry_point != int(init_hook, 0):
self.do_breakpoint(init_hook)

if self.ql.entry_point:
self.cur_addr = self.ql.entry_point
else:
self.cur_addr = self.ql.loader.entry_point

self.init_state = self.ql.save()

# stop emulator once interp. have been done emulating
if addr_elf_entry := getattr(self.ql.loader, 'elf_entry'):
handler = self.ql.hook_address(lambda ql: ql.stop(), addr_elf_entry)
else:
handler = self.ql.hook_address(lambda ql: ql.stop(), self.ql.loader.entry_point)

# suppress logging temporary
_verbose = self.ql.verbose
self.ql.verbose = QL_VERBOSE.DISABLED

# init os for integrity of hooks and patches,
self.ql.os.run()

handler.remove()

# ignore the memory unmap error for now, due to the MIPS memory layout issue
try:
self.ql.mem.unmap_all()
except:
pass

self.ql.restore(self.init_state)

# resotre logging verbose
self.ql.verbose = _verbose

if self.ql.os.type is QL_OS.BLOB:
self.ql.loader.entry_point = self.ql.loader.load_address

elif init_hook:
for each_hook in init_hook:
self.do_breakpoint(each_hook)

if self._script:
run_qdb_script(self, self._script)
else:
Expand Down Expand Up @@ -121,14 +149,7 @@ def _run(self, address: int = 0, end: int = 0, count: int = 0) -> None:
if getattr(self.ql.arch, 'is_thumb', False):
address |= 0b1

# assume we're running PE if on Windows
if self.ql.os.type == QL_OS.WINDOWS:
self.ql.count = count
self.ql.entry_point = address
self.ql.os.run()

else:
self.ql.emu_start(begin=address, end=end, count=count)
self.ql.emu_start(begin=address, end=end, count=count)

@contextmanager
def _save(self, reg=True, mem=True, hw=False, fd=False, cpu_context=False, os=False, loader=False):
Expand Down Expand Up @@ -372,7 +393,6 @@ def do_start(self, *args) -> None:
"""

if self.ql.arch != QL_ARCH.CORTEX_M:

self.ql.restore(self.init_state)
self.do_context()

Expand Down Expand Up @@ -528,13 +548,29 @@ def do_show_args(self, argc: int = -1):
args = reg_args + stk_args
qdb_print(QDB_MSG.INFO, f'args: {args}')

def do_show(self, *args) -> None:
def do_show(self, keyword: Optional[str] = None, *args) -> None:
"""
show some runtime information
"""

for info_line in self.ql.mem.get_formatted_mapinfo():
qdb_print(QDB_MSG.INFO, info_line)
qdb_print(QDB_MSG.INFO, f"Entry point: {self.ql.loader.entry_point:#x}")

if addr_elf_entry := getattr(self.ql.loader, 'elf_entry'):
qdb_print(QDB_MSG.INFO, f"ELF entry: {addr_elf_entry:#x}")

info_lines = iter(self.ql.mem.get_formatted_mapinfo())

# print filed name first
qdb_print(QDB_MSG.INFO, next(info_lines))

# keyword filtering
if keyword:
lines = filter(lambda line: keyword in line, info_lines)
else:
lines = info_lines

for line in lines:
qdb_print(QDB_MSG.INFO, line)

qdb_print(QDB_MSG.INFO, f"Breakpoints: {[hex(addr) for addr in self.bp_list.keys()]}")
qdb_print(QDB_MSG.INFO, f"Marked symbol: {[{key:hex(val)} for key,val in self.marker.mark_list]}")
Expand Down
13 changes: 8 additions & 5 deletions qiling/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -350,15 +350,18 @@ def __int_nothrow(v: str, /) -> Optional[int]:
return None

# qdb init args are independent and may include any combination of: rr enable, init hook and script
for a in args:
if a == 'rr':
arg_init_hook = []
for arg in args:
if arg == 'rr':
kwargs['rr'] = True

elif __int_nothrow(a) is not None:
kwargs['init_hook'] = a
elif __int_nothrow(arg) is not None:
arg_init_hook.append(arg)

else:
kwargs['script'] = a
kwargs['script'] = arg
else:
kwargs['init_hook'] = arg_init_hook

else:
raise QlErrorOutput('Debugger not supported')
Expand Down