C函数分析(地址似乎offseted) [英] C function profiling (address seem to be offseted)

查看:154
本文介绍了C函数分析(地址似乎offseted)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图分析的函数调用使用-finstrument-功能选项。
基本上,我所做的就是写以下任何编译来源:

I'm trying to profile the function calls using -finstrument-functions option. Basically, what I have done is to write the following into any compiled source:

static int __stepper=0;
void __cyg_profile_func_enter(void *this_fn, void *call_site)
                              __attribute__((no_instrument_function));
void __cyg_profile_func_enter(void *this_fn, void *call_site) {
  int i=0;
  for( ; i<__stepper; i++ ) printf(" ");
  printf("E: %p %p\n", this_fn, call_site);
  __stepper ++;
} /* __cyg_profile_func_enter */

void __cyg_profile_func_exit(void *this_fn, void *call_site)
                             __attribute__((no_instrument_function));
void __cyg_profile_func_exit(void *this_fn, void *call_site) {
  int i=0;
  __stepper --;
  for( ; i<__stepper; i++ ) printf(" ");
  printf("L:  %p %p\n", this_fn, call_site);
} /* __cyg_profile_func_enter */

和得到以下结果:

 E: 0xb7597ea0 0xb75987a8
  E: 0xb7597de0 0xb7597ef5
  L:  0xb7597de0 0xb7597ef5
 L:  0xb7597ea0 0xb75987a8

所有的函数调用的地址是围绕区域(0xb7 .......)
但是,如果我尝试读取符号使用readelf -s功能它提供了以下内容:

All the function calls address is around that region (0xb7.......) But, if I try to read the symbols for function using 'readelf -s' it gives the following:

2157: 00101150   361 FUNC    LOCAL  DEFAULT   13 usb_audio_initfn
2158: 00100940   234 FUNC    LOCAL  DEFAULT   13 usb_audio_handle_reset
2159: 00100de0   867 FUNC    LOCAL  DEFAULT   13 usb_audio_handle_control

的二进制所有功能的地址区域是围绕0×00 ......
所以,我不能够从函数指针获取函数名。
貌似有些函数指针是如何得到的偏移或东西。

The address region of all the functions in binary is around 0x00...... So, I can not be able to get the function name from the function pointers. Looks like some how the function pointer gets an offset or something.

任何人有任何想法?

推荐答案

从问题它看起来像你分析的库函数。

From the question it looks like you're profiling a library function.

要知道是正在测量的功能,你有2个选择:

To know what are the functions being measured you have 2 options:

1 运行,它使用库在 GDB 程序,并在停止主 。在这一点上,获得 PID 程序的 PID = ... 并做`猫的/ proc / $ PID /地图。在那里,你应该看到这样的事情:

1 Run the program which uses the library under gdb and stop at main. At this point, get the pid of the program PID=... and do `cat /proc/$PID/maps'. There you should see something like this:

➜  ~  ps
  PID TTY          TIME CMD
18533 pts/4    00:00:00 zsh
18664 pts/4    00:00:00 ps
➜  ~  PID=18533
➜  ~  cat /proc/$PID/maps
00400000-004a2000 r-xp 00000000 08:01 3670052                            /bin/zsh5
006a1000-006a2000 r--p 000a1000 08:01 3670052                            /bin/zsh5
006a2000-006a8000 rw-p 000a2000 08:01 3670052                            /bin/zsh5
006a8000-006bc000 rw-p 00000000 00:00 0 
...
7fa174cc9000-7fa174ccd000 r-xp 00000000 08:01 528003                     /lib/x86_64-linux-gnu/libcap.so.2.22
7fa174ccd000-7fa174ecc000 ---p 00004000 08:01 528003                     /lib/x86_64-linux-gnu/libcap.so.2.22
7fa174ecc000-7fa174ecd000 r--p 00003000 08:01 528003                     /lib/x86_64-linux-gnu/libcap.so.2.22
7fa174ecd000-7fa174ece000 rw-p 00004000 08:01 528003                     /lib/x86_64-linux-gnu/libcap.so.2.22
...

下面 7fa174cc9000 为基址 /lib/x86_64-linux-gnu/libcap.so.2.22 图书馆。因此,所有你通过 readelf -S 获得地址将由值来区分。会心的基地址就可以计算出回一下原来的文件抵消了。

Here 7fa174cc9000 is base address of the /lib/x86_64-linux-gnu/libcap.so.2.22 library. So all the addresses you get by readelf -s will be offset by that value. Knowing base address you can calculate back what the original offset in file was.

即。如果你得到了价值 7fa174206370 和库的基地址为 7fa1741cf000 然后偏移 7fa174206370 - 7fa1741cf000 = 37370 。在我的例子是 sigsuspend 从GLIBC:

