如何捕捉无意识的功能插入?

时间:2010-05-06 16:53:52

标签: c gcc linker function-interposition

通过我的书专家C编程,我看到了关于功能插入的章节以及如果无意中发生了如何导致严重难以发现的错误。

本书中给出的例子如下:

my_source.c

mktemp() { ... }

main() {
  mktemp();
  getwd();
}

的libc

mktemp(){ ... }
getwd(){ ...; mktemp(); ... }

根据这本书,main()中发生的事情是mktemp()(标准C库函数)被 my_source.c 中的实现插入。虽然main()调用我的mktemp()实现是行为,但getwd()(另一个C库函数) 会调用我的实现mktemp()不是。

显然,这个例子是SunOS 4.0.3版lpr中存在的真实生活中的错误。本书继续解释修复方法是将关键字static添加到 my_source.c mktemp()的定义中;虽然改名完全应该也解决了这个问题。

本章给我留下了一些未解决的问题,希望你们能回答:

  1. GCC是否有办法警告功能干预?我们当然不会打算发生这种情况,如果有的话,我想知道它。
  2. 我们的软件组是否应采用将关键字static放在我们不希望公开的所有功能之前的做法?
  3. 可以用静态库引入的函数进行插入吗?
  4. 感谢您的帮助。

    修改

    我应该注意到,我的问题不仅仅是针对插入标准C库函数,还包括其他库中包含的函数,可能是第三方,也许是内部创建的。从本质上讲,无论插入函数位于何处,我都想捕获 任何 插入实例。

6 个答案:

答案 0 :(得分:2)

这实际上是一个链接器问题。

编译一堆C源文件时,编译器将为每个文件创建一个目标文件。每个.o文件将包含此模块中的公共函数列表,以及由模块中的代码调用的函数列表,但实际上并未在那里定义,即此模块期望某些库提供的函数。

当您将一堆.o文件链接在一起以生成可执行文件时,链接器必须解析所有这些缺少的引用。这是可以发生插入的点。如果对名为“mktemp”的函数有未解析的引用,并且有几个库提供具有该名称的公共函数,那么它应该使用哪个版本?对此没有简单的答案,如果选择了错误的,可能会发生奇怪的事情

所以是的,除非你确实需要从其他源文件中使用它,否则在C中使用“静态”一切都是个好主意。实际上在许多其他语言中,这是默认行为,如果您希望从外部访问它们,则必须将其标记为“公共”。

答案 1 :(得分:1)

纯粹形式上,您描述的插入是直接违反C语言定义规则(ODR规则,用C ++的说法)。任何体面的编译器都必须检测这些情况,或提供检测它们的选项。在C语言中定义多个具有相同名称的函数是完全不合法的,无论这些函数在何处定义(标准库,其他用户库等)。

据我所知,许多平台提供了通过将一些标准函数定义为弱符号来自定义[标准]库行为的方法。虽然这确实是一个有用的功能,但我相信编译器仍然必须为用户提供强制执行标准诊断的方法(最好是按功能或按库)。

因此,如果你的库中没有弱符号,你不应该担心插入。如果你这样做(或者你怀疑你这样做了),你必须查阅你的编译器文档,看看它是否为你提供了检查弱符号解析的方法。

例如,在GCC中,您可以使用-fno-weak禁用弱符号功能,但这基本上会杀死与弱符号相关的所有内容,这并不总是令人满意。

答案 2 :(得分:1)

听起来你想要的是工具检测到函数中存在名称冲突 - 即,你不希望你的外部可访问的函数名称意外地具有相同的名称,因此“覆盖”或隐藏函数在图书馆中使用相同的名称。

最近有一个与此问题有关的问题:Linking Libraries with Duplicate Class Names using GCC

在您链接的所有库上使用--whole-archive选项可能会有所帮助(但正如我在那里的答案中所提到的,我真的不知道这有多好用或说服构建到将选项应用于所有库)

答案 3 :(得分:0)

如果函数不需要在它存在的C文件之外访问那么是的,我建议使用函数static

您可以做的一件事就是使用具有可配置语法突出显示的编辑器。我个人使用SciTE,我已将其配置为以红色显示所有标准库函数名称。这样,很容易发现我是否正在重复使用我不应该使用的名称(编译器没有强制执行任何内容)。

答案 4 :(得分:0)

编写一个在所有.o文件和库上运行nm -o的脚本并检查是否在程序和库中都定义了外部名称相对容易。只是Unix链接器没有提供的许多理智的服务之一,因为它在1974年陷入困境,一次查看一个文件。 (尝试以错误的顺序放置库,看看是否收到有用的错误消息!)

答案 5 :(得分:0)

当链接器尝试链接单独的模块时会发生Interposistioning。 它不能在模块内发生。如果模块中存在重复的符号,则链接器将此报告为错误。

对于* nix链接器,意外的Interposistioning是一个问题,链接器很难防范它。 出于本答案的目的,请考虑两个链接阶段:

  1. 链接器将翻译单元链接到模块(基本上 应用程序或库)。
  2. 链接器通过搜索模块链接任何剩余的未发现符号。
  3. 考虑“专家C编程”和SiegeX问题中描述的场景。 链接器尝试构建应用程序模块。 它认为符号mktemp()是外部的,并试图找到符号的函数定义。链接器找到 应用程序模块的目标代码中的函数定义,并将符号标记为找到。 在此阶段,符号mktemp()完全解析。它不以任何方式考虑暂时允许 对于anothere模块可能定义符号的可能性。 在许多方面,这是有道理的,因为链接器应首先尝试解析模块中的外部符号 目前正在联系它只是在其他模块中链接时搜索的非符号符号。 此外,由于符号已标记为已解析,因此链接器将在任何应用程序中使用应用程序mktemp() 其他情况需要解决此符号。 因此,库将使用mktemp()的应用程序版本。

    一种简单的方法来防范问题是尝试使应用程序或库中的所有外部sysmbol都是唯一的。 对于仅在有限的基础上共享的模块,可以通过确保所有模块轻松完成 通过附加唯一标识符,模块中的外部符号是唯一的。

    对于广泛共享组成唯一名称的模块是一个问题。