是的,通过给定符号名称和可执行文件链接的库集合,您可以唯一地识别函数。这种行为对于链接和动态链接是必需的。
一个说明性示例
考虑以下两个文件:
librarytest1.c:
#include <stdio.h>
int testfunction(void)
{
printf("version 1");
return 0;
}
和 librarytest2.c:
#include <stdio.h>
int testfunction(void)
{
printf("version 2");
return 0;
}
两者都编译成共享库:
% gcc -fPIC -shared -Wl,-soname,liblibrarytest.so.1 -o liblibrarytest.so.1.0.0 librarytest1.c -lc
% gcc -fPIC -shared -Wl,-soname,liblibrarytest.so.2 -o liblibrarytest.so.2.0.0 librarytest2.c -lc
请注意,我们不能将具有相同名称的这两个函数放入同一共享库中:
% gcc -fPIC -shared -Wl,-soname,liblibrarytest.so.0 -o liblibrarytest.so.0.0.0 librarytest1.c librarytest2.c -lc
/tmp/cctbsBxm.o: In function `testfunction':
librarytest2.c:(.text+0x0): multiple definition of `testfunction'
/tmp/ccQoaDxD.o:librarytest1.c:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status
这表明在共享库内符号名称是唯一的,但不必在一组共享库中唯一。
% readelf --dyn-syms liblibrarytest.so.1.0.0 | grep testfunction
12: 00000000000006d0 28 FUNC GLOBAL DEFAULT 10 testfunction
% readelf --dyn-syms liblibrarytest.so.2.0.0 | grep testfunction
12: 00000000000006d0 28 FUNC GLOBAL DEFAULT 10 testfunction
现在让我们将共享库与可执行文件链接起来。考虑linktest.c:
int testfunction(void);
int main()
{
testfunction();
return 0;
}
我们可以将此编译并链接到任何共享库:
% gcc -o linktest1 liblibrarytest.so.1.0.0 linktest.c
% gcc -o linktest2 liblibrarytest.so.2.0.0 linktest.c
然后运行它们(请注意,我设置了动态库路径,以便动态链接器可以找到这些库,这些库不在标准库路径中):
% LD_LIBRARY_PATH=. ./linktest1
version 1%
% LD_LIBRARY_PATH=. ./linktest2
version 2%
现在让我们把可执行文件与两个库链接起来。每个库都导出相同的符号 testfunction
,而且每个库都有不同的实现。
% gcc -o linktest0-1 liblibrarytest.so.1.0.0 liblibrarytest.so.2.0.0 linktest.c
% gcc -o linktest0-2 liblibrarytest.so.2.0.0 liblibrarytest.so.1.0.0 linktest.c
唯一的区别是引用库的顺序不同会影响编译器。
% LD_LIBRARY_PATH=. ./linktest0-1
version 1%
% LD_LIBRARY_PATH=. ./linktest0-2
version 2%
以下是相应的ldd
输出:
% LD_LIBRARY_PATH=. ldd ./linktest0-1
linux-vdso.so.1 (0x00007ffe193de000)
liblibrarytest.so.1 => ./liblibrarytest.so.1 (0x00002b8bc4b0c000)
liblibrarytest.so.2 => ./liblibrarytest.so.2 (0x00002b8bc4d0e000)
libc.so.6 => /lib64/libc.so.6 (0x00002b8bc4f10000)
/lib64/ld-linux-x86-64.so.2 (0x00002b8bc48e8000)
% LD_LIBRARY_PATH=. ldd ./linktest0-2
linux-vdso.so.1 (0x00007ffc65df0000)
liblibrarytest.so.2 => ./liblibrarytest.so.2 (0x00002b46055c8000)
liblibrarytest.so.1 => ./liblibrarytest.so.1 (0x00002b46057ca000)
libc.so.6 => /lib64/libc.so.6 (0x00002b46059cc000)
/lib64/ld-linux-x86-64.so.2 (0x00002b46053a4000)
在这里,我们可以看到符号并不是唯一的,但链接器解析它们的方式是定义好的(它似乎总是解析遇到的第一个符号)。请注意,这是一个有点病态的情况,因为通常情况下你不会这样做。在你需要这样做的情况下,有更好的处理符号命名以便在导出时保持唯一性的方法(符号版本控制等)。
总之,是的,你可以根据函数名唯一地识别该函数。如果有多个同名符号,则根据库的解析顺序(从 ldd
或 objdump
等工具中查看)来确定正确的符号。是的,在这种情况下,你需要比仅有函数名更多的信息,但如果你有可执行文件进行检查,是可以做到的。