LINUX.ORG.RU

Бинарная совместимость и старые версии JDK

 , , бинарная совместимость, ,


0

5

Всем здравствуйте.

С целью опытов над умершими пытаюсь завести старую версию JDK (1.3) на умеренно современной операционке (Debian 10). И да, я не оговорился – это именно JDK 1.3, выпущенный в мае 2000 года н. э., а не JDK 13, выпущенный в 2019.

И всё бы хорошо с бинарной совместимостью в том смысле, что, на первый взгляд, достаточно установить 32-разрядные зависимости, которые до сих пор есть в Debian:

  • libbsd0
  • libc6
  • libgcc1
  • libice6
  • libnspr4
  • libodbc1
  • libsm6
  • libuuid1
  • libx11-6
  • libxau6
  • libxcb1
  • libxdmcp6
  • libxext6
  • libxi6
  • libxt6
  • libxtst6
  • unixodbc-dev

Из нестандартного нужно доустановить лишь libxp6, которая без проблем достаётся из почти свежего Debian 8, да libstdc++ от gcc 2.96 (пакет compat-libstdc++-296 из ранних версий CentOS).

И вот такой набор (вплоть до моста JDBC-ODBC) прекрасно работал у меня вплоть до Debian 9 включительно.

В Debian 10 что-то случилось, и команда java -version вместо привычного

java version "1.3.1_20"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.1_20-b03)
Java HotSpot(TM) Client VM (build 1.3.1_20-b03, mixed mode)

выдаёт

Error occurred during initialization of VM
java/lang/NoClassDefFoundError: java/lang/Object

И, если присмотреться, она таки действительно не может распаковать rt.jar и достать оттуда java/lang/Object.class. При запуске strace -f видно, что на Debian 9 процесс открывает rt.jar и отображает (mmap) его содержимое в виртуальную память. Это java здорового некрофила:

