Cygwin/Linux或版本差异
g++ Cygwin/Linux or version discrepancy
谁能解释一下两个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知识来知道如何在两个机器上安装不同/匹配的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.so.
这与我脑海中基于真正的Linux机器上的行为的画面有点不同。我认为每个.so
将是"独立的"二进制代码,就像.o
文件,或.a
文件,只由一个对象文件组成。
我想我脑子里有麻烦的是Cygwin(或g++ 5.4)的行为说库不能有未解析的符号——这感觉与我以前根深蒂固的经验相反。我知道可执行文件不能有未解析文件,但库应该可以有未解析文件,对吧?毕竟,你不能执行一个库——它没有main()
。我确信静态库可能有未解析的,我认为共享库和静态库之间的区别在于它们的代码是链接时添加到可执行二进制文件中的,还是它们的代码是可执行文件在运行时查找的。
感谢社区在这里提供的进一步澄清。谢谢你。
我认为这比cygwin vs Linux更像是g++ 5.4 vs 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
报告的最后一节。
Windows上没有共享对象。有dll。dll的行为不同于Un*x共享对象。特别是它们不允许有未定义的符号。Windows中用于dll和可执行文件的PE(可移植可执行文件)格式只是没有一种表达它们的方法。谷歌一下"pe dll" "未定义的符号",有很多信息。
Cygwin非常努力地向程序员隐藏Windows的特性,但它能做的只有这么多。
当你链接libBar.so
和libFoo.so
时,来自libFoo.so
的代码是而不是物理上包含在libBar.do
中的。这将破坏在运行时加载dll的目的。相反,链接过程为从其他dll导入的所有函数创建存根。这些不是真正的函数。您可以通过查找字符串
% strings libFoo.so | grep "Greetings from"
Greetings from Foo
% strings libBar.so | grep "Greetings from"
Greetings from Bar
- 在Linux Mint上使用多个版本的libboost
- 一个32位版本的应用程序,建立在CentOS 6 x64上,当在较新的Linux上启动时,在"dl_itera
- 在Mac OS X Mountain Lion(或更高版本)和Linux上使用X11/Xlib.h
- 如何链接到与 Linux 中不同版本的 boost 链接的共享库
- 如何在QT Creator开源版本中为嵌入式Linux交叉编译qt
- Linux 中 gcc 4.3 版本的编译问题
- 我如何在 Linux 中获取程序版本
- /usr/lib/x86_64-linux-gnu/libstdc++.so.6:未找到版本 CXXABI_1.3.8'
- 在旧版本的linux上运行我的代码
- 在我的Linux中调用了哪个版本的close(),来自posix lib或内核
- 在 VS 和 Linux 版本中包含相同的 #define
- 基于内核版本 X.XX 构建的 C++ Linux 应用是否会在早期版本上运行
- 你如何找到你的 linux 机器上安装了什么版本的 libstdc++ 库
- 无法在 Linux 版本中更改文件类型
- 如何使用linux系统调用编写一个c++程序,该调用提供有关可用内存、已用内存和内核版本的输出
- 如何在c++中获得包版本?(linux)
- 如何在 Linux 中检查正在运行的进程版本读取"ps aux"输出
- 如何将 C/C++ 应用程序移植到旧版 Linux 内核版本
- 在基于HPC的远程Linux上切换gcc版本(没有root权限)
- 构建运行在不同版本linux上的cpp程序