LINUX.ORG.RU

Вопрос новичка: Как заменить xstat64/lxstat64/fxstat64 из GLIBC-2.17 64-битной машины на свои версии? (длинное)

 ,


0

1

Добрый день!

  1. Ранее была тема про компиляцию своих вызовов stat, lstat, xstat для замены стандартных из GLIBC. Оказалось, что эти вызовы на самом деле статические врапперы и заменить их LD_PRELOAD нельзя. Фактически работают скрытые вызовы __xstat, __lxstat, __fxstat, которые линкуются динамически, поэтому их заменить уже можно своими версиями. GLIBC использует такую последовательность вызовов для stat-подобных функций
функция     скрытая      системный
GRLIB       функция      вызов INLINE_SYSCALL
--------    ----------   --------------------					
stat()  ->  __xstat()   -> stat
lstat() ->  __lxstat()  -> lstat
fstat() ->  __fxstat()  -> fstat
  1. Были написаны свои версии вызовов __xstat, __lxstat, __fxstat. Они подключаются c LD_PRELOAD, заменяют стандартные вызовы GLIBC и работают как нужно.

  2. Приложение, с которыми я отлаживал вызовы, использует также вызовы __xstat64, __lxstat64 и __fxstat64 и свои врапперы stat(), lstat(). Которые пока остаются оригинальными. Хочется и их заменить своими версиями.

$ nm application | grep stat

  U __fxstat64@@GLIBC_2.2.5	из GLIBC!!!!!!!   требует замены
  U __fxstat@@GLIBC_2.2.5	из GLIBC!!!!!!!	  есть своя версия!!!
  U stat			внешняя!!!!!!!    требует замены
  U fstat			внешняя!!!!!!!    требует замены
  U __lxstat			внешняя!!!!!!!	  есть своя версия!!!
  U __lxstat64			внешняя!!!!!!!    требует замены
  U __xstat			внешняя!!!!!!!	  есть своя версия!!!
  U __xstat64			внешняя!!!!!!!    требует замены
  1. Врапперы stat, fstat тут динамические, поэтому их можно заменить своей версией. Пример врапперов есть в GLIBC и их можно просто повторить как переходник между stat и __xstat.
#include <sys/stat.h>
int
stat (const char *file, struct stat *buf)
{
  return __xstat (_STAT_VER, file, buf);
}
  1. Возникает вопрос - что делать с вызовами __xstat64, __lxstat64 и __fxstat64? Вызовы stat64, lstat64, fstat64 существуют на 32-битных платформах и на моей 64-бит платформе они заменяются едиными вызовами stat, lstat, fstat. Исходники вызовов stat64, lstat64, fstat64 пустые.

Разрешение имен вызовов осуществляется алиасами GRLIB

weak_hidden_alias (__stat, stat)
weak_hidden_alias (__lstat, lstat)
weak_hidden_alias (__fstat, fstat)
hidden_def (__xstat)
weak_alias (__xstat, _xstat)
hidden_def (__lxstat)
weak_alias (__lxstat, _lxstat)
hidden_def (__fxstat)
weak_alias (__fxstat, _fxstat)
strong_alias (__xstat, __xstat64);
strong_alias (__lxstat, __lxstat64);
weak_alias (__fxstat, _fxstat);
strong_alias (__fxstat, __fxstat64);
  1. Я сдублировал три своих вызова __xstat, __lxstat, __fxstat под новыми именами __xstat64, __lxstat64, __fxstat64 и собрал их всех в новую shared библиотеку mylib.so в надежде, что приложение их подхватит c LD_PRELOAD.
int __xstat64 (int vers, const char *name, struct stat *buf)
{
......
    // call system task
    res = INLINE_SYSCALL (stat, 2, name, CHECK_1 (buf));
......
    return res;
}

int __lxstat64 (int vers, const char *name, struct stat *buf)
{
......
    // call system task
    res = INLINE_SYSCALL (lstat, 2, CHECK_STRING (name), CHECK_1 (buf));
......
    return res;
}

int __fxstat64 (int vers, int fd, struct stat *buf)
{
......
    // call system task
    res = INLINE_SYSCALL (fstat, 2, fd, CHECK_1 (buf));
......
    return res;
}
  1. Для проверки работоспособности новых вызовов я использовал специальный тест, где делал вызовы stat(), lstat(), fstat(), stat64(), lstat64(), fstat64() для специально созданных файлов и линка и печатал информацию о них сначала изнутри моих вызовов а потом повторно из тела тестовой программы. Изначально тестовая программа ссылалась на вызовы из GRLIB
