Programação em Baixo Nível

C, Assembly e execução de programas na arquitetura Intel 64

Programação em Baixo Nível Esgotado
× Programação em Baixo Nível

Programação em Baixo Nível

Autor:

ISBN impresso: 978-85-7522-667-4
ISBN ebook: 978-85-7522-670-4
Ano: 2018
Páginas: 576
Preço impresso: R$ 149,00 O ebook deste livro está disponível na Amazon.

7 opiniões | Opine sobre este livro

Descrição do livro

Conheça a linguagem Assembly e a arquitetura do Intel 64, torne-se proficiente em C e entenda como os programas são compilados e executados até o nível das instruções de máquina, permitindo-lhe escrever um código robusto e de alto desempenho.

Programação em baixo nível explica a arquitetura do Intel 64 como resultado da evolução da arquitetura de von Neumann. O livro o ensina a usar a versão mais recente da linguagem C (C11) e a linguagem Assembly desde o básico. Todo o caminho, do código-fonte à execução do programa, incluindo a geração de arquivos-objeto ELF, além das ligações estática e dinâmica, será discutido. Há exemplos de código e exercícios, junto com as melhores práticas de programação. Os recursos de otimização e os limites dos compiladores modernos serão analisados, permitindo-lhe promover um equilíbrio entre a legibilidade do programa e o desempenho.

O uso de diversas técnicas para ganho de desempenho, por exemplo, instruções SSE e pre-fetching, será demonstrado. Assuntos relevantes em ciência da computação, como os modelos de computação e as gramáticas formais, também serão tratados, explicando-se sua importância prática.

Programação em baixo nível ensina os programadores a:

  • escrever livremente em linguagem Assembly;

  • compreender o modelo de programação do Intel 64;

  • escrever um código robusto e fácil de manter em C11;

  • acompanhar o processo de compilação e decifrar as listagens em Assembly;

  • depurar erros em código Assembly compilado;

  • usar modelos de computação apropriados para reduzir drasticamente a complexidade dos programas;

  • escrever códigos críticos quanto ao desempenho;

  • compreender o impacto de um modelo de memória fraco em aplicações com várias threads.

Ver menos ▲

Sumário

