0

我已阅读有关共享库和静态库的相关教程,例如:

“使用 gnu 编译器 [gcc] 创建共享和静态库”

“静态、共享动态和可加载的 Linux 库”

然而,不幸的是,他们使用的所有示例都是一个函数一个 .c 文件。

我有两个问题:

(1) 如果我有一个文件有两个以上的功能,例如example1.c

void ctest11(int *i)
{ *i = 5; }

void ctest12(int *i)
{ *i = 5; }

将exmaple1.c编译成libexample1.so后,可以在里面调用ctest11和ctest12吗?

(2) 如果我有一个文件有两个以上的函数,其中一个是主函数,比如example2.c

void ctest21(int *i)
{ *i = 5; }

void main(int *i)
{ *i = 5; }

将exmaple2.c编译成libexample2.so后,和编译一个只有ctest21函数的.c文件一样吗?

(3)如果我有一个文件example3.c和exmaple4.c example3.c中的函数会使用example4.c中的函数 例如:example3.c

void ctest31(int *i)
{ *i = ctest41(2,3); }

例子4.c

int ctest41(int a, int b)
{ return a+b; }

当我将example2.c和example3.c编译为libexample23.so时,我可以同时调用ctest31和ctest41吗?

但是如果gcc example2.c example3.o to libexample2.so,我想我只能调用ctest31吧?

4

2 回答 2

1

您应该查看(并构建)一些现有的免费软件库,对其进行编译,并研究其代码和构建过程。

一般来说,一个共享对象可以由几个 C 源文件src1sh.csrc2sh.c......很多时候,编译是由一个构建器程序驱动的,通常是GNU make

首先,您需要将共享对象的每个源文件编译为与位置无关的代码(PIC),例如

gcc -Wall -fPIC src1sh.c -c -o src1sh.pic.o
gcc -Wall -fPIC src2sh.c -c -o src2sh.pic.o

您可能希望添加-ggcc标志以进行调试。一旦你的程序和共享对象没有错误,因为你已经用gdb和调试了它们valgrind,传递-O2gcc它们以优化它们。

然后您需要将所有这些 PIC 对象文件链接到单个共享对象(*.so文件)中,例如

gcc -shared src1sh.pic.o src2sh.pic.o -o shared.so

如果您的意图是创建一个共享库,请调用它,lib*.so例如libfoo.so,并将其作为使用共享库的链接命令的-lfoo标志。gcc

请注意,链接共享对象也可能链接其他共享库,因此您可以这样做

gcc -shared src1sh.pic.o src2sh.pic.o -lsome -o shared.so

将一些链接libsome.so到您的shared.so

您通常不会编译包含 a 的共享对象main(请记住,这main是一个非常特殊的函数,在Ccrt*.o标准中专门描述,并从链接gcc到每个程序的启动代码中调用);这几乎是无意义的(就像你的libexample2.so)。您main在您的程序中定义(并且您的程序可执行文件不需要 PIC 代码)。如果您的程序由源文件制成src1pr.c并且src2pr.c(定义main)您首先将它们编译为

gcc -Wall src1pr.c -c -o src1pr.o
gcc -Wall src2pr.c -c -o src2pr.o

你把它们都用例如

gcc src1pr.o src2pr.o -o prog -lshared

where-lshared指的是一个共享库libshared.so(您可能希望编译和链接您的程序文件以-g获取调试信息,并且您可能希望-I为包含目录传递其他标志,并-L为库目录传递标志,例如-L.在当前目录中搜索库.. .)

有一种方法可以在运行时动态链接一些共享对象,特别是对于拥有plugins。然后,您想为此使用dlopen & dlsymfunctons(并且您通常希望将主程序与-rdynamic标志链接)。

您可以(从您的程序)调用共享对象内的任何可见函数。您可能想要使用visibility 函数属性来限制共享对象内某些函数的可见性。您可能希望稍后使用该constructor属性,以便在初始化时尽早调用共享对象内的函数(如果它是插件,则在其dlopen时间)。

阅读Program Library HowtoLevine 的“Linkers and Loaders”一书了解更多信息。Linux 共享对象(以及可重定位对象*.o和可执行二进制文件)采用可执行和可链接格式(ELF 是行业标准)。应用程序二进制接口(尤其是处理器的 ABI 补充,例如AMD64 ABI 补充)中描述了一些进一步的细节。

PS。你真的想要一个像 GNU make这样的构建器来组合所有这些步骤,所以请阅读它的文档。你可能想通过-vgcc了解它在做什么......

于 2013-04-10T05:33:25.213 回答
0

感谢 Basile 的精彩解释。据我了解,与我的问题有关,

(a) 对于我的第一个问题 (1),一个目标文件中有多个函数。我可以在 libexample1.so 中调用 ctest11 和 ctest12

我可以设置 libexample1.so 中函数的可见性。

(b) 对于我的第三个问题 (3),第一个场景与从两个目标文件创建一个库有关。我可以调用文件中的任何函数。第二个场景与创建一个库并与另一个库链接有关。我可以调用库中的任何函数,包括链接库。

(3) 我还是不明白 main 函数的情况。你说,“你通常不会编译包含 main 的共享对象;这几乎是无意义的(比如你的 libexample2.so)。”

我知道这是无稽之谈。但是如果我不想改变程序文件,想把它编译成一个库,比如说在example2.c中,我把它编译成example2.so,并想调用函数ctest21。我可以这样做吗?

例子2.c

void ctest21(int *i)
{ *i = 5; }

void main(int *i)
{ *i = 5; }

我把它编译成一个库。

gcc -fPIC -g -c -Wall example2.c
gcc -shared -o libexample2.so example2.o 

我想我可以在 example2.o 中调用 crest21 函数。但是主要功能是没用的。我的理解正确吗?

于 2013-04-10T16:37:37.763 回答