$ nm test_stat64 | grep stat

                 U __fxstat64@@GLIBC_2.2.5
                 U __fxstat@@GLIBC_2.2.5
                 U __lxstat64@@GLIBC_2.2.5
                 U __lxstat@@GLIBC_2.2.5
                 U __xstat64@@GLIBC_2.2.5
                 U __xstat@@GLIBC_2.2.5
  1. Затем тестовая программа запускалась с подключением библиотеки с новыми вызовами
LD_PRELOAD=./mylib.so test_stat64

Сначала запрашивается информация о файле testfile1 с помощью stat() Сначала запрашивается информация о файле testfile2 с помощью open(),fstat() Сначала запрашивается информация о линке testlink1 с помощью lstat()

stat -> __xstat

Сначала информация печатается изнутри вызова, затем из теста. Вызов заменен на новый __xstat(), что видно по печати.

Info: __xstat(1) call for /var/tmp/testfile1
__xstat: file name: /var/tmp/testfile1
__xstat: __xstat() result: 0
__xstat: stat.st_dev : 2050
__xstat: stat.st_ino : 5505095
__xstat: stat.st_mode : 33204
__xstat: stat.st_nlink : 1
__xstat: stat.st_uid : 1001
__xstat: stat.st_gid : 1001
__xstat: stat.st_rdev : 0
__xstat: stat.st_size : 0
__xstat: stat.st_blksize : 4096
__xstat: stat.st_blocks  : 0
__xstat: stat.st_atim : {1677590502, 0}
__xstat: stat.st_mtim : {1677590502, 0}
__xstat: stat.st_ctim : {1688124431, 879050715}

FILE /var/tmp/testfile1 stat(): 0
st_dev = 2050
st_ino = 5505095
st_mode = 100664
st_nlink = 1
st_uid = 1751
st_gid = 1751
st_rdev = 0
st_size = 0
st_blksize = 4096
st_blocks = 0
st_atim = 1677590502  Tue Feb 28 16:21:42 2023
st_mtim = 1677590502  Tue Feb 28 16:21:42 2023
st_ctim = 1688124431  Fri Jun 30 14:27:11 2023

fstat -> __fxstat

Сначала информация печатается изнутри вызова, затем из теста. Вызов заменен на новый __fxstat(), что видно по печати.

FILE /var/tmp/testfile2 open(): 3

Info: _fxstat(1) call for 3
_fxstat: file descr: 3
_fxstat: fstat() result: 0
_fxstat: stat.st_dev : 2050
_fxstat: stat.st_ino : 5516674
_fxstat: stat.st_mode : 33204
_fxstat: stat.st_nlink : 1
_fxstat: stat.st_uid : 1001
_fxstat: stat.st_gid : 1001
_fxstat: stat.st_rdev : 0
_fxstat: stat.st_size : 0
_fxstat: stat.st_blksize : 4096
_fxstat: stat.st_blocks  : 0
_fxstat: stat.st_atim : {1655144133, 0}
_fxstat: stat.st_mtim : {1655144133, 0}
_fxstat: stat.st_ctim : {1688124431, 942049389}

FILE /var/tmp/testfile2 fstat(): 0
st_dev = 2050
st_ino = 5516674
st_mode = 100664
st_nlink = 1
st_uid = 1751
st_gid = 1751
st_rdev = 0
st_size = 0
st_blksize = 4096
st_blocks = 0
st_atim = 1655144133  Mon Jun 13 21:15:33 2022
st_mtim = 1655144133  Mon Jun 13 21:15:33 2022
st_ctim = 1688124431  Fri Jun 30 14:27:11 2023

lstat -> __lxstat

Сначала информация печатается изнутри вызова, затем из теста. Вызов заменен на новый __lxstat(), что видно по печати.

Info: _lxstat(1) call for testlink1
_lxstat: file name: testlink1
_lxstat: lstat() result: 0
_lxstat: stat.st_dev : 2050
_lxstat: stat.st_ino : 3683704
_lxstat: stat.st_mode : 41471
_lxstat: stat.st_nlink : 1
_lxstat: stat.st_uid : 1001
_lxstat: stat.st_gid : 1001
_lxstat: stat.st_rdev : 0
_lxstat: stat.st_size : 9
_lxstat: stat.st_blksize : 4096
_lxstat: stat.st_blocks  : 0
_lxstat: stat.st_atim : {1688647746, 698036866}
_lxstat: stat.st_mtim : {1679927030, 675625380}
_lxstat: stat.st_ctim : {1679927030, 675625380}

