Я пытаюсь использовать старую программу (где доступен только двоичный файл), которая просто застревает в IO в Debian Buster при работе на системах с более старыми выпусками glib c. Кто-то обнаружил, что проблема связана с исправлением glib c, введенным через https://sourceware.org/bugzilla/show_bug.cgi?id=1190
Мне удалось скомпилировать glib c с исправлением, закомментированным и запустив программу с помощью этого glib c он работает нормально.
Системный glib c показывает две функции, которые изменяет вышеуказанное исправление, поэтому я предполагаю, что они могут быть заменены собственными, которые эффективно отменяют исправление:
$ readelf -a /lib/x86_64-linux-gnu/libc.so.6 | grep underflow
1544: 000000000007ed60 86 FUNC GLOBAL DEFAULT 13 _IO_str_underflow@@GLIBC_2.2.5
1597: 0000000000074550 346 FUNC GLOBAL DEFAULT 13 __wunderflow@@GLIBC_2.2.5
1604: 00000000000753d0 1603 FUNC GLOBAL DEFAULT 13 _IO_wfile_underflow@@GLIBC_2.2.5
2152: 000000000007c520 705 FUNC GLOBAL DEFAULT 13 _IO_file_underflow@@GLIBC_2.2.5
2216: 000000000007d420 258 FUNC GLOBAL DEFAULT 13 __underflow@@GLIBC_2.2.5
Мне также удалось перехватить эти функции с помощью gdb.
Итак, я написал небольшую общую библиотеку, перехватывающую измененные функции. К сожалению, это не работает, потому что мои функции замены никогда не вызываются. В качестве теста я добавил замены для fread
и fwrite
таким же образом, и были названы те . Так что мой общий подход работает.
Еще несколько деталей: - исправление изменяет две функции _IO_new_file_underflow
в glib c iolib/fileops.c
и _IO_old_file_underflow
в iolib/oldfileops.c
- я написал два функции замены, которые отменяют исправление и затем вызывают оригинал:
int
_IO_new_file_underflow (FILE *fp)
{
int (*next)(FILE *fp) = dlsym(RTLD_NEXT, "_IO_new_file_underflow");
fprintf(stderr, "%s: called\n", __func__);
if (fp->_flags & _IO_EOF_SEEN)
fp->_flags &= ~_IO_EOF_SEEN;
return next(fp);
}
int
_IO_old_file_underflow (FILE *fp)
{
int (*next)(FILE *fp) = dlsym(RTLD_NEXT, "_IO_old_file_underflow");
fprintf(stderr, "%s: called\n", __func__);
if (fp->_flags & _IO_EOF_SEEN)
fp->_flags &= ~_IO_EOF_SEEN;
return next(fp);
}
- скомпилируйте его с помощью
gcc -o x.so -shared x.c -ldl -fPIC
- , запустите неисправную программу с помощью
LD_PRELOAD=/path/to/x.so faultyprogram
- Результат: функции замены никогда не вызываются, программа завершается ошибкой.
- Эти символы находятся в файле .so:
$ readelf -a x.so | grep underflow
8: 000000000000118a 117 FUNC GLOBAL DEFAULT 12 _IO_old_file_underflow
9: 0000000000001115 117 FUNC GLOBAL DEFAULT 12 _IO_new_file_underflow
47: 0000000000001115 117 FUNC GLOBAL DEFAULT 12 _IO_new_file_underflow
50: 000000000000118a 117 FUNC GLOBAL DEFAULT 12 _IO_old_file_underflow
В отличие от вышеприведенного вывода системного glib c в моей версии отсутствуют символы, поэтому я также добавил их через скрипт версии компоновщика:
$ cat version
VERSION {
GLIBC_2.2.5 {
global: *;
};
$ gcc -o x.so -shared x.c -ldl -fPIC version
$ readelf -a x.so | grep underflow
8: 0000000000001115 117 FUNC GLOBAL DEFAULT 13 _IO_new_file_underflow@@GLIBC_2.2.5
9: 000000000000118a 117 FUNC GLOBAL DEFAULT 13 _IO_old_file_underflow@@GLIBC_2.2.5
48: 0000000000001115 117 FUNC GLOBAL DEFAULT 13 _IO_new_file_underflow
51: 000000000000118a 117 FUNC GLOBAL DEFAULT 13 _IO_old_file_underflow
Но это тоже не работает.
Запуск ldd в программе с помощью LD_DEBUG=all
Я вижу много символов, но эти два вопроса не были разрешены компоновщиком. Я думаю, это потому, что они обрабатываются как некоторые частные функции в glib c. Но если это правда, почему символы вообще видны в lib c .so.6?
Теперь я задаюсь вопросом, упускаю ли я здесь что-то важное или вообще невозможно заменить LD_PRELOAD? для двух рассматриваемых функций.
Обновление: добавлен опыт работы с GDB, исправлена опечатка