В общем случае загрузчик автоматически загружает все необходимые библиотеки при запуске приложения. Но мы также можем загружать их вручную. Для этого предназначены функции dlopen и dlsym. Функция dlopen указывает имя файла общей библиотеки, которую нужно открыть, а также параметр флагов для открытия библиотеки. Он возвращает указатель на дескриптор общей библиотеки, которую можно использовать для поиска символов с помощью dlsym. Функция dlsym ищет в общей библиотеке указанный символ и, если находит, возвращает его значение (обычно указатель). Подобные функции часто используются для добавления плагинов в код.
Например, пусть у нас есть файл exponent.s, который содержит функцию возведения числа в степень:
.globl exponent .text # Функция возвращает число в определенной степени # Принимает два параметра: # %rdi - число, возводимое в степень # %rsi - степень, в которую надо возвести число # Результат функции возвращается через регистр %rax exponent: movq $1, %rax # результат в %rax jmp condition # переход к проверке условия mainloop: mulq %rdi # %rax = %rax *%rdi decq %rsi condition: cmpq $0, %rsi jnz mainloop ret
Через регистр %rdi функция получает число, а через регистр %rsi - степень, в которую надо возвести число. Результат возвращается через регистр %rax. Для упрощения примера пусть наша функция работает только положительными показателями степеней.
Скомпилируем файл в общую библиотеку "libexp.so":
gcc -shared exponent.s -o libexp.so
И пусть у нас есть файл hello.s, который использует эту библиотеку:
.globl main .data file_name: .ascii "libexp.so\0" function_name: .asciz "exponent" message: .asciz "%d^%d = %d\n" # строка форматирования num: .quad 2 power: .quad 4 .text main: subq $8, %rsp # выравнивание должно быть по 16 байтам movq $file_name, %rdi movq $1, %rsi # флаг для отложенной загрузки call dlopen # загружаем библиотеку movq %rax, %rdi # помещаем дескриптор в %rdi movq $function_name, %rsi # помещаем имя функции в %rsi call dlsym # получаем указатель на функцию - в регистре %rax movq num, %rdi # первый параметр функции exponent - возводимое число movq power, %rsi # второй параметр функции exponent - степень call *%rax # выхываем функцию exponent через указатель в %rax # вывод результата movq $message, %rdi # первый параметр функции printf - строка форматирования movq num, %rsi # второй параметр функции printf - первое число movq power, %rdx # третий параметр функции printf - второе число movq %rax, %rcx # четвертый параметр функции printf - третье число - результат call printf addq $8, %rsp ret
Весь код разбивается на четыре части. Вначале открываем библиотеку, название которой хранится в переменной file_name:
movq $file_name, %rdi movq $1, %rsi # флаг для отложенной загрузки call dlopen # загружаем библиотеку
После вызова в регистре %rax будет дескриптор библиотеки. Далее вызываем функцию dlsym
, которой передается полученный дескриптор и имя функции:
movq %rax, %rdi # помещаем дескриптор в %rdi movq $function_name, %rsi # помещаем имя функции в %rsi call dlsym # получаем указатель на функцию - в регистре %rax
После вызова в регистр %rax помещается указатель на найденную функцию - в данном случае на функцию exponent. Используя полученный указатель, мы можем вызвать функцию:
movq num, %rdi # первый параметр функции exponent - возводимое число movq power, %rsi # второй параметр функции exponent - степень call *%rax # выхываем функцию exponent через указатель в %rax
Через регистры %rdi и %rsi функции передаются параметры. Поскольку в регистре %rax
хранится адрес функции, то с помошью выражения
call *%rax
вызываем код, который располагается по хранимому адресу.
В последней части программы с помощью функции printf
выводим полученный результат на консоль.
Для доступа к функциям dlopen
и dlsym
нам нужно загрузить библиотеку libdl.so. Скомпилируем приложение:
gcc -rdynamic -no-pie hello.s -ldl -o hello
Для упрощения примера здесь применяется флаг -no-pie
, которое создает зависимое от позиции приложение. Затем запустим приложение. Полный вывод компиля
root@Eugene:~/asm# gcc -shared exponent.s -o libexp.so root@Eugene:~/asm# gcc -rdynamic -no-pie hello.s -ldl -o hello root@Eugene:~/asm# ./hello 2^4 = 16 root@Eugene:~/asm#