LINK testlink1 stat(): 0
st_dev = 2050
st_ino = 3683704
st_mode = 120777
st_nlink = 1
st_uid = 1751
st_gid = 1751
st_rdev = 0
st_size = 9
st_blksize = 4096
st_blocks = 0
st_atim = 1688647746  Thu Jul  6 15:49:06 2023
st_mtim = 1679927030  Mon Mar 27 17:23:50 2023
st_ctim = 1679927030  Mon Mar 27 17:23:50 2023

С тремя первыми вызовами все нормально - они заменяются и работают

Далее те же действия повторяются с вызовами stat64, lstat64, fstat64.

stat64 -> __xstat64

Печати изнутри вызова __xstat64 нет, то есть он не заменился.

FILE /var/tmp/testfile1 stat64(): 0
st_dev = 2050
st_ino = 5505095
st_mode = 100664
st_nlink = 1
st_uid = 1751
st_gid = 1751
st_rdev = 0
st_size = 0
st_blksize = 4096
st_blocks = 0
st_atim = 1677590502  Tue Feb 28 16:21:42 2023
st_mtim = 1677590502  Tue Feb 28 16:21:42 2023
st_ctim = 1688124431  Fri Jun 30 14:27:11 2023

Печати изнутри вызова __fxstat64 нет, то есть он не заменился.

FILE /var/tmp/testfile2 open(): 3
FILE /var/tmp/testfile2 fstat64(): 0
st_dev = 2050
st_ino = 5516674
st_mode = 100664
st_nlink = 1
st_uid = 1751
st_gid = 1751
st_rdev = 0
st_size = 0
st_blksize = 4096
st_blocks = 0
st_atim = 1655144133  Mon Jun 13 21:15:33 2022
st_mtim = 1655144133  Mon Jun 13 21:15:33 2022
st_ctim = 1688124431  Fri Jun 30 14:27:11 2023

Печати изнутри вызова __lxstat64 нет, то есть он не заменился.

LINK testlink1 lstat64(): 0
st_dev = 2050
st_ino = 3683704
st_mode = 120777
st_nlink = 1
st_uid = 1751
st_gid = 1751
st_rdev = 0
st_size = 9
st_blksize = 4096
st_blocks = 0
st_atim = 1688647746  Thu Jul  6 15:49:06 2023
st_mtim = 1679927030  Mon Mar 27 17:23:50 2023
st_ctim = 1679927030  Mon Mar 27 17:23:50 2023

Таким образом, если вызовы __xstat, __lxstat, __fxstat были заменены на новые из моей shared библиотеки, то вызовы __xstat64, __lxstat64, __fxstat64 остались стандартными и не были заменены.

Судя по обсуждениям на форумах, проверить какие библиотеки были использованы приложением можно только в /proc/номерпроцесса/maps но тест выполняется очень быстро и зафиксировать номер процесса не получается.

Почему замена вызовов __xstat64, __lxstat64, __fxstat64 не произошла?

Что у меня идет не так?

Сам подход для замены __xstat64, __lxstat64, __fxstat64 работоспособный?

Спасибо за любые советы!


Сделал в конце теста останов, чтобы посмотреть номер процесса и посмотрел что находится внутри /proc/номерпроцесса/maps. К моему удивлению, там не никаких упоминаний про мою shared библиотеку mylib.so, хотя в протоколе есть печать изнутри моих __xstat, __lxstat, __fxstat, то есть они подключены. Странно.

igoro
() автор топика

Не вникал, но просто грепнул глибц там помимо обёртки __lxstat64 например есть ещё ___lxstat64 в lxstat.c lxstat64.c Может просто это вот «маршрутизация вызовов» попутана, не то меняешь.

Я бы сначала взял всё что найду в одну охапку и заменил на просто заглушки которые только выводят своё название в терминал. И только добившись этого (получив гарантию подмены символов в LD ) тыкал дальше.

LINUX-ORG-RU ★★★★★
()

возможно будет проще с переопределением функций через параметры линкера, как пример https://github.com/clibs/cmocka/tree/master/example/mock/uptime

как пример я использую такое

.........

/* glibc getline() call __getdelim() with '\n' */

ssize_t __wrap___getdelim(char ** restrict lineptr,
		size_t * restrict n,
		int delim,
		FILE * restrict stream)

{
  ........
}

/* mock for musl */

ssize_t __wrap_getline(char ** restrict lineptr,
		size_t * restrict n,
		FILE * restrict stream)
{
	return __wrap___getdelim(lineptr, n, '\n', stream);
}
.......

в правилах сборки

LDFLAGS = Wl,--wrap=__getdelim
imb ★★
()