【问题标题】:Strange linking behavior, latest g++奇怪的链接行为,最新的 g++
【发布时间】:2011-04-15 03:31:45
【问题描述】:

我在使用 g++ 时遇到了奇怪的链接行为,但是,我只是一名学生,我想知道这是否正常。

我正在尝试将汇编代码(机器:fedora 14 gnome 32bits x86 i686 intel i7)与 c++ 代码链接,并让汇编代码从 c++ 文件中实例化的函数调用方法。似乎在类声明中实现一个方法会阻止它被放入链接器表中,除非它在原始源代码中至少使用过一次。

class A
{
public:
    void showSUP() {
        cout<<"sup";
    }
};

实例化A后,你将无法调用_ZN1A7showSUPEv,因为它还没有放入链接表中:

call _ZN1A7showSUPEv

但是,如果您在声明 A 的同一 .cpp 中调用 A::showSUP(),则可以从单独的程序集文件中调用它。

使用(以及A 的实例化)

class A
{
    void showSUP();
};

A::showSUP()
{
    cout<<"sup";
}

拨打_ZN1A7showSUPEv 可以。

我的问题是,为什么第一个示例不起作用。

提前谢谢大家。

【问题讨论】:

  • 听起来你的编译器忽略了未使用的函数。快速谷歌搜索虽然看起来像 -Wunused-function 会警告这一点,但没有关于省略的内容。很奇怪。
  • 我猜是类里面的函数定义是内联的。
  • @wjlafrance:不,这并不奇怪。稍加了解,应该是可以预料的。由于该函数是在类声明中定义的,因此在编译任何使用它时它的定义都将清晰可见,并且编译器可以在多个模块中看到它的定义。由于不同模块的编译是独立的,编译器无法判断它是否会从另一个模块中获取编译后的代码,并且必须在所有模块中生成它并在链接期间将它们合并。为了避免浪费资源,它只在使用的地方生成。
  • @Jan Hudec 感谢您的澄清。 +1!

标签: c++ gcc linker g++


【解决方案1】:

有一些属性,您可以通过这种方式为函数指定属性

classe A
{
  public:
    void showSUP(){
      cout<<"sup";
    } __attribute__((used))
};

gcc attribute overview

使用过 在版本中找到:3.1-3.4 说明:

     This attribute, attached to a function, means that code must be
     emitted for the function even if it appears that the function is
     not referenced.  This is useful, for example, when the function

是 仅在内联汇编中引用。

【讨论】:

  • 你的答案和 Zan 的答案是完美的。非常感谢您向我介绍 gcc 的属性。
  • @Julius:请注意,属性通常是编译器特定的。如果您已经涉足组装,那么您已经绑定到某些平台,但这是另一个需要考虑的依赖项。
【解决方案2】:
  • 对于内联函数,编译器 只会在使用函数的地方输出代码。
  • 定义的函数 类定义里面是 内联(通常)。
  • 该功能不 用过。
  • 因此:在 二进制。

【讨论】:

  • 在类定义中定义的函数的行为就像通过规范使用 inline 修饰符指定的一样,通常不是(尽管它们不必总是内联,因为它并不总是可行或合理的)。
  • 所以,@Julius,不,它不是 gcc 特有的。所有 C++ 编译器都以这种方式运行。他们必须。在类体内定义的函数肯定会在所有使用它的模块中可用,并且没有一个模块可以被选为生成它们的位置。所以它们必须在所有使用它的模块中生成(在所有看到它的模块中生成它会非常浪费资源)并且在链接期间合并实例。副作用是,如果它从未使用过,或者如果所有实例都被内联,则根本不会为它生成任何符号。
  • @Jan:是的,你是对的。 (通常)我的意思是它们通常实际上是内联的。内联,因为没有单独的函数调用/返回。
  • 即使是内联的,就像在调用位置实际插入的代码一样,很多时候编译器也会至少在第一次使用之后生成一个单独的符号。原因是标准保证您可以获取函数的地址(顺便说一下,函数的地址在程序中将是唯一的,因此它是一个弱符号,因此链接器可以丢弃除一份以外的所有副本)再次,这是实现的细节。
【解决方案3】:

一般来说,如果你想在最终的库/可执行文件中包含一个函数,它需要是:

  • 使用
  • 非内联

inlined 函数是一个函数,其代码被简单地复制并粘贴到使用函数的位置(由编译器),因此没有函数调用。这是一个机会优化,因此函数可能在某些地方内联,而在其他地方不内联,具体取决于上下文。大多数非常短的函数(所谓的单行函数)通常是内联的。

在过去,要内联一个函数需要在当前翻译单元中定义,即:

  • 要么在标头中定义(就像您的情况一样),因此可以在包括此标头在内的所有源中内联
  • 要么在源文件中定义,因此可以在源文件中内联

虽然现在我们也有 LTO(链接时间优化),如果激活链接器实际上可能内联函数调用。这些优化还负责从 unused 符号中清理生成的库/二进制文件。

您的问题有两种可能的解决方案:

  • 改为在源文件中定义函数,它是标准的,可能不会被清除
  • 使用编译器特定的属性将函数标记为已使用,这样它就不会被清除

在后一种情况下,如果您希望具有可移植性,我只能建议使用宏 (ATTRIBUTE_USED) 并根据当前使用的编译器定义其内容。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-13
    • 1970-01-01
    相关资源
    最近更新 更多