假设我有以下文件:
libmy_static_lib.c:
#include <stdio.h>
void func1(void){
printf("func1() called from a static library\n");
}
void unused_func1(void){
printf("printing from the unused function1\n");
}
void unused_func2(void){
printf("printing from unused function2\n");
}
libmy_static_lib.h:
void func(void);
void unused_func1(void);
void unused_func2(void);
my_prog.c:
#include "libmy_static_lib.h"
#include <stdio.h>
void func_in_my_prog()
{
printf("in my prog\n");
func1();
}
以下是我链接库的方式:
# build the static library libmy_static_lib.a
gcc -fPIC -c -fdata-sections --function-sections -c libmy_static_lib.c -o libmy_static_lib.o
ar rcs libmy_static_lib.a libmy_static_lib.o
# build libmy_static_lib.a into a new shared library
gcc -fPIC -c ./my_prog.c -o ./my_prog.o
gcc -Wl,--gc-sections -shared -m64 -o libmy_shared_lib.so ./my_prog.o -L. -l:libmy_static_lib.a
libmy_static_lib.c中有2个未使用的功能,而this post则认为
gcc fdata-sections --function-sections
应为每个函数创建一个符号,并
gcc -Wl,--gc-sections
应在链接时删除未使用的符号
然而,当我跑
时nm libmy_shared_lib.so
显示这两个未使用的函数也链接到共享库中。
有关如何让gcc自动删除未使用的功能的任何建议吗?
编辑: 如果我将静态库直接链接到可执行文件,我可以使用gcc的上述选项来删除未使用的函数。但是,如果我将静态库链接到共享库,它不会删除未使用的函数。
答案 0 :(得分:3)
您可以使用版本脚本将入口点与-ffunction-sections
和--gc-sections
结合使用。
例如,请考虑此C文件(example.c
):
int
foo (void)
{
return 17;
}
int
bar (void)
{
return 251;
}
此version script,名为version.script
:
{
global: foo;
local: *;
};
编译并链接这样的来源:
gcc -Wl,--gc-sections -shared -ffunction-sections -Wl,--version-script=version.script example.c
如果您查看objdump -d --reloc a.out
的输出,您会注意到共享对象中只包含foo
,但bar
不包含。{/ p>
以这种方式删除函数时,链接器会考虑间接依赖性。例如,如果您将foo
转换为:
void *
foo (void)
{
extern int bar (void);
return bar;
}
链接器会将foo
和bar
放入共享对象,因为两者都是必需的,即使只导出bar
。
(显然,这不适用于所有平台,但ELF支持此功能。)
答案 1 :(得分:2)
您正在创建库,并且您的符号不是 static ,因此链接器不会删除任何全局符号是正常的。
此-gc-sections
选项专为可执行文件而设计。链接器从入口点(main
)开始,并发现函数调用。它标记了所使用的部分,并丢弃其他部分。
库没有1个入口点,它具有与全局符号一样多的入口点,这说明它无法清理符号。如果有人在他的程序中使用您的.h
文件并调用“未使用”的函数会怎么样?
要找出哪些功能未被“使用”,我建议您将void func_in_my_prog()
转换为int main()
(或将源复制到包含main()
的修改后的内容中) ,然后使用源创建可执行文件,并在链接时添加-Wl,-Map=mapfile.txt
选项以创建mapfile。
gcc -Wl,--gc-sections -Wl,--Map=mapfile.txt -fdata-sections -ffunction-sections libmy_static_lib.c my_prog.c
此mapfile包含丢弃的符号:
Discarded input sections
.drectve 0x00000000 0x54 c:/gnatpro/17.1/bin/../lib/gcc/i686-pc-mingw32/6.2.1/crt2.o
.drectve 0x00000000 0x1c c:/gnatpro/17.1/bin/../lib/gcc/i686-pc-
...
.text$unused_func1
0x00000000 0x14 C:\Users\xx\AppData\Local\Temp\ccOOESqJ.o
.text$unused_func2
0x00000000 0x14 C:\Users\xx\AppData\Local\Temp\ccOOESqJ.o
.rdata$zzz 0x00000000 0x38 C:\Users\xx\AppData\Local\Temp\ccOOESqJ.o
...
现在我们看到已删除未使用的功能。它们不再出现在最终的可执行文件中。
现有工具可以做到这一点(使用此技术但不需要main
),例如Callcatcher。也可以轻松创建一个工具来反汇编库并检查已定义但未调用的符号。
要清理,您可以从源手动删除未使用的功能
您还可以删除库文件中的相关部分,避免更改源代码,例如删除部分:
$ objcopy --remove-section .text$unused_func1 --remove-section text$unused_func2 libmy_static_lib.a stripped.a
$ nm stripped.a
libmy_static_lib.o:
00000000 b .bss
00000000 d .data
00000000 r .rdata
00000000 r .rdata$zzz
00000000 t .text
00000000 t .text$func1
00000000 T _func1
U _puts