Funções
Funções são unidades fundamentais de organização e execução em C. Elas encapsulam lógica, permitem reuso e facilitam abstrações - mas também são diretamente responsáveis pela organização da memória de execução via stack frames. Para quem mexe com baixo nível, cada chamada de função representa uma dança entre registradores, ponteiros e pilha.
Declaração e definição de função
int soma(int a, int b) {
return a + b;
}Tipo de retorno:
intNome:
somaParâmetros:
aeb(inteiros)Corpo: código executado quando chamada
Chamada de função
int resultado = soma(10, 20);Essa chamada envolve:
Passagem de parâmetros (por valor)
Alocação de stack frame
Salto (
call)Retorno (
ret)Reempilhamento da stack
Como funciona a chamada (x86 / x86_64)
Em x86 (32 bits):
Os parâmetros são passados pela stack (empilhados da direita pra esquerda).
A instrução
callempilha o endereço de retorno.A função chamada cria seu próprio stack frame com
push ebp/mov ebp, esp.Ao terminar, a função usa
ret, que desempilha o endereço de retorno e volta.
Em x86_64 (64 bits):
Os primeiros parâmetros vão em registradores:
rdi,rsi,rdx,rcx,r8,r9
Parâmetros extras ainda vão pela stack.
O valor de retorno vem no registrador
rax.
Esse comportamento segue a calling convention, como a System V ABI (Unix) ou stdcall, fastcall, etc (Windows).
Stack frame simplificado
A stack cresce "para baixo" - endereços menores.
Ponteiros de função
Funções também são endereços - podem ser atribuídas, passadas e chamadas dinamicamente:
Esse tipo de uso é comum em callbacks, tabelas de funções, e... exploração com ROP (Return-Oriented Programming).
Manipulação de stack e riscos
Funções são alvos frequentes de exploração:
Buffer overflow em variáveis locais pode sobrescrever o endereço de retorno → base de ataques como ret2libc.
Funções sem proteção (
-fno-stack-protector) ou com variáveis automáticas mal dimensionadas são vulneráveis.
Exemplo clássico:
Aqui, buffer usa a stack frame - e um input maior que 32 bytes pode sobrescrever o retorno da função.
Inline, recursão e otimizações
Funções simples podem ser inlined pelo compilador (sem salto, o código é copiado direto no lugar da chamada).
Recursão cria múltiplos stack frames — isso limita a profundidade e pode causar stack overflow.
O compilador pode usar técnicas como tail-call optimization para evitar empilhamentos desnecessários.
Last updated