gpt4 book ai didi

c++ - 链接错误 : construction vtable defined in a discarded section

转载 作者:行者123 更新时间:2023-12-05 07:00:27 29 4
gpt4 key购买 nike

原始问题分布在来自不同项目的数十万个 LoC 中。它包含很多成分:内联汇编、虚拟继承、间接级别、不同的编译器和编译器选项。 (这就像一部惊悚片。)我很难简化到这个 SSCCE:

// a.hpp
struct A {
int i;
~A() { asm("" : "=r"(i)); }
};

struct B : public virtual A { };

struct C : public B { };

struct D {
D(C);
};

// a.cpp
#include "a.hpp"

void f(C) {
}

D::D(C c) {
f(c);
}

// main.cpp
#include "a.hpp"

int main() {
C c;
D d(c);
}

使用这些命令行构建:

g++ -O3 -fPIC -c a.cpp
clang++ -O3 -fPIC -c main.cpp
clang++ -fuse-ld=gold main.o a.o -o main

链接器的输出是:

a.o:a.cpp:function D::D(C) [clone .cold]: error: relocation refers to global symbol "construction vtable for B-in-C", which is defined in a discarded section
section group signature: "_ZTV1C"
prevailing definition is from main.o
clang-10: error: linker command failed with exit code 1 (use -v to see invocation)

我相信 gcc、clang 或 gold 中存在错误。 我的问题是它在哪里?(我猜它是金子,但我想在报告错误之前确定一下。)

FWIW:正如我所说,所有成分都很重要,如果例如删除 asm,问题就会消失。使问题消失的更显着的变化是:

  1. 对所有 TU 使用相同的编译器,(无论是 g++ 还是 clang++。)
  2. 与 ld 链接(即删除 -fuse-ld=gold)
  3. 编译main.cpp,不带-O3
  4. 不使用-fPIC编译main.cpp
  5. 在链接器命令行中交换 a.omain.o

最佳答案

这似乎是 GCC 中的一个错误,但是内联汇编在 ABI 之外,可以简单地使这成为 GCC 和 Clang 之间不幸的不兼容。

问题是内联汇编让 GCC 认为 ~A::A() 可以引发异常,因此它在 D::D() 中创建异常处理路径,这需要一个用于 B-in-C 的构造 vtable,它放置在还包含 C 的 vtable (_ZV1C) 的 COMDAT 组。

因为 Clang 不会在 _ZV1C COMDAT 组中生成构造 vtable,而 GCC 会生成,所以您最终会遇到这样一种情况,即链接器可能会保留 Clang 生成的 COMDAT 组,并丢弃 GCC - 具有构造 vtable 的生成版本。如果您链接到 GCC 生成的需要额外符号定义的代码,则会收到此错误。

在你的链接中反转 main.o 和 a.o 也可以解决这个问题,因为所有三个链接器都会让 COMDAT 组远离 a.o,这是第一个看到的。

这是 GCC 从 a.o 为 D::D() 生成的代码:

0000000000000002 <_ZN1DC1E1C>:
2: 48 83 ec 18 sub $0x18,%rsp
6: 48 8b 06 mov (%rsi),%rax
9: 48 8b 40 e8 mov -0x18(%rax),%rax
d: 8b 04 06 mov (%rsi,%rax,1),%eax
10: 89 44 24 08 mov %eax,0x8(%rsp)
14: 48 8b 05 00 00 00 00 mov 0x0(%rip),%rax
17: R_X86_64_REX_GOTPCRELX _ZTV1C-0x4
1b: 48 8d 40 18 lea 0x18(%rax),%rax
1f: 48 89 04 24 mov %rax,(%rsp)
23: 48 89 e7 mov %rsp,%rdi
26: e8 00 00 00 00 callq 2b <_ZN1DC1E1C+0x29>
27: R_X86_64_PLT32 _Z1f1C-0x4
2b: eb 17 jmp 44 <_ZN1DC1E1C+0x42>
2d: 48 89 c7 mov %rax,%rdi
30: 48 8d 05 00 00 00 00 lea 0x0(%rip),%rax
33: R_X86_64_PC32 _ZTC1C0_1B+0x14
37: 48 89 04 24 mov %rax,(%rsp)
3b: 89 44 24 08 mov %eax,0x8(%rsp)
3f: e8 00 00 00 00 callq 44
40: R_X86_64_PLT32 _Unwind_Resume-0x4
44: 48 83 c4 18 add $0x18,%rsp
48: c3 retq

从偏移量 0x2d 到 0x3f 处的 callq 的代码是异常的处理路径,在f(c)调用过程中发生异常时生成。 0x30 处的 lea 指令引用了 B-in-C 的构造 vtable 中的一个条目 (_ZTC1C0_1B)。

如果没有内联汇编,GCC 会生成与 clang 相同的代码,没有异常处理路径,也不需要构建 vtable。

--no-exceptions编译,问题也消失了。

无论是在 -O0-O1-O2 还是 -O3 编译 a.cpp,我都看到了同样的问题

至少GCC在编译a.cpp和main.cpp的时候是一致的,所以可以有人认为这种情况根本不包括在 C++ ABI 和 GCC 中Clang 可以自由地以不同的方式对待它。我做了一些微不足道的尝试用内联 asm 以外的东西重现,但不能。

至于为什么您从 gold 中收到错误,而不是从 bfd 或 lld 中收到错误,黄金报告可能是一个真正的错误,尽管在这个特殊情况,因为永远不会抛出异常,异常处理代码永远不会执行。但是当你链接bfd ld 或 lld,0x30 处的 lea 指令未重定位,没有警告,程序可能会崩溃调用 f() 时抛出异常。

关于c++ - 链接错误 : construction vtable defined in a discarded section,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64118757/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com