Atualizado: 16/11/2025

Este conteúdo é original e não foi gerado por inteligência artificial.

Carregamento Dinâmico de Bibliotecas em Tempo de Execução em Assembly NASM

Normalmente, o loader dinâmico carrega automaticamente todas as bibliotecas necessárias quando um aplicativo é iniciado. No entanto, é possível carregar bibliotecas manualmente durante a execução do programa. Esse processo, conhecido como carregamento dinâmico explícito (explicit dynamic loading), é realizado com as funções dlopen e dlsym da biblioteca C.

  • dlopen: Abre uma biblioteca compartilhada especificada por seu nome de arquivo. Se bem-sucedida, retorna um "handle" (um ponteiro) para a biblioteca, que pode ser usado em chamadas subsequentes.
  • dlsym: Procura por um símbolo (geralmente o nome de uma função) dentro de uma biblioteca aberta. Se o símbolo for encontrado, a função retorna um ponteiro para ele.

Essa técnica é comumente usada para implementar sistemas de plugins ou extensões, onde a funcionalidade pode ser adicionada a um programa sem a necessidade de recompilá-lo.

Exemplo Prático

Vamos criar um exemplo onde um programa principal carrega dinamicamente uma biblioteca e chama uma função contida nela.

Primeiro, criamos a nossa biblioteca compartilhada, print.asm, com a função print:

global print:function

; Função print: imprime um texto no console
; Parâmetros:
; RDI - número de caracteres
; RSI - endereço da string
section .text
print:
    mov rdx, rdi
    mov rdi, 1
    mov rax, 1
    syscall
    ret

Compilamos este arquivo para criar a biblioteca libprint.so:

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

Agora, criamos o programa principal, app.asm, que irá carregar libprint.so e usar a função print.

global _start
extern dlopen
extern dlsym

section .data
    lib_name:      db "libprint.so", 0
    function_name: db "print", 0
    message:       db "Hello programicio.com", 10
    len:           equ $-message

section .text
_start:
    ; 1. Abrir a biblioteca
    lea rdi, [rel lib_name]         ; 1º parâmetro: nome do arquivo da biblioteca
    mov rsi, 1                      ; 2º parâmetro: flag (RTLD_LAZY)
    call dlopen wrt ..plt           ; Carrega a biblioteca

    ; 2. Encontrar o endereço da função
    mov rdi, rax                    ; 1º parâmetro: handle da biblioteca retornado por dlopen
    lea rsi, [rel function_name]    ; 2º parâmetro: nome da função
    call dlsym wrt ..plt            ; Obtém o ponteiro para a função (retornado em rax)

    ; 3. Chamar a função através do ponteiro
    mov rdi, len                    ; 1º parâmetro para 'print': tamanho da string
    lea rsi, [rel message]          ; 2º parâmetro para 'print': endereço da string
    call rax                        ; Chama a função cujo endereço está em rax

    mov rax, 60
    syscall

O código é dividido em três etapas claras:

  1. Abrir a biblioteca: Chamamos dlopen, passando o nome do arquivo da biblioteca ("libprint.so") e um flag. O valor 1 corresponde a RTLD_LAZY, que indica que os símbolos só serão resolvidos quando forem efetivamente usados. dlopen retorna o handle da biblioteca no registrador rax.
  2. Encontrar a função: Chamamos dlsym, passando o handle da biblioteca (que está em rax) e o nome da função que procuramos ("print"). dlsym retorna o endereço de memória da função print no registrador rax.
  3. Chamar a função: Preparamos os parâmetros para a nossa função print (tamanho e endereço da string) e, em seguida, usamos call rax para executar o código no endereço retornado por dlsym.

Para compilar o aplicativo, precisamos linká-lo com a libc (que contém dlopen e dlsym), mas não com a nossa libprint.so, pois ela será carregada em tempo de execução.

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

Ao executar, o programa app encontrará e carregará libprint.so por conta própria:

$ ./app
Hello programicio.com

É importante notar que o loader dinâmico ainda precisa ser capaz de encontrar libprint.so. Se a biblioteca não estiver em um diretório padrão, a variável LD_LIBRARY_PATH pode ser necessária, como vimos no artigo anterior.


Resumo

  • O carregamento dinâmico explícito permite que um programa carregue bibliotecas compartilhadas sob demanda durante sua execução.
  • A função dlopen é usada para abrir um arquivo de biblioteca (.so) e obter um handle para ela.
  • A função dlsym é usada para obter o endereço de uma função (ou variável) dentro de uma biblioteca já aberta, usando seu nome como uma string.
  • Uma vez obtido o endereço de uma função, ela pode ser chamada indiretamente, por exemplo, com call rax.
Política de Privacidade

Copyright © www.programicio.com Todos os direitos reservados

É proibida a reprodução do conteúdo desta página sem autorização prévia do autor.

Contato: programicio@gmail.com