我的理解是-
(1) 符号绑定(全局/本地/弱)由链接器使用,将符号的范围限制为其定义的目标文件或链接在一起的其他目标文件/库,并确定是否可以从它们中覆盖它.
(2) 符号 可见性(默认/受保护/内部/隐藏)由 loader 使用来控制链接的二进制文件中的符号是否可见或可从不同的二进制文件中插入。
仍然,可执行文件/共享库(即链接的二进制文件)中的符号表维护符号绑定:
$ readelf --all /usr/bin/ls
...
Symbol table '.dynsym' contains 139 entries:
Num: Value Size Type **Bind** Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __ctype_toupper_loc@GLIBC_2.3 (2)
2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND getenv@GLIBC_2.2.5 (3)
3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND sigprocmask@GLIBC_2.2.5 (3)
...
我的理解正确吗?符号绑定是否以某种方式在加载/运行时使用,我缺少什么?
加载/运行时是否以某种我缺少的方式使用了符号绑定?
确实如此。
通常您只会在动态符号表中看到
GLOBAL
和 WEAK
符号——没有理由将 PROTECTED
或 HIDDEN
符号放入其中。
如果您有一个 unresolved
WEAK
符号,并且该符号没有其他定义,并获取它的地址,则加载程序会将此类符号解析为 NULL
。
但是,如果符号是 unresolved 和
GLOBAL
,您将收到致命的加载器错误。
示例:
// x.c
#include <stdio.h>
extern int foo() WEAK;
void fun() {
printf("In fun: &foo = %p\n", &foo);
}
// main.c
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
void *h = dlopen("./x.so", RTLD_LAZY);
if (h == NULL) {
printf("Failure: %s\n", dlerror());
return 1;
}
void (*fun)(void) = dlsym(h, "fun");
printf("Success, calling fun() @%p\n", fun);
fun();
return 0;
}
gcc main.c -ldl
gcc -DWEAK="" -fPIC -shared -o x.so x.c &&
./a.out
Failure: ./x.so: undefined symbol: foo
现在有一个弱符号:
gcc -DWEAK="__attribute__((weak))" -fPIC -shared -o x.so x.c &&
./a.out
Success, calling fun() @0x7f6d37988109
In fun: &foo = (nil)