Showing changes from revision #2 to #3:
Added | Removed | Changed
The annotated exception dumps produced by the debugger module consist of two main sections. The first section is a summary of the content of the blocks contained in the raw dump block, including the error block and register dump. The second section contains the annotated stack dump(s).
Note that currently only the IRQ, SVC and USR stacks will be annotated. For IRQ and SVC, only the non-empty portions will be annotated (as defined by the current value of R13 for the mode), the stack being omitted completely if R13 indicates the stack is empty. For USR mode, there is no defined limit for the stack, so only the region of memory from R13_usr to R13_usr+1024 will be shown.
Each stack dump has the following basic structure:
R15 = fc173850 = SharedCLibrary +327c = _sys_istty +8 R14_usr = fc18256c = SharedCLibrary +11f98 = _write +28 Function call to fc173848 = SharedCLibrary +3274 = _sys_istty +0 USR stack: 0000b18c : 00000015 : - R4 0000b190 : 00000001 : | R5 0000b194 : 00009460 : | R6 0000b198 : 00000000 : | R7 0000b19c : 00009a14 : | R8 0000b1a0 : 0000b1c0 : | R11 0000b1a4 : 0000b1b0 : | R12 0000b1a8 : fc182844 : | R14: fc182844 : : | = SharedCLibrary +12270 : : | = fwrite +30 0000b1ac : fc182550 : | APCS function: fc182548 : : | = SharedCLibrary +11f74 : : | = _write +4 0000b1b0 : 0000b7a0 : | R5 0000b1b4 : 0000b22c : | R11 0000b1b8 : 0000b1c4 : | R12 0000b1bc : 000085d0 : | R14: 000085d0 : : | = +5d0 in application memory : : | = recurse3 +c8 0000b1c0 : fc18282c : | APCS function: fc182824 : : | = SharedCLibrary +12250 : : | = fwrite +10 0000b1c4 : 718021cb : | 0000b1c8 : 5f947b49 : |
The first few lines provide annotations for certain key registers, such as R15 (for the first stack output) and the mode-specific R14. Following the registers is a header giving the name of the stack, and then the stack content itself.
The stack display is split into several columns. The first column gives the address, the second column gives the raw content at that address, and subsequent columns are used for annotations.
Annotations can span multiple addresses, and multiple lines in the output. For example the annotation for the first APCS stack frame in the output above spans addresses &b18c to &b1ac, and the anntations for addresses &b1a8 and &b1ac are split across multiple lines in order to avoid excessive column widths.
The vertical bar to the left of the annotation text is used to indicate that the annotations are linked; the output above shows two APCS stack frames, and by looking at the frame pointer values contained in the frames (R11) the Debugger has been able to determine that both of the frames are part of the same callstack and has linked them together with one vertical bar sequence. APCS is unique in the ability for the Debugger to link stack frames together; for ordinary assembler stack frames, even adjacent ones, each annotation/frame will start with a ‘-’ to make it clear that there is no guarantee that the frames are part of the same callstack. For example:
fa207f04 : fa207f78 : - R0 fa207f08 : 00000000 : | R1 fa207f0c : fc047189 : | R2 fa207f10 : 0000004a : | R3 fa207f14 : fa207f80 : | R4 fa207f18 : 00000000 : | R5 fa207f1c : fa207f88 : | R6 fa207f20 : 00000040 : | R7 fa207f24 : 00000000 : | R8 fa207f28 : 00000000 : | R9 fa207f2c : fc04b6f0 : | R14: fc04b6f0 (ASM call to fc04b6f4) : : | fc04b6f0 = FileSwitch +4b84 : : | = TopPath_DoBusinessToPath +8 : : | fc04b6f4 = FileSwitch +4b88 : : | = TopPath_DoBusinessToPathFSPrefix +0 fa207f30 : 00000040 : - R7 fa207f34 : fc05190c : | R14: fc05190c (ASM call to fc04b6e8) : : | fc05190c = FileSwitch +ada0 : : | = DoTheOpen +20 : : | fc04b6e8 = FileSwitch +4b7c : : | = TopPath_DoBusinessToPath +0 fa207f38 : 00000040 : - R0 fa207f3c : 00000000 : | R1 fa207f40 : 00000000 : | R2 fa207f44 : 00000000 : | R3 fa207f48 : 00000000 : | R4 fa207f4c : 00000000 : | R5 fa207f50 : 00000000 : | R6 fa207f54 : 00000000 : | R7 fa207f58 : 00000000 : | R9 fa207f5c : fc051870 : | R14: fc051870 (ASM call to fc051880) : : | fc051870 = FileSwitch +ad04 : : | = TopOpenFile +28 : : | fc051880 = FileSwitch +ad14 : : | = OpenFile +0
This output contains three assembler stack frames, but because assembler code generally doesn’t use the APCS stack frame structure there are no frame pointers for the Debugger to use to conclusively link the frames together. Therefore each annotation begins with a ‘-’.
Note that the output can contain multiple columns of annotations. This is necessary in order to deal with situations where stack frames have been partially overwritten. The Debugger makes no assumptions as to which frames are and aren’t important – it annotates everything it sees. This highlights a key difference between annotated stack dumps and stack backtraces – a stack backtrace will only list the details of stack frames which are believed to be relevant, while the annotation will list all stack frames which are found.
Annotated stack dumps have the downside that they are harder to read than a stack backtrace. But the advantages are that they are more likely to produce useful results when presented with stack/memory/register corruption, and they can theoretically operate on any stack structure – there is no need to make extensive modifications to system components in order to e.g. standardise on using the APCS stack frame layout everywhere.
When you are looking at annotations in order to reconstruct the callstack, it is important to realise that if multiple columns of annotations are in use, there is no guarantee that all the stack frames you are interested in will be in the same column. Apart from linked APCS stack frames, the Debugger assigns annotations to columns on a first-come, first-served basis.
The first thing you’ll want to familiarise yourself with is the way in which annotated addresses are presented.
R15 = fc173850 = SharedCLibrary +327c = _sys_istty +8 R14_usr = fc18256c = SharedCLibrary +11f98 = _write +28 Function call to fc173848 = SharedCLibrary +3274 = _sys_istty +0
Here we can see the annotated R15 and R14 values from the original example.
For each register, the first value shown is the register value (i.e. the address), the second value is the module name and offset (or some other general location information, e.g. “nowhere in RAM”), and the third value (if present) is the function name and offset. Offsets are shown in hex. When this type of information is displayed as part of an actual stack annotation, it will be split across multiple lines in order to keep column widths to a minimum.
The debugger has two methods of determining the function name:
No matter what method is used to obtain the function name, it’s important to realise that it may be inaccurate. For example when dealing with C code, static or inline functions may not have embedded names, potentially causing the name of the preceding function in memory to be used instead. If a function has been inlined, then the reported function name will be that of the containing function.
Looking at the above example again, you can see that the Debugger has determined that R14 is pointing to the return address of a function call; i.e. the code at R14-4 is a function call. The details of that function call have been displayed on the second line of output for the register.
Function call annotations are the most common type of annotation. There are four different types detected by the Debugger, as described below.
0000b18c : 00000015 : - R4 0000b190 : 00000001 : | R5 0000b194 : 00009460 : | R6 0000b198 : 00000000 : | R7 0000b19c : 00009a14 : | R8 0000b1a0 : 0000b1c0 : | R11 0000b1a4 : 0000b1b0 : | R12 0000b1a8 : fc182844 : | R14: fc182844 : : | = SharedCLibrary +12270 : : | = fwrite +30 0000b1ac : fc182550 : | APCS function: fc182548 : : | = SharedCLibrary +11f74 : : | = _write +4
These can be identified by the “APCS function” annotation in the output, as shown above. Note that because the output is in the form of an annotated stack dump (rather than a stack backtrace), the return address is listed before the address of the function itself.
The annotations for the registers are determined by looking at the code at the address that’s been identified as being the start of the function (i.e. the address shown for the “APCS function” annotation).
As described above, the Debugger will attempt to link together APCS stack frames, based on the frame pointer value (R11). If it arrives at the address of the next frame but fails to find a valid frame there, it will display a “? Broken APCS chain?” annotation.
fa207f04 : fa207f78 : - R0 fa207f08 : 00000000 : | R1 fa207f0c : fc047189 : | R2 fa207f10 : 0000004a : | R3 fa207f14 : fa207f80 : | R4 fa207f18 : 00000000 : | R5 fa207f1c : fa207f88 : | R6 fa207f20 : 00000040 : | R7 fa207f24 : 00000000 : | R8 fa207f28 : 00000000 : | R9 fa207f2c : fc04b6f0 : | R14: fc04b6f0 (ASM call to fc04b6f4) : : | fc04b6f0 = FileSwitch +4b84 : : | = TopPath_DoBusinessToPath +8 : : | fc04b6f4 = FileSwitch +4b88 : : | = TopPath_DoBusinessToPathFSPrefix +0
This type of annotation is produced when the Debugger detects an address X on the stack, where the code at X-4 is a function call to address Y, and the instruction at address Y is a stack push (STMFD R13,{...}
or STR R14,[R13,#-4]!
). The registers involved in the stack push are annotated.
Note that tail-call optimisation may result in the register annotations being inaccurate.
fa207fe4 : fc0107d8 : - fc018ecc return to fc0107d8? : : | fc018ecc = +18ecc in the Kernel : : | = CallVector +0 : : | fc0107d8 = +107d8 in the Kernel : : | = VecSwiDespatch +24
This type of annotation is produced when the Debugger detects an address X on the stack, where the code at X-4 is a function call to address Y, but there no stack push instruction has been identified at address Y.
The lack of a stack push instruction means that there are no additional registers included in the annotation.
fa207f70 : fc018ef0 : - Return to fc018ef0? : : | = +18ef0 in the Kernel : : | = CallVecLoop +8
This type of annotation is produced when the Debugger detects an address X on the stack, and the code at X-4 suggests that a function call has taken place, but the Debugger cannot determine what the target address was. This happens when any of the following instructions are detected:
MOV PC,...
BLX ...
LDR PC,...
LDM ...,{...PC}
MOV LR,PC
(at X-8)The lack of a stack push instruction means that there are no additional registers included in the annotation.
fa207e5c : 40000193 : - PSR? fa207e60 : 00074a43 : | SWI XUSBDriver_TransferComplete fa207e64 : fc184358 : | R14: fc184358 : : | = SharedCLibrary +13d80 : : | = _swix +58 fa207e68 : fa20021c : | R10 fa207e6c : fa207ec8 : | R11 fa207e70 : 00074a43 : | R12
SWI calls leave a distinctive signature on the SVC stack that allows them to be identified with a high degree of certainty. Usually the value marked “PSR?” will be the PSR at the time of the SWI call. However for kernel SWIs this may not the case.
fa101fdc : 00000000 : - IRQsema link fa101fe0 : 000000ff : | R1 fa101fe4 : 000000ff : | R2 fa101fe8 : 00000000 : | R3 fa101fec : 201fe664 : | R11 fa101ff0 : 20003bb4 : | R12 fa101ff4 : 80000113 : | PSR fa101ff8 : 0000269d : | R0 fa101ffc : fc15b5b8 : | R14: fc15b5b8 : : | = WindowManager +b800 : : | = nothing +3c
By using the captured IRQsema value, the Debugger is able to predict the location of IRQsema frames in the IRQ stack. These frames are inserted whenever an interrupt is taken. Unfortunately they do not currently contain the IRQ device number, so the content of preceding stack frames will have to be used to identify the device if necessary.
If there are nested interrupts then the “IRQsema link” annotation will indicate the expected address of the next frame.
fa207de0 : 20024254 : | R0 \ CMHG veneer _kernel_swi_regs? fa207de4 : 20023cf4 : | R1 | fa207de8 : fa207e18 : | R2 | fa207dec : fc1ea47c : | R3 | fa207df0 : 00000000 : | R4 | fa207df4 : 23e747e4 : | R5 | fa207df8 : fa207e30 : | R6 | fa207dfc : 00000000 : | R7 | fa207e00 : 2008c883 : | R8 | fa207e04 : 20000193 : | R9 /
When the end of an APCS stack chain is detected (R11==0) the Debugger will check the call site of the function to look for certain key markers that the call was from a CMHG veneer. If these markers are detected it will annotate the next few words of stack under the assumption that they are the R0-R9 registers that were captured by the veneer. However note that the location of this structure is just a guess, and the C code which has been called may have already overwritten some of them (the same structure is used for both input and output).