I promised to have a look at how to add a GDB stub into magic lantern.
looked quite simple to do, so i implemented it.
as it is a quite big thing, please understand that i cannot publish a full guide to this tool.
also it is (very) far from perfect.
see it as a tool that helps you to breakpoint into a specific function, get the memory/stack/register contents and
*
eventually* continuing the execuion.
latter wont work reliable on the camera, as it is some complex realtime system.
we cannot simply halt some tasks without side effects like ERR70 or even total lockup.
but i was able to test some basic breakpointing, changing registers and continuing in some test task.
in canon tasks it did sometimes lock up everything.
the way how i implemented it does not perfectly match how gdb frontends expect it.
that would mean we would not simply set a BP wherever we want (no interrupts!) and wait for some task(s) being stalled.
i intentionally decided not to do it like they expect as i had some special goals using that code.
i want it to be a swiss tool to e.g. hook code anywhere in ROM for testing purpose or capturing registers etc.
nevertheless it may help with inspecting "what are the parameters to this function?"
you can also add "watchpoints" that are breakpoints that trigger
once and save the registers.
this feature is not yet available via GDB interface, but you must call the functions manually using
gdb_add_bkpt(uint32_t address, GDB_BKPT_FLAG_WATCHPOINT | GDB_BKPT_FLAG_ARMED)and then read the captured registers from
gdb_breakpoints[pos].ctxbut you can very simple extend the gdb.c to allow setting such watchpoints from your favorite GDB frontend.
be warned - you will have to repower your camera very often
here it is:magic lantern code:
http://upload.g3gg0.de/pub_files/8e155ddcc88ee690cd07b6c2da365807/gdbstub.zipptpcam: use the one inside the contrib folder of the repository (as of 2012-10-16)
(
Merged)
updated tasks.h: http://upload.g3gg0.de/pub_files/4015304003c3c336e66f651e1418439e/tasks.h(
Merged)
ptpcam patch: http://upload.g3gg0.de/pub_files/92879a741f5b8863da832ca8fe9327db/gdb.patchhow it works: * it adds two new PTP messages PTP_CHDK_GDBStub_Download and PTP_CHDK_GDBStub_Upload (defined for 600D)
* "void gdb_setup()" is starting the processing task that handles GDB serial commands - call it from e.g. run_test
* ptpcam has 3 new commands "gdb s" to send gdb serial commands, "gdb r" to receive the response and "gdbproxy" to forward gdb commands between a network socket and the camera (for remote debugging using e.g. IDA)
* breakpoints are set using cache hacking - i place a undefined instruction that raises UNDEFINED interrupt
* in UNDEFINED interrupt, i stall the running process using continuous msleep(1) and *store* the process context
* when that process is resumed, i use another UNDEF instruction and the handler to *restore* the process context where it was stalled
important notes: * it is not possible to continue a process as long the breakpoint is still active. deactivate the BP, add another one behind the current PC, continue and then set the first one again
* there is no "single step" functionality - the camera will do nothing
* some tools (like IDA) update newly set breakpoints when e.g. "single step"ing and they fetch the current registers - so consdier "single step" as some kind of "refresh" or "sync"
* do not (!) set breakpoints in interrupts. that wont work.
* if you just continue execution in e.g. IDA without setting any breakpoint, IDA will wait. and wait. and wait. ... until you kill the network connection by exiting ptpcam or killing IDA (this break could also be done with a menu in ML to break the wait in in gdb.c:1159)
* this might also happen if the "continue" command failed for some reason (i warned you that it wont work reliable

)
how to debug: * call "gdb_setup()"
* start "ptpcam --chdk"
* enter "gdbproxy"
* connect to localhost:23946 using your favorite debugger
* you will see all the registers 0x0000..
* set a breakpoint in the function you want to debug
* "sync" using single step to activate the breakpoint
* "sync" again to get current process registers - they will change if breakpoint is reached
* you must now disable the triggered breakpoint and set a new one where you want to stop again
* "continue" execution until BP is reached
* some frontends like IDA allow "step over" commands that automatically set a breakpoint after the current instruction - this is supported of course
example code that displays all breakpoints and registers on-screen:
while(1)
{
uint32_t line = 0;
uint32_t bp = 0;
bmp_printf(FONT_MED, 0, line++ * 20, "exc %08X, l 0x%08X", gdb_exceptions_handled, loops++);
for(bp = 0; bp < GDB_BKPT_COUNT; bp++)
{
if(gdb_breakpoints[bp].flags & GDB_BKPT_FLAG_ENABLED)
{
uint32_t reg = 0;
bmp_printf(FONT_MED, 0, line++ * 20, "BP#%d 0x%08X flags 0x%1X hits %d", bp, gdb_breakpoints[bp].address, gdb_breakpoints[bp].flags, gdb_breakpoints[bp].hitcount);
for(reg = 0; reg < 15; reg+=2)
{
bmp_printf(FONT_MED, 0, line++ * 20, "R%02d %08X R%02d 0x%08X", reg, gdb_breakpoints[bp].ctx[reg], reg+1, gdb_breakpoints[bp].ctx[reg+1]);
}
bmp_printf(FONT_MED, 0, line++ * 20, "CPSR %08X", gdb_breakpoints[bp].ctx[16]);
}
}
msleep(100);
}
feel free to improve that tool. maybe it will get good enough to get into mainline tree somewhen