使用标准库(静态)编译静态库链接

6
我正在尝试编译一个静态库(我们称之为library.a)。这个库使用标准库的资源。有一些方法可以使库静态链接到标准库。
我已经证明了类似以下的事情:
g++ -c library -static-libstdc++ -o library.o
ar rcs library.o library.a

但如果我这样做,就没有与标准库的链接。
然后我就证明了这个方法:
g++ library -static-stdlib -o library.o
ar rcs library.o library.a

但是请问我添加一个主函数。

是否有可能通过静态链接也链接标准库(如std::string、std::vector、std::cin等)来创建静态库。

谢谢 :)


据我所知,标准库默认已链接。 - Guillaume Racicot
虽然,如果你想静态链接而不是动态链接,请参考这个链接:https://dev59.com/dF8e5IYBdhLWcg3wFnPm - Guillaume Racicot
问题在于我正在对C++代码进行包装,以便能够在C中使用,在C中我无法使用标准的C++库。唯一的方法是在库中包含我从标准库中使用的函数。我没有程序。要编译代码,我应该制作一个程序并删除g++参数“-c”,但如果我这样做,我必须将主函数放入代码中。然后我就不能将其用作库。 - Adria Ciurana
你正在编译的源文件叫做library还是library.c?你应该指定带有扩展名的文件名。 - cup
1个回答

21

有没有可能通过静态链接标准库来创建一个静态库?

不可能,因为你不能将任何东西链接到静态库中。你只能将其链接到由链接器生成的文件中。否则,在其生产过程中不涉及任何链接。

链接器可以生成两种类型的文件:程序和共享库。程序和共享库类似:它们都由可执行代码组成,程序加载器可以将其加载到进程中。它们与静态库不相似。静态库是由归档程序ar生成的一组对象文件 - 一个存档文件,链接器可以从中提取需要完成程序或共享库链接的文件。

像这样的命令:

g++ -c library.cpp -static-libstdc++ -o library.o

只编译library.cpplibrary.o,并忽略链接选项-static-libstdc++, 因为-c的意思是仅编译,不链接

您真正的问题在于您稍后的评论中才被揭示1:

问题在于我正在编写一个C ++代码包装器以便在C中使用它, 在C中,我不能使用标准C ++库。唯一的方法是在库中包含我从标准库中使用的函数。

现在,由于共享库是链接器可以生成的内容,所以您可以将libstdc ++静态链接到共享库中。 因此,您可以将C包装器库制作成共享库,而不是静态库。

看起来您已经知道如何在C API中包装C ++代码。因此,让我们编写这样的C包装器,将输入缓冲区中的前N个字符反转到输出缓冲区中, 使用标准C ++库完成所有工作:

reverse.h

#ifndef REVERSE_H
#define REVERSE_H

#ifdef __cplusplus
extern "C" {
#endif

void reverse(char const * in, char * out, unsigned len);

#ifdef __cplusplus
} // extern "C"
#endif

reverse.cpp

#include "reverse.h"
#include <string>
#include <algorithm>

extern "C" {

void reverse(char const * in, char * out, unsigned len)
{
    std::string s{in,len};
    std::reverse(s.begin(),s.end());
    std::copy(s.begin(),s.end(),out);
}

} // extern "C"

接下来是调用reverse函数的C程序:

main.c

#include <stdio.h>
#include <reverse.h>

int main()
{
    char in[] = "dlrow olleH";
    reverse(in,in,sizeof(in) - 1);
    puts(in);
    return 0;
}

现在我们将构建共享的包装库。
编译我们的一个库源文件:
$ g++ -fPIC -Wall -Wextra -std=c++11 -c reverse.cpp

请注意 -fPIC。我们将链接到共享库中的所有目标文件都必须是位置无关代码。由于我们只编译一个源文件 -c reverse.cpp,因此可以跳过 -o 选项并接受默认值 -o reverse.o

然后链接我们的共享库:

$ g++ -shared -o libreverse.so reverse.o -static-libstdc++

现在共享库是./libreverse.so,它没有运行时依赖于libstdc++
$ ldd libreverse.so 
    linux-vdso.so.1 =>  (0x00007ffca98c9000)
    libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fa178862000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa178498000)
    /lib64/ld-linux-x86-64.so.2 (0x000055d17658c000)

接下来编译我们的C程序源代码文件:

$ gcc -Wall -Wextra -I. -c main.c

最后,将程序与libreverse链接:

$ gcc -o prog main.o -L. -lreverse

这个程序 ./prog 在运行时依赖共享库 libreverse.so。 因为它不是静态库,所以无法静态链接 libreverse.so。 但是,libreverse.so 已经使用 libstdc++.a 静态链接,并且具有 C API。
如果 libreverse.so 是一个重要的库,我们现在需要做的是将其安装到运行时加载器的标准搜索目录之一,以便程序可以在运行时自动加载它。我们可以这样做:
$ sudo cp libreverse.so /usr/local/lib/
$ sudo ldconfig

但是由于 libreverse.so 只是一个玩具库,我们不会为它修改我们的系统。相反,我们只需像这样运行 ./prog

$ export LD_LIBRARY_PATH=. # For now, tell the loader to look for libraries here
$ ./prog
Hello world

所以,您可以使用C API和C++内部制作包装库,并通过使包装库成为共享库并静态链接libstdc ++来实现。 但是为什么要这样做呢? 似乎您想要这样做是因为您认为“在C中我无法使用标准的C ++库”。
这是一个误解。以下是如何使用静态包装库构建相同程序的方法。
首先创建您的静态包装库。
$ rm libreverse.so  # Better delete the old shared one just to avoid confusion.
$ g++ -Wall -Wextra -std=c++11 -c reverse.cpp 
$ ar rcs libreverse.a reverse.o

然后编译您的C源代码:
$ gcc -Wall -Wextra -I. -c main.c

在这一步,您有一个对象文件main.o,一个静态库libreverse.a,其中只包含reverse.o,要创建prog,您只需要将main.olibreverse.a(reverse.o)与标准C库和标准C++库链接在一起。没有关于C不允许您这样做的问题。当您编译main.c时,就已经完成了C语言的部分。这些对象文件和库将由系统链接器链接,它不会知道或关心它们是从哪种语言编译而来的。

因此,您可以通过g++调用链接器,如下所示:

$ g++ -o prog main.o -L. -lreverse

现在你有一个能够做到这一点的程序:

$ ./prog
Hello world

或者你可以通过gcc调用链接器,例如:

$ gcc -o prog main.o -L. -lreverse -lstdc++

这只是相同的链接,具有相同的结果:

$ ./prog
Hello world

正如您所见,使用g++链接与使用gcc链接的唯一区别在于,g++会自动将标准C库和标准C++库添加到链接器的输入中,而gcc不会添加C++库,因此您需要手动添加。
实际上,如果您的计算机上安装了GNU Fortran,您也可以使用它进行相同的链接:
$ $ gfortran -o prog main.o -L. -lreverse -lstdc++
$ ./prog
Hello world 

尽管 prog 中没有使用 Fortran,但这并不影响。
C 不会阻止您将 libstdc++ 与任何东西链接起来。您可能希望将 libstdc++ 静态链接到具有 C API 的库中的一个合理但不太可能的理性动机是,您希望其他人能够在没有 libstdc++ 或没有与您的ABI 兼容的系统上链接此库中的程序。如果这是您的动机,请像上面所示将您的库设置为共享库。否则,只需将 libstdc++ 与任何依赖它的程序链接即可。
[1] 在 Stackoverflow 上,请始终在问题中陈述您的真实问题。请参见 The XY Problem

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接