【问题标题】:Resolving references at link time or run time?在链接时或运行时解析引用?
【发布时间】:2012-04-03 07:49:43
【问题描述】:

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

据我了解,当编译器尝试编译bc时,它会看到foo的实现不在b中,所以它会在符号表中添加一个foo的条目,os要在链接时间。我正确理解了这个概念。

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

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

我注意到一些类似的问题,但在这里无法理解它们的意义???

【问题讨论】:

    标签: gcc compiler-construction linker runtime


    【解决方案1】:

    我觉得你的问题有点难以阅读,所以我不太清楚你是怎么理解的,所以我只描述一下它是如何工作的。

    1. 如果符号在同一个文件 (b.c) 中,那么编译器会直接引用它。链接器不用于解析任何内容。

    2. 如果符号不在同一个文件中,并且-fPIC 未指定,那么编译器只会发出对未定义符号的调用。在这种情况下,链接器将在其他 .o 文件或库中搜索该符号,并在链接时通过将其粘贴到空白处来插入直接引用。

      这正是您通常构建程序(而不是库)的方式。如果程序使用动态库,那么可能有一些符号在链接时无法修复。如果是这样,链接器将检查库中是否存在它们,并将其留给动态链接器在运行时完成工作。

      在共享库中也可以做到这一点,只是动态链接器总是在运行时将地址粘贴到程序中,但这样做意味着 shared 库不能'不被共享:每个程序都必须有自己的副本和自己的修复程序。这就是为什么不会发生这种情况的原因。

    3. 如果符号不在同一个文件中,并且指定了-fPIC ,那么编译器不会直接使用符号名称。相反,它通过 PLT(过程链接表)调用函数,并通过 GOT(全局偏移表)获取其他符号的地址。

      GOT 是由链接器创建的特殊表,它基本上只是一个未定义符号引用的列表,类似于您在常规非 PIC 程序中找到的符号引用(除了它们通常偏移到GOT 的基础)。动态链接器在运行时填充空白。编译器将 GOT 的地址安排在特定的 CPU 寄存器中,以便始终可以找到该表。

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

      这就是为什么动态库通常使用-fPIC 构建的原因:可以为每个程序修改 GOT 和 PLT,同时仍然保持库的文本不被修改,因此允许它们保持共享。

    那么,现在回答你的问题:

    我认为您所说的“存根”可能是 PLT?

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-29
      • 1970-01-01
      • 1970-01-01
      • 2011-04-21
      • 1970-01-01
      相关资源
      最近更新 更多