使用-gc-sections减小生成文件大小
环境:
Linux平台:CentOS Linux relase 7.2.1511、GCC_4.8.5-4、cmake version 2.8.11
场景:
当项目中使用了很多静态库文件(.a),会导致编译生成的文件非常大。其实,这些静态库中的函数,并非所有都有调用,项目只使用了其中一小部分。使用"-gc-sections"参数,将不链接未用的函数,减少文件的大小。
开始:
一、静态库示例(libtest_1.a)
test_1.cpp
#include <stdio.h>
__attribute__((visibility("default"))) void test_a()
{
printf("test_a\n");
}
__attribute__((visibility("default"))) void test_b()
{
printf("test_b\n");
}
二、动态库示例(libtest_2.so)
test_2.cpp
#include <stdio.h>
void test_a();
void test_b();
__attribute__((visibility("default"))) void test_2()
{
printf("test_2\n");
test_a();
printf("test_2\n");
}
__attribute__((visibility("default"))) void test_2_1()
{
printf("test_2_1\n");
}
三、可执行程序示例(test_3)
test_3.cpp
#include <stdio.h>
void test_a();
void test_b();
void test_2();
void test_2_1();
int main()
{
test_a();
return 0;
}
四、使用cmake编译生成
project(demo)
cmake_minimum_required(VERSION 2.6)
set(PRJ_VER "0.0.0")
set(ROOT ${PROJECT_SOURCE_DIR})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${ROOT}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${ROOT}/lib)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${ROOT}/lib)
set(CMAKE_SKIP_BUILD_RPATH TRUE)
set(CMAKE_VERBOSE_MAKEFILE ON)
set(TEST_1 "test_1")
set(TEST_2 "test_2")
set(TEST_3 "test_3")
#####include_directories#####
include_directories(${ROOT})
#####link_directories#####
link_directories(${ROOT}/lib)
#CMAKE_CXX_FLAGS
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11")
message("CMAKE_CXX_FLAGS = " ${CMAKE_CXX_FLAGS})
#LINK_FLAGS
string(REGEX REPLACE "-rdynamic" "" CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS}")
string(REGEX REPLACE "-rdynamic" "" CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "${CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS}")
message("CMAKE_SHARED_LIBRARY_LINK_C_FLAGS = " ${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS})
message("CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS = " ${CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS})
add_library(${TEST_1} STATIC ${ROOT}/test_1.cpp)
set_target_properties(${TEST_1} PROPERTIES COMPILE_FLAGS "-fPIC -ffunction-sections -fdata-sections")
add_library(${TEST_2} SHARED ${ROOT}/test_2.cpp)
target_link_libraries(${TEST_2} LINK_PRIVATE ${TEST_1})
set_target_properties(${TEST_2} PROPERTIES COMPILE_FLAGS "-fvisibility=hidden -ffunction-sections -fdata-sections")
set_target_properties(${TEST_2} PROPERTIES LINK_FLAGS "-Wl,-exclude-libs=ALL,-gc-sections")
add_executable(${TEST_3} ${ROOT}/test_3.cpp)
target_link_libraries(${TEST_3} LINK_PRIVATE ${TEST_1})
set_target_properties(${TEST_3} PROPERTIES LINK_FLAGS "-Wl,-exclude-libs=ALL,-gc-sections")
#####CopyRuntimeFiles#####
add_custom_command(TARGET ${TEST_3} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:${TEST_1}> ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:${TEST_2}> ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
COMMENT "[${TEST_3}] copy runtime files")
4.1)libtest_1.a使用 "-ffunction-sections -fdata-sections"参数,表示为每一个函数或符号生成sections
objdump -x libtest_1.a | grep test
函数test_a和test_b在2个sections(.text._Z6test_av和.text._Z6test_bv)中:
去掉"-ffunction-sections -fdata-sections"参数编译,函数test_a和test_b在1个sections(.text)中:
4.2)libtest_2.so使用"-Wl,-gc-sections"参数,表示不链接未用的函数和符号
objdump -t libtest_2.so | grep test
libtest_2.so只使用了函数test_a,所以只链接函数test_a,不链接函数test_b
libtest_2.so去掉"-Wl,-gc-sections"参数编译,函数test_a和test_b都会链接
4.3)test_3同4.2,这里不再详述
结尾
注意:使用cmake链接时会自动添加"-rdynamic"参数,会导致"-Wl,-gc-sections"无效,所以需要删除"-rdynamic"参数
#删除LINK_FLAGS中"-rdynamic"参数
string(REGEX REPLACE "-rdynamic" "" CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS}")
string(REGEX REPLACE "-rdynamic" "" CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "${CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS}")
message("CMAKE_SHARED_LIBRARY_LINK_C_FLAGS = " ${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS})
message("CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS = " ${CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS})
参考资料
CMake 2.8.8 Documentation https://cmake.org/cmake/help/v2.8.8/cmake.html