LINUX.ORG.RU

Древнее зло, излом API/ABI в glibc связанный с errno

 , , ,


4

3

Есть очень древняя программа без исходников, скомпилированная под Linux ориентировочно в 2000-2003 годах. Вероятно компилировалась на RHL 7.3 Valhalla (не путать с RHEL 7, тогда ещё вообще RHEL’ов не было), возможно даже каким-то GCC 2.95:

prism.original: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.2.5, not stripped, too many notes (256)

Нужно запустить её на современной Ubuntu LTS.

При запуске ругается так:

prism: symbol lookup error: prism: undefined symbol: errno, version GLIBC_2.0

Погуглил, и нашёл что GNU’тые когда-то там на рубеже веков сломали API/ABI и этот errno похоже что завернули в какие-то убермакросы и выкинули.

Извернулся, взял HEX-редактор и заменил несуществующий символ errno на существующий srand:

https://0x0.st/Xy00.png

После такого наглого вмешательства программа запустилась и всё отлично работает, но напрягает такой выхлоп в консоль:

prism: Symbol `srand' has different size in shared object, consider re-linking

Как сделать по красоте и избавиться от этого предупреждения?

Сам файл программы: https://0x0.st/Xy0d.zip/prism.zip

★★★★★

Сделай пустую so-шку экспортирующую int errno. Подкинь её своему приложению через LD_PRELOAD.

Разумеется, этот вариант как и твой ломает обработку ошибок. В смысле приложение не будет знать причины ошибок вызовов libc. Это может иметь разные последствия. Может быть только в UI будут кривые сообщения в нештатных ситуациях, а может быть поломается что-то серьёзное (например, некоторые функции могут возвращать ошибку EAGAIN и приложение должно повторить вызов, а с таким решением оно не будет этого делать).

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

Положи протухшие либы рядом с протухшей программой, напиши обёртку для запуска.

aol ★★★★★
()

Я аж вспомнил как патчил либу от драйвера mali под xorg. Нехорошие люди в arm неявно юзают треды в либе а xorg для этого требует явный вызов XInitThreads. Собственно софт который это не делал крошился. В итоге в _init либы были найдены нужное количество байт от паддинга для вкорячивания вызова XInitThreads благо этот символ был в стабах для динамической линковки.

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

Это можно сделать проще - добавить в линковку либу, в которой в attribute((constructor)) вызвать XInitThreads. После чего линковщиком обеспечить порядок инициализации чтобы ваша была раньше

PPP328 ★★★★★
()

Достать или собрать подходящую версию glibc (например, взять бинарный пакет из RH) и прописать путь к ld.so в бинарник через patchelf --set-interpreter

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

Сделай пустую so-шку экспортирующую int errno. Подкинь её своему приложению через LD_PRELOAD.

Мне тоже пришло это в голову сперва. Я попытался сделать вот так, но увы:

// errno_lib.c
extern int errno;

int errno;

int use_errno(void) {
	errno = 0;
	return errno;
}

$ gcc -m32 errno_lib.c -shared -fPIC -o liberrno.so

$ LD_PRELOAD=lib/liberrno.so ./prism
Floating point exception (core dumped)
EXL ★★★★★
() автор топика
Последнее исправление: EXL (всего исправлений: 1)
Ответ на: комментарий от novus

В виртуалке с RHL 7.2 работает, с RHEL 4 не работает. Но виртуалки и контейнеры не подходят для задачи, увы.

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

рядом ей скомпилировать glibc 2.0

Думается мне что компиляция древнющего glibc 2.0 на современной системе с современным GCC будет адовой.

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

Спасибо, интересный вариант!

Можешь расписать подробнее?

Вот скачал я условный Debian 3.0 ISO:

https://cdimage.debian.org/mirror/cdimage/archive/3.0_r6/i386/iso-cd/

Выдернул из pool/main/g/glibc/libc6_2.2.5-11.8_i386.deb кучку либ:

ld-2.2.5.so
ld-linux.so.2
libanl-2.2.5.so
libanl.so.1
libBrokenLocale-2.2.5.so
libBrokenLocale.so.1
libc-2.2.5.so
libcrypt-2.2.5.so
libcrypt.so.1
libc.so.6
libdb1-2.2.5.so
libdb1.so.2
libdb.so.2
libdl-2.2.5.so
libdl.so.2
libm-2.2.5.so
libm.so.6
libnsl-2.2.5.so
libnsl.so.1
libnss_compat-2.2.5.so
libnss_compat.so.2
libnss_dns-2.2.5.so
libnss_dns.so.2
libnss_files-2.2.5.so
libnss_files.so.2
libnss_hesiod-2.2.5.so
libnss_hesiod.so.2
libnss_nis-2.2.5.so
libnss_nisplus-2.2.5.so
libnss_nisplus.so.2
libnss_nis.so.2
libpthread-0.9.so
libpthread.so.0
libresolv-2.2.5.so
libresolv.so.2
librt-2.2.5.so
librt.so.1
libSegFault.so
libthread_db-1.0.so
libthread_db.so.1
libutil-2.2.5.so
libutil.so.1

Что со всем этим делать дальше?

EXL ★★★★★
() автор топика
Ответ на: комментарий от EXL
  1. Распаковываем пакет (целиком!) в некоторый префикс, который станет нашим «сисрутом». Для краткости назову его /prefix, тогда вышеперечисленные либы должны оказаться в /prefix/lib. Если у пакета есть какие-то зависимости, кроме ядра, возможно придётся и их тоже распаковать (в тот же префикс).

  2. Для проверки можно запустить бинарник через ld.so:

LD_LIBRARY_PATH=/prefix/lib /prefix/lib/ld-linux.so.2 ./prism

Нужен ли LD_LIBRARY_PATH — я точно не помню, возможно и без него сойдёт

  1. Переписываем у prism загрузчик, после чего его можно будет запускать напрямую, в том числе под своим системным gdb:
patchelf --set-interpreter /prefix/lib/ld-linux.so.2 ./prism

Если приложение после этого все ещё не заработает без LD_LIBRARY_PATH, то можно сделать patchelf --add-rpath /prefix/lib ./prism

annulen ★★★★★
()

Тебя интересует errno@@GLIBC_PRIVATE.

intelfx ★★★★★
()
Ответ на: комментарий от annulen
LD_LIBRARY_PATH=/prefix/lib /prefix/lib/ld-linux.so.2 ./prism
./prism: gethostbyname of <> failed. EXIT

Да, это работает с prism, вот только этот prism – программка, которая запускается другим бинарником внутри себя, и вот она при такой конструкции похоже развалилась:

LD_LIBRARY_PATH=`pwd`/lib:`pwd`/lib/prefix:$LD_LIBRARY_PATH lib/prefix/ld-linux.so.2 xsimphone -f prism.cnfg 
Inconsistency detected by ld.so: dynamic-link.h: 62: elf_get_dynamic_info: Assertion `! "bad dynamic tag"' failed!

