LINUX.ORG.RU

Перенос самописной библиотеки и so-библиотек на другой компьютер

 


0

1

Как правильно собрать динамическую библиотеку, зависящую от других динамических библиотек (дополнительных), чтобы можно было переносить между различными компьютерами без сборки дополнительных библиотек на всех компьютерах? Является ли строгим требованием совпадение версии ядра или эту проблему можно обойти? Не уверен, что смогу найти все нужные библиотеки в виде статических (*.a).

Другими словами, есть самописная библиотека, зависящая, в частности от opencv (в общем случае и других сторонних библиотек). Переношу ее с одной Убунты 18.04 на другую. При этом посредством ldd скопировал все библиотеки, которые зависят от моей самописной. Добавляю на новый комп только те библиотеки, которых нет

lists=`ls *so*`
TARGET=/home/user/work/libs
for name in $lists ; do
    res=`ldconfig -p | grep $name`
    if [ -z "$res" ] ; then
        echo "[+] $name"
        cp $name $TARGET
    else
        echo "[-] $name"
    fi
done
Экспортирую LD_LIBRARY_PATH и получаю
UnsatisfiedLinkError: /home/user/mylibrary/bin/mylibrary.so: /lib/x86_64-linux-gnu/libm.so.6: version `GLIBC_2.27' not found (required by /home/user/libs/libopencv_imgproc.so.4.3)

Ядро у операционок различное 5.3 и 4.15. Похоже это проблема, но я не админ на целевой.

Если, я копирую все библиотеки, то в целевой ОС возникают проблемы: любая стандартная команда (ls, cat, ...) приводит к segmentation fault.

P.S. Может существует какие-то книжки или статьи по переносу, прошу порекомендовать.

Экспортирую LD_LIBRARY_PATH

Не экспортируйте, а задавайте только для запуска нужной вам программы, тогда копирование всех библиотек не будет влиять на работу ls, cat и т.д.

А так, либо собирать под нужной ОС (поставив её в виртуалку), либо всякие snap, flatpack и пр.

P.S. Я может не правильно понял, но вы хотите копировать именно библиотеку .so, а сам исполняемый файл, который её использует, будет компилироваться на каждом компе?

mky ★★★★★ ()
Последнее исправление: mky (всего исправлений: 1)

Ядро у операционок различное 5.3 и 4.15. Похоже это проблема, но я не админ на целевой.

Никакого значения версии ядра не имеют. В вас версии системных либ не совпадают! Никогда либа, скомпилированная на новой версии glibc, не будет работать на старой версии. Надо, наоборот, компилить либу на самой старой системе.

sigurd ★★★★★ ()
Ответ на: комментарий от sigurd

Понятно. А я правильно пониамю, что вот этот набор и есть системные либы (это либы, которые были на целевом компьютере):

[-] libc.so.6
[-] libdl.so.2
[-] libdouble-conversion.so.1
[-] libgcc_s.so.1
[-] libglib-2.0.so.0
[-] libm.so.6
[-] libpcre.so.3
[-] libpthread.so.0
[-] librt.so.1
[-] libstdc++.so.6
[-] libtbb.so.2
[-] libudev.so.1
[-] libusb-1.0.so.0
[-] libz.so.1
? Или проблема только в libc?

cppprogger ()
Ответ на: комментарий от mky

но вы хотите копировать именно библиотеку .so, а сам исполняемый файл, который её использует, будет компилироваться на каждом компе

я компилирую so-шку для Ява приложения. Оно где-то было собрано и работает везде.

cppprogger ()
Ответ на: комментарий от mky

задавайте только для запуска нужной вам программы

не получилось, в этом случае программа падает с

segmentation fault (core dumped)
как и любая команда баша после экспорта (

cppprogger ()
Ответ на: комментарий от sigurd

Никогда либа, скомпилированная на новой версии glibc, не будет работать на старой версии

А можно ли обновлять glibc в Линуксах? И как это сделать?

P.S. Чтобы проверить, что проблема только в glibc, установил Убунту 18.04 на виртуалке и сделал update, upgrade. Получил glibc 2.23. А на компьтере, на котором собирал - 2.27, почему не понимаю.

cppprogger ()

Так не получится. Обновление Glibc у дистрибутивостроителей означает пересборка всех пакетов.

Тебе же проще установить виртуалку с той версией Ubuntu куда переносишь и уже в этой виртуалке пересобрать свой код.

bhfq ★★★★★ ()

Ты ведь знаешь, что внешние зависимости so разрешаются динамическим заргузчиком? Т.е. во время линковки можешь не укзывать линкеру либы, тогда останется лишь одна зависимость от libc (посмотри через readelf). Никогда не экспериментировал так, но можно попробовать слинковать без зависимости от libc через прямой вызов ld (т.е. будет использована та, которая линкуется с исполняемым файлом), либо подправить elf (удалить запись в .dynamic секции) может какие утилиты есть, можно самому написать.

pavlick ★★ ()
Ответ на: комментарий от pavlick

Для теста слинковал либу напрямую через ld, он всё равно потянул libc в зависимости.
Кстати, а почему нельзя собрать на месте?

pavlick ★★ ()
Последнее исправление: pavlick (всего исправлений: 1)
Ответ на: комментарий от cppprogger

А можно ли обновлять glibc в Линуксах? И как это сделать?

Зачем обновлять? Ты собрался прямо у пользователя системную glibc заменять?

Тебе надо не обновлять, а, наоборот, взять самую старую, слинковаться с ней, и тогда твоя библиотека, скорее всего, заработает и с новой.

А если ты собрался glibc приложить к приложению, то это не особо приветствуется. Лучше сначала попробовать musl, её можно хоть статически влинковать.

В качестве альтернативы можно попробовать взять Linux Standards Base SDK: там есть специальная болванка для glibc с чётко определённым интерфейсом. Но там свои заморочки, да и вообще он теряет популярность.

Если, я копирую все библиотеки, то в целевой ОС возникают проблемы

Это почему? Ты их что, в /usr ставишь?

proud_anon ★★★★★ ()

Собрал .so без единой зависимости (от других .so). Т.е. все необходимый либы подключаем при линковки исполняемого файла, этими либами будут разрешаться все зависимости (в том числе и libc.so).
Пример:

pavlick-fm /tmp/1 $ cat lib.c
#include <stdio.h>
void fn() { printf("hello world\n"); }

pavlick-fm /tmp/1 $ cat exe.c
void fn();
int main() {fn();}

pavlick-fm /tmp/1 $ gcc -fPIC -shared lib.c -o libmy.so
pavlick-fm /tmp/1 $ gcc exe.c ./libmy.so -o exe
pavlick /tmp/1 $ ./edit_elf libmy.so
ELF has been successfully fixed

pavlick-fm /tmp/1 $ readelf -d libmy.so
# было
Динамический раздел со смещением 0x2e20 содержит 24 элемента:
  Тег                Тип                  Имя/Знач
 0x0000000000000001 (NEEDED)             Совм. исп. библиотека: [libc.so.6]
 0x000000000000000c (INIT)               0x1000
 0x000000000000000d (FINI)               0x111c

# стало
Динамический раздел со смещением 0x2e20 содержит 23 элемента:
  Тег                Тип                  Имя/Знач
 0x000000000000000c (INIT)               0x1000
 0x000000000000000d (FINI)               0x111c
 ...

Утилиту edit_elf написал сам, её задача - найти .dynamic секцию, добраться до первой записи «NEEDED», и затереть её сдвигом остальными записями секции. Т.е. либа ничего не требует, подставляю только нужные -l при во время линковки исполняемого файла.

pavlick /tmp/2 $ cat edit_elf.c
#include <elf.h>
#include <stdio.h>

#define ERROR                   \
	do {                        \
		printf("error, exit\n");\
		return 1;               \
	} while (0);

int main(int argc, const char *argv[])
{
	if (argc < 2)
		ERROR;
	FILE *file = fopen(argv[1], "r+b");
	if (file == NULL)
		ERROR;
	Elf64_Ehdr elf_header;
	if (fread(&elf_header, sizeof(elf_header), 1, file) != 1  ||
			fseek(file, elf_header.e_shoff, SEEK_SET) != 0)
		ERROR;
	if (elf_header.e_shoff == 0) {
		printf("ELF has no section table, exit");
		return 0;
	}
	Elf64_Shdr sec_header;
	for (int i = 0;  i < elf_header.e_shnum;  ++ i) {
		if (fread(&sec_header, sizeof(sec_header), 1, file) != 1)
			ERROR;
		if (sec_header.sh_type == SHT_DYNAMIC)
			break;
	}
	if (sec_header.sh_type != SHT_DYNAMIC) {
		printf("ELF has no dynamic section, exit");
		return 0;
	}
	if (fseek(file, sec_header.sh_offset, SEEK_SET) != 0)
		ERROR;
	Elf64_Dyn dyn_entry;
	while (1) {
		if (fread(&dyn_entry, sizeof(dyn_entry), 1, file) != 1)
			ERROR;
		if (dyn_entry.d_tag == DT_NULL) {
			printf("ELF has no NEEDED libraries, exit");
			return 0;
		}
		if (dyn_entry.d_tag == DT_NEEDED)
			break;
	}
	while (1) {
		if (fread(&dyn_entry, sizeof(dyn_entry), 1, file) != 1  ||
				fseek(file, ftell(file)-sizeof(dyn_entry)*2, SEEK_SET) != 0  ||
				fwrite(&dyn_entry, sizeof(dyn_entry), 1, file) != 1  ||
				fseek(file, ftell(file)+sizeof(dyn_entry), SEEK_SET) != 0)
			ERROR;
		if (dyn_entry.d_tag == DT_NULL)
			break;
	}
	printf("ELF has been successfully fixed\n");
}

pavlick ★★ ()
Последнее исправление: pavlick (всего исправлений: 2)
Ответ на: комментарий от proud_anon

Лучше сначала попробовать musl, её можно хоть статически влинковать.

И получить полную вакханалию в итоговом исполняемом файле если такая so попадёт в link map перед чем-то ещё. Нужно вообще очень осторожно что-то статически прилинковывать к сошкам.

pavlick ★★ ()

Мне больно смотреть на наркоманов в этом треде (@cppprogger и @pavlick), пожалуйста, пожалейте меня и почитайте книжки по тому, как работают компы, что такое линковка, динамические и статические библиотеки, объектные файлы и так далее.

peregrine ★★★★★ ()
Ответ на: комментарий от mky

Да ))) На целевом хосте 16-я. Я так понимаю, нужно на 16 пересобрать.

cppprogger ()
Ответ на: комментарий от proud_anon

Это почему? Ты их что, в /usr ставишь?

Нет. Экспортирую LD_LIBRARY_PATH, после этого не работают даже команды баша.

cppprogger ()
Ответ на: комментарий от pavlick

Утилиту edit_elf написал сам, её задача - найти .dynamic секцию, добраться до первой записи «NEEDED», и затереть её сдвигом остальными записями секции.

Вот это — знатная наркомания. Даже объяснять почему так не буду, если тебе не очевидно, то бесполезно разговаривать даже. А потом ещё удивляются, почему столько отборных костылей при разработке софта под linux, а кони орут, что это linux виноват. В M$ такого бреда нет, а оно в головах у местных говнокодеров такой бред.

Как думаешь, почему за столько лет тебе пришлось писать утилиту edit_elf? Как же без неё обходились то со времён царя гороха (unix, как и концепция библиотек поди старше тебя, а пацаны то и не знали)? Неужто все такие тупые? Ты просто занимаешься велосипедостроительством. patchelf написан 16 лет назад. Ну и да, вам с ТС-ом на пару стоит погуглить про LSB, если вы этого не знаете, то что вы вообще забыли в разработке проприетарщины в Linux-ах?

peregrine ★★★★★ ()
Последнее исправление: peregrine (всего исправлений: 1)
Ответ на: комментарий от anonymous

Я там не админ, могу только LD_LIBRARY_PATH заэкспортировать или передать программе. Ну и кроме того, одно дело неспеша на домашнем компе, например, opencv полдня собирать, другое дело без удаленного доступа настраивать, и потом отвечать за «чужие» компы. Я хочу отвечать за свою библиотечку.

cppprogger ()
Последнее исправление: cppprogger (всего исправлений: 1)
Ответ на: комментарий от peregrine

Ну и да, вам с ТС-ом на пару стоит погуглить про LSB, если вы этого не знаете, то что вы вообще забыли в разработке проприетарщины в Linux-ах?

Разъясни, пожалуйста, в общих чертах, могу ли я принципиально собрать свою динамическую библиотеку, которая будет работать с любой версией библиотек из пакета libc6-udev? Неужели у libc6-udeb 2.27 и 2.23 нет совместимости?

Имеется ли совместимость в рамках одной версии дистрибутивов? На практике, sdk и фреймворки выкладываются для конкретной версии дистрибутива, как они этого достигают?

cppprogger ()
Ответ на: комментарий от peregrine

Естественно костыль, но автор сам захотел странного, подстать и решине. Для себя бы пересобрал либу на целевой платформе и не занимался бы таким.

Ну вот patchelf есть, а говоришь, что это не нужно (я её и раньше встречал, но забыл, и она тоже может модифицировать NEEDED).

Можно было и с меньшей эксперссией дать ссылку на patchelf, а не обвинять в какой-то дремучей некомпетентности.

pavlick ★★ ()
Ответ на: комментарий от cppprogger

Принципиально ответ нет. Но ты можешь собрать библиотеку, которая скорее всего будет работать практически везде.

peregrine ★★★★★ ()
Последнее исправление: peregrine (всего исправлений: 1)
Ответ на: комментарий от peregrine

Но ты можешь собрать библиотеку, которая скорее всего будет работать практически везде.

Динамическую? Как? Основной посыл в чем?

cppprogger ()
Ответ на: комментарий от cppprogger

Объясни, вот ты собираешь динамическую библиотеку, это полуфабрикат, нужен компилятор+линкер на целевой платформе для сборки конечного исполняемого файла её использующего. Ну собири статическую библиотеку, чего ты зациклился на динамической? Если настаиваешь на динамической, то чем patchelf не подходит? Ты можешь получить чистую (без зависимостей) библиотеку, вообще ничего копировать не надо как ты в начале, ты ведь мечтал об этом.

pavlick ★★ ()
[-] libc.so.6
[-] libdl.so.2
[-] libdouble-conversion.so.1
[-] libgcc_s.so.1
[-] libglib-2.0.so.0
[-] libm.so.6
[-] libpcre.so.3
[-] libpthread.so.0
[-] librt.so.1
[-] libstdc++.so.6
[-] libtbb.so.2
[-] libudev.so.1
[-] libusb-1.0.so.0
[-] libz.so.1

Лучше всего оставить в зависимостях только glibc. Скорее всего норм будет еще оставить зависимость от libz.

С чуть меньшей вероятностью, но всё равно со значительной, ок будет оставить libudev, libusb и libglib.

libgcc_s и libstdc++ (если библиотека - один файл) лучше вкомпилировать статически.

сторонние либы (libpcre.so.3, libtbb.so.2 libdouble-conversion.so.1) надо вкомпилить.

Если вкомпиливать не вариант, то rpath $ORIGIN и прочее.

Собирать это все надо максимально древним тулчейном, и максимально старым ABI либ, с которыми это все собирается и работает. Проще всего поэксперементировать с debootstrap (fakechroot/podman/docker/whatever) и дистрами начиная с lenny :)

vasily_pupkin ★★★★★ ()
Последнее исправление: vasily_pupkin (всего исправлений: 1)

Смешные вы, да хоть десять версий libc.so и подобного подгрузите, использоваться будет лишь одна - та, которая раньше в link map окажется, все эти телодвижения бессмысленны.

pavlick ★★ ()
Ответ на: комментарий от pavlick

Объясни, вот ты собираешь динамическую библиотеку

Так задача у меня такая, мне нужно для Ява приложения сделать so, которая делает вычислительную часть. Я попробовал собрать статику mylib.a (в qtcreator один ключик всего лишь), но Ява обругала, что библиотека не динамическая.

cppprogger ()
Ответ на: комментарий от cppprogger

Я хочу отвечать за свою библиотечку.

ну так и отвечай. нахрена ты за ней в придачу тащишь тонну неконтролируемых тобой зависимостей? ты считаешь это нормально? я бы точно не стал такой библиотекой пользоваться. да и это уже больше похоже не на библиотеку, а на какой-то непродуманный фреймворк.

если ты сам хочешь на несовместимой системе использовать свою библиотеку, то тут либо пересобирать всё подряд, либо, что проще, воспользоваться контейнерами/виртуализацией.

anonymous ()
Ответ на: комментарий от pavlick

Если настаиваешь на динамической, то чем patchelf не подходит?

спасибо, буду разбираться, я же работал над кодом в qtcreator, подключил библиотеки через гуевый диалог и нажал build.

cppprogger ()
Ответ на: комментарий от cppprogger

ЗЫ: если хочешь скопировать в «старую» систему «новые» либы и гарантированно юзать их, то посмотри в сторону LD_PRELOAD, список этих либ будет попадать в начало link map. (man ld.so)

pavlick ★★ ()
Последнее исправление: pavlick (всего исправлений: 1)

Посмотрите, какие бинарники получаются у nix/NixOS после сборки (https://hydra.nixos.org/jobset/nixpkgs/trunk#tabs-jobs). Возможно, окажется удобным переписать сборочные скрипты на nix, собрать на локальной машине и скопировать замыкание (nix-copy-closure) на удалённую машину. Nix можно поставить на многие дистрибутивы, избежав таким образом радикального перехода на NixOS на локальной машине.

Для сложных случаев, затрагивающих binary-only компоненты у NixOS есть patchelf.

kmeaw ★★★ ()
Ограничение на отправку комментариев: только для зарегистрированных пользователей