[java, C, linux] .a –> .so

Zusk

Имеется некоторая .so библиотека, которая не читается из явы.
System.loadLibrary(“libName”);
Суть ошибки описывает сообщение из exception:
Can't load IA 32-bit .so on a AMD 64-bit platform
Понятно, что платформа и версия используемой jre – amd64.
К тому же на x86 c jre-i586 библиотека читается и работает.
Во всех случаях используется jre-1.5.0_12
Задача следующая.
Дано:
1) исходников библиотеки нет.
2) есть разделяемая 32bit JNI библиотека (которая не читается)
3) есть 2 статических аналога этой библиотеки 32bit и 64bit (не-JNI, собранные без fPIC)
Получить:
1) разделяемую 64bit (JNI либо не-JNI – одно сводится к другому) библиотеку.
Более общая задача – это обеспечить загрузку библиотеки в яве.
Если есть решения – пишите.

Hastya

ну так возьми 32-битовую JVM

Dasar

ради одной либы?

Zusk

идея не нова, она проверялась в первую очередь.
Результат: jre-1.5.0_12-i586 там не работает сама по себе,
не может загрузить свои же либы. Сообщение слегка идиотское:
Can't load IA32 shared library on IA32 platform
задача сводится к оной из подзадач для линукса
(которые формулируются без использования JNI)
1) используя статическую 64bit библиотеку скомпилированную
без fPIC получить ее же, как если бы она была собрана с fPIC.
Как вариант:
2) найти дизассемблер или декомпилятор (в зависимости от того,
на каком уровне действует fPIC) который бы давал компилируемый
код. Пока работающих вариантов найдено не было.

Landstreicher

> 1) используя статическую 64bit библиотеку скомпилированную
> без fPIC получить ее же, как если бы она была собрана с fPIC.
IMHO это в принципе не возможно
> Как вариант:
> 2) найти дизассемблер или декомпилятор (в зависимости от того,
> на каком уровне действует fPIC) который бы давал компилируемый
> код. Пока работающих вариантов найдено не было.
IMHO это тоже невозможно.
-fPIC генерирует независимый от положения код (position independent code). Все смещения становятся относительными. Обращение к переменным производится с помощью косвенной адресации. Для этого либо выделяется специальный регистр, либо используется адресация относительно %rip. В обоих случаях требуются значительные изменения кода. Посмотри в .

Zusk

> IMHO это в принципе не возможно
Я так и знал. По крайней мере не буду больше заниматься этими подходами.
Заодно проверены jre-1.6.0_02-i586, jre-1.6.0_12-amd64
Результат тот же.

Marinavo_0507

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

Olyalyau

> 1) используя статическую 64bit библиотеку скомпилированную
> без fPIC получить ее же, как если бы она была собрана с fPIC.
IMHO это в принципе не возможно

Возможно.
Во-первых можно сделать враппер для функций в <...>.a И скомпилировать враппер с этой статической либой и ключиками -shared и -fPIC. После чего враппер можно использовать как <...>.so-либу.
Пожалуйста:
код статической либы 1.c:
 #include <stdio.h>

int a = 777;

int
b
{
return printf ("%d\n", a);
}

Компилируем и собираем в либу:

gcc -c 1.c -o 1.o -static
ar q lib1.a 1.o

Код враппера 2.c (функция dummy нужна чтобы линкер подцепил объекты статической либы, без неё он их может отбросить за ненадобностью при линковке враппера):
 
extern int a;
extern int b ;

void
dummy
{
a = 0;
b ;
}

Компилируем:
 
gcc 2.c -o lib2.so -shared -fPIC -L. -l1

А теперь тест 3.c:
 
extern int a;
extern int b ;

int
main
{
b ;
a = 888;
b ;
return 0;
}

Компилируем:
 
gcc 3.c -o 3 -L. -l2

Запускаем:
 
$ LD_LIBRARY_PATH=. ./3
777
888

Для любопытных:

$ LD_LIBRARY_PATH=. ldd ./3
linux-gate.so.1 => (0xb7f77000)
lib2.so => ./lib2.so (0xb7f75000)
libc.so.6 => /lib/tls/libc.so.6 (0x007ea000)
/lib/ld-linux.so.2 (0x007cc000)
$ objdump -x ./3 | grep '\<[ab]\>'
00000000 F *UND* 0000001e b
0804968c g O .bss 00000004 a
$ objdump -x lib2.so | grep '\<[ab]\>'
00000584 g F .text 0000001e b
00001700 g O .data 00000004 a

А во-вторых есть ещё вариант, соответствующий Дао Явы: берём нужную библиотеку, собранную как угодно (статически/динамически) под любую архитектуру, даже не совместимую с host-архитектурой. И интерпретируем (для этого сгодится допиленный эмулятор host-архитектуры) вызываемые функции этой библиотеки!

