as: ARM Unwinding Tutorial

1 
1 9.4.7 Unwinding
1 ---------------
1 
1 The ABI for the ARM Architecture specifies a standard format for
1 exception unwind information.  This information is used when an
1 exception is thrown to determine where control should be transferred.
1 In particular, the unwind information is used to determine which
1 function called the function that threw the exception, and which
1 function called that one, and so forth.  This information is also used
1 to restore the values of callee-saved registers in the function catching
1 the exception.
1 
1    If you are writing functions in assembly code, and those functions
1 call other functions that throw exceptions, you must use assembly pseudo
1 ops to ensure that appropriate exception unwind information is
1 generated.  Otherwise, if one of the functions called by your assembly
1 code throws an exception, the run-time library will be unable to unwind
1 the stack through your assembly code and your program will not behave
1 correctly.
1 
1    To illustrate the use of these pseudo ops, we will examine the code
1 that G++ generates for the following C++ input:
1 
1 void callee (int *);
1 
1 int
1 caller ()
1 {
1   int i;
1   callee (&i);
1   return i;
1 }
1 
1    This example does not show how to throw or catch an exception from
1 assembly code.  That is a much more complex operation and should always
1 be done in a high-level language, such as C++, that directly supports
1 exceptions.
1 
1    The code generated by one particular version of G++ when compiling
1 the example above is:
1 
1 _Z6callerv:
1 	.fnstart
1 .LFB2:
1 	@ Function supports interworking.
1 	@ args = 0, pretend = 0, frame = 8
1 	@ frame_needed = 1, uses_anonymous_args = 0
1 	stmfd	sp!, {fp, lr}
1 	.save {fp, lr}
1 .LCFI0:
1 	.setfp fp, sp, #4
1 	add	fp, sp, #4
1 .LCFI1:
1 	.pad #8
1 	sub	sp, sp, #8
1 .LCFI2:
1 	sub	r3, fp, #8
1 	mov	r0, r3
1 	bl	_Z6calleePi
1 	ldr	r3, [fp, #-8]
1 	mov	r0, r3
1 	sub	sp, fp, #4
1 	ldmfd	sp!, {fp, lr}
1 	bx	lr
1 .LFE2:
1 	.fnend
1 
1    Of course, the sequence of instructions varies based on the options
1 you pass to GCC and on the version of GCC in use.  The exact
1 instructions are not important since we are focusing on the pseudo ops
1 that are used to generate unwind information.
1 
1    An important assumption made by the unwinder is that the stack frame
1 does not change during the body of the function.  In particular, since
1 we assume that the assembly code does not itself throw an exception, the
1 only point where an exception can be thrown is from a call, such as the
1 'bl' instruction above.  At each call site, the same saved registers
1 (including 'lr', which indicates the return address) must be located in
1 the same locations relative to the frame pointer.
1 
1    The '.fnstart' (⇒.fnstart pseudo op arm_fnstart.) pseudo op
1 appears immediately before the first instruction of the function while
1 the '.fnend' (⇒.fnend pseudo op arm_fnend.) pseudo op appears
1 immediately after the last instruction of the function.  These pseudo
1 ops specify the range of the function.
1 
1    Only the order of the other pseudos ops (e.g., '.setfp' or '.pad')
1 matters; their exact locations are irrelevant.  In the example above,
1 the compiler emits the pseudo ops with particular instructions.  That
1 makes it easier to understand the code, but it is not required for
1 correctness.  It would work just as well to emit all of the pseudo ops
1 other than '.fnend' in the same order, but immediately after '.fnstart'.
1 
1    The '.save' (⇒.save pseudo op arm_save.) pseudo op indicates
1 registers that have been saved to the stack so that they can be restored
1 before the function returns.  The argument to the '.save' pseudo op is a
1 list of registers to save.  If a register is "callee-saved" (as
1 specified by the ABI) and is modified by the function you are writing,
1 then your code must save the value before it is modified and restore the
1 original value before the function returns.  If an exception is thrown,
1 the run-time library restores the values of these registers from their
1 locations on the stack before returning control to the exception
1 handler.  (Of course, if an exception is not thrown, the function that
1 contains the '.save' pseudo op restores these registers in the function
1 epilogue, as is done with the 'ldmfd' instruction above.)
1 
1    You do not have to save callee-saved registers at the very beginning
1 of the function and you do not need to use the '.save' pseudo op
1 immediately following the point at which the registers are saved.
1 However, if you modify a callee-saved register, you must save it on the
1 stack before modifying it and before calling any functions which might
1 throw an exception.  And, you must use the '.save' pseudo op to indicate
1 that you have done so.
1 
1    The '.pad' (⇒.pad arm_pad.) pseudo op indicates a modification
1 of the stack pointer that does not save any registers.  The argument is
1 the number of bytes (in decimal) that are subtracted from the stack
1 pointer.  (On ARM CPUs, the stack grows downwards, so subtracting from
1 the stack pointer increases the size of the stack.)
1 
1    The '.setfp' (⇒.setfp pseudo op arm_setfp.) pseudo op indicates
1 the register that contains the frame pointer.  The first argument is the
1 register that is set, which is typically 'fp'.  The second argument
1 indicates the register from which the frame pointer takes its value.
1 The third argument, if present, is the value (in decimal) added to the
1 register specified by the second argument to compute the value of the
1 frame pointer.  You should not modify the frame pointer in the body of
1 the function.
1 
1    If you do not use a frame pointer, then you should not use the
1 '.setfp' pseudo op.  If you do not use a frame pointer, then you should
1 avoid modifying the stack pointer outside of the function prologue.
1 Otherwise, the run-time library will be unable to find saved registers
1 when it is unwinding the stack.
1 
1    The pseudo ops described above are sufficient for writing assembly
1 code that calls functions which may throw exceptions.  If you need to
1 know more about the object-file format used to represent unwind
1 information, you may consult the 'Exception Handling ABI for the ARM
1 Architecture' available from <http://infocenter.arm.com>.
1