Перенаправление вызова функции system() с использованием LD_PRELOAD

Я хочу заменить вызовы функции system(), сделанные моей программой, с помощью LD_PRELOAD.

Поэтому я создал следующие функции-оболочки в общей библиотеке для тестирования.

// syshook.c

int system(const char * command)
{
    printf("system() called for %s ************************************\n", command);
    return 55;
}

char * getenv (const char* name)
{
    printf("my getenv() *********************");
    return 0;
}

И скомпилирован и связан с общим объектом libsyshook.so с помощью gcc.

gcc -Wall -fPIC -c *.c
gcc -shared -Wl,-soname,libsyshook.so -o libsyshook.so.1.0
ln -s libsyshook.so libsyshook.so.1.0

Однако, когда я запускаю программу с LD_PRELOAD, как показано ниже, моя функция-оболочка для system() не вызывается, а вызывается оболочка для getenv().

LD_PRELOAD="libsyshook.so" myprog

Когда я подключаю отладчик, я вижу, что вызов system() вызывает реализацию в libpthread.so. Итак, почему перенаправление system() не работает. Я не думаю, что есть какие-либо ограничения на это ??

Изменить: моя тестовая программа, скомпилированная в myprog выше, выглядит так. Комментарии отражают мои наблюдения.

void TestClass::testMethod()
{
    string cmdLine = "date";
    if (!mainWin) cmdLine = "time";

    int retFromSys = system(cmdLine.c_str());   // goes into libpthread when stepped in.
    cout << "return from system " << retFromSys << endl; // prints 0, not 55  
    getenv("DEBUG_SYS");  // Wrapper function called for this. Prints "my getenv ****** ..."

person Sampath    schedule 07.04.2015    source источник
comment
Возможно, вам следует использовать абсолютный путь или хотя бы LD_PRELOAD="./libsyshook.so"   -  person Basile Starynkevitch    schedule 07.04.2015
comment
Используйте strace, чтобы понять, что происходит   -  person Basile Starynkevitch    schedule 07.04.2015
comment
@Basile Starynkevitch Я уже добавил путь к libsyshook.so в начало $LD_LIBRARY_PATH. На самом деле проблема только для system(), я вижу успешно вызванную оболочку getenv(). Так что это не из-за проблемы пути.   -  person Sampath    schedule 07.04.2015
comment
Покажите свою рабочую программу - отредактировав свой вопрос - или небольшой пример, похожий (для этого вопроса) на нее. Дайте нам команды компиляции и как вы это проверили.   -  person Basile Starynkevitch    schedule 07.04.2015
comment
@BasileStarynkevitch Я отредактировал свой пост, включив в него тестовый код и командную строку компиляции. Благодарю.   -  person Sampath    schedule 07.04.2015


Ответы (1)


Самый распространенный случай плохой компоновки с LD_PRELOAD — это когда GCC заменяет вашу функцию другой, когда он думает, что это может ускорить выполнение вашего кода.

Например, если GCC прочитает эту строку в вашем коде:

printf("%d", strlen("toto"));

Перед компиляцией он заменит эту строку:

puts("4");

Потому что он знает функции printf и strlen и думает, что результат будет таким же, как и с функцией puts.

В этом примере, если вы создали свою собственную функцию printf или strlen в библиотеке, загруженной с помощью LD_PRELOAD, ваша функция не будет вызываться после компиляции, потому что GCC заменил бы вызовы функций.

Я думаю, что ваша проблема по той же причине. system — очень тяжелая функция, и GCC может заменить вызов вашей функции другим. Например, если вы попытались:

system("ls");

GCC может заменить вашу строку на:

execlp("ls", "ls");

Вот бы сделать то же самое, но в менее тяжелом. Он не мог знать, что вы хотите использовать свою собственную функцию system. Попробуйте разобрать свой код, чтобы проверить, не в этом ли проблема.

В качестве решения я предлагаю вам попробовать вызвать system с более "случайным" параметром, чтобы заставить GCC думать, что он не должен пытаться заменить его. Может быть, что-то вроде:

int main(int argc, char** argv)
{
    char* line = NULL;

    // Useless condition to make GCC think the parameter is variable
    if (argc == 1)
        line = "ls";

    return (system(line));
}
person Aracthor    schedule 07.04.2015
comment
Насколько я знаю, system не заменяется оптимизацией GCC. Если да, то его нельзя заменить на execve, а следует заменить на fork, затем на execve, затем на waitpid. - person Basile Starynkevitch; 07.04.2015
comment
Вы уверены, что ? execlp был просто примером, некоторые менее известные функции, такие как popen, делают почти то же самое, что и system... Особенно, если параметр представляет собой одно двоичное имя, а не сложную командную строку. - person Aracthor; 07.04.2015
comment
Да, вы можете проверить с помощью strace или заглянув внутрь исходного кода GCC. Действительно, printf можно оптимизировать (через __builtin_printf), но system объявлена ​​как функция в <stdlib.h> (по крайней мере, в моем Debian/Sid/amd64), а __builtin_system нет. - person Basile Starynkevitch; 07.04.2015
comment
Ок... Спасибо за разъяснение так. - person Aracthor; 07.04.2015
comment
После входа в system() в режиме сборки в моей основной программе я увидел, что на самом деле это реализация system() в libpthread.so (уже упомянутая в исходном посте) (gdb) nexti 0x000000319840e3b0 in system () from /lib64/libpthread.so.0 (gdb) nexti 0x0000003198404e58 in __libc_system@plt () from /lib64/libpthread.so.0 (gdb) nexti 0x000000319783c5d0 in system () from /lib64/libc.so.6 - person Sampath; 07.04.2015
comment
Не могли бы вы попытаться скомпилировать и запустить часть вашего кода с вызовом system без привязки к libpthread? - person Aracthor; 07.04.2015
comment
@Aractor Да! Я написал простую программу без какой-либо конкретной привязки к libpthread. Теперь вызывается моя оболочка для system() !! Однако для исходного кода у меня нет большого контроля над средой сборки, поэтому мне нужно, чтобы это работало. Поскольку libpthread также вызывает libc-версию system() внутри, разве моя оболочка не должна вызываться в любом случае?? - person Sampath; 07.04.2015
comment
Я до сих пор не понимаю, почему ваша библиотека в LD_PRELOAD не имеет приоритета над другими... Вы сказали, что добавили libsyshook.so в начале LD_LIBRARY_PATH... Вы пытались добавить ее в конец, а не в начало? Здесь может быть важность порядка. - person Aracthor; 07.04.2015
comment
@Aracthor Пытался добавить путь libsyshook.so в конец LD_LIBRARY_PATH, а не в начало. Но не повезло :-( - person Sampath; 07.04.2015
comment
Извините, но я не понимаю, почему ваш libpthread вызывается перед вашим libsyshook... Кстати, я не знаю о вашей среде сборки, поэтому, если вы не можете получить к ней доступ или изменить ее, я не знаю. посмотрите, как вы могли бы использовать свою библиотеку. - person Aracthor; 07.04.2015
comment
На самом деле командная строка, которую я использовал для вызова своей программы, была LD_PRELOAD="libsyshook.so" myprog, где myprog был скриптом оболочки, который фактически вызывал исполняемый файл, который имел другое значение LD_PRELOAD при вызове. Итак, я решил проблему, поместив в скрипт правильное значение LD_PRELOAD. Но я не понимаю, почему не перехватывается только system(), а getenv перехватывается корректно. - person Sampath; 08.04.2015
comment
Вы должны были сказать, что myprog был сценарием оболочки, а не исполняемым файлом ... Я полагаю, что ваша оболочка использует перегрузку system для выполнения сценария оболочки, поэтому может стереть вашу собственную версию функции, и может быть поэтому getenv работал а не system. Но я в этом не уверен... - person Aracthor; 08.04.2015
comment
Да @Aractor. Я должен был сказать это, но различное поведение для system() и getenv() сбило меня с толку. Спасибо за все ваши отзывы. - person Sampath; 08.04.2015