是的,给定一个符号名称和可执行文件所链接的库集,您可以唯一标识该函数。此行为是链接和动态链接工作所必需的。
一个说明性的例子
考虑以下两个文件:
图书馆测试1.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%
现在让我们将我们的可执行文件链接到 both 库。每个都导出相同的符号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 等)识别正确的符号。是的,在这种情况下,除了名称之外,您还需要更多信息,但如果您有要检查的可执行文件,则有可能。