2025-05-14 12:52:20 -03:00
|
|
|
|
#+title: 1 -- Arquitetura de computadores
|
2025-05-14 11:36:15 -03:00
|
|
|
|
#+author: Blau Araujo
|
|
|
|
|
#+email: cursos@blauaraujo.com
|
2025-05-14 11:32:30 -03:00
|
|
|
|
|
2025-05-14 11:36:15 -03:00
|
|
|
|
#+options: toc:3
|
|
|
|
|
|
|
|
|
|
* Objetivos
|
2025-05-14 11:32:30 -03:00
|
|
|
|
|
|
|
|
|
- Compreender os principais componentes de um computador sob o modelo de von Neumann.
|
2025-05-17 15:13:38 -03:00
|
|
|
|
- Primeiro contato com os registradores da arquitetura x86_64.
|
|
|
|
|
- Entender a relação entre o hardware e código Assembly.
|
|
|
|
|
- Criar um primeiro programa em Assembly com uma chamada de sistema.
|
2025-05-14 11:32:30 -03:00
|
|
|
|
|
2025-05-14 11:36:15 -03:00
|
|
|
|
* Modelo de von Neumann
|
2025-05-14 11:32:30 -03:00
|
|
|
|
|
|
|
|
|
Um computador possui:
|
|
|
|
|
|
|
|
|
|
- Unidade de processamento (ALU + Controladora)
|
|
|
|
|
- Memória (armazenamento de instruções e dados)
|
|
|
|
|
- Dispositivos de entrada/saída
|
|
|
|
|
|
2025-05-17 15:13:38 -03:00
|
|
|
|
#+begin_example
|
|
|
|
|
┌───────────────────────────────────────┐
|
|
|
|
|
│ UNIDADE DE PROCESSAMENTO (CPU) │
|
|
|
|
|
│ │
|
|
|
|
|
│ • Unidade Lógica e Aritmética (ALU) │
|
|
|
|
|
│ • Unidade de Controle (CU) │
|
|
|
|
|
└───────────────────┬───────────────────┘
|
|
|
|
|
│
|
|
|
|
|
┌───────────────────┴───────────────────┐
|
|
|
|
|
│ BARRAMENTO │
|
|
|
|
|
└───────┬──────────────────────┬────────┘
|
|
|
|
|
│ │
|
|
|
|
|
┌───────┴───────┐ ┌────────┴────────┐
|
|
|
|
|
│ MEMÓRIA │ │ DISPOSITIVOS DE │
|
|
|
|
|
│ │ │ ENTRADA E SAÍDA │
|
|
|
|
|
└───────────────┘ └────────┬────────┘
|
|
|
|
|
│
|
|
|
|
|
┌────────┴────────┐
|
|
|
|
|
│ ARMAZENAMENTO │
|
|
|
|
|
└─────────────────┘
|
|
|
|
|
|
|
|
|
|
#+end_example
|
|
|
|
|
|
|
|
|
|
A arquitetura de Von Neumann é um modelo de hardware no qual a Unidade Central
|
|
|
|
|
de Processamento (CPU) e a memória utilizam um espaço único de armazenamento
|
|
|
|
|
para guardar tanto as instruções dos programas quanto os dados. Isso significa
|
|
|
|
|
que a CPU acessa a mesma memória para buscar instruções e para ler ou gravar
|
|
|
|
|
dados, utilizando um único barramento para ambas as operações.
|
2025-05-14 11:32:30 -03:00
|
|
|
|
|
2025-05-17 15:13:38 -03:00
|
|
|
|
Resumindo, no modelo de Von Neumann:
|
2025-05-14 11:32:30 -03:00
|
|
|
|
|
|
|
|
|
- Instruções e dados compartilham o mesmo espaço de memória.
|
2025-05-17 15:13:38 -03:00
|
|
|
|
- A CPU executa o ciclo: busca → decodificação → execução.
|
2025-05-14 11:32:30 -03:00
|
|
|
|
|
2025-05-17 15:13:38 -03:00
|
|
|
|
Em outros modelos da época (como a arquitetura de Harvard):
|
2025-05-14 11:32:30 -03:00
|
|
|
|
|
|
|
|
|
- Instruções e dados armazenados em memórias separadas.
|
2025-05-17 15:13:38 -03:00
|
|
|
|
- A CPU executa o ciclo: busca → decodificação → execução, podendo buscar
|
|
|
|
|
dados e instruções em paralelo.
|
2025-05-14 11:32:30 -03:00
|
|
|
|
|
2025-05-14 11:36:15 -03:00
|
|
|
|
** Influência nas arquiteturas modernas
|
2025-05-14 11:32:30 -03:00
|
|
|
|
|
|
|
|
|
O modelo de Von Neumann influenciou profundamente as arquiteturas modernas de
|
|
|
|
|
computadores, estabelecendo a base para a maioria dos designs de sistemas
|
|
|
|
|
computacionais atuais. Algumas das principais influências incluem:
|
|
|
|
|
|
|
|
|
|
- Memória unificada ::
|
|
|
|
|
|
|
|
|
|
A ideia de compartilhar a mesma memória para dados e instruções se manteve
|
|
|
|
|
como o padrão em muitas arquiteturas modernas, simplificando o design dos
|
|
|
|
|
sistemas, embora existam variações: como a memória cache, que separa dados e
|
|
|
|
|
instruções em níveis mais próximos ao processador.
|
|
|
|
|
|
|
|
|
|
- Ciclo de busca, decodificação e execução ::
|
|
|
|
|
|
|
|
|
|
O modelo de Von Neumann introduziu o ciclo básico de execução de instruções,
|
|
|
|
|
que ainda é fundamental em CPUs modernas. As arquiteturas atuais, como x86 e
|
|
|
|
|
ARM, seguem esse ciclo de maneira semelhante, embora com otimizações como a
|
|
|
|
|
/pipelining/, onde múltiplas fases do ciclo podem ser realizadas em paralelo.
|
|
|
|
|
|
|
|
|
|
- Programação e flexibilidade ::
|
|
|
|
|
|
|
|
|
|
O modelo permite que os programas sejam tratados como dados, o que possibilita
|
|
|
|
|
a criação de sistemas que podem ser facilmente modificados e adaptados. Isso
|
|
|
|
|
contribui para o desenvolvimento de linguagens de programação de alto nível,
|
|
|
|
|
sistemas operacionais e aplicativos dinâmicos.
|
|
|
|
|
|
|
|
|
|
- Simplicidade e custo ::
|
|
|
|
|
|
|
|
|
|
O modelo de Von Neumann simplificou o design de computadores ao integrar a
|
|
|
|
|
memória de instruções e dados. Isso contribuiu para a redução de custos de
|
|
|
|
|
hardware, tornando os sistemas mais acessíveis e facilitando o desenvolvimento
|
|
|
|
|
de sistemas comerciais e pessoais.
|
|
|
|
|
|
|
|
|
|
#+begin_quote
|
|
|
|
|
*Nota:* A /pipelining/ (do conceito de /"linha de montagem"/) é uma técnica de
|
|
|
|
|
execução paralela em que múltiplas instruções são processadas simultaneamente
|
|
|
|
|
em diferentes /estágios/ do ciclo de execução para aumentar o desempenho da
|
|
|
|
|
CPU. Isso difere do conceito geral de /paralelismo/, onde /tarefas completas/, e
|
|
|
|
|
não estágios do processamento, são executadas simultaneamente.
|
|
|
|
|
#+end_quote
|
|
|
|
|
|
2025-05-14 11:36:15 -03:00
|
|
|
|
** Gargalo de Von Neumann
|
2025-05-14 11:32:30 -03:00
|
|
|
|
|
2025-05-17 15:13:38 -03:00
|
|
|
|
O gargalo de Von Neumann é uma limitação de desempenho causada pelo fato de
|
|
|
|
|
que a CPU e a memória compartilham o mesmo barramento para acessar instruções
|
|
|
|
|
e dados.
|
2025-05-14 11:32:30 -03:00
|
|
|
|
|
|
|
|
|
Isso significa que:
|
|
|
|
|
|
|
|
|
|
- A CPU não pode buscar uma instrução e acessar dados ao mesmo tempo.
|
|
|
|
|
- O tempo de espera entre operações aumenta, especialmente em programas que
|
|
|
|
|
exigem muitos acessos à memória.
|
|
|
|
|
- A largura de banda do barramento se torna um fator crítico para o desempenho.
|
|
|
|
|
|
|
|
|
|
Esse gargalo levou ao desenvolvimento de soluções como:
|
|
|
|
|
|
|
|
|
|
- Memória cache (para reduzir acessos à RAM)
|
|
|
|
|
- Execução paralela e /pipelines/
|
|
|
|
|
- Arquiteturas modificadas (como o modelo Harvard modificado)
|
|
|
|
|
|
|
|
|
|
#+begin_quote
|
|
|
|
|
*Nota:* A /arquitetura Harvard modificada/ é uma variação do modelo de Von Neumann
|
|
|
|
|
que usa memórias separadas internamente (como em caches) para dados e instruções,
|
|
|
|
|
mantendo um espaço de memória unificado do ponto de vista do programador.
|
|
|
|
|
#+end_quote
|
|
|
|
|
|
2025-05-14 11:36:15 -03:00
|
|
|
|
* Arquiteturas x86
|
2025-05-14 11:32:30 -03:00
|
|
|
|
|
|
|
|
|
A arquitetura x86 é uma família de conjuntos de instruções (ISA – /Instruction
|
|
|
|
|
Set Architecture/) baseada no modelo de Von Neumann e desenvolvida originalmente
|
2025-05-17 15:13:38 -03:00
|
|
|
|
pela Intel a partir do processador 8086. Todas as CPUs x86 implementam (ou
|
|
|
|
|
emulam) o conjunto de instruções da Intel 8086 (16 bits), lançada em 1978.
|
2025-05-14 11:32:30 -03:00
|
|
|
|
|
2025-05-17 15:13:38 -03:00
|
|
|
|
Como uma evolução prática do modelo de Von Neumann, a arquitetura x86 implementa
|
|
|
|
|
otimizações como:
|
2025-05-14 11:32:30 -03:00
|
|
|
|
|
2025-05-17 15:13:38 -03:00
|
|
|
|
- Uso extensivo de memória cache.
|
|
|
|
|
- Execução fora de ordem (/out-of-order execution/).
|
|
|
|
|
- /Pipelines/ e paralelismo interno.
|
2025-05-14 11:32:30 -03:00
|
|
|
|
|
2025-05-14 11:36:15 -03:00
|
|
|
|
** Características
|
2025-05-14 11:32:30 -03:00
|
|
|
|
|
|
|
|
|
- ISA complexa (CISC – /Complex Instruction Set Computing/), com centenas de
|
|
|
|
|
instruções e modos de endereçamento.
|
|
|
|
|
- Suporte a múltiplos tamanhos de palavra (16, 32 e 64 bits nas versões modernas).
|
|
|
|
|
- Registradores de uso geral (AX, BX, CX, etc.) e segmentados (CS, DS, SS...,
|
|
|
|
|
não utilizados no Linux), herdados de versões mais antigas.
|
|
|
|
|
- Ampla compatibilidade com versões anteriores (retrocompatibilidade).
|
|
|
|
|
- Utilizada em desktops, laptops e servidores, sendo a base da maioria dos PCs atuais.
|
|
|
|
|
|
|
|
|
|
#+begin_quote
|
2025-05-17 15:13:38 -03:00
|
|
|
|
*Nota:* Embora a arquitetura x86 inclua registradores segmentados (como CS, DS,
|
|
|
|
|
ES, SS), o Linux não faz uso da segmentação de memória no modo protegido. Em
|
|
|
|
|
vez disso, ele utiliza o chamado /endereçamento plano/, tratando toda a memória
|
|
|
|
|
como um único espaço contínuo. A segmentação é mantida apenas em um nível
|
|
|
|
|
mínimo para atender exigências da arquitetura (como troca de contexto e
|
|
|
|
|
proteção básica), mas a segmentação lógica, como era usada no MS-DOS, é
|
|
|
|
|
totalmente evitada.
|
2025-05-14 11:32:30 -03:00
|
|
|
|
#+end_quote
|
|
|
|
|
|
2025-05-17 15:13:38 -03:00
|
|
|
|
** Gerações da família x86:
|
|
|
|
|
|
|
|
|
|
| Processador | Ano | Registradores | Endereçamento | Modos de operação | Destaques principais |
|
|
|
|
|
|----------------+------+---------------+--------------------------------+--------------------------+-----------------------------------------------------------------|
|
|
|
|
|
| 8086 | 1978 | 16 bits | 20 bits (1 MB) | Real mode | Primeiro da linha x86, sem proteção ou multitarefa |
|
|
|
|
|
| 80286 | 1982 | 16 bits | 24 bits (16 MB) | Real, Protected | Introduziu o modo protegido e privilégio de acesso por segmento |
|
|
|
|
|
| 80386 | 1985 | 32 bits | 32 bits (4 GB) | Real, Protected, Virtual | Registradores de 32 bits, suporte à paginação, multitarefa |
|
|
|
|
|
| 80486 | 1989 | 32 bits | 32 bits | Idem 80386 | Pipeline simples, cache L1, primeira versão com FPU integrada |
|
|
|
|
|
| Pentium | 1993 | 32 bits | 32 bits | Idem 80486 | Pipeline duplo, superscalar, introdução de MMX |
|
|
|
|
|
| Pentium Pro | 1995 | 32 bits | 36 bits (PAE) | Idem | Execução fora de ordem, cache L2 on-die, suporte a PAE |
|
|
|
|
|
| x86-64 (AMD64) | 2003 | 64 bits | 48 bits virtuais (até 57 hoje) | Real, Protected, Long | ISA de 64 bits, registradores expandidos, compatível com x86 |
|
|
|
|
|
|
|
|
|
|
A transição da arquitetura x86 de 32 para 64 bits foi liderada pela AMD com a
|
|
|
|
|
criação da AMD64 em 2003. Essa extensão manteve compatibilidade com o conjunto
|
|
|
|
|
de instruções x86 original (IA-32), mas adicionou registradores de 64 bits e
|
|
|
|
|
suporte a endereçamento ampliado. Posteriormente, a Intel adotou essa mesma
|
|
|
|
|
arquitetura sob o nome Intel 64 (anteriormente chamada EM64T), e o termo x86_64
|
|
|
|
|
passou a ser usado de forma genérica para se referir à arquitetura compatível
|
|
|
|
|
com AMD64. Assim, o AMD64 é a origem técnica da arquitetura x86 de 64 bits
|
|
|
|
|
utilizada na maioria dos sistemas modernos.
|
2025-05-14 11:32:30 -03:00
|
|
|
|
|
2025-05-14 11:36:15 -03:00
|
|
|
|
** Comparativo com outras arquiteturas
|
2025-05-14 11:32:30 -03:00
|
|
|
|
|
|
|
|
|
Todas as arquiteturas modernas seguem, em maior ou menor grau, os princípios
|
|
|
|
|
do modelo de Von Neumann. No entanto, diferem na forma como organizam e executam
|
|
|
|
|
instruções. Veja o comparativo:
|
|
|
|
|
|
|
|
|
|
*x86 (CISC – /Complex Instruction Set Computing/):*
|
|
|
|
|
|
|
|
|
|
- Conjunto de instruções extenso e complexo.
|
|
|
|
|
- Instruções de vários tamanhos e com múltiplos modos de endereçamento.
|
|
|
|
|
- Maior consumo de energia, mas com alta compatibilidade e maior desempenho bruto.
|
|
|
|
|
- Retrocompatibilidade com código legado.
|
|
|
|
|
- Comum em PCs, laptops e servidores.
|
|
|
|
|
|
|
|
|
|
*ARM (RISC – /Reduced Instruction Set Computing/)*
|
|
|
|
|
|
|
|
|
|
- Conjunto de instruções reduzido e regular.
|
|
|
|
|
- Foco em simplicidade, baixa energia e eficiência.
|
|
|
|
|
- Desempenho por watt muito superior ao x86.
|
|
|
|
|
- Predominante em dispositivos móveis (smartphones, tablets) e embarcados.
|
|
|
|
|
- ARM64 (AArch64) é a versão de 64 bits moderna.
|
|
|
|
|
|
|
|
|
|
*RISC-V (RISC e /open source/):*
|
|
|
|
|
|
|
|
|
|
- ISA aberta, modular e extensível.
|
|
|
|
|
- Sem royalties: qualquer um pode implementar.
|
|
|
|
|
- Design limpo e simples, adequado para pesquisa, educação e sistemas customizados.
|
|
|
|
|
- Crescimento em sistemas embarcados e processadores personalizados.
|
|
|
|
|
|
|
|
|
|
*Resumo comparativo:*
|
|
|
|
|
|
|
|
|
|
| Arquitetura | Tipo | Complexidade | Consumo | Uso comum |
|
|
|
|
|
|-------------+------+--------------+---------+-------------------------------|
|
|
|
|
|
| x86 | CISC | Alta | Alto | PCs, laptops, servidores |
|
|
|
|
|
| ARM | RISC | Média | Baixo | Celulares, IoT, Apple M1+ |
|
|
|
|
|
| RISC-V | RISC | Baixa | Baixo | Pesquisa, sistemas embarcados |
|
|
|
|
|
|
2025-05-14 11:36:15 -03:00
|
|
|
|
* Componentes de uma CPU x86_64
|
2025-05-14 11:32:30 -03:00
|
|
|
|
|
|
|
|
|
- Unidade de Controle (/Control Unit/ - CU) ::
|
|
|
|
|
|
|
|
|
|
Gerencia o fluxo de dados e as instruções dentro da CPU, coordenando as operações
|
|
|
|
|
de execução.
|
|
|
|
|
|
|
|
|
|
- Unidade Lógica e Aritmética (ALU – /Arithmetic and Logic Unit/) ::
|
|
|
|
|
|
|
|
|
|
Responsável pela execução de operações aritméticas (soma, subtração, etc.) e
|
|
|
|
|
lógicas (AND, OR, NOT, etc.).
|
|
|
|
|
|
|
|
|
|
- Registradores de uso geral ::
|
|
|
|
|
|
|
|
|
|
Utilizados para armazenar dados temporários durante a execução de instruções
|
|
|
|
|
(ex.: RAX, RBX, RCX, etc.).
|
|
|
|
|
|
|
|
|
|
- Registradores de propósito específico ::
|
|
|
|
|
|
|
|
|
|
Como o ponteiro de pilha (RSP), ponteiro de instrução (RIP) e flags (como EFLAGS).
|
|
|
|
|
|
|
|
|
|
- Registradores de segmentos ::
|
|
|
|
|
|
|
|
|
|
Armazenam endereços de segmentos de memória para código (CS), dados (DS) e pilha
|
|
|
|
|
(SS), além dos registradores de segmentos adicionais para dados (ES, FS e GS).
|
|
|
|
|
|
|
|
|
|
- Cache ::
|
|
|
|
|
|
|
|
|
|
Memória de acesso ultrarrápido usada para armazenar dados frequentemente
|
|
|
|
|
acessados, visando reduzir o tempo de acesso à memória principal. Normalmente
|
|
|
|
|
dividida em L1, L2 e, em algumas CPUs, L3.
|
|
|
|
|
|
|
|
|
|
- Barramentos ::
|
|
|
|
|
|
|
|
|
|
Conjunto de trilhas de comunicação que transportam dados entre a CPU e outros
|
|
|
|
|
componentes, como a memória, dispositivos de entrada/saída e outros núcleos.
|
|
|
|
|
|
|
|
|
|
- Unidade de Execução (/Execution Unit/ – EU) ::
|
|
|
|
|
|
|
|
|
|
Responsável por executar as instruções. Em CPUs modernas, pode haver múltiplas
|
|
|
|
|
unidades de execução para executar diferentes tipos de operações em paralelo.
|
|
|
|
|
|
|
|
|
|
- Decodificador de Instruções ::
|
|
|
|
|
|
|
|
|
|
Interpreta as instruções da linguagem de máquina (bytecode) e as converte para
|
|
|
|
|
operações que podem ser executadas pela ALU ou outras unidades de execução.
|
|
|
|
|
|
|
|
|
|
- Unidade de carga e armazenamento (/Load-Store Unit/ - LSU) ::
|
|
|
|
|
|
|
|
|
|
Controla o carregamento e armazenamento de dados na memória, realizando
|
|
|
|
|
operações de leitura e escrita.
|
|
|
|
|
|
|
|
|
|
- Unidade de Endereçamento (/Address Generation Unit/ - AGU) ::
|
|
|
|
|
|
|
|
|
|
Calcula endereços de memória, especialmente no contexto de operações de acesso
|
|
|
|
|
à memória e cálculo de ponteiros.
|
|
|
|
|
|
|
|
|
|
- Controlador de Interrupções (/Interrupt Controller/) ::
|
|
|
|
|
|
|
|
|
|
Responsável por lidar com interrupções externas e internas, gerenciando a
|
|
|
|
|
prioridade e o tratamento adequado das interrupções no sistema.
|
|
|
|
|
|
2025-05-14 11:36:15 -03:00
|
|
|
|
* Principais registradores e seus propósitos (64 bits)
|
2025-05-14 11:32:30 -03:00
|
|
|
|
|
|
|
|
|
- =RAX=: acumulador/propósito geral
|
|
|
|
|
- =RBX=: base/propósito geral
|
|
|
|
|
- =RCX=: contador/propósito geral
|
|
|
|
|
- =RDX=: dados/propósito geral
|
|
|
|
|
- =RSI=: índice de origem
|
|
|
|
|
- =RDI=: índice de destino
|
|
|
|
|
- =RSP=: ponteiro da pilha
|
|
|
|
|
- =RBP=: base da pilha
|
|
|
|
|
- =RIP=: ponteiro de instrução
|
|
|
|
|
- =RFLAGS=: sinalizações diversas
|
|
|
|
|
|
|
|
|
|
A arquitetura x86_64 ainda inclui oito registradores de propósito geral, de =R8=
|
|
|
|
|
a =R15=.
|
|
|
|
|
|
2025-05-17 15:13:38 -03:00
|
|
|
|
** Relação com outras arquiteturas x86
|
|
|
|
|
|
|
|
|
|
Cada um dos registradores de 64 bits pode ser dividido em partes que, de certo
|
|
|
|
|
modo, correspondem às capacidades de 32 e 16 bits de outras arquiteturas da
|
|
|
|
|
família x86. Tomando o registrador =RAX= como exemplo:
|
|
|
|
|
|
|
|
|
|
#+begin_example
|
|
|
|
|
┌────────────────────────────────────────────────────┐
|
|
|
|
|
│ RAX │ 64 bits
|
|
|
|
|
└────────────────────────┬───────────────────────────┤
|
|
|
|
|
│ EAX │ 32 bits
|
|
|
|
|
└────────────┬──────────────┤
|
|
|
|
|
│ AX │ 16 bits
|
|
|
|
|
├───────┬──────┤
|
|
|
|
|
│ AH │ AL │
|
|
|
|
|
└───────┴──────┘
|
|
|
|
|
#+end_example
|
|
|
|
|
|
|
|
|
|
#+begin_quote
|
|
|
|
|
*Importante:* O diagrama não mostra registradores diferentes, mas os nomes
|
|
|
|
|
pelos quais as subdivisões do registrador podem ser acessadas!
|
|
|
|
|
#+end_quote
|
|
|
|
|
|
|
|
|
|
Aplicando a mesma ideia aos principais registradores, os nomes de suas
|
|
|
|
|
subdivisões seriam:
|
|
|
|
|
|
|
|
|
|
| 64 bits | 32 bits | 16 bits | 8 bits "altos" | 8 bits "baixos" |
|
|
|
|
|
|---------+---------+---------+----------------+-----------------|
|
|
|
|
|
| RAX | EAX | AX | AH | AL |
|
|
|
|
|
| RBX | EBX | BX | BH | BL |
|
|
|
|
|
| RCX | ECX | CX | CH | CL |
|
|
|
|
|
| RDX | EDX | DX | DH | DL |
|
|
|
|
|
| RSI | ESI | SI | (sem acesso) | SIL |
|
|
|
|
|
| RDI | EDI | DI | (sem acesso) | DIL |
|
|
|
|
|
| RSP | ESP | SP | (sem acesso) | SPL |
|
|
|
|
|
| RBP | EBP | BP | (sem acesso) | BPL |
|
|
|
|
|
| RIP | EIP | IP | (sem acesso) | (sem acesso) |
|
|
|
|
|
| R8 | R8D | R8W | (sem acesso) | R8B |
|
|
|
|
|
| ... | ... | ... | ... | ... |
|
|
|
|
|
| R15 | R15D | R15W | (sem acesso) | R15B |
|
|
|
|
|
|
|
|
|
|
#+begin_quote
|
|
|
|
|
*Nota:* Assim como os registradores de R8 a R15, nem todas as subdivisões
|
|
|
|
|
existiam nas arquiteturas de 32 e 16 bits da família x86, ou seja, elas só
|
|
|
|
|
foram introduzidas na arquitetura x86_64.
|
|
|
|
|
#+end_quote
|
|
|
|
|
|
2025-05-14 11:36:15 -03:00
|
|
|
|
* Primeiro exemplo em Assembly x86_64
|
2025-05-14 11:32:30 -03:00
|
|
|
|
|
2025-05-17 15:13:38 -03:00
|
|
|
|
Apenas para apresentar a aparência de um código em Assembly, aqui está o
|
|
|
|
|
código de um programa que não faz nada além de terminar com o estado de
|
|
|
|
|
término =42=...
|
|
|
|
|
|
2025-05-14 11:43:10 -03:00
|
|
|
|
Arquivo [[exemplos/01/exit42.asm][exit42.asm]]
|
|
|
|
|
|
2025-05-14 11:42:35 -03:00
|
|
|
|
#+begin_src asm :tangle exemplos/01/exit42.asm
|
2025-05-14 11:32:30 -03:00
|
|
|
|
; Retorna 42 como estado de término
|
|
|
|
|
|
|
|
|
|
section .text
|
|
|
|
|
global _start
|
|
|
|
|
|
|
|
|
|
_start:
|
|
|
|
|
mov rax, 60 ; syscall: exit
|
|
|
|
|
mov rdi, 42 ; código de saída
|
|
|
|
|
syscall
|
|
|
|
|
#+end_src
|
|
|
|
|
|
2025-05-17 15:13:38 -03:00
|
|
|
|
O programa é pequeno, mas nos dá a oportunidade de conhecer muitos dos elementos
|
|
|
|
|
de um programa escrito com a linguagem Assembly.
|
|
|
|
|
|
|
|
|
|
** Seção do código executável
|
|
|
|
|
|
|
|
|
|
#+begin_src asm
|
|
|
|
|
section .text
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
A diretiva =section= não é uma instrução da CPU, mas uma orientação sobre como
|
|
|
|
|
o binário do programa deverá ser montado. No caso do exemplo, estamos dizendo
|
|
|
|
|
ao montador que tudo que vier depois de =section=, até que se encontre outra
|
|
|
|
|
definição de seção, deve ser montado na seção =.text= do binário. O nome =.text=
|
|
|
|
|
é uma convenção que se refere à seção das instruções do programa em si (o seu
|
|
|
|
|
código, essencialmente).
|
|
|
|
|
|
|
|
|
|
** O ponto de entrada
|
|
|
|
|
|
|
|
|
|
#+begin_src asm
|
|
|
|
|
global _start
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
A diretiva =global= diz ao montador que um determinado /rótulo/ (como =_start=)
|
|
|
|
|
deve ser visível fora do arquivo em que é escrito. Em outras palavras, o
|
|
|
|
|
símbolo marcado com =global= pode ser referenciado por outros arquivos durante
|
|
|
|
|
o processo de link-edição. No exemplo, =_start= é o rótulo que representa, por
|
|
|
|
|
padrão, o endereço da primeira instrução a ser executada em um programa ou,
|
|
|
|
|
como se costuma dizer, o seu /ponto de entrada/.
|
|
|
|
|
|
|
|
|
|
** Chamada de sistema
|
|
|
|
|
|
|
|
|
|
Nem todo programa em Assembly fará chamadas de sistema, que são funções
|
|
|
|
|
internas do kernel para diversas finalidades que envolvam o acesso aos
|
|
|
|
|
recursos do hardware ou controlados pelo sistema. No exemplo, porém, nós
|
|
|
|
|
queremos utilizar a chamada de sistema =exit= para terminar a execução do
|
|
|
|
|
programa retornando o valor =42= para o sistema operacional.
|
|
|
|
|
|
|
|
|
|
Para isso, nós temos que seguir as convenções de chamada, definidas pelo
|
|
|
|
|
kernel para a arquitetura x86_64. Essas convenções estabelecem, por exemplo,
|
|
|
|
|
quais registradores devem ser utilizados para receber a identificação da
|
|
|
|
|
chamada de sistema e os argumentos que serão passados para ela.
|
|
|
|
|
|
|
|
|
|
No caso da chamada de sistema =exit=:
|
|
|
|
|
|
|
|
|
|
- O registrador =rax= deve receber o valor =60= (identificação da chamada =exit=).
|
|
|
|
|
- O registrador =rdi= deve receber o valor que será retornado como estado de
|
|
|
|
|
término (=42=).
|
|
|
|
|
|
|
|
|
|
Sendo assim, nós utilizamos as instruções =mov= para carregar (mover) os
|
|
|
|
|
valores esperados (argumentos) nos registradores apropriados:
|
|
|
|
|
|
|
|
|
|
#+begin_src asm
|
|
|
|
|
mov rax, 60 ; syscall: exit
|
|
|
|
|
mov rdi, 42 ; código de saída
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
Em seguida, nós utilizamos a instrução =syscall= para informar à CPU que
|
|
|
|
|
ela deveria invocar a chamada de sistema definida:
|
|
|
|
|
|
|
|
|
|
#+begin_src asm
|
|
|
|
|
syscall
|
|
|
|
|
#+end_src
|
|
|
|
|
|
2025-05-14 11:44:53 -03:00
|
|
|
|
** Montagem e execução (no terminal)
|
2025-05-14 11:32:30 -03:00
|
|
|
|
|
|
|
|
|
#+begin_example
|
|
|
|
|
:~$ nasm -f elf64 -o exit42.o exit42.asm
|
2025-05-17 15:13:38 -03:00
|
|
|
|
#+end_example
|
|
|
|
|
|
|
|
|
|
O resultado desse comando é a geração de um arquivo binário que nós chamamos
|
|
|
|
|
de /objeto/ (com terminação =.o=). O arquivo objeto contém a tradução de todo o
|
|
|
|
|
código em Assembly para código de máquina e já poderia, por exemplo, ser
|
|
|
|
|
compilado com outros objetos para compor um programa completo, mas ainda não
|
|
|
|
|
é capaz de ser executado.
|
|
|
|
|
|
|
|
|
|
Para isso, é necessário submeter o objeto a diversos procedimentos finais
|
|
|
|
|
chamados de /link-edição/ -- no caso, com o editor de ligações =ld=:
|
|
|
|
|
|
|
|
|
|
#+begin_example
|
2025-05-14 11:32:30 -03:00
|
|
|
|
:~$ ld -o exit42 exit42.o
|
2025-05-17 15:13:38 -03:00
|
|
|
|
#+end_example
|
|
|
|
|
|
|
|
|
|
Assim, o editor de ligações =ld=:
|
|
|
|
|
|
|
|
|
|
- Resolve endereços e símbolos (como =_start=);
|
|
|
|
|
- Organiza o layout final do programa;
|
|
|
|
|
- Adiciona seções obrigatórias (como os cabeçalhos ELF);
|
|
|
|
|
- Produz um binário executável.
|
|
|
|
|
|
|
|
|
|
Com o binário final gerado, nós podemos executá-lo e testar seu estado
|
|
|
|
|
de término com:
|
|
|
|
|
|
|
|
|
|
#+begin_example
|
2025-05-14 11:32:30 -03:00
|
|
|
|
:~$ ./exit42
|
|
|
|
|
:~$ echo $?
|
|
|
|
|
42
|
|
|
|
|
#+end_example
|
|
|
|
|
|
2025-05-17 15:13:38 -03:00
|
|
|
|
Onde =$?= é a expansão do parâmetro especial do shell =?=, que registra o estado
|
|
|
|
|
de término do último comando executado. Então, com o valor em =?= expandido,
|
|
|
|
|
nós podemos imprimi-lo com o comando interno =echo=.
|
|
|
|
|
|
2025-05-14 11:36:15 -03:00
|
|
|
|
* Exercícios sugeridos
|
2025-05-14 11:32:30 -03:00
|
|
|
|
|
2025-05-17 15:13:38 -03:00
|
|
|
|
1. Antecipe-se e pesquise o que são "chamadas de sistema" (/syscalls/).
|
|
|
|
|
2. Modifique o programa Assembly para retornar "sucesso", segundo as convenções
|
|
|
|
|
do shell do GNU/Linux.
|
|
|
|
|
3. Por que eu usei o termo "montagem" para me referir à produção do arquivo
|
|
|
|
|
binário a partir do código-fonte?
|
|
|
|
|
4. Escreva um programa em qualquer linguagem de alto nível que reproduza o
|
|
|
|
|
funcionamento do exemplo em Assembly.
|
2025-05-14 11:32:30 -03:00
|
|
|
|
|
2025-05-14 11:36:15 -03:00
|
|
|
|
* Referências
|
2025-05-14 11:32:30 -03:00
|
|
|
|
|
2025-05-17 15:13:38 -03:00
|
|
|
|
- [[https://a.co/d/8Dm1Z93][Organização e Projeto de Computadores -- David A. Patterson e John L. Hennesy]]
|
|
|
|
|
- [[https://a.co/d/9QtcLPI][Organização Estruturada de Computadores -- Andrew S. Tanenbaum]]
|
2025-05-14 11:32:30 -03:00
|
|
|
|
- [[https://namazso.github.io/x86/][Intel® 64 and IA-32 Instruction Set Reference]]
|
|
|
|
|
- [[https://wiki.osdev.org/CPU_Registers_x86-64][OS Dev: CPU Registers x86-64]]
|
|
|
|
|
- [[https://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/][Linux System Call Table for x86 64]]
|
2025-05-17 15:13:38 -03:00
|
|
|
|
- [[https://www.gnu.org/software/bash/manual/bash.html#Exit-Status][Manual do Bash: Estado de Saída]]
|
2025-05-14 11:32:30 -03:00
|
|
|
|
- =man 2 exit=
|