Interrupção do x86, Minix e, de quebra, uma pitada de estouro de pilha
Um dos trabalhos que tivemos (eu, o João Misko e o Rafael Medeiros) que fazer para a disciplina de Sistemas Operacionais foi sobre estouro de pilhas no Minix. Como e onde ocorria, e como era tratado.
O grande salto quântico disso, era entender a tabela de interrupções do x86. Onde fica, para onde aponta e o que faz. Salto dado graças à imensa ajuda do Mestre Jorge Sabaliauskas! Se você não conhece o Jorjão, sinto muito!
Tabela de interrupção
Todo processador moderno possui uma tabela de interrupção (chamada de IDT – Interrupt Description Table – a partir daqui), que tem a função principal de executar uma determinada rotina (definida pelo SO) quando ocorre uma interrupção de hardware.
No x86, a tabela de interrupções é descrita conforme a tabela abaixo:

Tabela de exceções e interrupções retirada do manual "IA32-System-Programming-3A.pdf", Vol 3 5-3, para ser mais exato
Para entender um pouco melhor o que isso faz, usaremos o exemplo clássico da divisão por zero (não, não pode dividir por zero).
Sempre que algum programador tosco (como eu) solicitar uma divisão por zero (não pode mesmo!), o processador dá um berro e passa aponta o PC (Program Counter – o cara que aponta para o próximo trecho da memória a ser executado) para o endereço armazenado na posição zero da IDT.
A partir daí, o SO passa a tratar isso, inserindo o código de como quer tratar esse erro na posição apontada pela IDT.
Use Bochs, die alone
Antes de tudo, para conseguir encontrar o código apontado pela tabela de interrupção, você precisa usar o Bochs e com a versão de debug. Por que? Porque ele é um emulador e não um virtualizador. Dessa forma, você consegue ver as chamadas feitas ao hardware e manipular coisas que você não conseguiria manipular num virtualizador ou num computador real.
Direto da Wikipedia: “Num emulador, todos os recursos do sistema são processados nele (SIC) ao contrário da virtualização (SIC) que é uma ponte entre o hardware nativo e as chamadas do sistema operacional.”
Recomendo o Bochs de Windows, já que ele já vem compilado em sua última versão. Testei tanto pra OS X através do DarwinPorts, como pro Linux, mas a versão de Windows foi a que rodou mais tranquila. No Mac, o programa rodava extremamente lento, no Linux, tive diversos problemas durante a compilação para conseguir ativar o debuger. No Windows, foi só clicar duas vezes.
Achando a IDT e vendo a interrupção
Até então, vimos a parte teórica, relativamente fácil de entender… Conseguir achar isso na prática requer um pouco mais de paciência e ajuda do Jorjão (ou de um blog como esse, se você não conhece o Jorjão).
Para proteger o código apontado pela IDT, o x86 usa algumas artimanhas. Em vez de simplesmente apontar para o endereço da memória, ele utiliza uma base e um offset.
Para explicar melhor isso, vou usar o exemplo do trabalho sobre estouro de pilhas que fiz, e mostrar na prática o endereço usado pelo x86. Você pode ver na tabela acima que Stack-Segment Fault encontra-se na 12ª posição da IDT. Então pare a execução do Bochs (com CONTROL+C) e digite o seguinte na janela do do emulador (não na do sistema em andamento):
info idt 0xc

Saída do Bochs após o pedido de informação do IDT
Da resposta “Interrupt Gate target = 0×30:0x4b4” tiraremos duas informações que nos levarão ao endereço real apontado pela IDT.
O “0×30” é o 16 bit segment selector e o “0x4b4” o offset.
O segmente selector é dividido conforme a figura abaixo:

Divisão do segment selector
Portanto, a informação que procuramos está no GDT, na posição 6 (se você não entende como 12 virou c e 110 virou 6, estude um pouco sobre bases hexadecimal e binária)
Passe agora o seguinte comando para o Bochs:
info gdt 0x6

Saída do Bochs após o pedido de informação do GDT (Global Description Table)
O valor “0×1000” é o linear address, que usaremos junto ao offset para encontrar o endereço real de onde fica a rotina de interrupção.
Se você tiver um interesse masoquista por conhecimento, pode entender todas essas siglas e posições lendo os manuais da Intel que encontram-se aqui. É nele que toda essa loucura de proteção do endereço real é explicada.
Finalmente, com linear addres + offset, temos o endereço real, que é 0x14b4.
Para confirmar isso, digite o seguinte na janela de emulação do Bochs:
u 0x14b4

Saída do Bochs após o pedido de informação do endereço 0x14b4
A resposta é que na posição “0x14b4“, encontra-se “push 0x0c” em assembly. Para confirmar que esse é o início da rotina de interrupção de estouro de pilha do Minix, temos que olhar para dois arquivos:
kernel/mpx386.s e kernel/protect.h
No primeiro, na linha 457, vemos o seguinte comando em assembly: push STACK_FAULT_VECTOR e no segundo, na linha 58, #define STACK_FAULT_VECTOR 12 .
Juntando tudo, verificamos que estamos exatamente na posição de início do código que o Minix executará caso uma interrupção de estouro de pilha ocorra.
Se você quiser brincar um pouco e ver uma interrupção ocorrendo, pode inserir uma pausa no Bochs usando:
b 0x14b4
Com isso, caso PC aponte para 0x14b4, o emulador será pausado.
Agora faça um programa eternamente recursivo como o abaixo para ver a interrupção ocorrendo:
#include <stdio.h>
#include <stdlib.h>
void meChama(int i){
printf("iteracao %d\n", i++);
meChama(i);
}
int main (int argc, const char * argv[]) {
printf("iteracao 1\n");
meChama(1);
return 0;
}
Salve, compile, execute e veja a mágica.

Breackpoint em 0x14b4 mostrando sua mágica
A imagem acima mostra o exato momento em que o código que trata a interrupção de estouro de pilha começa a ser executado. Isso ocorre no momento em que o programa recursivo começaria a destruir toda sua memória e o processador (no caso, o emulador do processador) grita “PARA TUDO!!!” pro Minix e este passa a lidar com a bucha!
Tudo isso pode ser visto também nessa apresentação do Prezi que fizemos sobre estouro de pilha, mas que dependeu do entendimento da IDT e de todo o caminho tortuoso para achar o endereço real apontado por ela.


[...] This post was mentioned on Twitter by Rafael Barbolo and Eduardo Russo, Bit a Bit. Bit a Bit said: Interrupção do x86, Minix e, de quebra, uma pitada de estouro de pilha http://bit.ly/2g4lN6 [...]
Esse bochs é do mal. Cuidado com ele.
Estou pensando em postar os trabalhos que meu grupo fez aqui no Blog.
Parabéns pelo post Russo!
Demorou!
Eu fiz esse aqui, porque foi algo bastante difícil de encontrar. Acho que vai ajudar muita gente conseguir entender os caminhos tortuosos do IDT e GDT!
muito bom russo…
ps: bochs sux!