blob: 2852310ad88cc707911f08d850089afabdffa26d [file] [log] [blame]
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <asm/unistd.h>
#include "i386.h"
#include "ltrace.h"
#include "process.h"
#include "symbols.h"
#include "functions.h"
#include "syscall.h"
#include "signal.h"
#include "output.h"
struct process * list_of_processes = NULL;
unsigned int instruction_pointer;
static void detach_process(int pid);
static struct process * pid2proc(int pid);
static void process_child(struct process * current_process);
int execute_process(const char * file, char * const argv[])
{
struct process * tmp;
int pid = fork();
if (pid<0) {
perror("fork");
exit(1);
} else if (!pid) {
trace_me();
execvp(file, argv);
fprintf(stderr, "Can't execute \"%s\": %s\n", argv[1], sys_errlist[errno]);
exit(1);
}
tmp = (struct process *)malloc(sizeof(struct process));
tmp->pid = pid;
tmp->breakpoints_enabled = 0;
proc_arch_init(&tmp->proc_arch);
tmp->within_function = 0;
tmp->next = list_of_processes;
list_of_processes = tmp;
return pid;
}
static void detach_process(int pid)
{
struct process *tmp, *tmp2;
if (list_of_processes && (pid == list_of_processes->pid)) {
tmp2 = list_of_processes->next;
free(list_of_processes);
list_of_processes = tmp2;
} else {
tmp = list_of_processes;
while(tmp && tmp->next) {
if (pid == tmp->next->pid) {
tmp2 = tmp->next->next;
free(tmp->next);
tmp->next = tmp2;
}
tmp = tmp->next;
}
}
if (!kill(pid,0)) {
disable_all_breakpoints(pid);
untrace_pid(pid);
}
}
static struct process * pid2proc(int pid)
{
struct process * tmp;
tmp = list_of_processes;
while(tmp) {
if (pid == tmp->pid) {
return tmp;
}
tmp = tmp->next;
}
return NULL;
}
void wait_for_child(void)
{
int pid;
int status;
struct process * current_process;
pid = wait4(-1, &status, 0, NULL);
if (pid==-1) {
if (errno == ECHILD) {
if (opt_d>0) {
send_line("No more children");
}
exit(0);
}
perror("wait4");
exit(1);
}
current_process = pid2proc(pid);
if (!current_process) {
fprintf(stderr, "wrong pid %d ???\n", pid);
exit(1);
}
if (!current_process->breakpoints_enabled) {
if (opt_d>0) {
send_line("Enabling breakpoints for pid %d...", pid);
}
enable_all_breakpoints(pid);
current_process->breakpoints_enabled=1;
}
if (WIFEXITED(status)) {
send_line("pid %u exited", pid);
detach_process(pid);
return;
}
if (WIFSIGNALED(status)) {
send_line("--- %s (%s) ---", signal_name[WSTOPSIG(status)], strsignal(WSTOPSIG(status)));
send_line("+++ killed by %s +++", signal_name[WSTOPSIG(status)]);
detach_process(pid);
return;
}
if (!WIFSTOPPED(status)) {
send_line("pid %u ???", pid);
exit(1);
}
if (WSTOPSIG(status) != SIGTRAP) {
send_line("--- %s (%s) ---", signal_name[WSTOPSIG(status)], strsignal(WSTOPSIG(status)));
continue_process(pid, WSTOPSIG(status));
return;
}
process_child(current_process);
}
static void process_child(struct process * current_process)
{
int pid;
unsigned long eip;
int esp;
int function_seen;
int status;
struct library_symbol * tmp = NULL;
pid = current_process->pid;
eip = get_eip(pid);
instruction_pointer = eip;
switch (type_of_stop(current_process->pid, &current_process->proc_arch, &status)) {
case PROC_SYSCALL:
if (status==__NR_fork) {
disable_all_breakpoints(pid);
}
if (opt_S) {
send_line("SYSCALL: %s()", syscall_list[status]);
}
continue_process(pid, 0);
return;
case PROC_SYSRET:
if (status==__NR_fork) {
enable_all_breakpoints(pid);
}
if (opt_S && (opt_d>0)) {
send_line("SYSRET: %u", status);
}
continue_process(pid, 0);
return;
case PROC_BREAKPOINT:
default:
}
/* pid is breakpointed... */
/* TODO: I could be here after a PTRACE_SINGLESTEP ... */
esp = get_esp(pid);
instruction_pointer = get_return(pid, esp);
tmp = library_symbols;
function_seen = 0;
if (eip == current_process->return_value.addr) {
function_seen = 1;
#if 0
send_line("return");
print_libret(tmp->name, pid, esp);
#endif
continue_after_breakpoint(pid, &current_process->return_value, 1);
} else while(tmp) {
if (eip == tmp->sbp.addr) {
function_seen = 1;
if (current_process->within_function) {
delete_breakpoint(pid, &current_process->return_value);
}
current_process->return_value.addr = instruction_pointer;
insert_breakpoint(pid, &current_process->return_value);
current_process->within_function=1;
#if 0
print_libcall(tmp->name, pid, esp);
#endif
continue_after_breakpoint(pid, &tmp->sbp, 0);
break;
}
tmp = tmp->next;
}
if (!function_seen) {
send_line("pid %u stopped; continuing it...", pid);
continue_process(pid, 0);
}
}