为什么使用 extern struct {} foo,用 g++ 触发无效的 fPIC 需要错误消息?

Pee*_*oot 5 c++ gcc

我正在构建一个共享库,它具有各自的代码和编译规则,如下所示:

// x.C
struct {
   short len;
   char s[32700];
   } foo;

// u.C
extern struct {
   short len;
   char s[32700];
   } foo;

void blah(void)
{
   foo.s[0] = 0 ;
}

$CXX -c x.C -fPIC
$CXX -c u.C -fPIC
$CXX -shared -o x.so.1 -Wl,-soname,x.so.1 x.o u.o
Run Code Online (Sandbox Code Playgroud)

此代码使用 intel (v13-v16) 编译器和 clang 编译器 (v3.6) 进行编译和链接,但使用 g++(版本 4.9.2)时出现链接错误:

u.o: relocation R_X86_64_PC32 against undefined symbol `foo' can not be used when making a shared object; recompile with -fPIC
Run Code Online (Sandbox Code Playgroud)

这里关于 -fPIC 的链接错误显然是错误的,因为代码是用 fPIC 编译的。我还在输出中看到符号的重定位记录objdump -x

RELOCATION RECORDS FOR [.text]:
OFFSET           TYPE              VALUE
0000000000000006 R_X86_64_PC32     foo-0x0000000000000003
Run Code Online (Sandbox Code Playgroud)

clang 和 gcc 编译器都会产生警告,指导修正允许其链接的代码(至少在这个独立版本中,实际代码将更难弄清楚在哪里以及如何修复):

u.C:5:10: warning: anonymous type with no linkage used to declare variable '<anonymous struct> foo' with linkage
        } foo;
          ^
Run Code Online (Sandbox Code Playgroud)

果然我可以通过删除匿名结构来解决这个问题:

// u.h
struct fooT {
   short len;
   char s[32700];
   } ;

extern fooT foo ;

// u.C
#include "u.h"

void blah(void)
{
   foo.s[0] = 0 ;
}

// x.C
#include "u.h"

struct fooT foo ;
Run Code Online (Sandbox Code Playgroud)

因此,虽然我有解决办法,但我想了解这里发生了什么。我看到一些问题有类似的链接错误:

其中一些表明这可能与符号可见性有关。gcc 是否将此符号标记为隐藏,从而触发最终的链接错误?g++、clang++ 或 intel 编译器中哪一个在此表现正常或不正确,或者此构造是否无效并且只是不可移植?如果这是无效代码,它违反了 C++ 标准的哪一部分?

Mar*_*k B 4

我正在阅读部分3.5,假设我正确阅读了所有内容,您的非 typedef 未命名类没有链接,因此不能称为extern.

让我们快速回顾一下:

第一段和第二段是定义和序言。

/3有多种原因表明它是内部链接。它们似乎都不适用(静态、const/constexpr、匿名联合)。

/4都与未命名的命名空间相关

/5class 作用域和 typedef 作用域不适用。

/6阻止不适用的范围项目。

/7更多块作用域项目。

/8啊,我们走了Names not covered by these rules have no linkage....

所以在我看来,链接器只是感到困惑,因为你试图extern引用没有链接的东西。虽然错误消息看起来很糟糕/措辞不佳,但它发出错误似乎确实不错。