计算内存中函数的偏移量

时间:2016-06-16 16:33:32

标签: linux memory gdb objdump

我正在阅读uprobe tracer的文档,并且有一条指令如何计算内存中函数的偏移量。我在这里引用它。

  

以下示例显示了如何转储指令指针和%ax   在探测的文本地址注册。在/ bin / zsh中探测zfree函数:

# cd /sys/kernel/debug/tracing/
# cat /proc/`pgrep zsh`/maps | grep /bin/zsh | grep r-xp
00400000-0048a000 r-xp 00000000 08:03 130904 /bin/zsh
# objdump -T /bin/zsh | grep -w zfree
0000000000446420 g    DF .text  0000000000000012  Base        zfree
     

0x46420是加载的object / bin / zsh中zfree的偏移量   0x00400000。

我不知道为什么,但他们输出0x446420并减去0x400000得到0x46420。它对我来说是错误的。为什么选择0x400000?

我尝试在我的Fedora 23上使用4.5.6-200内核执行相同的操作。

首先我关闭了内存地址随机化

echo 0 > /proc/sys/kernel/randomize_va_space

然后我弄清楚二进制文件在内存中的位置

$ cat /proc/`pgrep zsh`/maps | grep /bin/zsh | grep r-xp
555555554000-55555560f000 r-xp 00000000 fd:00 2387155                    /usr/bin/zsh

取消偏移量

marko@fedora:~ $ objdump -T /bin/zsh | grep -w zfree
000000000005dc90 g    DF .text  0000000000000012  Base        zfree

并找出zfree通过gdb的位置

$ gdb -p 21067 --batch -ex 'p zfree'
$1 = {<text variable, no debug info>} 0x5555555b1c90 <zfree>

marko@fedora:~ $ python
Python 2.7.11 (default, Mar 31 2016, 20:46:51) 
[GCC 5.3.1 20151207 (Red Hat 5.3.1-2)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> hex(0x5555555b1c90-0x555555554000)
'0x5dc90'

你知道,我在objdump中得到的结果与没有减去任何东西的结果相同。

但是后来我在另一台使用SLES的机器上尝试了相同的操作,并且它与uprobe文档中的相同。

为什么会有这样的差异?我如何计算正确的偏移呢?

1 个答案:

答案 0 :(得分:3)

据我所知,差异可能仅由检查二进制文件的构建方式引起。更准确地说 - 如果ELF有固定的加载地址。让我们做简单的实验。我们有简单的测试代码:

int main(void) { return 0; }

然后,以两种方式构建它:

$ gcc -o t1 t.c      # create image with fixed load address
$ gcc -o t2 t.c -pie # create load-base independent image

现在,让我们检查这两个图像的加载基地址:

$ readelf -l --wide t1 | grep LOAD
  LOAD           0x000000 0x0000000000400000 0x0000000000400000 0x00067c 0x00067c R E 0x200000
  LOAD           0x000680 0x0000000000600680 0x0000000000600680 0x000228 0x000230 RW  0x200000
$ readelf -l --wide t2 | grep LOAD
  LOAD           0x000000 0x0000000000000000 0x0000000000000000 0x0008cc 0x0008cc R E 0x200000
  LOAD           0x0008d0 0x00000000002008d0 0x00000000002008d0 0x000250 0x000258 RW  0x2000

在这里,您可以看到第一张图片需要固定的加载地址 - 0x400000,第二张图片根本没有地址要求。

现在我们可以比较objdump讲述main的地址:

$ objdump -t t1 | grep ' main'
00000000004004b6 g     F .text  000000000000000b              main
$ objdump -t t2 | grep ' main'
0000000000000710 g     F .text  000000000000000b              main

如我们所见,地址是一个完整的虚拟地址,如果图像在地址加载,存储在程序头中,main的第一个字节将占用。当然,第二张图像永远不会在0x0加载,而是在另一个随机选择的位置加载,这将抵消实际功能位置。