Quick tutorial for finding stubs using the
GPL tools from CHDK (very easy to get started under Linux).
BackgroundThese stubs are functions from Canon firmware, that can be called from ML code. To use them, one needs to know:
- the function address in Canon code (that's the stub you need to find). The exact address is unique for each camera.
- the function declaration (what parameters it accepts?) - declared in some header file, and usually common for all cameras.
- what does it do (if it's not obvious from its name and usage context, a comment is welcome)
0) Preparation- make sure you can
compile ML yourself
- take a look at
http://magiclantern.wikia.com/wiki/ASM_introduction1) Downloading the tools- grab
disassemble.pl [DIGIC 2...8] and
disassemblev7.pl [DIGIC 6..8]
- make the following changes (your paths may be different; these are the paths for the default ML toolchain):
@@ -25,14 +25,14 @@
# adjust these for your needs (note final slash):
#$path = "$ENV{'HOME'}/gcc-4.1-arm/bin/";
-$path = "";
+$path = "$ENV{'HOME'}/gcc-arm-none-eabi-4_8-2013q4/bin/"; # or gcc-arm-none-eabi-5_4-2016q3 or gcc-arm-none-eabi-7-2017-q4-major etc
# note on "strings": default is a minimum length of 4 chars.
# So if u are hunting for e.g. "FI2" add -n3
# However, it gives a lot of false positive.
$strdump = "strings -t x";
-$objdump = "${path}arm-elf-objdump";
-$objcopy = "${path}arm-elf-objcopy";
+$objdump = "${path}arm-none-eabi-objdump";
+$objcopy = "${path}arm-none-eabi-objcopy";
if (@ARGV != 2) {
die("Usage: $0 0x<offset> <dump.bin>");
2)
DisassemblingYou can find a ROM dump from your camera under ML/LOGS, on your card. For DIGIC 4/5, the one containing code is ROM1 and loads at 0xFF000000 (
see here for other models). So, run the script with:
perl disassemble.pl 0xFF000000 ROM1.BIN
This script saves two interesting files: ROM1.BIN.strings and ROM1.BIN.dis. For finding stubs, you are interested in the disassembled code, ROM1.BIN.dis. Open that in a text editor.
3) Identifying stubsMost ML stubs can be found from strings. Most of them are functions, so they start with a PUSH instruction (or STMFD in IDA). The PUSH usually indicates a start of a function, but not always; there may be one or two instructions before it. If in doubt, look for other code calling your function (code references to it, usually BL instructions).
Some common patterns:
- a function referencing the string inside its body (for example, GUI_Control):
GUI_Control:
push ...
...
DebugMsg(..., ..., "GUI_Control: ...");
....
pop ...
which, in pure ASM, looks like this:
loc_ff0ded1c: ; 21 refs
ff0ded1c: e92d40f8 push {r3, r4, r5, r6, r7, lr} ; ff0ded1c is the address you are looking for; notice there are a few references to it (other Canon code calling it)
...
ff0ded34: e28f2e16 add r2, pc, #352 ; ff0dee9c: (5f495547) *"GUI_Control:%d 0x%x"
ff0ded38: e3a01003 mov r1, #3
ff0ded3c: e3a00085 mov r0, #133 ; 0x85
ff0ded40: eb3c9b92 bl loc_5b90 ; this must be DebugMsg (aka DryosDebugMsg in stubs.S)
- another function calling the function you are looking for, and printing an error message if it fails. For example, if you look for GetSizeOfMaxRegion:
AllocateMemory:
...
BL GetSizeOfMaxRegion
if (returned_value != 0)
{
DebugMsg(..., ..., "[MEM] ERROR GetSizeOfMaxRegion [%#x]", returned_value);
}
which, in pure ASM, looks like this:
ff9fa1c0: ebffff39 bl loc_ff9f9eac ; this is GetSizeOfMaxRegion you are looking for
ff9fa1c4: e3500000 cmp r0, #0
ff9fa1c8: 11a03000 movne r3, r0
ff9fa1cc: 128f20d0 addne r2, pc, #208 ; ff9fa2a4: (4d454d5b) *"[MEM] ERROR GetSizeOfMaxRegion [%#x]"
ff9fa1d0: 13a01016 movne r1, #22
ff9fa1d4: 13a00000 movne r0, #0
ff9fa1d8: 1bfff05c blne loc_ff9f6350 ; this must be DebugMsg (aka DryosDebugMsg in stubs.S)
Repeat the process for other stubs, until you get comfortable with the workflow.
Other stubs may not be easy to find for strings; in this case, you may look for the context (other functions calling it, which are identifiable from strings). Comparing with a dump from another camera, which has these stubs, is helpful.
Other stubs are not functions, but data structures. You will need to find some functions using those structures first.
A very useful hint: the differences between related stubs may be constant, or at least helpful to get you within the ballpark. For example, the offset between FIO_SeekFile and FIO_SeekSkipFile is usually
0xD0. Check the stub files for more cameras, and look for such similarities. It's not a hard rule though, but it helps in many cases.
A note about DIGIC V cameras: they copy a section from the ROM, starting from assert_0 (string "Assert: File %s, Expression %s, Line %d"), to 0x1900. So, there will be a difference between the address where your function may run, and the address where it's placed in the ROM. This difference is called RAM_OFFSET (it's declared in stubs.S). So, if say AllocateMemory is at 0xFF9FA160 in ROM (5D3.123), its RAM copy will be at 0xFF9FA160 - RAM_OFFSET = 0xFF9FA160 - 0xFF9F07C0 = 0x99a0. So, you will find many BL calls to 0x99a0, for example:
ff0e7644: eb3c88d5 bl loc_99a0
...
ff0e7658: e59f20e8 ldr r2, [pc, #232] ; ff0e7748: (ff0e5904) **"%s : AllocateMemory(READ_ONE_PARAM)"
Function calls are usually relative to the program counter. Therefore:
- if the code calls a RAM function from another RAM function, and you look at the ROM copy of the caller, you will see BL rom_address
- if the code calls a RAM function from a ROM function, and you look at the caller code (the ROM function), you will see BL ram_address
- if the code calls a ROM function from a RAM one, and you look at the ROM copy of the caller, you are screwed

(I'll let you do the math for this one)
4) Testing outOnce you have guessed a stub, put it in ML's stubs file (platform/camera.version/stubs.S), look it up in ML code to see where it's used (so you know what exactly to test), compile and test it out. There is also a stub testing routine under Debug menu, and running it is always a good idea. If the stub test routine does not contain yet a test for your stub, consider adding one.
Double-check that your stub is indeed at the beginning of the function. Jumping in the middle of a function may have undesired consequences

Also double-check that you are not calling EraseSectorOfRom or other stuff like that.
Other tools:You may prefer to use Pelican's
ARMu (freeware, but not free software) or
IDA (commercial, with demo version available). You can find some hints for these programs
here and
here, but tutorials are welcome.
I still use
ARM-console for browsing around and decompling, but it may be a little difficult to install. If you don't need a decompiler, browsing the ASM from disassemble.pl or ARMu should be enough.