stat64("/usr/lib/jvm/java-1.3.1_20-sun-i386/jre/lib/rt.jar", {st_mode=S_IFREG|0644, st_size=13904932, ...}) = 0
lstat64("/usr", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/usr/lib", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/usr/lib/jvm", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/usr/lib/jvm/java-1.3.1_20-sun-i386", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
lstat64("/usr/lib/jvm/java-1.3.1_20-sun-i386/jre", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
lstat64("/usr/lib/jvm/java-1.3.1_20-sun-i386/jre/lib", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
lstat64("/usr/lib/jvm/java-1.3.1_20-sun-i386/jre/lib/rt.jar", {st_mode=S_IFREG|0664, st_size=13904932, ...}) = 0
open("/usr/lib/jvm/java-1.3.1_20-sun-i386/jre/lib/rt.jar", O_RDONLY|O_LARGEFILE) = 3
fstat64(3, {st_mode=S_IFREG|0664, st_size=13904932, ...}) = 0
_llseek(3, 0, [13904932], SEEK_END)     = 0
mmap2(NULL, 13904932, PROT_READ, MAP_SHARED, 3, 0) = 0xf6b9c000
close(3)                                = 0

А вот ровно то же самое, но на Debian 10. Так сказать, java некрофила-курильщика:

stat64("/usr/lib/jvm/java-1.3.1_20-sun-i386/jre/lib/rt.jar", {st_mode=S_IFREG|0644, st_size=13904932, ...}) = 0
openat(AT_FDCWD, "/usr/lib/jvm/java-1.3.1_20-sun-i386/jre", O_RDONLY|O_LARGEFILE) = 3
fstat64(3, {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
close(3)                                = 0

Как видно, мы вместо того, чтобы выполнить open()/openat() для файла /usr/lib/jvm/java-1.3.1_20-sun-i386/jre/lib/rt.jar, выполняем то же самое действие для каталога /usr/lib/jvm/java-1.3.1_20-sun-i386/jre – с предсказуемым исходом.

Проблема совершенно точно не в ядре – её можно воспроизвести на чистом Docker-контейнере i386/debian:10, а на i386/debian:9 всё работает. Ну, т. е., на худой конец, можно гонять старый софт в Docker’е и этим удовлетвориться.

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

★★★★★

Как видно, мы вместо того, чтобы выполнить open()/openat() для файла /usr/lib/jvm/java-1.3.1_20-sun-i386/jre/lib/rt.jar, выполняем то же самое действие для каталога /usr/lib/jvm/java-1.3.1_20-sun-i386/jre – с предсказуемым исходом.

Мне кажется немного не так.

В debian 9 он же делает lstat на весь путь по очереди, в debian 10 пропускает эту проверку. Подозреваю, он проверяет, нет ли в середине пути где-то jar-файла, который надо открыть вручную архиватором, а не в качестве директории. В debian 10 он же «магически» (не делая lstat) узнаёт, что тот самый jar архив это «/usr/lib/jvm/java-1.3.1_20-sun-i386/jre», ошибочно.

Попробуй в deb9 окружение подсунуть glibc от deb10 (это точно можно сделать ничего не ломая, я deb10 glibc даже в deb8 подсовывал и всё работало). А вообще - найди отладчиком кто эти lstat вызывает и там наверно будет видно почему в deb10 их нет.

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

А нет, наличие реального файла он проверил в самом начале. Ну, тогда может быть симлинки ищет. Как бы то ни было, думаю дело в отсутствующих lstat.

firkax ★★★★★
()

Чтобы исключить банальное

Кроме установки JAVA_HOME и PATH, укажи полный путь при запуске java -version. Новая жаба (openjdk) из дистрибутива, получив JAVA_HOME от говна мамонта, точно ведёт себя неадекватно

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

Спасибо. Нет, это исключил первым делом. С одной стороны, на Debian 9 у меня зоопарк JDK, и JDK 1.3 при этом ведёт себя корректно. С другой стороны, как я уже написал, проблема проявляется в чистом окружении Debian 10, где других JDK просто нет.

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

Попробуй в deb9 окружение подсунуть glibc от deb10

Попробую. А как быть с использованием «правильного» ld-linux.so.2? Тем же путь жёстко прописан в libc.so.6. patchelf?

А вообще - найди отладчиком кто эти lstat вызывает

Возможно, я что-то не так делаю, но мой опыт с gdb следующий:

(gdb) set args -version
(gdb) catch syscall 18
Catchpoint 1 (syscall 'oldstat' [18])
(gdb) catch syscall 106
Catchpoint 2 (syscall 'stat' [106])
(gdb) r
Starting program: /usr/lib/jvm/java-1.3.1_20-sun-i386/bin/i386/native_threads/java -version
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Error occurred during initialization of VM
java/lang/NoClassDefFoundError: java/lang/Object
[Inferior 1 (process 16871) exited with code 01]

Т. е. я пытаюсь поймать вызов 32-разрядного stat() (потому что программа 32-разрядная) – и ничего не ловится. А в выводе strace выше – stat64(), которого в 32-разрядной таблице системных вызовов, естественно, просто нет.

Bass ★★★★★
() автор топика

Надо смотреть в код жвм той версии в то место, где он ищет rt.jar. Но что-то мне подсказывает, что её ты хрен найдёшь. Емнип открыли начиная с 1.4

З.Ы. В более новых жвм твой софт не работает, как я понимаю? Насколько я помню, 1.3->1.4->1.5 вроде нормально обновлялись, без несовместимостей.

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

Будет.

Ровно до тех пор, пока версии интерпретатора динамических библиотек различаются не очень сильно.

С таким подходом, пытаясь побороть проблемы совместимости libc.so, мы рискуем огрести проблемы совместимости ld.so.

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

Именно.

Я хотел написать обзор на старые версии IDE для Java — в частности, Sun Forte/Workshop, NetBeans и JBuilder.

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

В NetBeans 3.0 отладчик написан с использованием чёрной магии и потому требует именно Java 1.3.

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

Что характерно, Java 1.4 прекрасно работает и на Debian 10, а вот с Java 1.5 ровно та же проблема, что и с 1.3 (32-разрядная версия работает, 64-разрядная — нет).

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

Да, спасибо. Этот вариант, разумеется, работает.

Есть и более простой — Docker.

Виртуалку, кстати, можно и на Free/Net/DragonFly BSD завести. Там более современный юзерспейс одновременно с корректно работающим Linux ABI.

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

Два варианта.

  1. Открыть TCP-порт 6000 на локальном X-сервере и присоединиться по TCP (xhost в помощь). Это вариант «здесь и сейчас».

  2. Запустить в контейнере headless X-сервер (Xvfb) и любой VNC-сервер. После этого ты можешь присоединиться к рабочему столу Windows хоть из браузера. Далее такой образ можно распространять как душе угодно, ибо Docker работает везде. Экскременты мамонта становятся воистину кроссплатформенными. На эту тему на вражеском YouTube есть 100500 видео, заботливо озвученных профессиональными индийскими актёрами.

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

Просто сделай (в chroot-е с всем этим) apt-get install -t buster libc6, и разреши ему установить всё что он хочет. По крайне мере у меня он ничего за собой не тянул, ставил пакеты locales, libc6, libc-bin, libc-l10n (всё версии 2.28-10+deb10u1).

deb для скачивания: libc-bin libc6 libc-l10n locales

Не stat а lstat. Или там путаница с названиями? Можно брейкпоинт на libc-обёртку поставить.

java/lang/NoClassDefFoundError: java/lang/Object

Это в deb10 окружении что ли? Лови в deb9 где он есть.

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

Надо смотреть в код жвм той версии в то место, где он ищет rt.jar. Но что-то мне подсказывает, что её ты хрен найдёшь. Емнип открыли начиная с 1.4

Так jvm же одинаковая, разница во внешних библиотеках, там и надо искать.

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

Так jvm же одинаковая, разница во внешних библиотеках, там и надо искать.

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

Может оно ожидает, что путь до директории не будет оканчиваться на /, например.

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

Не надо если uid клиента подходящий (совпадает с Xorg-ом или чё-то такое, не помню). Если uid рандомный то авторизация на общих основаниях. Докер позволяет изолировать uid-ы вроде. Но всё равно докер не нужен.

firkax ★★★★★
()

1.3.1_20

До этой версии для линукса ещё было выпущено 49 других версий JDK 1.3. Не знаю, насколько там отличается инициализация, но нужно попробовать выбрать пару разных вариантов для сравнения.

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

BEA JRockit и IBM J9 появились существенно позднее. Во всяком случае, у меня есть лишь более поздние версии. Кроме того, проблем с совместимостью там будет больше — просто в силу того, что Sun JVM оттестирована гораздо большей пользовательской аудиторией. Уж проще собрать виртуалку.

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

Это не креш, а отсутствие зависимости, которая каким-то хреном была раньше в составе JDK, а затем её выкинули. Положи корбу в classpath и пользуйся своим приложением.

Это как кричать, что JavaFX приложение крешится в новых JDK, хотя нужно тупо зависимости добавить.

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

В моём случае это отсутствие зависимости на уровне внутреннего API JDK.

Грубо говоря, в 1.4 уже мог быть вменяемый JVMDI, а в 1.3 ещё нет (например).

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

Да он какой-то херней мается. Дрочит старую jdk в 2023 и Debian 10. Если все отлично пашет в 9-ке. То и сиди на 9-ке со своим мсофтом мамонта. Че не хватает - собери в чрут или напрямую, 9-ка не такая уж и древняя ось, если так прикинуть, что на ней преркрасно идёт софт из 2000 года и есть тот же докер и относительно современные clang/llvm

menangen ★★★★★
()

В качестве резюме (влруг кому интересно?): эксперименты с patchelf ни к чему толковому не привели. Всё работает для программы уровня «hello, world», но для ELF-файлов, имеющих сложные зависимости, всё далеко не так однозначно.

Проблема в том, что при замене libc.so.6 на более старый вариант необходимо заменять на более старые и прочие зависимости, а это целый ворох из libX11, libXt, libXp, libXdmcp и т. д.

В результате путь поиска libc.so.6, конечно, меняется, но в выводе ldd наблюдаем ошибки:

$ ldd libawt.so 
./libawt.so: /lib/i386-linux-gnu/glibc-2.24/libc.so.6: version `GLIBC_2.28' not found (required by /usr/lib/i386-linux-gnu/libX11.so.6)
./libawt.so: /lib/i386-linux-gnu/glibc-2.24/libc.so.6: version `GLIBC_2.28' not found (required by /usr/lib/i386-linux-gnu/libxcb.so.1)
./libawt.so: /lib/i386-linux-gnu/glibc-2.24/libc.so.6: version `GLIBC_2.25' not found (required by /lib/i386-linux-gnu/libuuid.so.1)
./libawt.so: /lib/i386-linux-gnu/glibc-2.24/libc.so.6: version `GLIBC_2.28' not found (required by /lib/i386-linux-gnu/libuuid.so.1)
./libawt.so: /lib/i386-linux-gnu/glibc-2.24/libc.so.6: version `GLIBC_2.25' not found (required by /usr/lib/i386-linux-gnu/libbsd.so.0)
./libawt.so: /lib/i386-linux-gnu/glibc-2.24/libc.so.6: version `GLIBC_2.28' not found (required by /lib/i386-linux-gnu/librt.so.1)
        linux-gate.so.1 (0xf7fc9000)
        libmlib_image.so => not found
        libjvm.so => not found
        libXp.so.6 => /usr/lib/i386-linux-gnu/libXp.so.6 (0xf7b28000)
        libXt.so.6 => /usr/lib/i386-linux-gnu/libXt.so.6 (0xf7abf000)
        libXext.so.6 => /usr/lib/i386-linux-gnu/libXext.so.6 (0xf7aaa000)
        libXtst.so.6 => /usr/lib/i386-linux-gnu/libXtst.so.6 (0xf7aa2000)
        libX11.so.6 => /usr/lib/i386-linux-gnu/libX11.so.6 (0xf7954000)
        libm.so.6 => /lib/i386-linux-gnu/glibc-2.24/libm.so.6 (0xf78fe000)
        libdl.so.2 => /lib/i386-linux-gnu/glibc-2.24/libdl.so.2 (0xf78f8000)
        libjava.so => not found
        libc.so.6 => /lib/i386-linux-gnu/glibc-2.24/libc.so.6 (0xf7737000)
        libXau.so.6 => /usr/lib/i386-linux-gnu/libXau.so.6 (0xf7732000)
        libSM.so.6 => /usr/lib/i386-linux-gnu/libSM.so.6 (0xf7727000)
        libICE.so.6 => /usr/lib/i386-linux-gnu/libICE.so.6 (0xf770a000)
        libXi.so.6 => /usr/lib/i386-linux-gnu/libXi.so.6 (0xf76f7000)
        libxcb.so.1 => /usr/lib/i386-linux-gnu/libxcb.so.1 (0xf76c9000)
        /lib/ld-linux.so.2 (0xf7fca000)
        libuuid.so.1 => /lib/i386-linux-gnu/libuuid.so.1 (0xf76c0000)
        libbsd.so.0 => /usr/lib/i386-linux-gnu/libbsd.so.0 (0xf76a1000)
        libXdmcp.so.6 => /usr/lib/i386-linux-gnu/libXdmcp.so.6 (0xf769a000)
        librt.so.1 => /lib/i386-linux-gnu/librt.so.1 (0xf768f000)
        libpthread.so.0 => /lib/i386-linux-gnu/libpthread.so.0 (0xf766c000)

Ну и при запуске, конечно, segmentation fault. Так что единственный выход – либо контейнер, либо виртуальная машина.

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