【问题标题】:How does the Linux C++ compiler (and linker) decide where to put typeinfo?Linux C++ 编译器(和链接器)如何决定将 typeinfo 放在哪里?
【发布时间】:2011-05-23 12:47:40
【问题描述】:

我在一个 h 文件中定义了一个类,并在一个 cpp 中实现,该 cpp 是一个 lib(我们称之为 libdef)的一部分。

我有另外两个库,它们的 cpp 文件包含这个 h 文件。其中一个对这个类执行 dynamic_cast()(我们称之为 libdyn),另一个对这个类执行 new(我们称之为 libnew)。

似乎在其中一个库中有类型的类型信息,但在另一个库中没有:

user@machine> ld --cref libdef.so | grep -E "typeinfo for MyClass"
ld: warning: cannot find entry symbol _start; not setting start address
typeinfo for MyClass libdef.so

user@machine> ld --cref libnew.so | grep -E "typeinfo for MyClass"
ld: warning: cannot find entry symbol _start; not setting start address
typeinfo for MyClass libdef.so

user@machine> ld --cref libdyn.so | grep -E "typeinfo for MyClass"
ld: warning: cannot find entry symbol _start; not setting start address
typeinfo for MyClass libdyn.so

正如您所见,libdef 和 libnew 都使用来自 libdef 的 typeinfo,但 libdyn 使用自己的 typeinfo。这是为什么?编译器/链接器如何决定是将 typeinfo 放在一个库中还是从另一个库中引用它?

我应该注意到 libnew 和 libdyn 都是用 -llibdef 构建的。

user@machine> icpc -V
Intel(R) C++ Intel(R) 64 Compiler XE for applications running on Intel(R) 64, Version 12.0.0.084 Build 20101006
Copyright (C) 1985-2010 Intel Corporation.  All rights reserved.

user@machine> ld -V
GNU ld version 2.17.50.0.6-14.el5 20061020
Supported emulations:
 elf_x86_64
 elf_i386
 i386linux

在我做了一些检查之后,这取决于 lib 的 cpp 文件是否“看到”虚拟方法定义。

此代码不会导致 typeinfo 符号出现在库中:

class SomeClass { public: SomeClass(); virtual void func(); };

此代码将在库中生成一个 typeinfo 符号:

class SomeClass { public: SomeClass() {} virtual void func() {} };

如果存在,typeinfo 符号将具有模糊的链接。

【问题讨论】:

  • 您似乎假设每种类型只有一个 typeinfo 对象......标准不保证这一点,它只保证如果有多个,它们将比较相等。
  • @Ben 问题是如果有多个,它们在 Linux 中不会比较相等(因为使用了地址比较)。如果您仅按照 gcc 的指示使用 lib,则不会发生这种情况,但我们仍然遇到问题,我正在尝试为我们的产品寻找一些解决方法。
  • 通过“比较相等”,我不是指地址比较。我的意思是operator=,它是为typeinfo 类型的对象定义的。这是标准保证用于比较 typeinfo 实例的唯一机制。
  • @Ben 我不知道地址比较是否在 operator== 中实现,但我知道(好吧,在某处阅读)dynamic_cast() 在 Linux 上使用地址比较来确定类型相等性。

标签: c++ linux compiler-construction linker rtti


【解决方案1】:

G++ 可能将类的类型信息定义为弱 符号,在每个需要它的目标文件中。对象之一 libdef 中的文件需要它,因为它是 vtable 的一部分 由构造函数安装。以及其中的一个目标文件 libdyn 需要它,因为 dynamic_cast。我摔倒 libnew 确实是一个新的,但是,它可能不需要它 (除非构造函数是内联的)。无论如何,如果真的 是一个弱符号,只有一个定义是 并入最终的so或可执行文件;哪个是 未指定(但我怀疑它是第一个链接器 遭遇——这与你所看到的相对应)。

大多数时候这些都不重要。 (例外情况是,如果 你打电话给dlopenRTLD_LOCAL;这样做,并且 dynamic_cast 如果在 .so 中执行,它可能会失败 除了带有对象构造函数的那个​​。)

【讨论】:

  • 你可能是对的,但有没有办法让它从另一个库引用它而不使用它自己的类型信息?
  • 我不知道。弱引用背后的全部理由是 每个 使用都会引发一个定义(在 .o 文件中),并且链接器会随机选择一个——出于所有目的和目的,随机。 ODR 的动机之一是允许这种实现技术。如果您的类定义中的任何内容会导致 typeinfo 不同,那么您就有未定义的行为。
【解决方案2】:

正如其他人所提到的,这取决于 C++ 实现。但是here是描述g++是如何做到的。

简单地说,如果可能,g++ 会在定义类的第一个非内联虚拟成员的翻译单元中定义类 vtable 和 type_info。

【讨论】:

  • 我想知道链接中的描述是否是最新的。 (我知道 URL 显示的是 4.6,但文本本身可能较旧,并且尚未更新。)没有提到弱绑定,这是 g++ 用于模板实例化之类的技术。 (如果您点击该页面上的模板链接,它会指向一个谈论 Borland 模型和 CFront 模型的页面。一个非常明确的迹象表明它不是最新的。)
  • @James:从我使用 nm 的情况来看,最新版本的 g++ 对 vtables 和 type_info 使用了弱绑定和“关键方法”技术。
  • @ashepler 我不确定他们为 typeinfo 做了什么——我的 cmets 关于弱绑定只是一个猜测。不过,我确实知道在模板实例化中使用了弱符号,而且在这里也使用它们似乎是合理的。
【解决方案3】:

我认为这将是编译器的实现细节。

通常(因为虚拟表本身是一个实现细节),virtual table 存储指向type info 的指针,我认为存储类型信息的位置是编译器的实现细节。很可能它将typeinfo 存储在声明类的so 中。

【讨论】:

  • 声明在一个标题中,该标题包含在所有 3 个库的翻译单元中。如果它们都包含类型信息,那将是有道理的,但让我感到困惑的是,一个有它而另一个没有。
【解决方案4】:

我不知道你的编译器和链接器的实现细节,但也许它足够聪明,不会在不需要的地方包含 typeinfo?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-01-08
    • 1970-01-01
    • 2010-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-07
    • 1970-01-01
    相关资源
    最近更新 更多