В общем, пока остановлюсь на костыльке с LD_PRELOAD и стабом errno, так как с ним всё вроде работает и ошибками не плюётся.

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

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

Да, это работает с prism, вот только этот prism – программка, которая запускается другим бинарником внутри себя, и вот она при такой конструкции похоже развалилась

Тогда нужен patchelf –set-interpreter

В общем, пока остановлюсь на костыльке с LD_PRELOAD и стабом errno, так как с ним всё вроде работает и ошибками не плюётся.

Это менее солидное решение. Но может работать.

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

patchelf –add-rpath

А вот такой вопрос ещё, относительную директорию в rpath добавить можно? Например, ./, чтобы ELF брал SO-библиотеки в той директории где он лежит?

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

Это анонимусный флуд, белый шум.

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

Относительный путь будет работать относительно текущего каталога пользователя. Относительно положения бинарника это $ORIGIN.

annulen ★★★★★
()

Будь линуксоидом! Напиши недостающий тебе софт сам!!!11

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

дизассемблировать и пересобрать

Поделишься историей узбека для чего-то существенно большего, чем hello world?

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

какая-нибудь IDA, потом в сишный код опционально сконвертировать можно, разными методами.

дрочиться надо много.

но если надо, то надо

никаких легких решений нет

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

там проблема то, собственно о чем уже сам ТС догадался, не просто в том чтобы «подсунуть старые либы». Там проблема вообще в принципе в glibc. И даже если ты подсунешь старую glibc, то какая вероятность что оно будет совместимо с современным ядром?

А вообще, красноглазые проблемы, конечно.

Красноглазые для себя открывают необходимость поддержания обратной совместимости. (а то обычно у них - А чой та в винде функции с таким большим количеством параметров, фуу, не то шо в ляликсе, в котором вона как удобна писать хелловорлды)

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