Sumário

  • Sobre o autor
  • Sobre o revisor técnico
  • Agradecimentos
  • Introdução
  • Parte I • Linguagem Assembly e arquitetura de computadores
  • Capítulo 1 • Básico sobre arquitetura de computadores
    • 1.1 Arquitetura do núcleo
      • 1.1.1 Modelo de computação
      • 1.1.2 Arquitetura de von Neumann
    • 1.2 Evolução
      • 1.2.1 Desvantagens da arquitetura de von Neumann
      • 1.2.2 Arquitetura Intel 64
      • 1.2.3 Extensões da arquitetura
    • 1.3 Registradores
      • 1.3.1 Registradores de propósito geral
      • 1.3.2 Outros registradores
      • 1.3.3 Registradores de sistema
    • 1.4 Anéis de proteção
    • 1.5 Pilha de hardware
    • 1.6 Resumo
  • Capítulo 2 • Linguagem Assembly
    • 2.1 Configurando o ambiente
      • 2.1.1 Trabalhando com os códigos de exemplo
    • 2.2 Escrevendo “Hello, world”
      • 2.2.1 Entrada e saída básicas
      • 2.2.2 Estrutura do programa
      • 2.2.3 Instruções básicas
    • 2.3 Exemplo: exibindo o conteúdo de registradores
      • 2.3.1 Rótulos locais
      • 2.3.2 Endereçamento relativo
      • 2.3.3 Ordem de execução
    • 2.4 Chamadas de função
    • 2.5 Trabalhando com dados
      • 2.5.1 Endianness
      • 2.5.2 Strings
      • 2.5.3 Pré-processamento de constantes
      • 2.5.4 Ponteiros e diferentes tipos de endereçamento
    • 2.6 Exemplo: calculando o tamanho de uma string
    • 2.7 Exercício: biblioteca de entrada/saída
      • 2.7.1 Autoavaliação
    • 2.8 Resumo
  • Capítulo 3 • Legado
    • 3.1 Modo real
    • 3.2 Modo protegido
    • 3.3 Segmentação mínima em modo longo
    • 3.4 Acessando partes de registradores
      • 3.4.1 Um comportamento inesperado
      • 3.4.2 CISC e RISC
      • 3.4.3 Explicação
    • 3.5 Resumo
  • Capítulo 4 • Memória virtual
    • 4.1 Caching
    • 4.2 Motivação
    • 4.3 Espaços de endereçamento
    • 4.4 Recursos
    • 4.5 Exemplo: acessando um endereço proibido
    • 4.6 Eficiência
    • 4.7 Implementação
      • 4.7.1 Estrutura dos endereços virtuais
      • 4.7.2 Tradução de endereços em detalhes
      • 4.7.3 Tamanhos de página
    • 4.8 Mapeamento de memória
    • 4.9 Exemplo: mapeando um arquivo na memória
      • 4.9.1 Nomes mnemônicos para constantes
      • 4.9.2 Exemplo completo
    • 4.10 Resumo
  • Capítulo 5 • Pipeline de compilação
    • 5.1 Pré-processador
      • 5.1.1 Substituições simples
      • 5.1.2 Substituições com argumentos
      • 5.1.3 Substituição condicional simples
      • 5.1.4 Condicionais sobre a definição
      • 5.1.5 Condicionais sobre a identidade de textos
      • 5.1.6 Condicionais sobre o tipo de argumento
      • 5.1.7 Ordem de avaliação: define, xdefine, assign
      • 5.1.8 Repetição
      • 5.1.9 Exemplo: calculando números primos
      • 5.1.10 Rótulos em macros
      • 5.1.11 Conclusão
    • 5.2 Tradução
    • 5.3 Ligação
      • 5.3.1 ELF (Executable and Linkable Format)
      • 5.3.2 Arquivos-objeto relocáveis
      • 5.3.3 Arquivos-objeto executáveis
      • 5.3.4 Bibliotecas dinâmicas
      • 5.3.5 Loader
    • 5.4 Exercício: dicionário
    • 5.5 Resumo
  • Capítulo 6 • Interrupções e chamadas de sistema
    • 6.1 Entrada e saída
      • 6.1.1 O registrador TR e o Task State Segment
    • 6.2 Interrupções
    • 6.3 Chamadas de sistema
      • 6.3.1 Registradores específicos de modelo
      • 6.3.2 syscall e sysret
    • 6.4 Resumo
  • Capítulo 7 • Modelos de computação
    • 7.1 Máquinas de estado finitas
      • 7.1.1 Definição
      • 7.1.2 Exemplo: paridade de bits
      • 7.1.3 Implementação em linguagem Assembly
      • 7.1.4 Importância prática
      • 7.1.5 Expressões regulares
    • 7.2 Máquina de Forth
      • 7.2.1 Arquitetura
      • 7.2.2 Trace de um exemplo de programa em Forth
      • 7.2.3 Dicionário
      • 7.2.4 Como as palavras são implementadas
      • 7.2.5 Compilador
    • 7.3 Exercício: compilador e interpretador de Forth
      • 7.3.1 Dicionário estático e interpretador
      • 7.3.2 Compilação
      • 7.3.3 Forth com bootstrap
    • 7.4 Resumo
  • Parte II • A linguagem de programação C
  • Capítulo 8 • Básico da linguagem C
    • 8.1 Introdução
    • 8.2 Estrutura do programa
      • 8.2.1 Tipos de dados
    • 8.3 Controle de fluxo
      • 8.3.1 if
      • 8.3.2 while
      • 8.3.3 for
      • 8.3.4 goto
      • 8.3.5 switch
      • 8.3.6 Exemplo: divisor
      • 8.3.7 Exemplo: é um número de Fibonacci?
    • 8.4 Instruções e expressões
      • 8.4.1 Tipos de instrução
      • 8.4.2 Construindo expressões
    • 8.5 Funções
    • 8.6 Pré-processador
    • 8.7 Resumo
  • Capítulo 9 • Sistema de tipos
    • 9.1 Sistema básico de tipos em C
      • 9.1.1 Tipos numéricos
      • 9.1.2 Casting de tipo
      • 9.1.3 Tipo booleano
      • 9.1.4 Conversões implícitas
      • 9.1.5 Ponteiros
      • 9.1.6 Arrays
      • 9.1.7 Arrays como argumentos de função
      • 9.1.8 Inicializadores designados em arrays
      • 9.1.9 Aliases de tipo
      • 9.1.10 Revendo a função main
      • 9.1.11 Operador sizeof
      • 9.1.12 Tipos const
      • 9.1.13 Strings
      • 9.1.14 Tipos função
      • 9.1.15 Escrevendo um bom código
      • 9.1.16 Exercício: produto escalar
      • 9.1.17 Exercício: verificador de número primo
    • 9.2 Tipos com tags
      • 9.2.1 Estruturas
      • 9.2.2 Uniões
      • 9.2.3 Estruturas e uniões anônimas
      • 9.2.4 Enumerações
    • 9.3 Tipos de dados nas linguagens de programação
      • 9.3.1 Espécies de tipagem
      • 9.3.2 Polimorfismo
    • 9.4 Polimorfismo in C
      • 9.4.1 Polimorfismo paramétrico
      • 9.4.2 Inclusão
      • 9.4.3 Sobrecarga
      • 9.4.4 Coerções
    • 9.5 Resumo
  • Capítulo 10 • Estrutura do código
    • 10.1 Declarações e definições
      • 10.1.1 Declarações de função
      • 10.1.2 Declarações de estrutura
    • 10.2 Acessando código a partir de outros arquivos
      • 10.2.1 Funções de outros arquivos
      • 10.2.2 Dados em outros arquivos
      • 10.2.3 Arquivos de cabeçalho
    • 10.3 A biblioteca-padrão
    • 10.4 Pré-processador
      • 10.4.1 Guarda de inclusão
      • 10.4.2 Por que o pré-processador é maléfico?
    • 10.5 Exemplo: soma de um array dinâmico
      • 10.5.1 Uma espiada na alocação dinâmica de memória
      • 10.5.2 Exemplo
    • 10.6 Exercício: lista ligada
      • 10.6.1 Exercício
    • 10.7 Palavra reservada static
    • 10.8 Ligação
    • 10.9 Resumo
  • Capítulo 11 • Memória
    • 11.1 Revendo os ponteiros
      • 11.1.1 Por que precisamos de ponteiros?
      • 11.1.2 Aritmética de ponteiros
      • 11.1.3 Tipo void*
      • 11.1.4 NULL
      • 11.1.5 Uma palavrinha sobre ptrdiff_t
      • 11.1.6 Ponteiros de função
    • 11.2 Modelo de memória
      • 11.2.1 Alocação de memória
    • 11.3 Arrays e ponteiros
      • 11.3.1 Detalhes da sintaxe
    • 11.4 Strings literais
      • 11.4.1 String Interning
    • 11.5 Modelos de dados
    • 11.6 Streams de dados
    • 11.7 Exercício: funções de alta ordem e listas
      • 11.7.1 Funções de alta ordem comuns
      • 11.7.2 Exercício
    • 11.8 Resumo
  • Capítulo 12 • Sintaxe, semântica e pragmática
    • 12.1 O que é uma linguagem de programação?
    • 12.2 Sintaxe e gramáticas formais
      • 12.2.1 Exemplo: números naturais
      • 12.2.2 Exemplo: aritmética simples
      • 12.2.3 Descida recursiva
      • 12.2.4 Exemplo: aritmética com prioridades
      • 12.2.5 Exemplo: linguagem imperativa simples
      • 12.2.6 Hierarquia de Chomsky
      • 12.2.7 Árvore de sintaxe abstrata
      • 12.2.8 Análise lexical
      • 12.2.9 Resumo sobre o parsing
    • 12.3 Semântica
      • 12.3.1 Comportamento indefinido
      • 12.3.2 Comportamento não especificado
      • 12.3.3 Comportamento definido pela implementação
      • 12.3.4 Pontos de sequência
    • 12.4 Pragmática
      • 12.4.1 Alinhamento
      • 12.4.2 Preenchimento de estruturas de dados
    • 12.5 Alinhamento no C11
    • 12.6 Resumo
  • Capítulo 13 • Boas práticas de programação
    • 13.1 Fazendo escolhas
    • 13.2 Elementos do código
      • 13.2.1 Nomenclatura em geral
      • 13.2.2 Estrutura de arquivos
      • 13.2.3 Tipos
      • 13.2.4. Variáveis
      • 13.2.5 Sobre variáveis globais
      • 13.2.6 Funções
    • 13.3 Arquivos e documentação
    • 13.4 Encapsulamento
    • 13.5 Imutabilidade
    • 13.6 Asserções
    • 13.7 Tratamento de erros
    • 13.8 Sobre a alocação de memória
    • 13.9 Sobre a flexibilidade
    • 13.10 Exercício: rotação de imagem
      • 13.10.1 Formato de arquivo BMP
      • 13.10.2 Arquitetura
    • 13.11 Exercício: alocador de memória personalizado
    • 13.12 Resumo
  • Parte III • Entre o C e o Assembly
  • Capítulo 14 • Detalhes sobre a tradução
    • 14.1 Sequência de chamadas de função
      • 14.1.1 Registradores XMM
      • 14.1.2 Convenção de chamadas
      • 14.1.3 Exemplo: uma função simples e sua pilha
      • 14.1.4 Zona vermelha
      • 14.1.5 Número variável de argumentos
      • 14.1.6 vprintf e seus companheiros
    • 14.2 volatile
      • 14.2.1 Alocação de memória em modo preguiçoso
      • 14.2.2 Código gerado
    • 14.3 Jumps não locais – setjmp
      • 14.3.1 volatile e setjmp
    • 14.4 inline
    • 14.5 restrict
    • 14.6 Aliasing rigoroso
    • 14.7 Problemas de segurança
      • 14.7.1 Stack buffer overrun (transbordamento de buffer na pilha)
      • 14.7.2 return-to-libc
      • 14.7.3 Vulnerabilidades de formatação de saída
    • 14.8 Mecanismos de proteção
      • 14.8.1 Cookie de segurança
        • Endereçamento)
      • 14.8.3 DEP
    • 14.9 Resumo
  • Capítulo 15 • Objetos compartilhados e modelos de código
    • 15.1 Carga dinâmica
    • 15.2 Relocações e PIC
    • 15.3 Exemplo: biblioteca dinâmica em C
    • 15.4 GOT e PLT
      • 15.4.1 Acessando variáveis externas
      • 15.4.2 Chamando funções externas
      • 15.4.3 Exemplo de PLT
    • 15.5 Pré-carga
    • 15.6 Resumo sobre o endereçamento de símbolos
    • 15.7 Exemplos
      • 15.7.1 Chamando uma função
      • 15.7.2 Sobre vários linkers dinâmicos
      • 15.7.3 Acessando uma variável externa
      • 15.7.4 Exemplo completo em Assembly
      • 15.7.5 Misturando C e Assembly
    • 15.8 Quais objetos são ligados?
    • 15.9 Otimizações
    • 15.10 Modelos de código
      • 15.10.1 Modelo de código pequeno (sem PIC)
      • 15.10.2 Modelo de código grande (sem PIC)
      • 15.10.3 Modelo de código médio (sem PIC)
      • 15.10.4 Modelo de código pequeno com PIC
      • 15.10.5 Modelo de código grande com PIC
      • 15.10.6 Modelo de código médio com PIC
    • 15.11 Resumo
  • Capítulo 16 • Desempenho
    • 16.1 Otimizações
      • 16.1.1 Mito sobre linguagens rápidas
      • 16.1.2 Conselhos gerais
      • 16.1.3 Omitindo o ponteiro do stack frame
      • 16.1.4 Recursão de cauda
      • 16.1.5 Eliminação de subexpressões comuns
      • 16.1.6 Propagação de constantes
      • 16.1.7 Otimização de valores de retorno (nomeados)
      • 16.1.8 Influência da previsão de desvio
      • 16.1.9 Influência das unidades de execução
      • 16.1.10 Agrupando leituras e escritas no código
    • 16.2 Caching
      • 16.2.1 Como usar o cache de modo eficaz?
      • 16.2.2 Prefetching
      • 16.2.3 Exemplo: busca binária com prefetching
      • 16.2.4 Ignorando o cache
      • 16.2.5 Exemplo: inicialização de matriz
    • 16.3 Classe de instruções SIMD
    • 16.4 Extensões SSE e AVX
      • 16.4.1 Exercício: filtro sépia
    • 16.5 Resumo
  • Capítulo 17 • Multithreading
    • 17.1 Processos e threads
    • 17.2 O que deixa o multithreading complicado?
    • 17.3 Ordem de execução
    • 17.4 Modelos de memória fortes e fracos
    • 17.5 Exemplo de reordenação
    • 17.6 O que é e o que não é volátil
    • 17.7 Barreiras de memória
    • 17.8 Introdução à pthreads
      • 17.8.1 Quando usar multithreading
      • 17.8.2 Criando threads
      • 17.8.3 Gerenciando threads
      • 17.8.4 Exemplo: fatoração distribuída
      • 17.8.5 Mutexes
      • 17.8.6 Deadlocks
      • 17.8.7 Livelocks
      • 17.8.8 Variáveis de condição
      • 17.8.9 Spinlocks
    • 17.9 Semáforos
    • 17.10 Quão robusto é o Intel 64?
    • 17.11 O que é uma programação sem lock?
    • 17.12 Modelo de memória do C11
      • 17.12.1 Visão geral
      • 17.12.2 Atômicos
      • 17.12.3 Ordenações de memória no C11
      • 17.12.4 Operações
    • 17.13 Resumo
  • Apêndice A • Usando o gdb
  • Apêndice B • Usando o Make
  • Apêndice C • Chamadas de sistema
  • Apêndice D • Informações sobre testes de desempenho
  • Bibliografia
Ver sumário completo ▼

Sobre o autor

Igor Zhirkov

Igor Zhirkov ministra seu muito bem-sucedido curso “Linguagens de programação de sistemas” na ITMO University (Universidade de Tecnologia da Informação, Mecânica e Ótica) de São Petersburgo, seis vezes vencedora do ACM-ICPC Intercollegiate World Programming Championship (Campeonato Mundial de Programação entre Universidades ACM-ICPC). Estudou… Ver perfil completo ▶

Opinião dos leitores

Rodrigo H W

Excelente livro, contém conteúdos específicos para programação de baixo nível e Linguagem C. O autor aborda os temas com exemplos e dicas importantes para formação do profissional.

Gunter C K

A encadernação, assim como, a impressão são primorosas. Como entusiasta e professor da área de informática/exatas achei o tema relevante e a apresentação muito didática. Lancem mais livros com esse tema.

João P S

Excelente livro!

Duilio B N

O conteúdo é denso e o autor soube ser didático, ainda começando mas já achei que valeu muito a pena a aquisição.

Ver todas ▼

Títulos relacionados