Программная загрузка библиотек

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

Обычно загрузчик автоматически загружает все необходимые библиотеки при запуске приложения. Но при необходимости также можно загружать библиотеки вручную. Для этого предназначены встроенные функции dlopen и dlsym. Функция dlopen указывает имя файла общей библиотеки, которую нужно открыть, а также параметр флагов для открытия библиотеки. Эта функция возвращает указатель на дескриптор общей библиотеки, который можно использовать для поиска символов с помощью dlsym. Функция dlsym ищет в общей библиотеке указанный символ и, если находит, возвращает его значение (обычно указатель). Подобные функции часто используются для добавления плагинов в код.

Например, пусть у нас есть файл print.asm, который содержит функцию для вывода строки на консоль:

global print:function

; Функция print выводит текст на консоль
; Параметры
; RDI - количество символов
; RSI - ссылка на строку
section .text
print:
    mov rdx, rdi
    mov rdi, 1
    mov rax, 1
    syscall
    ret

Через регистр rdi функция получает количество символов, а через регистр rsi - адрес выводимой строки. Для вывода строки выполняется системный вызов номер 1 - функция write.

Скомпилируем файл в общую библиотеку "print.so":

nasm -felf64 print.asm -o print.o
ld -shared print.o -o print.so

И пусть у нас есть файл app.asm, который использует эту библиотеку:

global _start
extern dlopen
extern dlsym
 
section .data 
file_name: db "print.so", 0
function_name: db "print",0
message: db "Hello METANIT.COM",10, 0      ; строка для вывода на консоль
len equ $-message

section .text
_start:

    lea rdi, [rel file_name]
    mov rsi, 1                  ; флаг для отложенной загрузки
    call dlopen                 ; загружаем библиотеку
 
    mov rdi, rax                    ; помещаем дескриптор библиотеки в rdi
    lea rsi, [rel function_name]    ; помещаем имя функции в rsi
    call dlsym                      ; получаем указатель на функцию - в регистре rax

    mov rdi, len                    ; первый параметр функци print - количество символов
    lea rsi, [rel message]          ; второй параметр функции print - выводимая строка
    call rax         ; вызываем функцию print через указатель в rax

    mov rax, 60    
    syscall 

Весь код разбивается на три части. Вначале открываем библиотеку, название которой хранится в переменной file_name:

lea rdi, [rel file_name]
mov rsi, 1                  ; флаг для отложенной загрузки
call dlopen                 ; загружаем библиотеку

После вызова в регистре rax будет дескриптор библиотеки. Далее вызываем функцию dlsym, которой передается полученный дескриптор и имя функции:

mov rdi, rax                    ; помещаем дескриптор библиотеки в rdi
lea rsi, [rel function_name]    ; помещаем имя функции в rsi
call dlsym                      ; получаем указатель на функцию - в регистре rax

После вызова в регистр rax помещается указатель на найденную функцию - в данном случае на функцию print. Используя полученный указатель, мы можем вызвать функцию:

mov rdi, len                    ; первый параметр функци print - количество символов
lea rsi, [rel message]          ; второй параметр функции print - выводимая строка
call rax         ; вызываем функцию print через указатель в rax

Через регистры rdi и rsi функции передаются параметры. Поскольку в регистре rax хранится адрес функции, то с помошью выражения call rax вызываем код, который располагается по хранимому адресу.

Для доступа к функциям dlopen и dlsym нам нужно загрузить библиотеку libc.so. Скомпилируем приложение:

nasm -felf64 app.asm -o app.o
ld --dynamic-linker=/lib64/ld-linux-x86-64.so.2 -lc app.o -o app

Полный консольный вывод:

root@Eugene:~/asm# nasm -felf64 print.asm -o print.o
root@Eugene:~/asm# ld -shared print.o -o print.so
root@Eugene:~/asm# nasm -felf64 app.asm -o app.o
root@Eugene:~/asm# ld --dynamic-linker=/lib64/ld-linux-x86-64.so.2 -lc app.o -o app
root@Eugene:~/asm# ./app
Hello METANIT.COM
root@Eugene:~/asm# 
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850