Como um depurador funciona?

170

Fico me perguntando como funciona um depurador? Especialmente aquele que pode ser 'anexado' ao executável já em execução. Entendo que o compilador converte código em linguagem de máquina, mas como o depurador 'sabe' ao que está sendo anexado?

ya23
fonte
5
O artigo de Eli foi transferido para eli.thegreenplace.net/2011/01/23/how-debuggers-work-part-1
Oktalist
@ Oktalist Este artigo é interessante, mas fala apenas da abstração no nível da API para depuração no Linux. Eu acho que a OP quer saber mais sobre o assunto.
smwikipedia

Respostas:

96

Os detalhes de como um depurador funciona dependerão do que você está depurando e do sistema operacional. Para depuração nativa no Windows, você pode encontrar alguns detalhes na API de depuração do MSDN: Win32 .

O usuário informa ao depurador a qual processo conectar, por nome ou por ID do processo. Se for um nome, o depurador procurará o ID do processo e iniciará a sessão de depuração por meio de uma chamada do sistema; no Windows, isso seria DebugActiveProcess .

Uma vez anexado, o depurador entrará em um loop de eventos semelhante a qualquer interface do usuário, mas, em vez de eventos provenientes do sistema de janelas, o sistema operacional gerará eventos com base no que acontece no processo que está sendo depurado - por exemplo, uma exceção. Consulte WaitForDebugEvent .

O depurador é capaz de ler e gravar a memória virtual do processo de destino e até ajustar seus valores de registro por meio de APIs fornecidas pelo sistema operacional. Veja a lista de funções de depuração para Windows.

O depurador é capaz de usar informações de arquivos de símbolos para converter endereços em nomes de variáveis ​​e locais no código-fonte. As informações do arquivo de símbolo são um conjunto separado de APIs e não são parte essencial do sistema operacional. No Windows, isso é feito através do Debug Interface Access SDK .

Se você estiver depurando um ambiente gerenciado (.NET, Java etc.), o processo normalmente parecerá semelhante, mas os detalhes serão diferentes, pois o ambiente da máquina virtual fornece a API de depuração em vez do SO subjacente.

Rob Walker
fonte
5
Essa pergunta pode parecer estúpida, mas como o sistema operacional monitora se um endereço específico dentro do programa é alcançado. Por exemplo, um ponto de interrupção é definido no endereço 0x7710cafe. À medida que o ponteiro de instruções muda, o SO (ou talvez a CPU) precisará comparar o ponteiro de instruções com todos os endereços de ponto de interrupção, ou estou enganado? Como é que isso funciona ..?
displayname 23/01
3
@StefanFalk Escrevi uma resposta que aborda alguns dos detalhes de nível inferior (no x86).
Jonathon Reinhart
Você poderia explicar como exatamente os nomes das variáveis ​​são mapeados para os endereços? O aplicativo usa os mesmos endereços de memória para variáveis ​​toda vez que é executado? Eu sempre assumi que ele acabou de ser mapeado a partir da memória disponível, mas nunca pensei realmente se os bytes seriam mapeados diretamente para o mesmo local no espaço de memória do aplicativo. Parece que isso seria um grande problema de segurança.
James Joshua Street
@JamesJoshuaStreet Imagino que esse seja um detalhe específico do depurador.
moonman239
Essa resposta revela algo. Mas acho que o op está mais interessado em alguns detalhes de baixo nível do que em algumas abstrações da API.
smwikipedia
63

Como eu entendo:

Para pontos de interrupção do software em x86, o depurador substitui o primeiro byte da instrução por CC( int3). Isso é feito WriteProcessMemoryno Windows. Quando a CPU chega a essa instrução e executa int3, isso faz com que a CPU gere uma exceção de depuração. O sistema operacional recebe essa interrupção, percebe que o processo está sendo depurado e notifica o processo do depurador de que o ponto de interrupção foi atingido.

Depois que o ponto de interrupção é atingido e o processo é interrompido, o depurador procura em sua lista de pontos de interrupção e substitui CCo byte que estava lá originalmente. O depurador define TF, o Trap Flag em EFLAGS(modificando o CONTEXT), e continua o processo. O Trap Flag faz com que a CPU gere automaticamente uma exceção de etapa única ( INT 1) na próxima instrução.

Quando o processo sendo depurado para na próxima vez, o depurador substitui novamente o primeiro byte da instrução de ponto de interrupção CCe o processo continua.

