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: int

  • Nome: soma

  • Parâmetros: a e b (inteiros)

  • Corpo: código executado quando chamada

Chamada de função

int resultado = soma(10, 20);

Essa chamada envolve:

  1. Passagem de parâmetros (por valor)

  2. Alocação de stack frame

  3. Salto (call)

  4. Retorno (ret)

  5. Reempilhamento da stack

Como funciona a chamada (x86 / x86_64)

Em x86 (32 bits):

  1. Os parâmetros são passados pela stack (empilhados da direita pra esquerda).

  2. A instrução call empilha o endereço de retorno.

  3. A função chamada cria seu próprio stack frame com push ebp / mov ebp, esp.

  4. Ao terminar, a função usa ret, que desempilha o endereço de retorno e volta.

Em x86_64 (64 bits):

  1. Os primeiros parâmetros vão em registradores:

    • rdi, rsi, rdx, rcx, r8, r9

  2. Parâmetros extras ainda vão pela stack.

  3. 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