如何优化共享库的大小?
假设我们有巨大的静态库,其中包含许多不需要的功能(在下面的示例中,我们有库 lib1.a
和 lib2.a
,其中包含不需要的函数 g1()< /code> 和
f2()
)。
我们希望使用一些导出方法来构建共享库,这些方法仅使用该巨大库中的少数函数/类。请参见下面的示例:我们要导出函数 foo()
。
问题
- 我们能否告诉链接器 (
ld
) 我们想要导出哪些函数/方法(就像我们在 Windows 中对 DLL 所做的那样)? - 链接器可以解决依赖关系并删除不需要的函数/方法吗?或者还有其他方法可以解决问题吗?
- 如果您有解决方案,请为下面的示例编写修复程序。
示例
文件1.h
:
int f1( int n );
int g1( int n );
文件2.h
:
int f2( int n );
文件foo.cpp
:
#include "1.h"
#include "2.h"
int foo( int n )
{
return f1( n );
}
文件1. cpp
:
int f1( int n ) { return n; }
int g1( int n ) { return n; }
文件2.cpp
:
int f2( int n ) { return n; }
文件makefile
:
CXXFLAGS = -g -I. -Wall -Wno-sign-compare
all: prepare libFoo.so
clean:
rm -f obj/*.a obj/*.o res/*.so
prepare:
mkdir -p src
mkdir -p obj
mkdir -p res
lib1.a lib2.a: lib%.a: %.o
ar r obj/$@ obj/$*.o
1.o 2.o foo.o: %.o:
g++ $(CXXFLAGS) -c -o obj/$@ src/$*.cpp
libFoo.so: lib1.a lib2.a foo.o
ld -shared -o res/libFoo.so obj/foo.o -Lobj -l1 -l2
在创建目标all
之后,我们有nm res/libFoo。 so
:
...
000001d8 T _Z2f1i
0000020e T _Z2g1i
000001c4 T _Z3fooi
...
所以ld
已被删除2.o
目标文件根据目标文件之间的依赖关系。但没有从 1.o
中删除函数 g1()
。
Say we have huge static libraries with lots of unneeded features (in the example below we have libraries lib1.a
and lib2.a
with unneeded functions g1()
and f2()
).
We want to build shared library with a few exported methods which use only a few functions/classes from that huge libraries. See example below: we want to export function foo()
.
QUESTIONS
- Can we tell linker (
ld
) which functions/methods we want to export (like we do it for DLL in Windows)? - Can linker resolve dependecies and remove unneeded functions/methods? Or is there any other way to solve the problem?
- If you have a solution, please, write the fixes for the example below.
EXAMPLE
File 1.h
:
int f1( int n );
int g1( int n );
File 2.h
:
int f2( int n );
File foo.cpp
:
#include "1.h"
#include "2.h"
int foo( int n )
{
return f1( n );
}
File 1.cpp
:
int f1( int n ) { return n; }
int g1( int n ) { return n; }
File 2.cpp
:
int f2( int n ) { return n; }
File makefile
:
CXXFLAGS = -g -I. -Wall -Wno-sign-compare
all: prepare libFoo.so
clean:
rm -f obj/*.a obj/*.o res/*.so
prepare:
mkdir -p src
mkdir -p obj
mkdir -p res
lib1.a lib2.a: lib%.a: %.o
ar r obj/$@ obj/$*.o
1.o 2.o foo.o: %.o:
g++ $(CXXFLAGS) -c -o obj/$@ src/$*.cpp
libFoo.so: lib1.a lib2.a foo.o
ld -shared -o res/libFoo.so obj/foo.o -Lobj -l1 -l2
And after making target all
we have nm res/libFoo.so
:
...
000001d8 T _Z2f1i
0000020e T _Z2g1i
000001c4 T _Z3fooi
...
So ld
has removed 2.o
object file according to dependencies between object files. But didn't remove function g1()
from 1.o
.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
也许链接时间优化(即
-flto< GCC 4.6 的 /code>
选项)有帮助吗?
还有 函数属性
__attribute__ ((visibility ("hidden ")))
和/或__attribute__ ((weak))
并且进入
*.so
共享对象的代码应使用-fPIC< 进行编译/代码>
Perhaps Link Time Optimization (i.e.
-flto
option to GCC 4.6) could help?And also the function attribute
__attribute__ ((visibility ("hidden")))
and/or__attribute__ ((weak))
And code going into
*.so
shared objects should be compiled with-fPIC
首先,正如 Basile 指出的,您应该在构建
lib{1,2}.a
时添加-fPIC
标志。其次,您可以链接所有
1.o
,因为这就是 UNIX 链接器的方式工作。最简单的解决方案(比使用
-flto
简单得多)是通过将-Wl,--gc-sections
添加到libFoo.so< 来打开链接器垃圾收集/code> 链接线,并使用
-ffunction-sections -fdata-sections
构建lib{1,2}.a
。这将有效地将每个功能变成自己单独的“书”。First, as Basile pointed out, you should add
-fPIC
flag when buildinglib{1,2}.a
.Second, you get all of
1.o
linked in because that's how UNIX linkers work.The simplest solution (much simpler than using
-flto
) is to turn on linker garbage collection by adding-Wl,--gc-sections
tolibFoo.so
link line, and to buildlib{1,2}.a
with-ffunction-sections -fdata-sections
. This will effectively turn each function into its own separate "book".我相信在
*.so
中不使用-fPIC
会使它们包含大量由ld.so
处理的重定位指令,所以这是首先要尝试的事情。-flto
应该在编译和链接时都使用,并增加编译时间。添加属性应该逐个函数完成,并且会花费开发人员大量时间(因为您需要选择哪些函数需要它们)。如果代码真的很大(例如超过 100KLOC 的源代码),您可以考虑编写 GCC 插件,或者最好是 GCC MELT 扩展来自定义 GCC 4.6 编译器以自动执行此类任务,但这需要一些工作(几周,而不是几小时)。我是 GCC MELT 的主要作者(如果对您有帮助的话,我什至会说一些糟糕的俄语),所以我很乐意帮助您使用 MELT。但就您而言,只有当您的库足够大,足以证明需要花费一周以上的时间使用 MELT 自定义 GCC 时,这是值得的。
I believe that not using
-fPIC
in*.so
make them contain a big lot of relocation instructions processed byld.so
, so this is the first thing to try.-flto
should be used both when compiling and when linking, and increase the compilation time. Adding the attributes should be done function by function, and would take a lot of your developer's time (because you need to choose which functions need them). If the code is really big (e.g. more than 100KLOC of source code), you could consider coding a GCC plugin or preferably a GCC MELT extension to customize your GCC 4.6 compiler to automate such tasks, but this require some work (weeks, not hours).I am the main author of GCC MELT (and I even speak some bad Russian if it helps you), so I would be delighted to help you using MELT. But in your case, it is worthwhile only if your library is big enough to justify working more than a week on customizing GCC with MELT.