在ARM上获取错误的glibc函数地址

在ARM上获取错误的glibc函数地址,c,function,gdb,arm,glibc,C,Function,Gdb,Arm,Glibc,我想得到一个函数的地址。使用函数名可以在x86上获得正确的地址,包括本地函数和glibc函数 但在ARM上,本地函数地址是正确的,而glibc函数地址是错误的 以下是我的简单程序: #include <stdio.h> int sum(int a, int b) { return a + b; } int main(int argc, char *argv[]) { char buffer[32] = { '\0' }; sprintf(buffer, "ca

我想得到一个函数的地址。使用函数名可以在x86上获得正确的地址,包括本地函数和glibc函数

但在ARM上,本地函数地址是正确的,而glibc函数地址是错误的

以下是我的简单程序:

#include <stdio.h>
int sum(int a, int b)
{
    return a + b;
}
int main(int argc, char *argv[])
{
    char buffer[32] = { '\0' };
    sprintf(buffer, "cat /proc/%d/maps", getpid());
    printf("sum = %p\n", sum);
    printf("fopen = %p\n", fopen);
    system(buffer);
    return 0;
}

# x-compile it to an ARM executable:
$ arm-linux-gnueabihf-4.9.1-gcc -g -o misc misc.c

# debug on ARM
/home # ./gdb ./misc
GNU gdb (GDB) 7.5.1
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "arm-linux".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/misc...done.
(gdb) b 16
Breakpoint 1 at 0x8534: file misc.c, line 16.
(gdb) r
Starting program: /home/misc 
sum = 0x8491
fopen = 0x835c
00008000-00009000 r-xp 00000000 00:13 1703976    /home/misc
00010000-00011000 rw-p 00000000 00:13 1703976    /home/misc
76ed9000-76fd0000 r-xp 00000000 1f:08 217        /lib/libc-2.19-2014.06.so
76fd0000-76fd7000 ---p 000f7000 1f:08 217        /lib/libc-2.19-2014.06.so
76fd7000-76fd9000 r--p 000f6000 1f:08 217        /lib/libc-2.19-2014.06.so
76fd9000-76fda000 rw-p 000f8000 1f:08 217        /lib/libc-2.19-2014.06.so
76fda000-76fdd000 rw-p 00000000 00:00 0 
76fdd000-76ff7000 r-xp 00000000 1f:08 199        /lib/ld-2.19-2014.06.so
76ffb000-76ffe000 rw-p 00000000 00:00 0 
76ffe000-76fff000 r--p 00019000 1f:08 199        /lib/ld-2.19-2014.06.so
76fff000-77000000 rw-p 0001a000 1f:08 199        /lib/ld-2.19-2014.06.so
7efdf000-7f000000 rw-p 00000000 00:00 0          [stack]
ffff0000-ffff1000 r-xp 00000000 00:00 0          [vectors]

Breakpoint 1, main (argc=1, argv=0x7efffe64) at misc.c:16
16      misc.c: No such file or directory.
(gdb) p fopen
$1 = {<text variable, no debug info>} 0x76f26a50 <fopen>
(gdb) 
#包括
整数和(整数a,整数b)
{
返回a+b;
}
int main(int argc,char*argv[])
{
字符缓冲区[32]={'\0'};
sprintf(缓冲区,“cat/proc/%d/maps”,getpid());
printf(“总和=%p\n”,总和);
printf(“fopen=%p\n”,fopen);
系统(缓冲区);
返回0;
}
#x-将其编译为ARM可执行文件:
$arm-linux-gnueabihf-4.9.1-gcc-g-o misc misc.c
#在ARM上调试
/主页#/gdb/杂项
GNU gdb(gdb)7.5.1
版权所有(C)2012免费软件基金会。
许可证GPLv3+:GNU GPL版本3或更高版本
这是自由软件:您可以自由更改和重新发布它。
在法律允许的范围内,不存在任何担保。键入“显示复制”
和“显示保修”了解详细信息。
此GDB配置为“arm linux”。
有关错误报告说明,请参阅:
...
从/home/misc…读取符号完成。
(gdb)b 16
0x8534处的断点1:文件misc.c,第16行。
(gdb)r
启动程序:/home/misc
总和=0x8491
fopen=0x835c
00008000-00009000 r-xp 00000000:13 1703976/主页/杂项
00010000-00011000 rw-p 00000000:13 1703976/主/杂项
76ed9000-76fd0000 r-xp 00000000 1f:08 217/lib/libc-2.19-2014.06.so
76fd0000-76fd7000---p 000f7000 1f:08 217/lib/libc-2.19-2014.06.so
76fd7000-76fd9000 r--p 000f6000 1f:08 217/lib/libc-2.19-2014.06.so
76fd9000-76fda000 rw-p 000f8000 1f:08 217/lib/libc-2.19-2014.06.so
76fda000-76fdd000 rw-p 00000000:00
76fdd000-76ff7000 r-xp 00000000 1f:08 199/lib/ld-2.19-2014.06.so
76ffb000-76FF000 rw-p 00000000:00
76FF000-76fff000 r--p 00019001F:08 199/lib/ld-2.19-2014.06.so
76fff000-77000000 rw-p 0001a000 1f:08 199/lib/ld-2.19-2014.06.so
7efdf000-7F000000RW-p 00000000:00[堆栈]
ffff0000-ffff1000 r-xp 00000000:00 0[向量]
misc.c处的主断点1(argc=1,argv=0x7efffe64):16
16 misc.c:没有这样的文件或目录。
(gdb)p fopen
$1={}0x76f26a50
(gdb)
注意glibc文本段映射到地址76ed9000,那么
fopen
怎么可能位于像0x835c这样的有线地址


