# CALL e RET

Quando se trata de chamadas de procedimentos existem dois conceitos relacionados ao endereço deste procedimento.

O primeiro conceito é que existem chamadas "próximas" (*near*) e "distantes" (*far*). Enquanto no *near* `call` nós apenas especificamos o *offset* do endereço, no *far* `call` nós também especificamos o segmento.

O outro conceito é o de endereço "relativo" (*relative*) e "absoluto" (*absolute*), que também se aplicam para saltos (*jumps*). Onde um endereço relativo é basicamente um número **sinalizado** que será somado à RIP quando o desvio de fluxo ocorrer. Enquanto o endereço absoluto é um endereço exato que será escrito no registrador RIP.

### Tamanho do offset

O tamanho que o *offset* do endereço deve ter acompanha a largura do barramento interno. Então se estamos em *real mode* (16 bit), por padrão o *offset* deve ser de 16-bit. Ou seja, basicamente o mesmo tamanho do *Instruction Pointer*.

### Near relative call

```
call rel16/rel32
```

Essa é a `call` que já usamos, não tem segredo. Ela basicamente recebe um número negativo ou positivo indicando o número de bytes que devem ser desviados. Veja da seguinte forma:

```
Instruction_Pointer = Instruction_Pointer + operand
```

A matemática básica nos diz que "mais com menos é menos", ou seja, se o operando for negativo essa soma resultará em uma subtração.

#### Onde está RIP?

Existe um detalhe bem simples porém importante para conseguir lidar com endereços relativos corretamente. Quando o processador for executar a instrução o *Instruction Pointer* já estará apontando para a instrução seguinte. Ou seja desvios de fluxo para trás precisam contar os bytes da própria instrução em si, enquanto os para frente começam contando em zero que já é a instrução seguinte na memória.

Claro que esse cálculo não é feito por nós e sim pelo assembler, mas é importante saber. Ah, e lembra do símbolo `$` que eu falei que o NASM expande para o endereço da instrução atual? Veja que ele não coincide com o valor de RIP, cujo o mesmo já está apontando para a instrução seguinte.

Por exemplo poderíamos fazer uma chamada na própria instrução gerando um loop "infinito" usando a sintaxe:

```nasm
bits 64

call $
```

Experimente ver com o ndisasm como essa instrução fica em código de máquina:

![](/files/-MfDmIufxHtFBaa95zLM)

O primeiro byte (`0xE8`) é o opcode da instrução, que é o byte do código de máquina que identifica a instrução que será executada. Os bytes posteriores são o operando imediato (em *little-endian*). Repare que o endereço relativo está como `0xFFFFFFFB` que equivale a `-5` em decimal.

### Near absolute call

```
call r/m
```

Diferente da chamada relativa que indica um número de bytes a serem somados com RIP, numa chamada absoluta você passa o endereço exato de onde você quer fazer a chamada. Você pode experimentar fazer uma chamada assim:

```nasm
mov  rax, rotulo
call rax
```

Se você passar `rotulo` para a `call` diretamente você estará fazendo uma chamada relativa porque desse jeito você estará passando um operando imediato. E a única `call` que recebe valor imediato é a de endereço relativo, por isso o NASM passa o endereço relativo daquele rótulo. Mas ao definir o endereço do rótulo para um registrador ou memória o assembler irá passar o endereço absoluto dele.

É importante entender que tipo de operando cada instrução recebe para evitar se confundir sobre como o assembler irá montar a instrução. E sim, saber como a instrução é montada em código de máquina é muitas vezes importante.

### Far call

```
call seg16:off16   ; Em 16-bit
call seg16:off32   ; Em 32-bit

call mem16:16  ; Em 16-bit
call mem16:32  ; Em 32-bit
call mem16:64  ; Em 64-bit
```

As chamadas *far* (distante) são todas absolutas e recebem no operando um valor seguindo o formato de especificar um *offset* seguido do segmento de 16-bit. No NASM um valor imediato pode ser passado da seguinte forma:

```nasm
call 0x1234:0xabcdef99
```

Onde o valor à esquerda especifica o segmento e o da direita o deslocamento. Detalhe que essa instrução não é suportada em 64-bit.

O segundo tipo de *far* `call`, suportado em 64-bit, é o que recebe como operando um valor na memória. Mas perceba que temos um *near* `call` que recebe o mesmo tipo de argumento, não é mesmo?

Por padrão o NASM irá montar as instruções como *near* e não *far* mas você pode evitar essa ambiguidade explicitando com *keywords* do NASM que são bem intuitivas. Veja:

```nasm
call [rbx]       ; Próximo e absoluto
call near [rbx]  ; Próximo e absoluto
call far [rbx]   ; Distante
```

O *near* espera o endereço do *offset* na memória, não tem segredo. Mas o *far* espera o *offset* seguido do segmento. Em um sistema de 32-bit vamos supor que nosso procedimento está no segmento `0xaaaa` e no *offset* `0xbbbb1111`. Em memória o valor precisa estar assim em *little-endian*:

```
11 11 bb bb aa aa
```

No NASM essa variável poderia ser *dumpada* da seguinte forma:

```nasm
bits 32

my_addr: dd 0xbbbb1111   ; Deslocamento
         dw 0xaaaa       ; Segmento

; E usada assim:
call far [my_addr]
```

Basicamente o *far* `call` modifica o valor de CS e IP ao mesmo tempo, enquanto o *near* `call` apenas modifica o valor de IP.

{% hint style="info" %}
No código de máquina a diferença entre o *far* e o *near* **call** que usam o operando em memória está no campo REG do byte ModR/M. O *near* tem o valor 2 e o *far* tem o valor 3. O opcode é **0xFF**.

Se você não entendeu isso aqui, não se preocupa com isso. Mais para frente no livro será escrito um capítulo só para explicar o código de máquina da arquitetura.
{% endhint %}

### RET

```
ret
retf
retn
ret  imm16
retf imm16
retn imm16
```

Como talvez você já tenha reparado intuitivamente a chamada *far* também preserva o valor de CS na *stack* e não apenas o valor de IP (lembrando que IP já estaria apontando para a instrução seguinte na memória).

Por isso a instrução `ret` também precisa ser diferente dentro de um procedimento que será chamado com um *far call*. Ao invés de apenas ler o *offset* na *stack* ela precisa ler o segmento também, assim modificando CS e IP do mesmo jeito que o `call`.

Repetindo que o NASM por padrão irá montar as instruções como *near* então precisamos especificar para o NASM, em um procedimento que deve ser chamado como *far*, que queremos usar um `ret` *far*.\
Para isso podemos simplesmente adicionar um sufixo 'n' para especificar como *near*, que já é o padrão, ou o sufixo 'f' para especificar como *far*. Ficando:

```nasm
retf  ; Usado em procedimentos que devem ser chamados com far call
```

Existe também uma outra opção de instrução `ret` que recebe como operando um valor imediato de 16-bit que especifica um número de bytes a serem desempilhados da *stack*.

Basicamente o que ele faz é somar o valor de SP com esse número, porque como sabemos a pilha cresce "para baixo". Ou seja se subtraímos valor em SP estamos fazendo a pilha crescer, se somamos estamos fazendo ela diminuir. Por exemplo, podemos escrever em pseudo-código a instrução `retf 12` da seguinte forma:

{% code title="pseudo.c" %}

```c
RIP = pop();
CS  = pop();
RSP = RSP + 12;
```

{% endcode %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://mentebinaria.gitbook.io/assembly/aprofundando-em-assembly/call-e-ret.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
