Одним из преимуществ общих библиотек и динамической компоновки является возможность переопределения функций библиотеки. Загрузчик поддерживает переменную среды
LD_PRELOAD, которая предварительно загружает символы библиотеки в исполняемый файл перед загрузкой общих библиотек, запрошенных исполняемым файлом.
Следовательно, если символ уже определен в библиотеке, указанной в LD_PRELOAD
, то этот символ предпочтительнее символов, загруженных позже.
Мы можем использовать эту возможность для переопределения библиотечных функций своими, даже если код уже скомпилирован, и у нас нет доступа к исходному коду!
Данная возможность часто используется для предоставления более оптимизированных версий различных системных функций, таких как malloc. В качестве простого примера переопределим функцию printf.
Пусть у нас изначально есть файл hello.s, который использует функцию printf
для вывода некоторой строки на консоль:
.globl main .data message: .asciz "Hello World\n" # строка для вывода .text main: subq $8, %rsp # выравнивание должно быть по 16 байтам leaq message(%rip), %rdi # параметр функции printf - строка call printf addq $8, %rsp ret
Скомпилируем и выполним программу:
root@Eugene:~/asm# gcc hello.s -o hello root@Eugene:~/asm# ./hello Hello World root@Eugene:~/asm#
В итоге мы получаем ожидаемый результат.
Далее определим следующий файл stdio.s:
.globl printf .data message: .ascii "new printf test\n" len=.-message .text printf: movq $1, %rdi leaq message(%rip), %rsi movq $len, %rdx movq $1, %rax # системный вызов номер 1 - функция write syscall ret
Данный код с помощью системного вызова номер 1 (системная функция write) выводит строку на консоль.
Для компиляции кода используем команду:
gcc -shared stdio.s -o libstdio.so
Затем установим скомпилированную библиотеку для предзагрузки с помощью команды
export LD_PRELOAD=./libstdio.so
В конце снова выполним приложение "hello"
root@Eugene:~/asm# gcc hello.s -o hello root@Eugene:~/asm# ./hello Hello World root@Eugene:~/asm# gcc -shared stdio.s -o libstdio.so root@Eugene:~/asm# export LD_LIBRARY_PATH=. root@Eugene:~/asm# export LD_PRELOAD=./libstdio.so root@Eugene:~/asm# ./hello new printf test root@Eugene:~/asm#
Для удаления предзагрузки нашей библиотеки (удаления значения переменной LD_PRELOAD) следует выполнить команду
unset LD_PRELOAD