C++ g++;Cygwin/Linux或版本差异

C++ g++;Cygwin/Linux或版本差异,c++,linux,linker,cygwin,shared-libraries,C++,Linux,Linker,Cygwin,Shared Libraries,有人能解释一下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; } #include "Bar.h"

有人能解释一下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;
}
#include "Bar.h"
#include "Foo.h"
#include <iostream>

void Bar()
{
    Foo();
    std::cout << "Greetings from Bar()!" << std::endl;
}
Bar.cpp

#include "Foo.h"
#include <iostream>

void Foo()
{
    std::cout << "Greetings from Foo()!" << std::endl;
}
#include "Bar.h"
#include "Foo.h"
#include <iostream>

void Bar()
{
    Foo();
    std::cout << "Greetings from Bar()!" << std::endl;
}
关于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)的行为是说一个库不能有未解析的符号——这感觉与我之前的很多经验是背道而驰的。我知道一个可执行文件不能有unsolved,但是一个库应该有unsolved,对吗?毕竟,您不能执行库-它没有
main()
。我确信静态库可以有unsolved,我认为共享库和静态库之间的区别在于它们的代码是添加到可执行二进制文件的链接时间,还是它们的代码是可执行文件在运行时查找的


感谢社区在此提供的进一步澄清。谢谢。

我认为这比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的汇编程序。因此
看起来像:

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
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
Foo libFoo.so
std::cout libc.so

std::ostream::operator我认为这比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的汇编程序。因此
看起来像:

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
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
Foo libFoo.so
std::cout libc.so

std::ostream::operatorWindows上没有共享对象。有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

Windows上没有共享对象。有DLL。