Atualizado: 18/10/2025

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

Passagem de Estruturas entre Assembly e C/C++

As estruturas (structs) são uma forma prática de organizar dados e são amplamente utilizadas como parâmetros ou valores de retorno de funções em C/C++. A seguir, veremos como é possível transferir estruturas entre código C e Assembly — tanto enviando uma estrutura de C para Assembly quanto retornando uma estrutura de Assembly para C.


Recebendo Estruturas em Assembly a partir de uma Função C

Considere o seguinte arquivo person.c:

struct person { char* name; int age; };

struct person create_person()
{
    struct person tom = { .name = "Tom", .age = 39 };
    return tom;
}

Aqui, definimos a estrutura person com dois campos — name (nome) e age (idade). A função create_person() cria uma instância dessa estrutura e a retorna.

Compilação do arquivo para objeto:

gcc -c person.c -o person.o

Agora, vamos analisar o código Assembly gerado por meio de um disassembly:

objdump -d -M intel person.o

Saída simplificada:

0000000000000000 <create_person>:
   0:   f3 0f 1e fa             endbr64
   4:   55                      push   rbp
   5:   48 89 e5                mov    rbp,rsp
   8:   48 8d 05 00 00 00 00    lea    rax,[rip+0x0]
   f:   48 89 45 f0             mov    QWORD PTR [rbp-0x10],rax
  13:   c7 45 f8 27 00 00 00    mov    DWORD PTR [rbp-0x8],0x27
  1a:   48 8b 45 f0             mov    rax,QWORD PTR [rbp-0x10]
  1e:   48 8b 55 f8             mov    rdx,QWORD PTR [rbp-0x8]
  22:   5d                      pop    rbp
  23:   c3                      ret

A função create_person retorna os campos da estrutura por meio dos registradores RAX e RDX:

  • RAX → campo name
  • RDX → campo age

Esse comportamento segue a convenção do System V ABI para funções que retornam dois valores.

Agora, podemos recuperar esses valores em Assembly. Crie o arquivo app.asm:

global main

extern printf
extern create_person

section .data
name db "Name: %s", 10, 0
age  db "Age: %d", 10, 0

section .text
main:
    sub rsp, 24               ; 16 bytes para variáveis + 8 para alinhamento

    call create_person        ; retorna person em RAX (name) e RDX (age)

    mov [rsp+16], rax         ; salva name na pilha
    mov [rsp+8], rdx          ; salva age na pilha

    mov rdi, name
    mov rsi, [rsp+16]
    call printf

    mov rdi, age
    mov rsi, [rsp+8]
    call printf

    add rsp, 24
    ret

Após a chamada de create_person, os valores retornados são armazenados temporariamente na pilha e, em seguida, usados para exibição com printf.

Compilação e execução:

nasm -f elf64 app.asm -o app.o
gcc person.c app.o -static -o app
./app

Saída:

Name: Tom
Age: 39

Estruturas Mais Complexas

Quando uma estrutura contém mais de dois campos simples, seus dados passam a ser retornados via pilha, e não apenas por registradores.

Modifique o arquivo person.c:

struct person { int id; char* name; int age; };

struct person create_person()
{
    struct person tom = { .id = 112, .name = "Tom", .age = 39 };
    return tom;
}

Após compilar e inspecionar com objdump, obtemos algo como:

0000000000000000 <create_person>:
   8:   48 89 7d d8             mov    QWORD PTR [rbp-0x28],rdi
   c:   c7 45 e0 70 00 00 00    mov    DWORD PTR [rbp-0x20],0x70
  13:   48 8d 05 00 00 00 00    lea    rax,[rip+0x0]
  1a:   48 89 45 e8             mov    QWORD PTR [rbp-0x18],rax
  1e:   c7 45 f0 27 00 00 00    mov    DWORD PTR [rbp-0x10],0x27
  25:   48 8b 4d d8             mov    rcx,QWORD PTR [rbp-0x28]
  29:   48 8b 45 e0             mov    rax,QWORD PTR [rbp-0x20]
  2d:   48 8b 55 e8             mov    rdx,QWORD PTR [rbp-0x18]
  31:   48 89 01                mov    QWORD PTR [rcx],rax
  34:   48 89 51 08             mov    QWORD PTR [rcx+8],rdx
  38:   48 8b 45 f0             mov    rax,QWORD PTR [rbp-0x10]
  3c:   48 89 41 10             mov    QWORD PTR [rcx+16],rax

Nesse caso:

  • O ponteiro de destino da estrutura é passado em RDI.
  • A função preenche os campos no endereço recebido, na ordem dos campos definidos no struct.

