Изменить порядок загрузки библиотеки во время выполнения (например, LD_PRELOAD, но во время выполнения) - PullRequest
7 голосов
/ 18 июня 2010

Как изменить библиотеку, из которой загружается функция во время выполнения?

Например, скажем, я хочу заменить стандартную функцию printf чем-то новым, я могу написать свою собственную версию и скомпилировать ее в общую библиотеку, а затем поместить "LD_PRELOAD = / my / library.so" в среду перед запуском моего исполняемого файла.

Но давайте скажем, что вместо этого я хочу изменить эту связь внутри самой программы. Конечно, это должно быть возможно ... верно?

EDIT
И нет, следующее не работает (но если вы можете сказать мне, как это сделать, то этого будет достаточно).

void* mylib = dlopen("/path/to/library.so",RTLD_NOW);
printf = dlsym(mylib,"printf");

Ответы [ 6 ]

4 голосов
/ 18 июня 2010

AFAIK, это невозможно.Общее правило состоит в том, что если один и тот же символ появляется в двух библиотеках, ld.so отдает предпочтение библиотеке, которая была загружена первой.LD_PRELOAD работает, удостоверяясь, что указанные библиотеки загружены перед любыми неявно загруженными библиотеками.

Таким образом, после запуска выполнения все неявно загруженные библиотеки будут загружены, и поэтому слишком поздно загружать вашу библиотеку перед ними.

2 голосов
/ 10 сентября 2010

Чистого решения не существует, но оно возможно. Я вижу два варианта:

  1. Перезаписать пролог функции printf с переходом к функции замены.

    Это довольно популярное решение для перехвата функций в MS Windows. Вы можете найти примеры перехвата функций при переписывании кода в Google.

  2. Переписать таблицы перестановки / связывания ELF.

    См. эту статью о codeproject , которая делает почти точно то, что вы просите, но только в области действия модулей dlopen (). В вашем случае вы также хотите отредактировать ваш основной (обычно не PIC) модуль. Я не пробовал, но, может быть, это так же просто, как вызвать предоставленный код:

    void* handle = dlopen(NULL, RTLD_LAZY);
    void* original;
    original = elf_hook(argv[0], LIBRARY_ADDRESS_BY_HANDLE(handle), printf, my_printf);
    

    Если это не поможет, вам придется прочитать исходный код вашего динамического компоновщика, чтобы выяснить, что нужно адаптировать.

1 голос
/ 26 июня 2010

Следует сказать, что попытка заменить функции из libc в вашем приложении имеет неопределенное поведение в соответствии с ISO C / POSIX, независимо от того, выполняете ли вы это статически или динамически. Это может работать (и в основном будет работать на GNU / Linux), но не стоит полагаться на это. Если вы просто хотите использовать имя «printf», но у вас есть что-то нестандартное в вашей программе, лучший способ сделать это - #undef printf и #define printf my_printf ПОСЛЕ включая любые системные заголовки. Таким образом, вы не будете вмешиваться в какое-либо внутреннее использование функции библиотеками, которые вы используете ... и ваша реализация my_printf может даже вызвать системный printf, если / когда это потребуется.

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

0 голосов
/ 18 августа 2010

Сохраните результат dlsym () в таблице поиска (массив, хэш-таблица и т. Д.). Затем #undef print и #define print, чтобы использовать версию таблицы поиска.

0 голосов
/ 18 июня 2010

Вы не можете это изменить. В общем, символ концепции * NIX (или, скорее, отсутствия концепции) выбирается из первого объекта, где он найден. (За исключением oddball AIX, который по умолчанию работает больше как OS / 2.)

Программно вы всегда можете попробовать dlsym(RTLD_DEFAULT) и dlsym(RTLD_NEXT). man dlsym для более. Хотя это выходит из-под контроля довольно быстро. Почему редко используется.

0 голосов
/ 18 июня 2010

есть переменная окружения LD_LIBRARY_PATH, где компоновщик ищет библиотеки уничтожения, предваряет ваш путь к LD_LIBRARY_PATH, надеюсь, это сработает

...