Статические библиотеки - это архивы объектных файлов, и при связывании в статической библиотеке добавляются только те члены объектного файла архива, которые разрешают хотя бы одну неопределенную ссылку.
Чтобы обеспечить добавление только необходимого кода, статическая библиотека должна состоять из небольших объектных файлов, предпочтительно с одним экспортированным глобальным в каждом.
Кроме этого, вы можете добиться аналогичного эффекта, если библиотека скомпилирована с -ffunction-sections
/ -fdata-sections
и затем вы передадите --gc-sections
компоновщику.
Подход -ffunction-sections -fdata-sections
в основном эквивалентен подходу «один глобальный источник», но использование исходных файлов для установления границ является более гибким, так как иногда группирование объектов может быть желательным (большие единицы перевода могут привести к более компактным и более оптимизированный код).
В любом случае, в вашем случае (библиотека не находится под вашим контролем), все, что вы можете попробовать, это -Wl,--gc-sections
(опция -Wl
для префиксов gcc, которую gcc должен передать компоновщику)
Благодаря вашему примеру и glibc я смог сбросить около 41 КБ с исходных 849 КБ.
Не очень впечатляет, но glibc не собирается с учетом статических связей.
Вы можете получить намного лучшие результаты с библиотекой libc, такой как musl-libc .
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
Теперь это лучше, но вам может быть интересно, почему одинаковые размеры, например, 1 и 2.
Если вы добавите -Wl,--print-map
, вы обнаружите, что соответствующие объектные файлы из musl-libc вообще не включены
в любом случае. Причина в том, что gcc знает об этих стандартных функциях и обманывает, вставляя коды операций вместо сгенерированных вызовов функций. Вы можете в некоторой степени победить обман gcc, добавив слой косвенности, поддерживаемый другим модулем перевода.
call.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); }
ex1.c
# 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);
}
Ex2.c
# 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
- заметное различие между двумя примерами, которое возможно благодаря тому, как мусл состоит из
много небольших исходных / объектных файлов , чтобы при статической компоновке добавлялось не больше (или не намного), чем соответствующий ссылочный код.