但是,下一行,
(gdb)p fopen
,gdb给出了正确的地址

不能保证指针的值实际上会给出所查找对象的内存地址。对于函数指针,实际上更可能有完全不同的值

下面我只是完全夸大了它,但删除这个解释将是一个耻辱。所以这里有一个简短的版本:函数指针只保证与另一个函数指针的比较相等,当涉及共享库时,这会很快变得复杂

这里发生的事情与动态链接有关。当链接程序时,链接器不知道libc在内存中的位置,这只能由动态链接器在运行时解决。一种简单的方法是在程序的代码中重写函数的地址,但这是低效的,因为这意味着程序的每次执行都无法与其他运行共享可执行文件的内存。相反,存在一种称为PLT的东西。当您对动态链接的函数进行函数调用时,实际运行的代码是跳转到程序中的本地函数,然后从表中加载函数的实际地址并跳转到该地址(这在不同的体系结构上是非常不同的,但这是一般的想法)

我在amd64上构建了您的程序,让我们看看它的实际应用:

(gdb) break system
Breakpoint 1 at 0x400510
(gdb) run
Starting program: /home/art/./foo
sum = 0x400660
fopen = 0x400550
[...]
因此,正如您在amd64上看到的,fopen的值也是可疑的。让我们看看该地址中隐藏着什么代码:

(gdb) x/i 0x400550
   0x400550 <fopen@plt>:    jmpq   *0x200aea(%rip)        # 0x601040 <fopen@got.plt>
这里发生了一些奇怪的事情。
fopen@got.plt
仅返回到
fopen@plt
,然后将某些内容推到堆栈上,然后跳转到其他一些代码,这些代码将更多的内容推到堆栈上,并从一个表中跳转以获取另一个奇怪的地址,该表在
\u dl\u runtime\u resolve
结束。正在发生的是延迟绑定。动态链接器的开发人员发现,动态库和程序包含的大多数链接信息永远不会被使用。当您运行从libc调用两个函数的程序时,您不想解析libc在内部执行的成千上万个动态函数调用,这是浪费时间。此外,对于大多数程序,我们重视快速启动而不是快速运行。因此,默认情况下,您的所有函数实际上都没有解析。第一次调用它们时,它们会在运行时得到解析。这就是
\u dl\u runtime\u resolve
所做的。向堆栈的推送很可能是向该函数传递参数的非标准方式,因为该代码不允许使用任何寄存器(调用代码认为它只是正常调用了
fopen


但是等一下。C标准规定,如果两个函数指针指向同一个函数,则它们之间的比较应相等。如果其中一个指针可能来自您的程序,而另一个来自动态库,那么这是如何工作的?这在很大程度上依赖于体系结构,但经过深入研究后,我发现在我的体系结构上,即使库返回函数指针,该函数指针也会被转换为主程序中的PLT函数。为什么?不知道。有人在某个时候决定以这种方式实现它。

我怀疑这与动态链接的实现方式有关。地址空间布局随机化(ASLR)?
(gdb) x/g 0x601040
0x601040 <fopen@got.plt>:   0x0000000000400556
(gdb) x/i 0x0000000000400556
   0x400556 <fopen@plt+6>:  pushq  $0x5
(gdb)
   0x40055b <fopen@plt+11>: jmpq   0x4004f0
(gdb) x/i 0x4004f0
   0x4004f0:    pushq  0x200b12(%rip)        # 0x601008
(gdb)
   0x4004f6:    jmpq   *0x200b14(%rip)        # 0x601010
(gdb) x/g 0x601010
0x601010:   0x00007ffff7df0290
(gdb) x/i 0x00007ffff7df0290
   0x7ffff7df0290 <_dl_runtime_resolve>:    sub    $0x78,%rsp
(gdb)