Переопределение общих библиотек

Последнее обновление: 29.09.2023

Одним из преимуществ общих библиотек и динамической компоновки является возможность переопределения функций библиотеки. Загрузчик поддерживает переменную среды 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
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850