I.e. if you got the value 7fa174206370 and base address of the library is 7fa1741cf000 then offset is 7fa174206370 - 7fa1741cf000 = 37370. In my example it's sigsuspend from GLIBC:

94: 0000000000037370   132 FUNC    WEAK   DEFAULT   12 sigsuspend@@GLIBC_2.2.5

2 运行 GDB 上使用这些库的程序。这将可以立即发现在内存中加载库,或将需要指出的库的.text 部分。

2 Run gdb on the program which uses these libraries. It'll either immediately find the loaded library in memory, or will need to be pointed to the .text section of the library.

> gdb
(gdb) attach YOUR_PID
(a lot of output about symbols)
(gdb) x/i 0x00007fa174206386
=> 0x7fa174206386 <sigsuspend+22>:  cmp    $0xfffffffffffff000,%rax

你知道这个方式, 0x7fa174206386 里面 sigsuspend

在案件 GDB 本身不加载任何符号(如阅读符号没有输出...载入中符号... 附后),您可以查找库的基址,在选择的 1 ,然后给它添加的.text 部分

In case gdb doesn't load any symbols by itself (no output like Reading symbols from ... Loading symbols for ... after attach), you can look up the base address of library as in option 1, then add to it the offset of .text section

➜  ~  readelf -S /lib/x86_64-linux-gnu/libcap.so.2.22 | grep '.text.'
  [11] .text             PROGBITS         0000000000001620  00001620

7fa174cc9000 + 0000000000001620 以十六进制给出 7FA174CCA620 ,然后通过附加 GDB 如上做

7fa174cc9000 + 0000000000001620 in hexadecimal gives 7FA174CCA620, and then you attach by gdb as above and do

(gdb) add-symbol-file /lib/x86_64-linux-gnu/libcap.so.2.22 7FA174CCA620

那么你应该能够找到的符号(通过 X / I地址作为选项的 1 ),即使 GDB 本身不加载它们。

Then you should be able to find symbols (via x/i ADDRESS as in option 1) even if gdb doesn't load them by itself.

请问,如果有不清楚的地方,我会尽力解释。

Please ask if anything is unclear, I'll try to explain.

这是为什么澄清这让

观察到的行为是由于库被编译为位置无关code 。它使我们能够轻松支持动态库。 PIC本质上意味着库的ELF有 .PLT .GOT 部分,可以在任何基地址被加载。 PLT是过程链接表,它包含的位于其他模块的功能调用,先去计划间的preTER以允许其重新定位调用的函数,然后就跳转到函数的第一个电话后的陷阱。它的工作原理,因为程序间preTER更新GOT(全局偏移表),其中包含函数的地址打电话。最初,GOT被初始化,这样在第一次函数调用跳转执行的程序间preTER的它执行当前调用函数的解析功能。

The observed behavior is due to the libraries being compiled as Position-Independent Code. It allows us to easily support dynamic libraries. PIC essentially means that library's ELF has .plt and .got sections and can be loaded at any base address. PLT is procedure linkage table and it contains traps for calls of functions located in other modules, which first go to program interpreter to allow it to relocate the called function, and then just jump to the function after the first call. It works because program interpreter updates GOT (Global Offset Table), which contains addresses of functions to call. Initially the GOT is initialized so that on first function call the jump is performed to the function of program interpreter which performs resolution of currently called function.

在X86-64,PLT条目通常是这样的:

On x86-64, PLT entries typically looks like this:

0000000000001430 <free@plt>:
    1430:       ff 25 e2 2b 20 00       jmpq   *0x202be2(%rip)        # 204018 <_fini+0x201264>
    1436:       68 00 00 00 00          pushq  $0x0
    143b:       e9 e0 ff ff ff          jmpq   1420 <_init+0x28>

第一个 jmpq 是跳转到地址,在位置存储在GOT %RIP + 0x202be2

The first jmpq is jump to address, stored in GOT at location %rip + 0x202be2:

  [20] .got              PROGBITS         0000000000203fd0  00003fd0
       0000000000000030  0000000000000008  WA       0     0     8

%RIP + 0x202be2 0x204012 ,而被添加到库中的基地址产生有关位置的绝对地址所在的库实际加载。即如果它在 0x7f66dfc03000 加载,然后将得到相应的GOT项的地址将是 0x7F66DFE07012 。存储在该位置的地址的地址(在这个例子中)免费功能。它是由程序间preTER保持指向实际免费的libc

%rip + 0x202be2 will be 0x204012, and that gets added to the base address of the library to produce absolute address relevant to location where the library is actually loaded. I.e. if it's loaded at 0x7f66dfc03000, then the resulting address of corresponding GOT entry will be 0x7F66DFE07012. The address stored at that location is address of (in this example) free function. It's maintained by program interpreter to point to actual free in libc.

在此的更多信息可以在这里找到

More information on this can be found here.

这篇关于C函数分析(地址似乎offseted)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