972
edits
[unchecked revision] | [unchecked revision] |
m (Bot: Replace deprecated source tag with syntaxhighlight) |
|||
(7 intermediate revisions by 6 users not shown) | |||
Line 6:
Often a stack trace is written in assembly as it involves finding the current value of the EBP register.
To write a stack trace routine in a higher-level language you will need to find EBP.
This can be done by
On some platform (e.g. x86), the compiler does not necessary save the EBP on the stack. For example, for gcc, use the -fno-omit-frame-pointer to make sure that the EBP is saved. Note that omission of the frame pointer merely causes functions to be missed from the backtrace.
The following C++ code shows how (given the existence of a Trace function) this can be used to walk up the stack:
<
/* Assume, as is often the case, that EBP is the first thing pushed. If not, we are in trouble. */
struct stackframe {
struct stackframe* ebp;
uint32_t eip;
};
void Debug::TraceStackTrace(unsigned int MaxFrames)
{
struct stackframe *stk;
asm ("movl %%ebp,%0" : "=r"(stk) ::);
Trace("Stack trace:\n");
for(unsigned int frame = 0; stk && frame < MaxFrames; ++frame)
{
break;▼
// Unwind to previous stack frame
}
}
</syntaxhighlight>
Note that the above code
<
mov $stack_end, %esp ; Initialize %esp
...
xor %ebp, %ebp ; Set %ebp to NULL
</syntaxhighlight>
With this, stack tracers will see the NULL %ebp
=== Assembly Implementation ===
This assembly implementation for x86 uses the same algorithm as above and similarly relies on a NULL
<
; Walks backwards through the call stack and builds a list of return addresses.
; Args:
Line 63 ⟶ 58:
push ebp
mov ebp, esp
mov [ebp - 4], edi
mov [ebp - 8], ebx
; Set up local registers.
xor eax, eax ; EAX = return value (number of stack frames found).
mov ebx, [esp +
mov edi, [esp +
mov ecx, [esp +
.walk:
; Walk backwards through EBP linked list, storing return addresses in EDI array.
jz .done
mov edx, [ebx + 4] ; EDX = previous stack frame's IP.
▲ test edx, edx
mov ebx, [ebx + 0] ; EBX = previous stack frame's BP.
mov [edi], edx ; Copy IP.
Line 84 ⟶ 80:
mov edi, [ebp - 4]
mov ebx, [ebp - 8]
leave
ret
</syntaxhighlight>
=== Resolving Function Names ===
Line 98 ⟶ 93:
One possible solution is to pre-process your map file to produce a smaller, more useful format for it. You could do this in a way that allows either binary or linear searching for a particular address. See NobleTech's Web site[http://www.nobletech.co.uk/Products/PenPot/Design/Kernel/Debug/FnNameLookup.aspx] for C# code showing a way of reading the map file produced by GNU ld and outputting a binary file that allows more efficient linear searching for symbols. A binary Win32 console application to do the pre-processing is also available for free from that site. C++ code that can be used in your kernel to look up function names in the pre-processed file format is also shown.
[[Category:Debugging]]
[[Category:X86]]
|