ppplva

Действительно работает, но в либе появляются text relocations, от которых серьезные люди плюются.
http://people.redhat.com/drepper/dsohowto.pdf

Marinavo_0507

$ LD_LIBRARY_PATH=. ldd ./3
linux-gate.so.1 => (0xb7f77000)
lib2.so => ./lib2.so (0xb7f75000)
libc.so.6 => /lib/tls/libc.so.6 (0x007ea000)
/lib/ld-linux.so.2 (0x007cc000)
$ objdump -x ./3 | grep '\<[ab]\>'
00000000 F *UND* 0000001e b
0804968c g O .bss 00000004 a
$ objdump -x lib2.so | grep '\<[ab]\>'
00000584 g F .text 0000001e b
00001700 g O .data 00000004 a

Непохоже это как-то на 64 бита

Olyalyau

Читал я этот толмуд, Дреппер мужик толковый.
Действительно работает, но в либе появляются text relocations, от которых серьезные люди плюются.
Если знаешь простой способ сделать из .a .so без исходников, и чтобы не появились text relocations, расскажи!
Потери производительности минимальны — порядка пары тактов процессора.
Тривиальный тест (4.c):

#include <stdint.h>
#include <sched.h>
#include <stdio.h>

extern int a;
extern int b ;

int
main
{
int i;
uint32_t s = UINT32_MAX;
uint32_t t = UINT32_MAX;

for (i = 0; i < (1 << 4); ++i)
{
uint32_t t0;
uint32_t t1;

sched_yield ;
asm volatile ("rdtsc" : "=a" (t0) : : "edx");
asm volatile ("rdtsc" : "=a" (t1) : : "edx");
if (s > t1 - t0)
s = t1 - t0;
}

for (i = 0; i < (1 << 20); ++i)
{
uint32_t t0;
uint32_t t1;

sched_yield ;
asm volatile ("rdtsc" : "=a" (t0) : : "edx");
b ;
asm volatile ("rdtsc" : "=a" (t1) : : "edx");
if (t > t1 - t0)
t = t1 - t0;
}

fprintf (stderr, "s = %u, t = %u\nt-s = %u\n", s, t, t-s);

return 0;
}

Запускаем:
 
gcc -c 1.c -o 1.o -static -O2
ar q lib1.a 1.o
gcc -c 1.c -o lib1.so -shared -fPIC -O2
gcc 2.c -o lib2.so -shared -fPIC -L. -l1 -O2
gcc 4.c -o 4-a -L. -l2 -O2
gcc 4.c -o 4-so -L. -l1 -O2
echo .a --\> .so:
LD_LIBRARY_PATH=. ./4-a >/dev/null
echo Native .so:
LD_LIBRARY_PATH=. ./4-so >/dev/null

Результат:
 
.a --> .so:
s = 11, t = 477
t-s = 466
Native .so:
s = 11, t = 475
t-s = 464

s постоянно, t менятся +/- 2 для обоих вариантов.

Olyalyau


Непохоже это как-то на 64 бита

Естественно:
$ uname -i
i386
Но, как это ни странно , метод работает на любой архитектуре.

ppplva

Плюются в основном из-за того, что это не очень грамотно в смысле безопасности, так как приходится маппить страницы одновременно на запись и исполнение. Ну и с SELinux работать не будет.

Olyalyau

Плюются в основном из-за того, что это не очень грамотно в смысле безопасности, так как приходится маппить страницы одновременно на запись и исполнение. Ну и с SELinux работать не будет.

Это всё зависит от реализации линкера. PLT можно сделать и без такого меппинга (он там для оптимизации). Так что отломают это оптимизирующую фичу у линкера — будет и под SELinux работать.

Zusk

Спасибо. На x86 работает. Но проделав это на amd64 мы приходим к изначальному результату,
способ борьбы с которым я безуспешно пытался найти

/usr/bin/ld: ./lib1.a(1.o): relocation R_X86_64_32S against `a local symbol'
can not be used when making a shared object; recompile with -fPIC
./lib1.a: could not read symbols: Bad value

ppplva

Насколько я понял, в x86_64 text relocations в исполнимых файлах не поддерживаются вообще. Это связано с тем, что там непосредственный операнд адреса может быть только 32-битным, и его подмена при динамической загрузке либы в общем случае невозможна (расстояние между инструкциями может оказаться больше чем 2^32). Поэтому все вызовы между разными объектами должны быть косвенными через GOT, то есть обязательно нужен -fPIC.
Оставить комментарий
Имя или ник:
Комментарий: