Речь пойдет о переносимости бинарных файлов между дистрибутивами Linux на одной аппаратной платформе.
Если нужно собрать бинарный deb или rpm пакет или просто собрать переносимый бинарник, но нет времени / возможности / желания собирать дистрибутив под каждую версию каждого дистрибутива, можно скомпоновать в него все библиотеки статически, оставив только зависимость от стандартной библиотеки си. Не рассматриваем вариант статической компоновки библиотеки си. Пишем на C++ и хотим стандарт C++ не ниже 11, а значит и gcc 4.8 как минимум. Очень удобным вариантом для сборки является виртуальная машина с CentOS 7. Получаем gcc 4.8.5 с glibc 2.17, которая в итоге и будет единственной зависимостью бинарника. Все, что нам нужно, можно установить из пакетов дистрибутива.
Статическая компоновка стандартных библиотек
Пишем программу main.cpp :
#include <iostream>
int main(){
std::cout << "Hello, world!" << std::endl;
return 0;
}
Собираем:
c++ -o app main.cpp
Смотрим зависимости:
ldd ./app
linux-vdso.so.1 => (0x00007ffd76b10000)
libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007fdc18ec2000)
libm.so.6 => /lib64/libm.so.6 (0x00007fdc18bc0000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fdc189aa000)
libc.so.6 => /lib64/libc.so.6 (0x00007fdc185dc000)
/lib64/ld-linux-x86-64.so.2 (0x00007fdc191c9000)
Из этого списка нас беспокоят:
- libstdc++.so.6
- libgcc_s.so.1
Чтобы скомпоновать статически эти библиотеки нужно передать флаги компоновщику:
c++ -o app -static-libstdc++ -static-libgcc main.cpp
Смотрим зависимости:
linux-vdso.so.1 => (0x00007ffdc87ce000)
libm.so.6 => /lib64/libm.so.6 (0x00007f13e3030000)
libc.so.6 => /lib64/libc.so.6 (0x00007f13e2c62000)
/lib64/ld-linux-x86-64.so.2 (0x00007f13e3332000)
Вероятность, что на целевой машине не будет этих библиотек... не встречал такого )))
Для статической компоновки упомянутых библиотек, необходимо сначала установить их статические версии, например:
yum search libstdc++
Среди прочего получим:
libstdc++-static.x86_64 : Static libraries for the GNU standard C++ library
libstdc++-static.i686 : Static libraries for the GNU standard C++ library
И устанавливаем нужный нам пакет.
Статическая компоновка других библиотек
К примеру, мы используем библиотеку libmodbus. Чтобы скомпоновать ее статически необходимо указать компоновщику полный путь до библиотеки, но... как бы ни хотелось установить статическую версию библиотеки из пакета, такого пакета нет и придется собирать вручную. Вообще при статической компоновке стоит ожидать, что все библиотеки придется собирать из исходников, библиотеки все разные и собираются по-разному, поэтому просто предположим, что мы уже собрали библиотеку libmodbus.a :
c++ -o app -static-libstdc++ -static-libgcc main.cpp /usr/lib/libmodbus.a
Возможные проблемы
При статической компоновке общей библиотеки (.so) необходимо, чтобы статическая библиотека была собрана с флагом -fPIC. На патформе x86_64 статические библиотеки, устанавливаемые из пакетов, например, Boost, собраны без этого флага, поэтому на 64-разрядной системе точно придется пересобирать все библиотеки, которые будут компоноваться в so, с флагом -fPIC.
Есть высокая вероятность того, что полученные бинарные файлы будут запускаться и на платформе с glibc младших версий, если не будут использованы символы, определенные в glibc старшей версии, чем на целевой машине. Например, описанный выше исходный код будет запускаться на Ubuntu 8, хотя на ней glibc 2.15. Однако, если в программу добавить невинный на первый взгляд вызов std::chrono::system_clock::now(), можем получить зависимость от символа clock_gettime@GLIBC_2.17, о чем мы непременно узнаем, пытаясь запустить бинарник на целевой платформе. В таких случаях:
- На нашей виртуалке запускаем gdb ./app
- break clock_gettime
- run
- where
- по стеку смотрим какая функция делает этот вызов и по возможности переписываем его другим способом.