"How can I read the data (values of variables) of process that is
created by ./test?".
Você pode querer olhar para o formato de depuração DWARF. A parte 3 do link How Debuggers Work abaixo discute o DWARF brevemente. Existem outras maneiras de resolver símbolos, mas por que não fazer como o GDB e usar o DWARF? [edit: Não é uma tarefa trivial extrair funções do código-fonte do gdb para usá-las em programas mais simples]. O código-fonte está disponível de qualquer maneira, verifique o link ' Como o GDB carrega os arquivos de símbolo ' abaixo, onde os links apontam diretamente para ele. A terceira opção seria analisar manualmente as tabelas de símbolos ELF com suas próprias funções. É mais feio e é provavelmente a estrada mais envolvida, mas não depende dos símbolos de depuração que o anão fornece.
Quanto aos pontos de interrupção, você pode configurá-los usando ptrace e trap = memory & 0xffffff00 | 0xcc depois de salvar a instrução no endereço e recuperar a instrução depois que o trap é atingido, como How Debuggers Work descreve. O byte 0xcc é opcode int 3.
Para ver como o gdb faz isso, este é o link para você:
Como o GDB Carrega os arquivos de símbolos
O seguinte é apenas para sugerir de onde vêm essas 5000+ etapas executando um programa de montagem sem bibliotecas vinculadas por meio de um rastreador semelhante:
;hello.asm
section .text
global _start
_start:
mov edx,5
mov ecx,msg
mov ebx,1
mov eax,4
int 0x80
mov eax,1
int 0x80
msg:
db "Hello"
O programa que eu usei para contar é similar (de Como funcionam os depuradores )
#include <sys/ptrace.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/wait.h>
void run_debugger(pid_t child_pid)
{
int wait_status;
unsigned icounter = 0;
printf("debugger started\n");
/* Wait for child to stop on its first instruction */
wait(&wait_status);
while (WIFSTOPPED(wait_status)) {
icounter++;
/* Make the child execute another instruction */
if (ptrace(PTRACE_SINGLESTEP, child_pid, 0, 0) < 0) {
perror("ptrace");
return;
}
/* Wait for child to stop on its next instruction */
wait(&wait_status);
}
printf("\nthe child executed %u instructions\n", icounter);
}
void run_target(const char* programname)
{
printf("target started. will run '%s'\n", programname);
/* Allow tracing of this process */
if (ptrace(PTRACE_TRACEME, 0, 0, 0) < 0) {
perror("ptrace");
return;
}
/* Replace this process's image with the given program */
execl(programname, programname, NULL);
}
int main(int argc, char** argv)
{
pid_t child_pid;
if (argc < 2) {
fprintf(stderr, "Expected a program name as argument\n");
return -1;
}
child_pid = fork();
if (child_pid == 0)
run_target(argv[1]);
else if (child_pid > 0)
run_debugger(child_pid);
else {
perror("fork");
return -1;
}
return 0;
}
Compilado como a.out e executado:
$ ./a.out helloasm
debugger started
target started. will run 'helloasm'
Hello
the child executed 7 instructions
vs.
#include <stdio.h>
int main()
{
printf("Hello World\n");
return 0;
}
Que totaliza 141 690 instruções.