Вопрос новичка: Как заменить xstat64/lxstat64/fxstat64 из GLIBC-2.17 64-битной машины на свои версии? (длинное)
Добрый день!
- Ранее была тема про компиляцию своих вызовов stat, lstat, xstat для замены стандартных из GLIBC. Оказалось, что эти вызовы на самом деле статические врапперы и заменить их LD_PRELOAD нельзя. Фактически работают скрытые вызовы __xstat, __lxstat, __fxstat, которые линкуются динамически, поэтому их заменить уже можно своими версиями. GLIBC использует такую последовательность вызовов для stat-подобных функций
функция скрытая системный
GRLIB функция вызов INLINE_SYSCALL
-------- ---------- --------------------
stat() -> __xstat() -> stat
lstat() -> __lxstat() -> lstat
fstat() -> __fxstat() -> fstat
-
Были написаны свои версии вызовов __xstat, __lxstat, __fxstat. Они подключаются c LD_PRELOAD, заменяют стандартные вызовы GLIBC и работают как нужно.
-
Приложение, с которыми я отлаживал вызовы, использует также вызовы __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 внешняя!!!!!!! требует замены
- Врапперы stat, fstat тут динамические, поэтому их можно заменить своей версией. Пример врапперов есть в GLIBC и их можно просто повторить как переходник между stat и __xstat.
#include <sys/stat.h>
int
stat (const char *file, struct stat *buf)
{
return __xstat (_STAT_VER, file, buf);
}
- Возникает вопрос - что делать с вызовами __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);
- Я сдублировал три своих вызова __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;
}
- Для проверки работоспособности новых вызовов я использовал специальный тест, где делал вызовы 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
- Затем тестовая программа запускалась с подключением библиотеки с новыми вызовами
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 работоспособный?
Спасибо за любые советы!