有人可以解释两个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
>
On 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 ++版本,看看是否是问题的原因(在其中一个盒子上我不会&#39无论如何都有权这样做。
我一直认为目标文件,以及扩展名,库(静态或共享)都允许有未解析的符号,并且仅在链接可执行文件时才需要所有符号解决。这个概念在几年的开发经验中也是如此,所以我对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
都是&#34;独立的&#34;二进制代码,就像.o
文件或只包含一个目标文件的.a
文件一样。
我想我无法解决的问题是Cygwin(或g ++ 5.4)行为是说库不能有未解析的符号 - 这感觉与许多以前的经验已经根深蒂固。我知道一个可执行文件不能解决,但是一个库应该能够解决,对吧?毕竟,您无法执行库 - 它没有main()
。我确信 static 库可能没有解析,我认为共享库和静态库之间的区别在于它们的代码是否是链接时已添加到可执行二进制文件,或者是否他们的代码是由可执行文件查找的运行时。
进一步明确社区可以在这里流传。谢谢。
答案 0 :(得分:2)
我认为这比ggg 5.4 vs 4.4(顺便说一句,这是一个很大的不同)比cygwin vs Linux更多。
共享对象是与目标文件完全不同的野兽。在某些方面,它更像是可执行文件。 5.4正在使用的模型是,当它链接共享对象时,它不需要拥有其手中所有符号的副本,但它需要告诉运行时加载器哪些共享对象加载程序可以在中找到剩余的符号。我建议:
g++ -shared -o libBar.so Bar.o libFoo.so
(或者,如果您愿意,可以使用-lFoo
,但是您需要正确设置库路径。)
更有趣的问题是为什么g ++ 4.4按原样运作:我不知道。
nm
在Foo
中显示libBar.so
? 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 *)
告诉加载程序使用Bar+1
的地址填写Foo
。最后会有一节说:
Foo libFoo.so
std::cout libc.so
std::ostream::operator<<(const char*) libc.so
告诉加载程序它可以在Foo
等中找到libFoo.so
。这是nm
报告的最后一部分。
答案 1 :(得分:1)
Windows上没有共享对象。有DLL。 DLL的行为与Un * x共享对象不同。特别是,不允许使用未定义的符号。 Windows中用于DLL和可执行文件的PE(可移植可执行文件)格式无法表达它们。谷歌“pe dll”“未定义的符号”,周围有很多信息。
Cygwin非常努力地从程序员那里隐藏Windows的特性,但它只能做很多事情。
当您将libBar.so
与libFoo.so
相关联时,来自libFoo.so
的代码 <{1}}。这将破坏在运行时加载DLL的目的。相反,链接过程为从其他DLL导入的所有函数创建存根。这些不是真正的功能。您可以通过查找字符串来确保确实如此:
libBar.do