Para receber a estrutura em Assembly, podemos escrever app.asm assim:

global main

extern printf
extern create_person

section .data
name db "Name: %s", 10, 0
age  db "Age: %d", 10, 0
id   db "Id: %d", 10, 0

section .text
main:
    sub rsp, 24             ; 24 bytes para os campos

    mov rdi, rsp            ; endereço de destino
    call create_person      ; função preenche a estrutura

    mov rdi, id
    mov rsi, [rsp]
    call printf

    mov rdi, name
    mov rsi, [rsp+8]
    call printf

    mov rdi, age
    mov rsi, [rsp+16]
    call printf

    add rsp, 24
    ret

Os três campos (id, name, age) são armazenados sequencialmente na pilha. A função create_person grava os dados diretamente nesse endereço.

Compilação e execução:

nasm -f elf64 hello.asm -o hello.o
gcc person.c hello.o -static -o hello
./hello

Saída:

Id: 112
Name: Tom
Age: 39

Enviando Estruturas de Assembly para C

Para enviar uma estrutura do Assembly para uma função em C, os campos são normalmente passados pela pilha.

Exemplo de person.c:

#include <stdio.h>

struct person { int id; char* name; int age; };

void print_person(struct person bob)
{
    printf("Id: %d\n", bob.id);
    printf("Name: %s\n", bob.name);
    printf("Age: %d\n", bob.age);
}

E o arquivo app.asm correspondente:

global main

extern print_person

section .data
name db "Bob", 0
age  dq 43
id   dq 111

section .text
main:
    sub rsp, 24

    mov rax, [id]
    mov [rsp], rax         ; id

    lea rax, [name]
    mov [rsp+8], rax       ; name

    mov rax, [age]
    mov [rsp+16], rax      ; age

    call print_person

    add rsp, 24
    ret

Os três campos são empilhados na mesma ordem em que aparecem na estrutura. A função C os interpreta corretamente ao receber o parâmetro struct person bob.

Compilação e execução:

nasm -f elf64 hello.asm -o hello.o
gcc person.c hello.o -static -o hello
./hello

Saída:

Id: 111
Name: Bob
Age: 43

Passagem de Ponteiro para Estrutura

Também é possível passar à função C um ponteiro para uma estrutura, em vez da estrutura completa. Nesse caso, a função recebe o endereço da estrutura como argumento.

Exemplo person.c:

#include <stdio.h>

struct person { int id; char* name; int age; };

void print_person(struct person* bob)
{
    printf("Id: %d\n", bob->id);
    printf("Name: %s\n", bob->name);
    printf("Age: %d\n", bob->age);
}

Código Assembly correspondente:

global main

extern print_person

section .data
name db "Sam", 0
age  dq 28
id   dq 110

section .text
main:
    sub rsp, 24

    mov rax, [id]
    mov [rsp], rax

    lea rax, [name]
    mov [rsp+8], rax

    mov rax, [age]
    mov [rsp+16], rax

    mov rdi, rsp            ; endereço da estrutura
    call print_person

    add rsp, 24
    ret

Nesse caso, o endereço da estrutura (armazenada temporariamente na pilha) é passado em RDI, o registrador usado para o primeiro parâmetro de funções no Linux (System V ABI).


Estrutura em Memória Estática

Também é possível armazenar os campos da estrutura diretamente em memória estática (na seção .data), evitando a alocação na pilha.

global main

extern print_person

section .data
name    db "Sam", 0
id      dq 110
namePtr dq name
age     dq 28

section .text
main:
    sub rsp, 8

    mov rdi, id            ; endereço do primeiro campo (início da estrutura)
    call print_person

    add rsp, 8
    ret

Nesse exemplo, RDI recebe o endereço de id, que é seguido pelos demais campos (namePtr e age) em sequência de 8 bytes cada. Essa abordagem economiza instruções e evita movimentação desnecessária de dados para a pilha, embora exija cuidado com alinhamento e tamanho dos campos.


Resumo

  • Estruturas pequenas (até dois campos simples) podem ser retornadas por registradores (RAX, RDX).
  • Estruturas maiores são retornadas por meio de um ponteiro, cujo endereço é passado em RDI.
  • Ao enviar estruturas de Assembly para C, os campos são empilhados na mesma ordem definida na estrutura.
  • Também é possível passar apenas o endereço da estrutura (ponteiro).
  • Códigos que utilizam .data podem economizar instruções e memória de pilha, desde que respeitem o alinhamento.
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