Write down some related knowledge about function call stacks. For C/C ++ development in Linux, it is a very common method to locate the call stack information during problem locating, according to the call relationship, you can know what the program execution process looks like. If you cannot view the call stack, it is difficult to locate the program's failure in a function. If this function is called in many places, it is hard to know what causes the error. Therefore, by viewing the call stack, you can know the call relationship and, of course, what scenarios lead to problems.
The commonly used imperative in GDB: BT or the full name "backtrace" can print the call stack of the current function execution. As shown in the following program
(GDB) BT
#0 0x080486da in func_3 ()
#1 0x08048766 in func_int ()
#2 0x0801_ AE in func_str ()
#3 0x0800000ff in main ()
The preceding Digital hierarchy. #0 indicates the top, that is, the current function. Except for the address above layer 0th, it indicates the current Pc value, and other address information indicates the return address of the function call. For example, after func_int () --> func_3 () and func_3 are executed, the command for 0x08011666 is executed.
The above briefly introduces how to locate the problem by calling the stack in Linux, but some people may not know how to obtain the call stack and how to obtain it. The reason for this introduction is that for some large systems, perfect log functions are essential. Otherwise, it is very painful for the system to have problems and no relevant logs. In some environments, for example, in the telecom field, most servers or applications run on a single board. If something goes wrong, we will not directly use GDB for debugging as we debug small programs. Although the problematic process on GDB attach can be used in some cases, most server boards do not have related debugging tools. Therefore, to locate the problem, it is basically through log analysis. Another case is the randomness. If there is no log, it will be even more painful. Even if you can use GDB, you will be powerless. So log is important, but what information does log usually need to record? Generally, when a function call fails, the parameter information of the input function or some key global variable information will be protected. In some cases, the date will be recorded. Generally, the date will be recorded for the server program. The call stack information is also important.
The following describes how to obtain the call Stack:
In the Linux + x86 environment, when calling C language functions, the following describes how C functions press stacks: stacks are moved from high addresses down to low addresses. A function usually contains parameters, local variables, and other related information. The stack is allocated using the following principles:
1. Stack information layout: first, local variables are stored, the returned values of the called functions are stored, and then other function parameter functions are called,
For example, the following program: int B (int c, int d) {return C + D;} int A (int A, int B) {int c = 0xff, D = 0 xFFFF; return B (c, d);} You can run the objdump-D command to view the disassembly command. The result is as follows: 00000079 <B>: 79: 55 push % EBP 7A: 89 E5 mov % ESP, % EBP 7c: 8B 45 0C mov 0xc (% EBP), % eax 7f: 03 45 08 add 0x8 (% EBP), % eax 82: 5D pop % EBP 83: C3 ret00000084 <a >:84: 55 push % EBP 85: 89 E5 mov % ESP, % EBP 87: 83 EC 18 sub $0x18, % ESP 8A: C7 45 FC ff 00 00 00 movl $0 XFF,-0x4 (% EBP) 91: C7 45 F8 FF 00 00 movl $0 xFFFF,-0x8 (% EBP) 98: 8b 45 F8 mov-0x8 (% EBP), % eax 9B: 89 44 24 04 mov % eax, 0x4 (% ESP) 9f: 8b 45 FC mov-0x4 (% EBP), % eax A2: 89 04 24 mov % eax, (% ESP) A5: e8 fc ff call A6 <A + 0x22> AA: C9 leave AB: C3 ret we can see from the disassembly above that when a calls B, a's call STACK layout information is as follows, high address: | --------- | EBP | <-- | push % EBP ------------- A ----------------- | --------- | c | movl $ 0xff, -0x4 (% EBP ); A function local variable c | --------- | d | movl $0 xFFFF,-0x8 (% EBP ); A function local variable d | --------- | C + % EBP | d | mov % eax, 0x4 (% ESP); When a calls function B, prepare the parameter d | --------- | 8 + % EBP | c | mov % eax, (% ESP ); when a calls function B, prepare the parameter C | --------- | <---- % ESP ------------- A -------------- 4 + % EBP | retaddr | return address of a's call to function B, when the call command is executed, the command automatically pushes the next part of the call command to this place. | --------- | % EBP-> | EBP | --- stores the EBP during function a running when function B: Push % EBP is executed. | --------- | Low address:
When B executes mov 0xc (% EBP) and % eax, it simply describes the function call process in a language. For example, if a calls B, function a first prepares parameters, put the local variables C and D on the stack, and then execute the call B (call A6 <A + 0x22>) command, when the call command is executed, the next instruction of the current instruction is pushed to the stack by default, and then the first instruction of function B is executed (push % EBP). Therefore, when the execution of function B is pushed % EBP, stack information is the same as above. Know how the general program presses the stack, and function a calls function B to press the next instruction of the Call Command that calls function B in function a to the stack, generally, the first command of a function is push.
% EBP is used to save the call function stack frame. When there are 2nd commands, mov % ESP and % EBP assign ESP to EBP, that is, initialize the current function stack frame. During the execution process, the function call first points to the call for execution, and then executes the first instruction (push % EBP) of the caller. This is usually the case for C function calls, another hidden action of the Call Command is to push the next command (return address) to the stack. So the layout in the stack is
--------- | Ret_addr | --------- | EBP | --------- | let's take a look at the second instruction, mov % ESP, % EBP, to initialize the current function stack frame. The final result is as follows: --------- | ret_addr | --------- | EBP | ---/| --------- | <-- |... | --------- | ret_addr | --------- | EBP | ---/| --------- | <-- |... | --------- | ret_addr | --------- | EBP | ---/| --------- | --- |
So as long as we know the current value of % EPB, we can use the above graphical method to analyze the call stack. Some people may ask why libc has function implementation and it is unnecessary. However, libc only provides information about the call stack of the current thread, and sometimes needs to obtain information about the call stack of other threads, at this time, you need to analyze and implement it by yourself. The general idea is the same. You only need to obtain the % EBP information of other threads. However, in the user State, the % EBP register cannot be obtained, the memory module can be used for implementation.
The following is a small program. One method is implemented using the backtrace function in the libc library, and the other is implemented by analyzing the call stack information.
# Include <stdio. h> # include <string. h> # include <execinfo. h>/* Get the EBP register value */void get_ebp (unsigned long * EBP) {_ ASM _ volatile _ ("mov % EBP, % 0 \ r \ n ":" = m "(* EBP):" Memory ");} int my_backtrace (void ** stack, int size, unsigned long EBP) {int layer = 0; while (layer <size & EBP! = 0 & * (unsigned long *) EBP! = 0 & * (unsigned long *) EBP! = EBP) {stack [layer ++] = * (unsigned long *) (EBP + 4); EBP = * (unsigned long *) EBP;} return layer ;} int func_3 (int A, int B, int c) {void * stack_addr [10]; int layer; int I; char ** ppstack_funcs; /* call the libc function to implement */layer = backtrace (stack_addr, 10); ppstack_funcs = backtrace_symbols (stack_addr, layer); for (I = 0; I <layer; I ++) printf ("\ n % s: % P \ n", ppstack_funcs [I], stack_addr [I]); /* self-implemented */unsigned long EBP = 0; get_ebp (& EBP); memset (stack_addr, 0, sizeof (stack_addr); layer = my_backtrace (stack_addr, 10, EBP); for (I = 0; I <layer; I ++) printf ("\ nmy: % P \ n", stack_addr [I]); free (ppstack_funcs ); return 3;} int func_int (int A, int B, int C, int d) {int AA, BB, CC; int ret = func_3 (AA, BB, CC ); return (A + B + C + D + RET);} int func_str () {int A = 1, B = 2; int ret; ret = func_int (, b, B); return ret;} int B (int c, int d) {return C + D;} int A (int A, int B) {int c = 0xff, D = 0 xFFFF; return B (c, d);} int main (INT argc, char * argv []) {int ret = func_str (); Return 0 ;}
Program compilation plus-rdynaminc
Otherwise, only the address is available for obtaining the call stack, and no function name information is available.
Running result:
./exe() [0x80484dd]:0x80484dd./exe() [0x80485ea]:0x80485ea./exe() [0x8048632]:0x8048632./exe() [0x8048683]:0x8048683/lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe6) [0xb7dd5bd6]:0xb7dd5bd6./exe() [0x8048401]:0x8048401my: 0x804858amy: 0x80485eamy: 0x8048632my: 0x8048683my: 0xb7dd5bd6