在运行时说,我想找出定义函数"printf"的位置.我该怎么做?我的第一次尝试是打印出"printf"的地址,并将其与进程的虚拟地址映射进行比较:
我的节目:
#include <stdio.h>
#include <unistd.h>
void main()
{
printf("address of printf is 0x%X\n", printf);
printf("pid is %d\n", getpid());
while (1);
}
Run Code Online (Sandbox Code Playgroud)
输出:
-bash-4.1$ ./a &
[1] 28837
-bash-4.1$ address of printf is 0x4003F8
pid is 28837
Run Code Online (Sandbox Code Playgroud)
但是,这表示该功能是在我自己的程序中定义的!
-bash-4.1$ head /proc/28837/maps
00400000-00401000 r-xp 00000000 08:06 6946857 /data2/temp/del/a <<<<<<< Address 0x4003F8 is in my own program?
00600000-00601000 rw-p 00000000 08:06 6946857 /data2/temp/del/a
397ec00000-397ec20000 r-xp 00000000 08:11 55837039 /lib64/ld-2.12.so
397ee1f000-397ee20000 r--p 0001f000 08:11 55837039 /lib64/ld-2.12.so
397ee20000-397ee21000 rw-p 00020000 08:11 55837039 /lib64/ld-2.12.so
397ee21000-397ee22000 rw-p 00000000 00:00 0
397f000000-397f18a000 r-xp 00000000 08:11 55837204 /lib64/libc-2.12.so
397f18a000-397f38a000 ---p 0018a000 08:11 55837204 /lib64/libc-2.12.so
397f38a000-397f38e000 r--p 0018a000 08:11 55837204 /lib64/libc-2.12.so
397f38e000-397f38f000 rw-p 0018e000 08:11 55837204 /lib64/libc-2.12.so
Run Code Online (Sandbox Code Playgroud)
不应该是对libc的调用吗?我如何找出这个"printf"或任何其他功能来自哪里?
Ctx*_*Ctx 31
您观察到的地址位于过程链接表(PLT)中.当编译和链接二进制文件时,当时不知道外部(动态链接)符号的位置时,将使用此机制.
目的是,外部链接仅发生在一个地方,即PLT,而不是在代码中发生符号调用的所有地方.所以,如果printf()
被调用,方式是:
main - > printf @ PLT - > printf @ libc
在运行时,您无法轻易找到您调用的函数所在的外部库; 你必须解析目的地(PLT)的操作码,它通常从.dynamic部分获取地址并跳转到那里,然后查看符号的位置,最后解析/ proc/pid/maps得到外部图书馆.
xhi*_*nne 23
在运行时,您可以使用gdb
:
(terminal 1)$ ./a
pid is 16614
address of printf is 0x400450
(terminal 2)$ gdb -p 16614
(...)
Attaching to process 16614
(...)
0x00000000004005a4 in main ()
(gdb)
(gdb) info sym printf
printf in section .text of /lib/x86_64-linux-gnu/libc.so.6
Run Code Online (Sandbox Code Playgroud)
如果您不想中断程序或不愿意使用gdb
,您可能还会要求ld.so
输出一些调试信息:
(terminal 1)$ LD_DEBUG=bindings LD_DEBUG_OUTPUT=syms ./a
pid is 17180
address of printf is 0x400450
(terminal 2)$ fgrep printf syms.17180
17180: binding file ./a [0] to /lib/x86_64-linux-gnu/libc.so.6 [0]: normal symbol `printf' [GLIBC_2.2.5]
Run Code Online (Sandbox Code Playgroud)
指针是printf
使用%p
,而不是%X
:
printf("address of printf is 0x%p\n", printf);
Run Code Online (Sandbox Code Playgroud)如果你编译反对静态libc printf
将链接到你的二进制文件
编译时
gcc -fPIC a.c # (older gccs)
...
gcc -fno-plt a.c # (gcc 6 and above)
Run Code Online (Sandbox Code Playgroud)
输出:
address of printf is 0x0x7f40acb522a0
Run Code Online (Sandbox Code Playgroud)
在里面
7f40acaff000-7f40accc2000 r-xp 00000000 fd:00 100687388 /usr/lib64/libc-2.17.so
Run Code Online (Sandbox Code Playgroud)阅读@plt在这里的含义是什么?找到更多相关信息.
在运行时说,我想找出定义函数"printf"的位置.
从一般和绝对的角度来看,你可能不会(至少不容易).给定的函数可能在几个库中定义(printf
因为它不太可能;因为它在C标准库中).
如果您从头开始构建Linux系统,您可能会想到在构建时处理每个库的某些事情(例如,在构建每个共享库时,您可以使用nm(1)获取其所有公共名称并将它们放在某个数据库中).今天还没有真正完成,但是一些研究项目正朝着这个方向发展(特别是软件遗留,以及2019年的其他研究项目).
顺便说一下,你可以定义几个库printf
.例如,如果您在计算机上安装了GNU glibc和musl-libc(或者更有可能,如果您有多个变体glibc
).一个特定的程序不太可能同时使用它们(但理论上dlopen
它们仍然可以使用它们).
也许你想要Linux特有的dladdr(3)函数.从某个给定的地址,它会告诉您拥有它的共享对象.
该函数在我自己的程序中定义
是.阅读有关动态链接的更多信息.特别是,请阅读Drepper的" 如何编写共享库"一文.了解过程链接表的目的是什么.