给出以下简单的代码片段:
int main (void) {
void* foo = scanf;
void* bar = sscanf;
}
这是反汇编(取自mach-o可执行文件):
非惰性符号指针的一小部分:
符号表的一小部分:
我似乎不理解可执行文件中的第5行和第6行(scanf& sscanf的movq到rax / rcx)。 foo和bar(最终)如何拥有scanf&的地址? sscanf分别。 我认为它与映射到进程的动态库有关(而且很可能指向那里的非惰性符号指针)但我无法理解。
感谢
答案 0 :(得分:1)
dyld是动态链接器,负责绑定符号。要查看会发生什么,请首先使用-fno-pie
选项编译测试项目,以禁用“位置独立可执行文件”。这样,您在MachOView中看到的偏移量在运行时将是相同的。然后启动DYLD_PRINT_BINDINGS
环境变量设置为YES
的可执行文件。结果如下:
$ DYLD_PRINT_BINDINGS=YES ./a.out
dyld: bind: a.out:0x100001010 = libsystem_c.dylib:_scanf, *0x100001010 = 0x7FFF94578017
dyld: bind: a.out:0x100001018 = libsystem_c.dylib:_sscanf, *0x100001018 = 0x7FFF94578707
...
答案 1 :(得分:1)
生成的代码从非惰性符号指针加载scanf
的地址。那里没什么特别的。当dyld加载可执行文件时,它会绑定"每个非惰性符号指针,将其值设置为符号的正确地址。
非惰性符号指针用于引用(1)指向不同库中的某些内容,(2)是数据引用,而不是对函数的调用。在您的示例中,您未直接致电scanf
,因此它是数据引用,而scanf
不在您的可执行文件中,因此它是对不同库的引用。
同一个可执行文件中的引用使用固定的PC相对偏移:编译器和链接器知道代码和数据将相互加载,因此它们可以在构建时选择偏移量。函数调用的引用是惰性的:在运行时,对函数的第一次调用首先通过dyld进行路由,它会查找符号并绑定惰性符号指针以供将来调用。
正如0xced指出的那样,你可以设置环境变量DYLD_PRINT_BINDINGS
来观察dyld工作。 dyld手册页描述了您可以设置的其他变量。