Gcc 在链接时或运行时解析引用?
朋友们,我有两个档案,a.c和b.c。 我在a.c中定义了一个函数foo,它是从b.c调用的 据我所知,当编译器尝试编译b.c时,它会看到Gcc 在链接时或运行时解析引用?,gcc,compiler-construction,linker,runtime,Gcc,Compiler Construction,Linker,Runtime,朋友们,我有两个档案,a.c和b.c。 我在a.c中定义了一个函数foo,它是从b.c调用的 据我所知,当编译器尝试编译b.c时,它会看到foo的实现不在b中,因此它会在符号表中添加一个foo条目,以便在链接时解析哪个操作系统。我完全理解这个概念 现在,我在b.c中有一个不同的函数printf,它在glibc中实现。据我所知,printf可以在加载时或运行时链接。如果将在运行时链接printf,则必须为每个对printf的调用提供一个存根,该存根将在运行时使用系统调用进行解析 我的问题是“我的理
foo
的实现不在b中,因此它会在符号表中添加一个foo条目,以便在链接时解析哪个操作系统。我完全理解这个概念
现在,我在b.c中有一个不同的函数printf,它在glibc中实现。据我所知,printf可以在加载时或运行时链接。如果将在运行时链接printf,则必须为每个对printf的调用提供一个存根,该存根将在运行时使用系统调用进行解析
我的问题是“我的理解正确吗?”+编译器如何确定函数foo将由链接器而不是在运行时解析
我注意到一些类似的问题,但无法理解它们在这里的意义???我发现你的问题有点难以理解,所以我不太确定你是如何理解的,所以我只描述一下它是如何工作的
-fPIC
,则编译器只会发出对未定义符号的调用。在这种情况下,链接器将在其他.O文件或库中搜索符号,并在链接时插入直接引用,基本上将其粘贴到空白处。
这正是您通常构建程序(而不是库)的方式。如果程序使用动态库,则可能存在一些无法在链接时修复的符号。如果是这样,链接器将检查库中是否有它们,并让动态链接器在运行时完成作业
在共享库中也可以做到这一点,只是动态链接器总是在运行时将地址粘贴到程序中,但这样做意味着共享库无法共享:每个程序都必须有自己的副本和自己的修复程序。这就是为什么没有发生-fPIC
,则编译器不会直接使用符号名称。相反,它通过PLT(过程链接表)调用函数,并通过GOT(全局偏移表)获取其他符号的地址
GOT是由链接器创建的一个特殊表,它基本上只是一个未定义的符号引用列表,类似于在常规非PIC程序中找到的符号引用(除了它们通常是到GOT底部的偏移)。动态链接器在运行时填充空格。编译器将GOT的地址安排在特定的CPU寄存器中,以便始终可以找到该表
PLT是由链接器创建的一组蹦床。编译器创建跳转到PLT中,动态链接器设置PLT以跳转到函数的实际位置。实际上,在许多情况下,加载库时动态链接器不会填充PLT:PLT在第一次使用GOT(它的自修改代码)调用时填充自己
这就是为什么动态库通常是用-fPIC
构建的:可以为每个程序修改GOT和PLT,同时保持库的文本不变,从而允许它们保持共享编译器不知道何时解析函数。它只知道自己无法解决问题。事实上,当您使用动态库时,链接器甚至不会尝试完全解析符号(尽管我认为它会检查它们是否在库中定义);这意味着可以通过提供具有相同名称的另一个函数来重写库中的特定函数。像
tsocks
这样的工具将其与LD\u PRELOAD
一起使用来拦截库调用。我发现你的问题有点难理解,所以我不太确定你是如何理解它的,所以我只描述它是如何工作的
-fPIC
,则编译器只会发出对未定义符号的调用。在这种情况下,链接器将在其他.O文件或库中搜索符号,并在链接时插入直接引用,基本上将其粘贴到空白处。
这正是您通常构建程序(而不是库)的方式。如果程序使用动态库,则可能存在一些无法在链接时修复的符号。如果是这样,链接器将检查库中是否有它们,并让动态链接器在运行时完成作业
在共享库中也可以做到这一点,只是动态链接器总是在运行时将地址粘贴到程序中,但这样做意味着共享库无法共享:每个程序都必须有自己的副本和自己的修复程序。这就是为什么没有发生-fPIC
,则编译器不会直接使用符号名称。相反,它通过PLT(过程链接表)调用函数,并通过GOT(全局偏移表)获取其他符号的地址
GOT是由链接器创建的一个特殊表,它基本上只是一个未定义的符号引用列表,类似于在常规非PIC程序中找到的符号引用(除了它们通常是到GOT底部的偏移)。d