Когда / Как Linux загружает разделяемые библиотеки в адресное пространство?
Мой вопрос заключается в следующем:
Когда адрес общих объектов указан в программах? Во время ссылки? Загрузка? Если бы я хотел найти адрес памяти system
командование внутри libc
внутри моей программы я мог легко найти это в gdb
, но что если я не хочу выводить программу в отладчик?
Может ли этот адрес меняться от запуска к запуску? Существуют ли какие-либо другие инструменты статического анализа, которые позволят увидеть, где библиотеки или функции будут загружены в область памяти этой программы при запуске?
РЕДАКТИРОВАТЬ: я хочу эту информацию за пределами программы (т.е. с использованием таких утилит, как objdump
собирать информацию)
5 ответов
Библиотеки загружаются ld.so
(динамический компоновщик или компоновщик времени выполнения aka rtld, ld-linux.so.2
или же ld-linux.so.*
в случае Linux; часть glibc). Он объявлен как "переводчик" (INTERP; .interp
раздел) всех динамически связанных двоичных файлов ELF. Итак, когда вы запустите программу, Linux начнет ld.so
(загрузить в память и перейти к точке входа), затем ld.so
загрузит вашу программу в память, подготовит ее и затем запустит. Вы также можете запустить динамическую программу с
/lib/ld-linux.so.2 ./your_program your_prog_params
ld.so
делает фактический open
а также mmap
всех необходимых файлов ELF, как файл ELF вашей программы, так и файлы ELF всех необходимых библиотек. Кроме того, он заполняет таблицы GOT и PLT и выполняет разрешение перемещений (он записывает адреса функций из библиотек на сайты вызовов, во многих случаях при косвенных вызовах).
Типичный адрес загрузки некоторой библиотеки, которую вы можете получить ldd
полезность. На самом деле это скрипт bash, который устанавливает переменную среды отладки ld.so (на самом деле LD_TRACE_LOADED_OBJECTS=1
в случае rtld glibc) и запускает программу. Вы даже можете сделать это самостоятельно без использования скрипта, например, с помощью bash, легко меняющего переменные окружения для одного запуска:
LD_TRACE_LOADED_OBJECTS=1 /bin/echo
ld.so
увидит эту переменную и разрешит все необходимые библиотеки и распечатает их адреса загрузки. Но с этой переменной, ld.so
фактически не запустит программу (не уверен насчет статических конструкторов программы или библиотек). Если функция ASLR отключена, адрес загрузки будет в большинстве случаев одинаковым. В современных Linux-системах часто включен ASLR, поэтому для его отключения используйте echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
,
Вы можете найти смещение system
функция внутри libc.so
с nm
Утилита от binutils. Я думаю, вы должны использовать nm -D /lib/libc.so
или же objdump -T /lib/libc.so
и вывод grep.
"Идите прямо к источнику и попросите лошадь..."
Drepper - Как писать общие библиотеки
Обязательно прочтите документацию для авторов библиотек Linux. Объясняет механику загрузки в некоторых деталях.
nm
команда, используемая на libc.so
, покажет вам местоположение system
символ в libc.so
, Однако, если ASLR включен, адрес libc.so
загружен в, и, таким образом, конечный адрес system
будет меняться случайным образом при каждом запуске вашей программы. Даже без ASLR вам нужно будет определить адрес libc.so
загружается и смещает адрес system
на эту сумму.
Если вы просто хотите получить адрес функции, не задавая имя в жестком виде, вы можете dlopen()
Основная программа:
void *self = dlopen(NULL, RTLD_NOW);
dlsym(self, "system"); // returns the pointer to the system() function
Если вам нужен только адрес функции, имя которой вы знаете во время компиляции, просто используйте void *addr = &system;
Я рекомендую, чтобы в вашей среде был путь LD_LIBRARY_PATH. Это определяет, где находятся общие библиотеки. Вам также может понадобиться заглянуть в /etc/ld.so.conf. Посмотрите на эту публикацию http://www.google.com/url?sa=t&source=web&cd=3&ved=0CCQQFjAC&url=http%3A%2F%2Fubuntuforums.org%2Fshowthread.php%3Ft%3D324660&ei=KqJpTey7JofEsAPE9_imBA&usg=AFQjCNEIbGGrTHp4fufRuj4Yfc58RTHcag&sig2=_9tdlyadMbPc-FcOdCko-w