linux的用户态程序栈的跟踪方法

linux的用户态程序栈的跟踪方法

  • GNU C中的libc相关函数介绍
  • 实例分析

GNU C中的libc相关函数介绍

这一部分文章来源于GNU C的在线帮助文档

实例分析

将下面的代码保存为main.c文件,然后用gcc编译

#include 
#include 
#include 

/* Obtain a backtrace and print it to stdout. */
void
print_trace (void)
{
  void *array[10];
  size_t size;
  char **strings;
  size_t i;

  size = backtrace (array, 10);
  strings = backtrace_symbols (array, size);

  printf ("Obtained %zd stack frames.\n", size);

  for (i = 0; i < size; i++)
     printf ("%s\n", strings[i]);

  free (strings);
}

/* A dummy function to make the backtrace more interesting. */
void
dummy_function (void)
{
  print_trace ();
}

int
main (void)
{
  dummy_function ();
  return 0;
}

然后编译并运行:

[ycwang@ycwang-desktop:backtrace-test]$ gcc -g main.c

[ycwang@ycwang-desktop:backtrace-test]$ ./a.out
Obtained 5 stack frames.
./a.out() [0x4006fe]
./a.out() [0x400795]
./a.out() [0x4007a1]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0) [0x7f06a6ef2ac0]
./a.out() [0x400609]

可以看到,程序运行之后,把stack frames完全打印出来了。
我们可以定位各个栈了。
然后用objdump来把反汇编文件导出到一个文件中

[ycwang@ycwang-desktop:backtrace-test]$ objdump -D a.out > dump

然后打开dump文件,查找函数在内存中的虚拟地址。

450 00000000004006d6 :
 451   4006d6:   55                      push   %rbp
 452   4006d7:   48 89 e5                mov    %rsp,%rbp
 453   4006da:   48 83 c4 80             add    $0xffffffffffffff80,%rsp
 454   4006de:   64 48 8b 04 25 28 00    mov    %fs:0x28,%rax
 455   4006e5:   00 00 
 456   4006e7:   48 89 45 f8             mov    %rax,-0x8(%rbp)
 457   4006eb:   31 c0                   xor    %eax,%eax
 458   4006ed:   48 8d 45 a0             lea    -0x60(%rbp),%rax
 459   4006f1:   be 0a 00 00 00          mov    $0xa,%esi
 460   4006f6:   48 89 c7                mov    %rax,%rdi
 461   4006f9:   e8 92 fe ff ff          callq  400590 @plt>
 462   4006fe:   48 98                   cltq   
 463   400700:   48 89 45 90             mov    %rax,-0x70(%rbp)
 464   400704:   48 8b 45 90             mov    -0x70(%rbp),%rax

可以看出:0x4006fe在print_trace函数里面,这也是函数栈的栈顶。
接下来查找另外几个地址。得到:

地址0x400795:

 503 000000000040078c :
 504   40078c:   55                      push   %rbp
 505   40078d:   48 89 e5                mov    %rsp,%rbp
 506   400790:   e8 41 ff ff ff          callq  4006d6 
 507   400795:   90                      nop
 508   400796:   5d                      pop    %rbp
 509   400797:   c3                      retq

地址0x4007a1:

 511 0000000000400798 
: 512 400798: 55 push %rbp 513 400799: 48 89 e5 mov %rsp,%rbp 514 40079c: e8 eb ff ff ff callq 40078c 515 4007a1: b8 00 00 00 00 mov $0x0,%eax 516 4007a6: 5d pop %rbp 517 4007a7: c3 retq 518 4007a8: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1) 519 4007af: 00

接下来的两个栈对于我们的程序分析没有意义,暂时不讨论。

然后我们可以得到栈的层次为

print_trace()
dummy_function()
main()

跟我们写出的函数的栈完全相同。

这种调试方法适用于不能打断点的一些程序的调试。

你可能感兴趣的:(C语言知识,c语言,linux,栈)