Não tenho certeza se é exatamente assim que é implementado por todos os depuradores, mas escrevi um programa Win32 que consegue se depurar usando esse mecanismo. Completamente inútil, mas educacional.

Jonathon Reinhart
fonte
25

No Linux, a depuração de um processo começa com a chamada do sistema ptrace (2) . Este artigo possui um ótimo tutorial sobre como usar ptracepara implementar algumas construções simples de depuração.

Adam Rosenfield
fonte
1
Isso nos (2)diz algo mais (ou menos) do que "ptrace é uma chamada do sistema"?
Lazer 17/05
5
@eSKay, não, não realmente. O (2)é o número da seção manual. Consulte en.wikipedia.org/wiki/Man_page#Manual_sections para obter uma descrição das seções do manual.
Adam Rosenfield
2
@AdamRosenfield Exceto pelo fato de a seção 2 ser especificamente "Chamadas do sistema". Então, indiretamente, sim, isso nos diz que ptraceé uma chamada do sistema.
Jonathon Reinhart
1
Mais praticamente, ele nos (2)diz que podemos digitar man 2 ptracee obter a página de manual correta - não importante aqui, porque não há outro ptracepara desambiguar, mas para comparar man printfcom o man 3 printfLinux.
magro
9

Se você estiver em um sistema operacional Windows, um ótimo recurso para isso seria "Depuração de aplicativos para Microsoft .NET e Microsoft Windows", de John Robbins:

(ou até a edição mais antiga: "Depurando aplicativos" )

O livro possui um capítulo sobre como funciona um depurador que inclui código para alguns depuradores simples (mas funcionais).

Como não estou familiarizado com os detalhes da depuração do Unix / Linux, essas coisas podem não se aplicar a todos os outros sistemas operacionais. Mas eu acho que, como introdução a um assunto muito complexo, os conceitos - se não os detalhes e as APIs - devem "portar" para quase todos os sistemas operacionais.

Michael Burr
fonte
3

Outra fonte valiosa para entender a depuração é o manual da CPU da Intel (Intel® 64 e IA-32 Architectures Software Developer Manual). No volume 3A, capítulo 16, ele apresentou o suporte de depuração de hardware, como exceções especiais e registros de depuração de hardware. A seguir, é desse capítulo:

Sinalizador T (trap), TSS - gera uma exceção de depuração (#DB) quando é feita uma tentativa de alternar para uma tarefa com o sinalizador T definido em seu TSS.

Não tenho certeza se o Windows ou o Linux usam esse sinalizador ou não, mas é muito interessante ler esse capítulo.

Espero que isso ajude alguém.

Jiang
fonte
2

Eu acho que existem duas perguntas principais para responder aqui:

1. Como o depurador sabe que ocorreu uma exceção?

Quando ocorre uma exceção em um processo que está sendo depurado, o depurador é notificado pelo sistema operacional antes que qualquer manipulador de exceção de usuário definido no processo de destino tenha a chance de responder à exceção. Se o depurador optar por não manipular essa notificação de exceção (primeira chance), a sequência de despacho da exceção continuará e o segmento de destino terá a chance de manipular a exceção, se desejar. Se a exceção SEH não for tratada pelo processo de destino, o depurador receberá outro evento de depuração, chamado notificação de segunda chance, para informar que ocorreu uma exceção não tratada no processo de destino. Fonte

insira a descrição da imagem aqui


2. Como o depurador sabe como parar em um ponto de interrupção?

A resposta simplificada é: Quando você coloca um ponto de interrupção no programa, o depurador substitui seu código nesse ponto por uma instrução int3 que é uma interrupção do software . Como efeito, o programa é suspenso e o depurador é chamado.

InTheNameOfScience
fonte
1

Meu entendimento é que, quando você compila um aplicativo ou arquivo DLL, tudo o que ele compila contém símbolos representando as funções e as variáveis.

Quando você tem uma compilação de depuração, esses símbolos são muito mais detalhados do que quando é uma compilação de lançamento, permitindo que o depurador forneça mais informações. Quando você anexa o depurador a um processo, ele analisa quais funções estão sendo acessadas no momento e resolve todos os símbolos de depuração disponíveis a partir daqui (como ele sabe como são as partes internas do arquivo compilado, ele pode obter o que pode estar na memória , com conteúdo de ints, floats, strings, etc.). Como o primeiro pôster disse, essas informações e como esses símbolos funcionam dependem muito do ambiente e do idioma.

DavidG
fonte
2
Isso é apenas sobre símbolos. Há muito, muito mais na depuração do que símbolos.
Jonathon Reinhart