有人可以解释一下 g++ 的两个实例如何处理将以下代码编译为共享库的差异吗?
Foo.h
#ifndef Foo_h
#define Foo_h
void Foo();
#endif // Foo_h
Foo.cpp
#include "Foo.h"
#include <iostream>
void Foo()
{
std::cout << "Greetings from Foo()!" << std::endl;
}
Bar.h
#ifndef Bar_h
#define Bar_h
void Bar();
#endif // Bar_h
Bar.cpp
#include "Bar.h"
#include "Foo.h"
#include <iostream>
void Bar()
{
Foo();
std::cout << "Greetings from Bar()!" << std::endl;
}
在真正的 Linux 机器上:
>g++ --version
g++ (GCC) 4.4.7 20120313 (Red Hat 4.4.7-11)
Copyright (C) 2010 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
>g++ -fpic -c Foo.cpp
>g++ -fpic -c Bar.cpp
>g++ -shared -o libFoo.so Foo.o
>g++ -shared -o libBar.so Bar.o
>
在 Cygwin 上:
>g++ --version
g++ (GCC) 5.4.0
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
>g++ -fpic -c Foo.cpp
>g++ -fpic -c Bar.cpp
>g++ -shared -o libFoo.so Foo.o
>g++ -shared -o libBar.so Bar.o
Bar.o:Bar.cpp:(.text+0x9): undefined reference to `Foo()'
Bar.o:Bar.cpp:(.text+0x9): relocation truncated to fit: R_X86_64_PC32 against undefined symbol `Foo()'
collect2: error: ld returned 1 exit status
我真的没有足够的*nix-savvy 来知道如何在任一盒子上安装不同/匹配版本的 g++ 以查看这是否是问题的原因(在其中一个盒子上我不会无论如何,有特权这样做)。
我一直认为目标文件和扩展库(无论是静态的还是共享的)都允许具有未解析的符号,并且只有在链接可执行文件时才需要解析所有符号。在多年的开发经验中,这个概念也几乎成立,所以我对 Cygwin 上生成的错误感到困惑。我很好奇这里发生了什么。谢谢。
更新
下面的回答者提供了以下有效的建议:g++ -shared -o libBar.so Bar.o libFoo.so
查看libBar.so生成的内容:
>nm --demangle libBar.so | grep Foo
00000004e4b791c4 I __imp__Z3Foov
00000004e4b7903c I _head_libFoo_so
00000004e4b71750 T Foo()
00000004e4b793ec I libFoo_so_iname
根据我的理解,这意味着 Foo()
以二进制形式包含在 libBar.so 中,即 Foo()
的编译二进制内容存在于 libBar 中。所以。
这与我根据真正的 Linux 机器上的行为在脑海中得到的画面有点不同。我认为每个 .so
都是“独立的”二进制代码,就像一个 .o
文件,或者一个由以下内容组成的 .a
文件只有一个目标文件。
我想我无法理解的是 Cygwin(或 g++ 5.4)的行为是说 library 不能有未解析的符号——这感觉与很多以前的经验在我心中根深蒂固。我知道可执行文件不能有 Unresolved 问题,但是库应该能够有 Unresolved 问题,对吗?毕竟,您不能执行 一个库——它没有main()
。我确信 static 库可能有 Unresolved 问题,我认为共享库和静态库之间的区别在于它们的代码是否在链接时添加到可执行二进制文件,或者是否它们的代码由可执行文件在运行时查找。
赞赏社区可以在这里流出的进一步清晰度。谢谢。
最佳答案
我认为这比 cygwin 与 Linux 更像是 g++ 5.4 与 4.4(顺便说一下,这是一个很大的区别)。
共享对象与目标文件截然不同。在某些方面,它更像是一个可执行文件。 5.4 使用的模型是,当它链接共享对象时,它不需要拥有所有符号的拷贝,但它需要告诉运行时加载器加载器加载哪些共享对象可以找到中剩余的符号。我建议:
g++ -shared -o libBar.so Bar.o libFoo.so
(或者,您可以根据需要使用 -lFoo
,但是您需要正确设置库路径)。
更有趣的问题是为什么 g++ 4.4 可以按原样工作:我不知道。
为什么nm
在libBar.so
中显示Foo
?
Foo()
不是包含在 libBar.so
中的二进制文件。以下是非常方便的波浪形和近似值。如果你想要更准确,你将不得不阅读加载程序和可执行文件的格式。
libBar.so
的汇编器看起来像这样:
Bar():
CALL 000000 # The zeros are a blank that will be filled in by
# the loader with the address of Foo
PUSH "Greetings from Bar()!"
PUSH 000000 # Blank to be filled with address of std::cout
CALL 000000 # Blank to be filled with address of
# std::ostream::operator<<(const char*)
... etc
然后在 libBar.so
的其他地方会有这样的部分:
Bar+1 Foo
Bar+9 std::cout
Bar+11 std::ostream::operator<<(const char *)
它告诉加载程序用 Foo
的地址填充 Bar+1
。最后会有一个部分说:
Foo libFoo.so
std::cout libc.so
std::ostream::operator<<(const char*) libc.so
它告诉加载器它可以在 libFoo.so
等中找到 Foo
。这是 nm
报告的最后一部分。
关于c++ - g++ Cygwin/Linux 或版本差异,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39159681/