g++ 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-savvy 来知道如何在任何一个盒子上安装 different/matching 版本的 g++ 看看这是否是问题的原因(在其中一个盒子上我会无论如何,我有幸这样做。
我一直认为目标文件以及扩展库(无论是静态的还是共享的)都允许具有未解析的符号,并且只有在 link 运行一个可执行文件时所有符号都需要解析得到解决。在多年的开发经验中,这个概念也几乎成立,所以我对 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
都是 "standalone" 二进制代码,就像一个 .o
文件,或者一个仅由一个目标文件组成的 .a
文件。
我想我无法理解的是 Cygwin(或 g++ 5.4)的行为是说 library 不能有未解析的符号 - 这感觉相反许多先前的经验在我心中根深蒂固。我知道可执行文件不能有未解决的问题,但是库应该能够有未解决的问题,对吗?毕竟,您无法 执行 库 - 它没有 main()
。我确定 static 库可以有未解决的问题,我认为共享库和静态库之间的区别在于它们的代码是否 link-time added 到可执行二进制文件,或者它们的代码是否由可执行文件在运行时查找。
感谢社区可以在这里表达的进一步清晰。谢谢。
我认为这比 cygwin 与 Linux.
更多的是 g++ 5.4 与 4.4(顺便说一句,这是一个很大的区别)
共享对象与目标文件截然不同。在某些方面,它更像是一个可执行文件。 5.4使用的模型是,当它链接共享对象时,它不需要拥有它手中所有符号的副本,但是它需要告诉运行时间loader which shared objects 加载器可以在中找到剩余的符号。我建议:
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(可移植可执行文件)格式无法表达它们。 Google "pe dll" "undefined symbols", 周围有很多信息。
Cygwin 非常努力地向程序员隐藏 Windows 的特性,但它能做的只有这么多。
当您 link libBar.so
反对 libFoo.so
时,来自 libFoo.so
的代码 未 物理包含在 libBar.do
.这将破坏在 运行 时间加载 DLL 的目的。相反,linking 进程会为从其他 DLL 导入的所有函数创建 stubs。这些不是真正的功能。您可以通过查找字符串来确保确实如此:
% strings libFoo.so | grep "Greetings from"
Greetings from Foo
% strings libBar.so | grep "Greetings from"
Greetings from Bar
有人可以解释 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 来知道如何在任何一个盒子上安装 different/matching 版本的 g++ 看看这是否是问题的原因(在其中一个盒子上我会无论如何,我有幸这样做。
我一直认为目标文件以及扩展库(无论是静态的还是共享的)都允许具有未解析的符号,并且只有在 link 运行一个可执行文件时所有符号都需要解析得到解决。在多年的开发经验中,这个概念也几乎成立,所以我对 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
都是 "standalone" 二进制代码,就像一个 .o
文件,或者一个仅由一个目标文件组成的 .a
文件。
我想我无法理解的是 Cygwin(或 g++ 5.4)的行为是说 library 不能有未解析的符号 - 这感觉相反许多先前的经验在我心中根深蒂固。我知道可执行文件不能有未解决的问题,但是库应该能够有未解决的问题,对吗?毕竟,您无法 执行 库 - 它没有 main()
。我确定 static 库可以有未解决的问题,我认为共享库和静态库之间的区别在于它们的代码是否 link-time added 到可执行二进制文件,或者它们的代码是否由可执行文件在运行时查找。
感谢社区可以在这里表达的进一步清晰。谢谢。
我认为这比 cygwin 与 Linux.
更多的是 g++ 5.4 与 4.4(顺便说一句,这是一个很大的区别)共享对象与目标文件截然不同。在某些方面,它更像是一个可执行文件。 5.4使用的模型是,当它链接共享对象时,它不需要拥有它手中所有符号的副本,但是它需要告诉运行时间loader which shared objects 加载器可以在中找到剩余的符号。我建议:
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(可移植可执行文件)格式无法表达它们。 Google "pe dll" "undefined symbols", 周围有很多信息。
Cygwin 非常努力地向程序员隐藏 Windows 的特性,但它能做的只有这么多。
当您 link libBar.so
反对 libFoo.so
时,来自 libFoo.so
的代码 未 物理包含在 libBar.do
.这将破坏在 运行 时间加载 DLL 的目的。相反,linking 进程会为从其他 DLL 导入的所有函数创建 stubs。这些不是真正的功能。您可以通过查找字符串来确保确实如此:
% strings libFoo.so | grep "Greetings from"
Greetings from Foo
% strings libBar.so | grep "Greetings from"
Greetings from Bar