在链接时或运行时解析引用?

问题描述 投票:0回答:1

朋友们,我有两个文件, a.c 和 b.c 。 我在 a.c 中定义了一个函数 foo,它是从 b.c. 调用的。

据我了解,当编译器尝试编译 b.c 时,它会发现

foo

 的实现不在 b 中,因此它将在符号表中添加 foo 的条目,该条目将在链接时解析。我正确理解了这个概念。

现在,我在 b.c 中有一个不同的函数 printf,它是在 glibc 中实现的。据我了解, printf 可以在加载时或运行时链接。如果 printf 将在运行时链接,则每次调用 printf 都必须有一个存根,该存根将在运行时使用系统调用进行解析。

我的问题是“我的理解正确吗???+编译器如何确定函数 foo 将由链接器解析而不是在运行时解析???”

我注意到一些类似的问题,但无法理解它们的重要性???

c gcc linker runtime
1个回答
3
投票
我发现你的问题有点难以阅读,所以我不太确定你是如何理解它的,所以我只会描述它是如何工作的。

  1. 如果符号位于同一个文件(b.c)中,则编译器直接引用它。链接器不用于解决任何问题。

  2. 如果符号不在同一个文件中,并且

    -fPIC

    未指定,则编译器只会发出对未定义符号的调用。在这种情况下,链接器将在其他 .o 文件或库中搜索符号,并在链接时插入直接引用,基本上将其粘贴到空白中。
    这正是您通常构建程序的方式(而不是库)。如果程序使用动态库,则可能存在一些无法在链接时修复的符号。如果是这样,链接器将检查库是否具有它们,并将留给动态链接器在运行时完成作业。

    也可以在共享库中执行此操作,只需动态链接器始终在运行时将地址粘贴到程序中,但这样做将意味着

    shared

    库无法共享:每个程序必须有自己的副本和自己的修复。这就是为什么这种情况不会发生。

    如果符号不在同一个文件中,并且指定了
  3. -fPIC
  4. is

    ,则编译器不会直接使用符号名称。相反,它通过 PLT(过程链接表)调用函数,并通过 GOT(全局偏移表)获取其他符号的地址。
    GOT 是由链接器创建的特殊表,它基本上只是一个未定义符号引用的列表,类似于您在常规非 PIC 程序中找到的符号引用(除了它们通常是到基址的偏移量)得到了)。动态链接器在运行时填补空白。编译器将 GOT 的地址安排在特定的 CPU 寄存器中,以便始终可以找到该表。

    PLT 是由链接器创建的一组蹦床。编译器创建到 PLT 的跳转,动态链接器将 PLT 设置为跳转到函数的实际位置。实际上,在很多情况下,加载库时动态链接器不会填充 PLT:PLT 在第一次使用 GOT 调用时会自行填充(它是自修改代码)。

    这就是通常使用

    -fPIC

    构建动态库的原因:可以为每个程序修改 GOT 和 PLT,同时仍保持库文本不变,从而允许它们保持共享。

    
    
    

    那么,现在回答你的问题:
我认为您所说的“存根”可能是 PLT?

编译器

知道函数何时会被解析。它只知道它自己无法解决它。事实上,当您使用动态库时,链接器甚至不会尝试完全解析符号(尽管我认为它确实检查它们是否在库中定义);这意味着可以通过提供另一个同名函数来覆盖库中的特定函数。像 tsocks 这样的工具将其与 LD_PRELOAD

 一起使用来拦截库调用。
	

© www.soinside.com 2019 - 2024. All rights reserved.