Задался вопросом как же правильно строить библиотеки для статической линковки. Вопрос возник после просмотра содержимого результирующего исполняемого файла. Дело в том, что после линковки некоторых библиотек в исполняемый файл попадает много незадействованного кода. Эксперимент оформил шелл-скриптом для *nix
:
#!/bin/sh
if [ `whoami` = "root" ]; then
echo "Под учеткой рута работать отказываюсь."
exit
fi
# если clang не установлен - нужно заменить на gcc и g++ соответственно
CC="clang -Wall -pedantic -O3 -c"
CXX="clang++ -Wall -pedantic -O3 -c"
LINK1="clang -static -O3"
LINK2="clang++ -static -O3"
# генерация библиотеки 1 ==================================
#
# все функции включаются в одну единицу трансляции
#
# =========================================================
printf "#include <stdio.h>\n\n" > testo-1.c
for i in $(seq 20)
do
printf "void SomeFunc_$i() {\n" >> testo-1.c
printf " printf(\"SomeFunc_$i\\\n\");\n" >> testo-1.c
printf "}\n\n" >> testo-1.c
done
$CC testo-1.c
$CC -ffunction-sections -fdata-sections -o testo-1f.o testo-1.c
ar rcs libtesto-1.a testo-1.o
ar rcs libtesto-1f.a testo-1f.o
rm -f *.c *.o
# генерация библиотеки 2 ==================================
#
# каждая функция включается в отдельную единицу трансляции
#
# =========================================================
for i in $(seq 20)
do
printf "#include <stdio.h>\n\n" > testo-2-$i.c
printf "void SomeFunc_$i() {\n" >> testo-2-$i.c
printf " printf(\"SomeFunc_$i\\\n\");\n" >> testo-2-$i.c
printf "}\n\n" >> testo-2-$i.c
done
$CC testo-2-*.c
ar rcs libtesto-2.a `ls testo-2-*.o`
rm -f *.c *.o
# генерация библиотеки 3 ==================================
#
# создается класс, где реализация методов включается в одну
# единицу трансляции
#
# =========================================================
printf "#pragma once\n\n" > testo-3.h
printf "class GodClass {\n" >> testo-3.h
printf " public:\n" >> testo-3.h
for i in $(seq 20)
do
printf " void SomeMethod_$i();\n" >> testo-3.h
done
printf "};\n" >> testo-3.h
printf "#include <iostream>\n" > testo-3.cpp
printf "#include \"testo-3.h\"\n\n" >> testo-3.cpp
for i in $(seq 20)
do
printf "void GodClass::SomeMethod_$i() {\n" >> testo-3.cpp
printf " std::cout << \"SomeMethod_$i\" << std::endl;\n" >> testo-3.cpp
printf "}\n\n" >> testo-3.cpp
done
$CXX testo-3.cpp
$CXX -ffunction-sections -fdata-sections -o testo-3f.o testo-3.cpp
ar rcs libtesto-3.a testo-3.o
ar rcs libtesto-3f.a testo-3f.o
rm -f *.cpp *.o
# генерация библиотеки 4 ==================================
#
# создается класс, где реализация методов разносится по
# разным единицам трансляции
#
# =========================================================
printf "#pragma once\n\n" > testo-4.h
printf "class GodClass {\n" >> testo-4.h
printf " public:\n" >> testo-4.h
for i in $(seq 20)
do
printf " void SomeMethod_$i();\n" >> testo-4.h
done
printf "};\n" >> testo-4.h
for i in $(seq 20)
do
printf "#include <iostream>\n" > testo-4-$i.cpp
printf "#include \"testo-4.h\"\n\n" >> testo-4-$i.cpp
printf "void GodClass::SomeMethod_$i() {\n" >> testo-4-$i.cpp
printf " std::cout << \"SomeMethod_$i\" << std::endl;\n" >> testo-4-$i.cpp
printf "}\n\n" >> testo-4-$i.cpp
done
$CXX testo-4-*.cpp
ar rcs libtesto-4.a `ls testo-4-*.o`
rm -f *.cpp *.o
# линкуем исполняемые файлы ===============================
printf "void SomeFunc_1();\n\n" > testo-1.c
printf "int main() {\n" >> testo-1.c
printf " SomeFunc_1();\n" >> testo-1.c
printf "}\n" >> testo-1.c
cp testo-1.c testo-2.c
$LINK1 testo-1.c -L. -ltesto-1 -o testo-1
$LINK1 testo-1.c -ffunction-sections -fdata-sections -Wl,--gc-sections -L. -ltesto-1f -o testo-1f
$LINK1 testo-2.c -L. -ltesto-2 -o testo-2
printf "#include \"testo.h\"\n\n" > testo-3.cpp
printf "int main() {\n" >> testo-3.cpp
printf " GodClass G;\n" >> testo-3.cpp
printf " G.SomeMethod_1();\n" >> testo-3.cpp
printf "}\n" >> testo-3.cpp
cp testo-3.cpp testo-4.cpp
mv testo-3.h testo.h
$LINK2 testo-3.cpp -L. -ltesto-3 -o testo-3
$LINK2 testo-3.cpp -ffunction-sections -fdata-sections -Wl,--gc-sections -L. -ltesto-3f -o testo-3f
$LINK2 testo-4.cpp -L. -ltesto-4 -o testo-4
rm -f *.c* *.h
# анализируем что попало в исполняемые файлы ==============
clear
echo "Найдено функций в 1-м исполняемом файле: "`nm testo-1 | grep SomeFunc | wc -l`
echo "Найдено функций во 2-м исполняемом файле: "`nm testo-2 | grep SomeFunc | wc -l`
echo "Найдено методов в 3-м исполняемом файле: "`nm testo-3 | grep SomeMethod | wc -l`
echo "Найдено методов в 4-м исполняемом файле: "`nm testo-4 | grep SomeMethod | wc -l`
echo "Найдено функций в 1f-м исполняемом файле: "`nm testo-1f | grep SomeFunc | wc -l`
echo "Найдено методов в 3f-м исполняемом файле: "`nm testo-3f | grep SomeMethod | wc -l`
Результат работы скрипта таков:
Найдено функций в 1-м исполняемом файле: 20
Найдено функций во 2-м исполняемом файле: 1
Найдено методов в 3-м исполняемом файле: 20
Найдено методов в 4-м исполняемом файле: 1
Найдено функций в 1f-м исполняемом файле: 1
Найдено методов в 3f-м исполняемом файле: 1
Как видно, что если разносить функции и методы в разные единицы трансляции, и результат собирать в библиотеку - то "ненужное" при линковке в исполняемый модуль не попадает.
Соответственно пара вопросов:
Можно ли как-то линкер "упросить" не заносить ненужное в исполняемый файл не используя выше означенный подход?
На сколько применим выше означенный подход для С++ (касаемо удаления реализации неиспользуемых методов класса)?
В данной теме прошу не поднимать вопросы проектирования, вопрос именно по линковке.
В большей мере с проблемой разобрался, отвечаю сам себе - может кому еще пригодится.
В случае, когда в библиотеку включается набор функций/методов, скомпилированных в одну единицу трансляции, можно использовать т.н. "function-level linking".
Для GCC/Clang это решается следующим образом:
-ffunction-sections
-fdata-sections
-ffunction-sections
-fdata-sections
-Wl,--gc-sections
Тестовый скрипт в вопросе изменил, действительно работает как должно.
Для VC++ протестировать нет возможности, решается так же флагами:
/Gy (Enable Function-Level Linking)
/OPT:REF
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Какие существуют виды рекламных бордов и как выбрать подходящий?
Мне нужно сделать выход по нажатию определенной кнопки, как сделать?
Как перебрать последовательность букв в цикле? каждую последовательность нужно иметь возможность получить, зная номер позиции
Что может означать операция i = i & (i+1) в реализации дерева отрезков?