Some additions and tips:
You can now
log the return value of any function (a temporary logging point is added on the LR address on the fly, similar to gdb tbreak). Example:
{ 0xFF304A74, "IsAddressEEP", 1 | RET },
{ 0xFF309F34, "IsAddressSerialFlash", 1 | RET },
{ 0xFF128228, "Compare_FROMAddress", 1 | RET },
75500> Terminate:000aee60:00:00: *** Compare_FROMAddress(0x2000000), from ff0ce434
75600> Terminate:000aee60:00:00: *** IsAddressEEP(0xf8f51000), from ff128260
75600> Terminate:000aec18:00:00: ==> 0x0, from ff128260
75600> Terminate:000aee60:00:00: *** IsAddressSerialFlash(0xf8f51000), from ff128274
76100> Terminate:000aec18:00:00: ==> 0x0, from ff128274
76200> Terminate:ff1282a4:02:03: Compare FROMAddress (0) 0x406cf800 0xfff40000 Size 4088
76300> Terminate:000aec18:00:00: ==> 0x0, from ff0ce434
(notice the nested calls)
-
RAM pointers that appear to be data structures are logged as well (no need to do anything special):
{ 0xFF127844, "prop_erase", 1 },
{ 0xFF1272FC, "propad_erase", 3 },
76300> Terminate:000aee60:00:00: *** prop_erase(0x2000000), from ff0ce444
76400> Terminate:000aee60:00:00: *** propad_erase(0x6cf708, 0x1, 0x147c70), from ff127870
76400> Terminate:000aef5c:00:00: *0x6cf708 = { f8f40000 fff40000 20000 2 f8f51000 f8f52000 406cf800 406cf790 1000 1 64f55c 6d0904 ... }
76400> Terminate:000aef5c:00:00: *0x147c70 = { 6cf708 0 19980218 19980218 19980218 23f2c 19980218 ff0ce448 0 becd3c 19980218 ff0f8ee0 ... }
-
Functions already defined in stubs can be taken from there (no need to type their address again):
STUB_ENTRY(task_create, 5),
STUB_ENTRY(msleep, 1),
STUB_ENTRY(_malloc, 1 | RET),
STUB_ENTRY(_free, 1),
- The logging points do not have to be functions - they can be anywhere in the middle of the code (in theory - bug reports welcome) and you can log
arbitrary register values (without a custom logging function):
{ 0xFF1C8C00, "resinfo_wait_smth", R(0) },
{ 0xFF1C8AC0, "resinfo_check", R(7) },
- You can log
interrupts as well. Define LOG_INTERRUPTS in dm-spy-extra.c:
ShootCaptu:000afe68:00:00: *** register_interrupt("Jp57IntrHandler", 0xa3, 0xff28dad0, 0x0), from ff28f71c
ShootPreDe:ff14f698:95:03: spsInit
**INT-A3h*:000afc54:00:00: >>> INT-A3h Jp57IntrHandler ff28dad0(0)
**INT-A3h*:00000588:00:00: <<< INT-A3h done
- You can
run the startup logging under QEMU, in real-time, with no cache conflicts:
CONFIG_DEBUG_INTERCEPT_STARTUP = y
CONFIG_QEMU = y
It's best to install QEMU from the qemu branch for now - which should not cause problems with branching, as QEMU runs outside the ML tree.
There is similar functionality in the GDB scripts from the QEMU branch - they output somewhat similar logs, without modifying the guest code (so they can be used very early in the boot process). The method from here requires running autoexec.bin alongside the main firmware.
Q: Then why not just use GDB? (when running in QEMU)
A: it's
very very slow. I bet you don't like to wait for half an hour for the firmware to boot the GUI under GDB, when it can do the same thing with DebugMsg hooks in about 1 minute.
Q: Then why do we use GDB?
A: It works, it's well-tested and
does not require modifying the guest code. The method from here must have ML ported on your camera before you can use it (not to mention bugs in this code possibly affecting the results), while with GDB, just add your logging points and you are good to go.
- Anything helpful for understanding
ENGIO, ADTG, EDMAC and similar monsters?
These things are a bit verbose, so a bunch of messages are disabled by default. Comment out this in dm-spy.c:
if (class == 21) // engio, lots of messages
return;
There is also a script in the iso-research branch, that can annotate logs with info about ADTG/CMOS/ENGIO registers:
https://bitbucket.org/hudson/magic-lantern/src/iso-research/modules/adtg_gui/annotate_log.pySome registers are set directly (without the EngDrvOut/engio_write wrappers) - simply by writing to memory. Currently, the only way to catch these is to run the analyzed routine in QEMU (assuming it can emulate at least some of it) and log the MMIO activity:
./run_canon_fw.sh 5D3,firmware="boot=1" -d io
- What about
timestamps?
We have those, too. The number on the left of > is the hardware counter aka digic timer (microseconds, wraps at 0x100000):
04A55> init:000af020:00:00: *** msleep(0x64), from 69674
...
1BACC> init:000af020:00:00: *** msleep(0x64), from 69674
All that's left is to unwrap the counter in post.
-
Common errors and how to solve them?Will NOT load on:
100D (debug_intercept)
Recompile ML core, double-checking Makefile.user (CONFIG_DEBUG_INTERCEPT = y) and running "make clean" before.
Birthday paradox: ff4fa224 RegisterHead3InterruptHandler
conflicts with: ff216a20 RegisterHead1InterruptHandler
That's a limitation of cache hacks - you can't patch two ROM addresses whose index bits (mask 0x7E0) are the same, but the more significant bits are different.
What you can do?
- Find a different logging address (sometimes, moving a few bytes around will change the index bits, solving the conflict)
- Log the two conflicting functions in two sessions
- Try under QEMU (which does not emulate cache)
- Update cache_fake to lock down two caching lines for ROM patches.
The examples were from 5D3 1.2.3 (most) and 60D 1.1.1 (resinfo).
Happy reversing.