Interrupção do x86, Minix e, de quebra, uma pitada de estouro de pilha

VN:RO [1.9.11_1134]
quarta-feira, 11 d novembro d 2009
Por Eduardo Russo, Coop10. Siga no Twitter

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

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

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

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)

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

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

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.

VN:F [1.9.11_1134]
Rating: 5.0/5 (3 votes cast)
Interrupção do x86, Minix e, de quebra, uma pitada de estouro de pilha, 5.0 out of 5 based on 3 ratings
Related Posts with Thumbnails

Eduardo Russo
Eduardo Russo

Formado em Engenharia de Computação pela Poli (2010) e em Design pela Belas Artes (2001), cofundador do Bit a Bit, fundador do Tubelivery e do Faviconit, cofundador da Fábrica de Aplicativos e coordenador de produto do Scup.

Tags: , , , , , , , , ,

4 Comentários para “Interrupção do x86, Minix e, de quebra, uma pitada de estouro de pilha”

  1. [...] 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 [...]

    #84
  2. 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!

    VN:F [1.9.11_1134]
    Rating: 0 (from 0 votes)
    #88
    • Eduardo 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!

      VN:F [1.9.11_1134]
      Rating: 0 (from 0 votes)
      #90
  3. Bruno Toshyaki Maeda

    muito bom russo…
    ps: bochs sux!

    VA:F [1.9.11_1134]
    Rating: 0 (from 0 votes)
    #93

Deixe um Comentário

Spam Protection by WP-SpamFree

Get Adobe Flash playerPlugin by wpburn.com wordpress themes