import gdb
from mcgdb.toolbox import my_gdb
[docs]class state:
def __init__(self):
pass
select_task = None
sched_task = None
stop_regs = None
comp_thr = None
######################################################
[docs]def get_selected_task():
if state.select_task is None:
state.select_task = do_get_selected_task()
return state.select_task
warned = False
[docs]def do_get_selected_task():
locked = gdb.parameter("scheduler-locking")
gdb.execute("set scheduler-locking on")
try:
current_ctx = gdb.parse_and_eval("get_current_context()")
except gdb.error as e:
global warned
if not warned:
my_gdb.log.warning("Could not compute the current "\
"thread context: %s", e)
warned = True
return None
finally:
gdb.execute("set scheduler-locking %s" % locked)
if current_ctx == 0:
return None
return current_ctx
[docs]def set_stopped_task(evt, task=None):
if isinstance(task, gdb.StopEvent):
task = None
if task is None:
task = do_get_selected_task()
state.select_task = task
state.sched_task = task
#gdb.events.stop.connect(set_stopped_task)
#my_gdb.events.bp_stop.connect(set_stopped_task)
[docs]def get_mark(task):
if task is None:
return "~"
if is_host_task(task):
if my_gdb.is_selected_task(task):
return "*"
else:
return " "
else:
if (gdb.selected_thread() is not None
and gdb.selected_thread().num == 2
and state.select_task == task):
return "*"
if state.sched_task is None:
return "?"
if task == state.sched_task:
return ">"
return "|"
[docs]def is_selected_task(task):
if is_host_task(task):
return my_gdb.is_selected_task(task)
else:
sel = (gdb.selected_thread().num == 2
and task == get_selected_task())
return sel
[docs]def switch_to(task, force=False):
if is_host_task(task):
return switch_host(task)
else:
thr = gdb.selected_thread()
if thr is None:
raise gdb.error("No thread currently selected")
if thr.num != 2:
for thr in gdb.selected_inferior().threads():
if thr.num == 2:
thr.switch()
if str(task["state"]) == "RUNNING":
if state.stop_regs is not None:
reload_current_thread(state.stop_regs)
state.stop_regs = None
state.select_task = task
return True
else:
return switch_inactive_thread(task, force=force)
####
[docs]def get_selected_host_task():
return my_gdb.get_selected_task()
[docs]def is_host_task(task):
return isinstance(task, tuple)
[docs]def switch_host(task):
return my_gdb.switch_to(task)
######################################################
[docs]def restore_stop_registers(event):
if state.stop_regs is None:
return
old_thr = gdb.selected_thread()
if old_thr != state.comp_thr:
state.comp_thr.switch()
reload_current_thread(state.stop_regs)
state.stop_regs = None
if old_thr != state.comp_thr:
old_thr.switch()
#gdb.events.cont.connect(restore_stop_registers)
[docs]def switch_inactive_thread(next_, master=False, force=False):
alternative = False
thr = gdb.selected_thread()
if thr.num != 2:
for thr in gdb.selected_inferior().threads():
if thr.num == 2:
thr.switch()
if state.stop_regs is None:
state.stop_regs = save_current_thread()
state.comp_thr = thr
JB_BP = 3
if master:
JB_SP = 1
else:
JB_SP = 2
if str(next_["state"]) == "INACTIVE":
my_gdb.log.warning("proc is inactive, use alternative method.")
alternative = True
if not master and str(next_["state"]) == "RUNNING":
my_gdb.log.warning("proc is running, abort!!")
if not force:
return False
jmbuf = next_["context"][0]["__jmpbuf"]
if int(jmbuf[JB_SP]) == 0:
my_gdb.log.warning("SP is null, use alternative method.")
alternative = True
diff = abs(jmbuf[JB_SP] - jmbuf[JB_BP])
if diff < 1500 or diff > 100000000 :
my_gdb.log.warning("Frame not large enough/too large (%db), "\
"use alternative method." % diff)
alternative = True
my_gdb.print_and_execute("flushregs")
my_gdb.print_and_execute("flushregs")
my_gdb.print_and_execute("set $ebp=%s" % jmbuf[JB_BP])
if alternative:
my_gdb.print_and_execute("set $esp=$ebp-0x2c")
else:
my_gdb.print_and_execute("set $esp=%s-0xd00" % jmbuf[JB_SP])
my_gdb.print_and_execute("set $eip=__longjmp")
my_gdb.print_and_execute("up", to_string=True)
my_gdb.print_and_execute("set $eip=contextSwitch+33")
#my_gdb.print_and_execute("down", to_string=True)
my_gdb.print_and_execute("flushregs")
state.select_task = next_
return True
#####################################################
REGISTERS_read = ("$esp", "$ebp", "(void *) $eip")
REGISTERS_write = ("$esp", "$ebp", "$eip")
[docs]def save_current_thread():
return [gdb.parse_and_eval(reg) for reg in REGISTERS_read]
[docs]def reload_current_thread(stop_regs):
for reg_name, reg_val in map(None, REGISTERS_write, stop_regs):
command = "set %s=%s" % (reg_name, str(reg_val))
my_gdb.log.debug(command)
gdb.execute(command)