но самое мне конечно интересное, это почему у красноглазого(или наоборот вашего, корпоративного) софта нет исходников. Если жопа такая - пишите с нуля. Если прямо надо надо или это реверсинг именно проприетарщины - варианты я выше сказал. Других - нет.

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

Если денег много - после дизассемблирования можете обратиться вот к этим пацанам, в одном чате их недавно вспоминал:

http://www.semdesigns.com/products/DMS/DMSToolkit.html

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

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

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

Я их использовал для запуска проприетарного по, которое работает до убунту 18 включительно, а под более свежими системами падает с сегфолтами. У ТСа ситуация аналогичная. Есть некая софтина, которая работает на древней оси. Соответственно, раскатываешь древнее окружение в контейнере и запускаешь софтину.

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

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

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

Нет, не возможный. Потому что ядро в контейнерах одно и то же. Ему надо то ядро, которое было тогда, или по крайней мере совместимое. Таких ядер с контейнерами нет в природе(я вообще лично не знаю что такое RHL Valhalla, и че там внутри было, но судя по всему ядро там 2000го года, или около).

И не факт что оно там не было какое-то кастомное еще, это ядро.

Этот софт нельзя в принципе запустить на хоть сколько-нибудь современных ядрах.

Или виртуализация, полная, тогда ему надо этот RHL Valhalla найти, или реверс-инженеринг.

Или писать с нуля.

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

glibc связана с ядром. Это обертка над сисколлами ядра. И точно так же, она полностью сломала обратную совместимость, что мы и видим.

старая glibc (2000 года) на сколько-нибудь современном ядре не запустится

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

старая glibc (2000 года) на сколько-нибудь современном ядре не запустится

Что же там сломалось?

glibc-2.0.bin.i386$ ./lib/ld-linux.so.2 ./lib/libc.so.6 
GNU C Library experimental release version 2.0, by Roland McGrath et al.
Compiled by GNU CC version 2.7.115 snapshot 961122.
Copyright (C) 1992, 93, 94, 95, 96, 97 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled on a Linux 2.1.1 system on 1997/01/26.
Available extensions:
	GNU libio by Per Bothner
	BIND-4.9.5-P1
	linuxthreads-0.5 by Xavier Leroy
	UFC-crypt, patchlevel 1e by Michael Glad
Report bugs using the `glibcbug' script to <bugs@gnu.ai.mit.edu>.

И точно так же, она полностью сломала обратную совместимость

Какие сисколы ломали совместимость?

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

неявно юзают треды

Простите за оффтопик, но что за больным ублюдком надо быть, чтобы так напортачить?

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

Но виртуалки и контейнеры не подходят для задачи, увы.

chroot?

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

старая glibc (2000 года) на сколько-нибудь современном ядре не запустится

Используя некоторые хаки (как минимум три из опубликованных в этом треде способа, сработали) мне удалось запустить эту древнюю программу на современном ядре и в современном окружении.

Печально, конечно, что они ломают API/ABI, но по сути поковырявшись можно восстановить работу программы.

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

undefined symbol: errno, version GLIBC_2.0

Наверное можно пропатчить и пересобрать glibc, добавив туда эту глобальную переменную errno и код установки значения.

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

дизассемблировать и пересобрать не вариант?

Оу, нет, точно не вариант. Там программа… точнее даже сказать полноценная система, дистрибутив из вереницы программ которые запускаются по цепочке и взаимодействуют между собой и все они довольно сложные. Внутри себя содержат кучу сущностей вроде трёх-четырёх слоёв эмуляции процессоров экзотических RISC-архитектур (M-CORE, IPCM SmartDMA) и VLIW-архитектур (StarCore, M96K) помимо этого внутри исполняемых файлов этих программ присутствуют интерпретаторы древних версий TCL, Python и парочки DSL’ов…

То что всё это запустилось без сильного потряхивания бубном на современном дистрибутиве Ubuntu’очки – воистину чудо.

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

И даже если ты подсунешь старую glibc, то какая вероятность что оно будет совместимо с современным ядром?

Огромная. В ядре политика такая: не ломать юзерспейс. И соблюдается она жёстко.

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

glibc связана с ядром. Это обертка над сисколлами ядра.

Совершенно верно. А сисколлы в ядре не ломают обратную совместимость. Когда нужно что-то изменить или добавить новый параметр, добавляется новая версия сисколла, а старая остаётся как есть навсегда.

annulen ★★★★★
()
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.