📄 trace-x86.c
字号:
/* * libDebug * * Copyright (C) 2000 Patrick Alken * This library comes with absolutely NO WARRANTY * * Should you choose to use and/or modify this source code, please * do so under the terms of the GNU General Public License under which * this library is distributed. * * $Id: trace-x86.c,v 1.6 2004/10/09 17:34:15 pa33 Exp $ */#include <stdio.h>#include <stdlib.h>#include <assert.h>#include <sys/time.h>#include <sys/types.h>#include <sys/wait.h>#include <sys/ptrace.h>#include <signal.h>#include <unistd.h>#include <errno.h>#include <string.h>/* * Top-level includes */#include "args.h"#include "break.h"#include "libDebug.h"static int x86GetDebugProcessStatus(struct debugWorkspace *ws, int ptfunc, int waitval, int *data);static int x86DoSingleStep(struct debugWorkspace *ws, int *data);static int x86DoContinue(struct debugWorkspace *ws, int *data);/*x86execDebug() Start execution of the debugged processInputs: ws - debug workspaceReturn: 2 if file is not executable 1 if successful 0 if notSide effects: DebugPid is assigned to the pid of the traced process*/intx86execDebug(struct debugWorkspace *ws){ pid_t pid; int waitval; int err; assert(ws->path != 0); if (dbIsRedirect(ws)) { if (pipe(ws->pipes) == (-1)) return (0); } pid = fork(); if (pid == (-1)) { if (dbIsRedirect(ws)) { close(ws->pipes[0]); close(ws->pipes[1]); } return (0); } else if (pid == 0) { /* * Child: allow parent to trace us */ if (ptrace(PT_TRACE_ME, 0, 0, 0) != 0) exit(0); assert(ws->args && ws->args[0]); if (dbIsRedirect(ws)) { /* * Redirect child's stdout and stderr to the parent's pipe */ dup2(ws->pipes[1], 1); dup2(ws->pipes[1], 2); /* * Redirect child's stdin to come from parent's pipe */ /* dup2(ws->pipes[1], 0); */ /* * Close file descriptors */ close(ws->pipes[0]); close(ws->pipes[1]); }#if 0 if (ws->args) { printf("arg1 ==== %s\n", ws->args[1]); }#endif execv(ws->path, ws->args); /* * If we get here the execv() returned -1 and it is most likely * an ENOEXEC - exiting now should alert the parent process that * something went wrong */ exit(0); } else { /* * Parent process */ ws->pid = pid; /* * wait for child to stop (execv) */ wait(&waitval); /* * Set the instruction pointer to the program's entry point */ err = 0; ws->instructionPointer = x86getCurrentInstruction(ws, &err); if (err) { /* * The most likely cause of this is that the file is not * executable */ ws->pid = NOPID; return (2); } dbSetRunning(ws); } return (1);} /* x86execDebug() *//*x86GetDebugProcessStatus()Inputs: ws - debug workspace ptfunc - ptrace flag (PT_STEP or PT_CONTINUE) waitval - value containing process status data - modified to contain data depending on process statusReturn: 0 if something goes wrong 1 if everything is successful 2 if program stops due to a signal (signal num goes in data) 3 if program encounters a breakpoint (brk pt num goes in data) 4 if program terminates normally (exit status goes in data) 5 if program writes to stdout or stderr and we are redirecting output - the calling function can read the output using GetDebugOutput() 6 (do not use) 7 if program terminates due to a signal (signal number goes in data)*/static intx86GetDebugProcessStatus(struct debugWorkspace *ws, int ptfunc, int waitval, int *data){ int sig; if (WIFEXITED(waitval)) { /* * Process exited normally */ endDebug(ws); *data = WEXITSTATUS(waitval); /* * When a program exits, it sends two signals to the parent - * one for the exit status and the other is a SIGCHLD, so * make sure we catch the second. */ wait(&waitval); return (4); } else if (WIFSTOPPED(waitval)) { sig = WSTOPSIG(waitval); if (sig == SIGTRAP) { unsigned long addr; struct Breakpoint *bptr; /* * InstructionPointer should be set appropriately by the * calling function */ addr = ws->instructionPointer; if (ptfunc == PT_STEP) { /* * A SIGTRAP is generated after every singlestep call * so check before declaring it a user-defined breakpoint */ if ((bptr = findBreakpoint(ws, addr))) { if (bptr->flags & BK_STEPOVER) printf("HIT A STEP OVER BRKPT\n"); *data = bptr->number; if (bptr->ignorecnt > 0) { /* * Ignore this breakpoint for now and continue * running the process */ --bptr->ignorecnt; return (1); } /* * Check if breakpoint should be deleted */ checkBreakpoint(ws, bptr); dbSetHitBreakpoint(ws); return (3); } /* * If we get here, the SIGTRAP is the normal response * after the cpu set the trap flag for singlestepping */ } /* if (ptfunc == PT_STEP) */ else if (ptfunc == PT_CONTINUE) { /* * We stopped exactly one byte after the breakpoint * instruction - go back one */ --addr; bptr = findBreakpoint(ws, addr); if (!bptr) { /* * It appears to be a SIGTRAP which we did not set - * this is rare, but possible. */ *data = sig; /* * Do not pass this signal to the program on the next * ptrace since it will cause the program to terminate */ /*ws->lastSignal = sig;*/ return (2); } /* * Set eip to the real address */ x86setCurrentInstruction(ws, addr); if (bptr->ignorecnt > 0) { /* * Ignore this breakpoint for now and continue * running the process */ --bptr->ignorecnt; return (1); } if (bptr->flags & BK_STEPOVER) *data = 0; else *data = bptr->number; /* * Check to see if this breakpoint should be deleted */ checkBreakpoint(ws, bptr); dbSetHitBreakpoint(ws); return (3); } /* if (ptfunc == PT_CONTINUE) */ } /* if (sig == SIGTRAP) */ else { *data = sig; /* * If we received a SIGINT, do not pass it to the process the * next time we continue ptracing, because it was probably * caused by the user debugging the process */ if (sig != SIGINT) ws->lastSignal = sig; return (2); } } /* else if (WIFSTOPPED(waitval)) */ else if (WIFSIGNALED(waitval)) { /* * Program terminated due to a signal */ *data = WTERMSIG(waitval); return (7); } /* * Everything is normal */ return (1);} /* x86GetDebugProcessStatus() *//*x86DoSingleStep() Singlestep one instruction in the process being debuggedInputs: ws - debug workspace data - modified to contain info depending on the return resultReturn: 0 if something goes wrong 1 if everything is successful 2 if program stops due to a signal (signal num goes in data) 3 if program encounters a breakpoint (brk pt num goes in data) 4 if program terminates normally (exit status goes in data) 5 if program writes to stdout or stderr and we are redirecting output - the calling function can read the output using GetDebugOutput()*/static intx86DoSingleStep(struct debugWorkspace *ws, int *data){ int waitval; int err; assert(ws->pid != NOPID); if (ptrace(PT_STEP, ws->pid, CONTADDR, ws->lastSignal) != 0) return (0); /* something went wrong */ /* * Clear the last signal */ ws->lastSignal = 0; /* * Wait for child to stop */ wait(&waitval); err = 0; ws->instructionPointer = x86getCurrentInstruction(ws, &err); return (x86GetDebugProcessStatus(ws, PT_STEP, waitval, data));} /* x86DoSingleStep() *//*x86DoContinue() Continue the process being debuggedInputs: ws - debug workspace data - modified to contain info depending on the return resultReturn: 0 if something goes wrong 1 if everything is successful 2 if program stops due to a signal (signal num goes in data) 3 if program encounters a breakpoint (brk pt num goes in data) 4 if program terminates normally (exit status goes in data) 5 if program writes to stdout or stderr and we are redirecting output - the calling function can read the output using GetDebugOutput() 6 (don't use) 7 if program terminates due to a signal (signal num goes in data)Special note about breakpoints: If this function is invoked from x86SingleStepOver(), it ispossible that we will run into a temporary breakpoint markedwith BK_STEPOVER. Since this breakpoint was set by x86SingleStepOver()and not by the user, it does not have a legitimate breakpoint number,and so the special value of 0 will be placed into 'data' indicatingto x86SingleStepOver() that it's temporary breakpoint was reached.*/static intx86DoContinue(struct debugWorkspace *ws, int *data){ int waitval; int ret; int err; pid_t wret; assert(ws->pid != NOPID); if (dbHitBreakpoint(ws)) { dbClearHitBreakpoint(ws); /* * We just hit a breakpoint on the previous ptrace() call, * and the user wants to continue from the break address. * We will singlestep past the breakpoint address, then * re-enable breakpoints and continue normally */ ret = x86DoSingleStep(ws, data); if (ret != 1) return (ret); /* something stopped the process */ } /* * An infinite loop is needed to handle cases where breakpoints * have an ignore count - we'll want to continue the process * without notifying the user that the breakpoint was hit. */ while (1) { /* * Enable all breakpoints */ enableBreakpoints(ws); /*fprintf(stderr, "lastsig = %d\n", ws->lastSignal);*/ if (ptrace(PT_CONTINUE, ws->pid, CONTADDR, ws->lastSignal) != 0) return (0); /* something went wrong */ /* * Wait for child to stop */ wret = wait(&waitval); /*fprintf(stderr, "wret = %d\n", wret);*/ /* * Disable all breakpoints */ disableBreakpoints(ws); /* * Clear last signal */ ws->lastSignal = 0; err = 0; ws->instructionPointer = x86getCurrentInstruction(ws, &err); ret = x86GetDebugProcessStatus(ws, PT_CONTINUE, waitval, data); if (ret != 1) { /* * Something stopped the program (breakpoint, signal, exit, etc) */ return (ret); } /* * If we get here it we most likely hit a breakpoint which * was ignored, so singlestep past the breakpoint instruction * and continue tracing the process */ ret = x86DoSingleStep(ws, data); if (ret != 1) return (ret); /* something stopped the process */ } /* while (1) */} /* x86DoContinue() *//*x86stepIntoDebug() Single step our program, stepping into any subroutinesInputs: ws - debug workspace num - number of instructions to step through data - modified to contain info depending on the return result, see belowReturn: 0 if unsuccessful 1 if successful 2 if program stops due to a signal 3 if breakpoint is encountered 4 if program terminatesSide effects: If the process stops due to a signal, data is modified to contain the signal number. If the process hits a breakpoint, the breakpoint number is put into data If the process terminates normally, data is modified to contain the exit status.*/intx86stepIntoDebug(struct debugWorkspace *ws, int num, int *data){ int ii; int ret; assert(num > 0); if (ws->pid == NOPID) { ret = x86execDebug(ws); if (ret == 2) return (6); /* not an executable file */ else if (ret == 0) return (0); /* something went wrong */ } dbSetRunning(ws); for (ii = 0; ii < num; ++ii) { /* * Perform single step */ ret = x86DoSingleStep(ws, data); if (ret != 1) { /* * Something stopped the process (signal, breakpoint, exit, etc) */ return (ret); } } /* for (ii = 0; ii < num; ++ii) */ /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -