当一个C库被静态链接时,整个库是否被添加到可执行文件中?
背景信息:我试图比较两段执行一些数值计算的代码的内存需求。为此,我将编译的C代码的大小与静态链接的数学库进行比较 然而,我发现了一些奇怪的结果,这似乎表明整个库正在被添加。我在下面描述一个MWE当一个C库被静态链接时,整个库是否被添加到可执行文件中?,c,linker,static-libraries,static-linking,C,Linker,Static Libraries,Static Linking,背景信息:我试图比较两段执行一些数值计算的代码的内存需求。为此,我将编译的C代码的大小与静态链接的数学库进行比较 然而,我发现了一些奇怪的结果,这似乎表明整个库正在被添加。我在下面描述一个MWE // Program ex1.c # include<math.h> void main (void) { float a = exp(2); } 如果程序1的编译对象只包含exp()的代码,而程序2的编译对象包含exp()、pow()和sin()的代码,那么第二个对象将比第一个
// Program ex1.c
# include<math.h>
void main (void)
{
float a = exp(2);
}
如果程序1的编译对象只包含exp()的代码,而程序2的编译对象包含exp()、pow()和sin()的代码,那么第二个对象将比第一个对象大。但这两个对象的大小相同,为912.6KB
为什么会发生这种情况?有没有办法确保只将所需的代码部分添加到对象中?静态库是对象文件的存档,而在静态库中链接只会添加存档中解析至少一个未定义引用的对象文件成员 为了确保只添加所需的代码,静态库需要由小对象文件组成,最好每个文件中有一个导出的全局文件 除此之外,如果使用
-fffunction节
/-fdata节
编译库,然后将-gc节
传递给链接器,则可以实现类似的效果
-ffunction sections-fdata sections
方法基本上等同于一种全局的每个源方法,但是使用源文件来建立边界更灵活,因为有时可能需要将内容分组在一起(较大的翻译单元可能会导致更紧凑和优化的代码)
无论如何,在您的情况下(lib不在您的控制之下),您所能尝试的就是-Wl,--gc sections
(gcc的-Wl
选项以gcc应该传递给链接器的内容为前缀)
以你的例子和glibc为例,我可以从原来的849KiB减去大约41KiB
不太令人印象深刻,但glibc的构建并没有考虑静态链接。
使用libc库可以获得更好的结果,例如
现在这更好了,但您可能想知道为什么示例1和2的尺寸相同
如果添加-Wl,--print map
,您会发现根本不包括musl libc中的相关对象文件
无论哪种情况。原因是,gcc知道这些标准函数,它通过插入操作码而不是生成的函数调用进行欺骗。通过添加由另一个翻译单元提供的间接层,您可以在某种程度上击败gcc的欺骗
c:
double call1(double(*X)(double A), double A) { return X(A); }
double call2(double(*X)(double A,double B), double A, double B){ return X(A,B); }
例1.c
-这两个例子之间有一个明显的区别,这可能要归功于musl的组成方式
这样,在静态链接时,添加的代码不会超过(或不超过)相关的引用代码。只有正在使用的函数才会被“添加”。除非你的链接器疯了。有关详细信息,请查看可以生成的
map
文件。@EugeneSh。谢谢你的建议。我已经生成了地图文件,但是它有很多我不理解的东西。你能提供一些具体的建议来更好地理解地图文件吗?如果你使用优化标志,你会发现有什么不同吗?但是我猜它不会链接任何东西,因为你不使用浮动。它们可能只是被忽略了。@Fredrik使用-O3时没有任何区别。请注意,共享公共(静态私有)函数的库函数通常在同一源/对象文件中分组在一起。例如,trig函数如sin()
和cos()
通常使用相同的公共低级函数,因此调用其中一个函数将导致将两者链接到可执行文件中。这通常不是什么大问题,尤其是当公共函数调用公共私有函数时,参数稍有改变。
gcc -static -o ex1static.out ex1.c -lm
gcc -static -o ex2static.out ex2.c -lm
for ex in ex{1,2}.c; do for flg in '' -Wl,--gc-sections; do echo "$ex $flg"; musl-gcc -O0 $ex -static -lm $flg call.c && \ls -l a.out ; done ; done
ex1.c
-rwxrwx--- 1 pjmp pjmp 8064 Jun 29 19:11 a.out
ex1.c -Wl,--gc-sections
-rwxrwx--- 1 pjmp pjmp 7744 Jun 29 19:11 a.out
ex2.c
-rwxrwx--- 1 pjmp pjmp 8064 Jun 29 19:11 a.out
ex2.c -Wl,--gc-sections
-rwxrwx--- 1 pjmp pjmp 7744 Jun 29 19:11 a.out
double call1(double(*X)(double A), double A) { return X(A); }
double call2(double(*X)(double A,double B), double A, double B){ return X(A,B); }
# include<math.h>
double call1(double(*X)(double A), double A);
double call2(double(*X)(double A,double B), double A, double B);
int main (void)
{
float a = call1(exp,2);
}
# include <math.h>
double call1(double(*X)(double A), double A);
double call2(double(*X)(double A,double B), double A, double B);
int main(void)
{
float a = call1(exp,(2));
float b = call2(pow,3,4);
float c = call1(sin,(3.14159));
}
Ex1.c
-rwxrwx--- 1 pjmp pjmp 8216 Jun 29 19:15 a.out
Ex1.c -Wl,--gc-sections
-rwxrwx--- 1 pjmp pjmp 7984 Jun 29 19:15 a.out
Ex2.c
-rwxrwx--- 1 pjmp pjmp 17088 Jun 29 19:15 a.out
Ex2.c -Wl,--gc-sections
-rwxrwx--- 1 pjmp pjmp 16856 Jun 29 19:15 a.out