Files
seta75D d6fe8fe829 Init
2021-10-11 22:19:34 -03:00

1767 lines
41 KiB
C

static char sccsid[] = "@(#)46 1.42.3.38 src/bos/usr/ccs/lib/libdbx/execute.c, libdbx, bos411, 9434B411a 8/24/94 19:25:53";
/*
* COMPONENT_NAME: (CMDDBX) - dbx symbolic debugger
*
* FUNCTIONS: arginit, beginproc, cont, contto, curpid, dbxexec, dread, dwrite,
* errarg, errcode, errnum, exitcode, ffork, fixintr, inarg, intr,
* iread, isbperr, isfinished, iwrite, meta_expand, newarg, next,
* notstarted, objfile_close, objfile_readbytes, objfile_readdata,
* objfile_readin, objfile_readtext, outarg, printloc, printsigs,
* printsigscaught, printsigsignored, printstatus, read_err, remade,
* resume, rtnfunc, run, siglookup, startprog, stepc, stepover,
* stepto, write_err, xto, process_load, update_load_maps,
* bp_lastaddr, shell_popen
*
* ORIGINS: 26, 27, 83, 3
*
* This module contains IBM CONFIDENTIAL code. -- (IBM
* Confidential Restricted when combined with the aggregated
* modules for this product)
* SOURCE MATERIALS
* (C) COPYRIGHT International Business Machines Corp. 1988, 1994
* All Rights Reserved
*
* US Government Users Restricted Rights - Use, duplication or
* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
*
* Copyright (c) 1982 Regents of the University of California
*
*/
/*
* LEVEL 1, 5 Years Bull Confidential Information
*/
#ifdef KDBXRT
#include "rtnls.h" /* MUST BE FIRST */
#endif
/* include file for message texts */
#include "dbx_msg.h"
nl_catd scmc_catd; /* Cat descriptor for scmc conversion */
/*
* Process management.
*
* This module contains the routines to manage the execution and
* tracing of the debuggee process.
*/
#include "defs.h"
#include "envdefs.h"
#include "execute.h"
#include "process.h"
#include "eval.h"
#include "object.h"
#include "mappings.h"
#include "main.h"
#include "cma_thread.h"
#ifdef K_THREADS
#include "k_thread.h"
#endif /* K_THREADS */
#include "runtime.h"
#include "frame.h"
#include <signal.h>
#include <errno.h>
#include <sys/user.h>
#include <sys/stat.h>
#include <sys/reg.h>
#include <scnhdr.h>
/* ldfcn.h and fcntl.h share FREAD and FWRITE */
#undef FREAD
#undef FWRITE
#include <ldfcn.h>
#include <setjmp.h>
int newdbg = 0; /* pid of new debugger (to be ignored in wait) */
int fptype;
int contsig = DEFSIG; /* Signal variable for cont command */
Boolean just_started;
Boolean noexec = false;
Boolean textaddrs = false; /* Address text of non-executable */
Boolean dataaddrs = false; /* Address data of non-executable */
Boolean fileaddrs = false; /* Address file of non-executable */
unsigned textorg = 0; /* Origin for referring to text section */
unsigned dataorg = 0; /* Origin for referring to the data section */
Boolean never_ran = true;
Boolean dynamic_loading = false;/* Set if process dynamically loads */
#ifdef K_THREADS
Boolean next_subcommand = false;
extern int nb_k_threads_sig;
#endif /* K_THREADS */
String infile, outfile, errfile;
extern Address dbargs_addr;
extern boolean fullcore;
extern pid_t debuggee_pgrp;
extern pid_t debugger_pgrp;
extern File srcfp; /* currently open File from skimsource() */
extern Boolean branch_link();
private Boolean remade();
/*private contto();*/
private xto();
private read_err();
private write_err();
private printsigs();
#ifndef _NO_PROTO
private void intr(int);
#else
private void intr();
#endif
/*
* Variables for $catchbp flag.
*/
typedef List Bplist;
extern Bplist bplist; /* List of active breakpoints */
extern boolean catchbp;
private int argc;
private String argv[MAXNCMDARGS];
private LDFILE *objfile = NULL;
extern SCNHDR text_scn_hdr;
extern SCNHDR data_scn_hdr;
extern unsigned short text_scn_num;
extern unsigned short data_scn_num;
extern unsigned short bss_scn_num;
extern int newdebuggee;
extern int rerunning;
/*extern FILHDR filhdr;*/
/*
* External variables needed to deal with catching
* breakpoints during a call command.
*/
extern Boolean call_command;
extern Boolean initdone;
extern long bp_skips;
extern int *envptr;
typedef struct parent_pid *parent_pid;
struct parent_pid
{
int pid;
parent_pid next;
};
parent_pid parent_pid_list = NULL;
/*
* NAME: bp_lastaddr
*
* FUNCTION: Set dbx brakpoint at last address of program (_exit) to
* prevent process from existing. Such that dbx can continue
* to view process content after execution is completed.
*
* NOTES: Also handle multiple _exit() in different libraries.
*
* RECOVERY OPERATION: NONE NEEDED
*
* DATA STRUCTURES: NONE
*
* RETURNS: NONE
*/
private void bp_lastaddr()
{
Address exitaddr;
Node cond;
Symbol s;
Name exit_name = identname("_exit", true);
s = lookup(exit_name);
if (s == nil) {
warning(catgets(scmc_catd, MS_runtime, MSG_307, "cannot find _exit"));
}
while (s != nil) {
if (s->name == exit_name && isroutine(s)
&& ((exitaddr = codeloc(s)) != 0)) {
cond = build(O_EQ, build(O_SYM, pcsym), build(O_LCON, exitaddr));
event_once(cond, buildcmdlist(build(O_ENDX)));
}
s = s->next_sym;
}
}
/*
* Begin execution.
*
* We set a breakpoint at the end of the code so that the
* process data doesn't disappear after the program terminates.
*/
public startprog (argv, infile, outfile)
String argv[];
String infile, outfile;
{
String pargv[4];
Boolean init_prog;
init_prog = false;
if (coredump) {
coredump = false;
fclose(corefile);
coredump_close();
init_prog = true;
}
if (argv == nil) {
argv = pargv;
pargv[0] = objname;
pargv[1] = nil;
} else {
argv[argc] = nil;
}
pstart(process, argv, infile, outfile);
if (!norun && (remade(objname))) {
reinit(argv, infile, outfile);
}
if (init_prog)
program->symvalue.funcv.beginaddr = reg(SYSREGNO(PROGCTR));
if (process->status == STOPPED) {
if (!attach) {
pc = CODESTART;
setcurfunc(program);
}
else {
pc = reg(SYSREGNO(PROGCTR));
}
#ifndef KDBX
if (objsize != 0) {
bp_lastaddr();
}
#endif /* KDBX */
}
}
/*
* Check to see if the object file has changed since the symbolic
* information last was read.
*/
private time_t modtime;
public char *fname = nil;
private Boolean remade (filename)
String filename;
{
struct stat s;
Boolean b;
int i;
if (fname == nil) {
modtime = 0;
fname = (char *) malloc((unsigned) (strlen(filename) + 1));
} else if (!streq(fname, filename)) {
modtime = 0;
fname = (char *) realloc(fname, (unsigned) (strlen(filename) + 1));
}
strcpy(fname, filename);
stat(filename, &s);
b = (Boolean) (modtime != 0 and modtime < s.st_mtime);
modtime = s.st_mtime;
return b;
}
/*
* Initialize the argument list.
*/
public arginit ()
{
int i;
infile = nil;
outfile = nil;
errfile = nil;
argv[0] = objname;
if (!rerunning)
argc = 1;
else {
(*rpt_output)(stdout, "[ ");
for (i = 0; i < argc; i++)
(*rpt_output)(stdout, "%s ",argv[i]);
(*rpt_output)(stdout, "]\n");
}
}
/*
* NAME: clear_filenames
*
* FUNCTION: Clear the names of the files for redirection of stdin, stdout, and
* stderr of debuggee
*
* POST CONDITIONS: stdin, stdout, and stderr will not be redirected for
* debuggee unless specified on dbx command line
*
* PARAMETERS: NONE
*
* DATA STRUCTURES:
* infile - Set to NULL
* outfile - Set to NULL
* errfile - Set to NULL
*
* RETURNS: NONE
*/
void clear_filenames( void )
{
infile = NULL;
outfile = NULL;
errfile = NULL;
}
/*
* NAME: shell_popen
*
* FUNCTION: Initiates a pipe to/from a process and
* use defined shell to execute command.
*
* PARAMETERS:
* cmd - command to be executed in shell
* mode - I/O mode of file stream
*
* NOTE: shell_popen is same as popen except popen always
* uses 'bsh' for the command but shell_popen will
* use shell defined by the "SHELL" envir. variable.
* shell_popen and shell_pclose (same as fclose)
* should be used as pair. pclose will return
* error if used with shell_popen.
*
* RECOVERY OPERATION: NONE NEEDED
*
* DATA STRUCTURES: NONE
*
* RETURNS: File pointer to opened stream, or
* NULL on error.
*/
#define tst(a,b) (*mode == 'r'? (b) : (a))
#define shell_pclose(a) fclose(a)
private FILE *shell_popen(cmd, mode)
char *cmd, *mode;
{
int p[2];
register int myside, yourside;
int pid;
String shell;
int status;
if (pipe(p) < 0)
return(NULL);
myside = tst(p[1], p[0]);
yourside = tst(p[0], p[1]);
if ((pid = fork()) == 0) {
int stdio;
stdio = tst(0, 1);
(void) close(myside);
if (stdio != yourside) {
(void) close(stdio);
(void) fcntl(yourside, F_DUPFD, stdio);
(void) close(yourside);
}
if ((shell = getenv("SHELL")) == nil) {
/* use "sh" if SHELL is not defined */
(void) execl("/usr/bin/sh", "sh", "-c", cmd, (char *)0);
} else {
/* else use defined "SHELL" */
(void) execlp(shell, shell, "-c", cmd, (char *)0);
}
exit(1); /* exec error if we reach here */
}
if (pid == -1)
return(NULL);
(void) close(yourside);
/* wait for the exit status of shell command */
wait(&status);
return (fdopen(myside, mode));
}
/*
* Given a string, use popen and echo to resolve
* any meta characters that are present.
*/
public meta_expand(unresolved_buf, resolved_buf)
String unresolved_buf;
char resolved_buf[];
{
FILE *stream;
Boolean special_echo = true; /* echo -n does not emit newline */
strcpy(resolved_buf, "echo -n");
if ((stream = shell_popen(resolved_buf,"r")) != NULL) {
special_echo =
(Boolean)(fgets(resolved_buf,MAXCMDLNLEN,stream) == (char *) 0);
shell_pclose(stream);
}
if (special_echo) {
strcpy(resolved_buf,"echo -n ");
strcat(resolved_buf,unresolved_buf);
strcat(resolved_buf,";echo");
} else {
strcpy(resolved_buf,"echo ");
strcat(resolved_buf,unresolved_buf);
}
if ((stream = shell_popen(resolved_buf,"r")) != NULL)
{
*resolved_buf = '\0'; /* reset buffer */
fgets(resolved_buf,MAXCMDLNLEN,stream);
shell_pclose(stream);
}
else
{
beginerrmsg();
(*rpt_error)(stderr, catgets(scmc_catd, MS_execute, MSG_103,
"Attempt to resolve \"wildcards\" in run or rerun failed."));
enderrmsg();
}
}
/*
* Add an argument to the list for the debuggee.
*/
public newarg (arg, quoted)
String arg;
Boolean quoted;
{
String newarg, ptr;
char resolved_buf[MAXCMDLNLEN];
Boolean done = false;
if (argc >= MAXNCMDARGS) {
error(catgets(scmc_catd, MS_execute, MSG_105, "too many arguments"));
}
if (rerunning == 1) {
argc = 1;
rerunning++;
}
if (quoted) /* if string was in quotes */
argv[argc++] = arg;
else /* else expand it */
{
meta_expand(arg,resolved_buf);
ptr = malloc(strlen(resolved_buf)+1);
strcpy(ptr,resolved_buf);
while (!done)
{
newarg = ptr;
while ((*ptr != '\n') && (*ptr != ' ') && (*ptr != '\0'))
ptr++;
if (*ptr == '\n' || (*ptr == '\0')) done = true;
*ptr++ = '\0';
argv[argc++] = newarg;
}
}
}
/*
* Set the standard input for the debuggee.
*/
public inarg (filename)
String filename;
{
if (infile != nil) {
error( catgets(scmc_catd, MS_execute, MSG_109,
"multiple input redirects"));
}
infile = filename;
}
/*
* Set the standard output for the debuggee.
* Probably should check to avoid overwriting an existing file.
*/
public outarg (filename)
String filename;
{
outfile = filename;
}
/*
* Set the standard error for the debuggee.
* Probably should check to avoid overwriting an existing file.
*/
public errarg (filename)
String filename;
{
errfile = filename;
}
/*
* Start debuggee executing.
*/
public run ()
{
Node resetmult;
prtaddr = 0;
process->status = STOPPED;
call_command = false;
fixbps();
if (!attach)
curline = 0;
startprog(argv, infile, outfile);
just_started = true;
isstopped = norun;
never_ran = false;
if (dynamic_loading) {
update_load_maps();
dynamic_loading = false;
}
if (multproc != off) {
resetmult = build(O_MULTPROC, build(O_LCON,(int) multproc));
eval(resetmult);
}
cont(0);
}
/*
* Continue execution wherever we left off.
*
* Note that this routine never returns. Eventually bpact() will fail
* and we'll call printstatus or step will call it.
*/
private Voidfunc dbintr;
public cont (signo)
int signo;
{
int s;
#ifndef _NO_PROTO
void intr(int);
#else
void intr();
#endif
dbintr = signal(SIGINT, intr);
#ifndef KDBX
if (never_ran) {
/* Set breakpoint at end of code if we never run() program so that */
/* the process data doesn't disappear after the program terminates */
never_ran = false;
if (objsize != 0) {
bp_lastaddr();
}
}
#endif /* KDBX */
#ifdef KDBX
just_started = false;
#else /* KDBX */
if (just_started) {
just_started = false;
} else {
if (not isstopped) {
error(catgets(scmc_catd, MS_execute, MSG_110,
"cannot continue execution"));
}
isstopped = false;
/* If we are blocking signals from the debuggee program */
/* and user wants to continue execution with a signal, */
/* the signal is passed down here thru global variable */
/* contsig. (routine resume is not reached in this case)*/
/* We need this to handle cont with a signo when tracei */
/* (single_stepping) is on as well. */
if ((varIsSet("$sigblock") or single_stepping) and (signo > 0)) {
contsig = signo;
signo = DEFSIG;
}
stepover();
}
#endif /* KDBX */
s = signo;
for (;;) {
if (single_stepping) {
printnews(false);
} else {
setallbps();
resume(s, 0);
unsetallbps();
s = DEFSIG;
if (not isbperr() or not bpact()) {
printstatus();
}
if (single_stepping)
/* print the first line if tracing in a function */
printnews(true);
}
if (!isstopped)
stepover();
else
/* Return if cont part of ; statement */
return;
}
/* NOTREACHED */
}
/*
* This routine is called if we get an interrupt while "running"
* but actually in the debugger. Could happen, for example, while
* processing breakpoints.
*
* We basically just want to keep going; the assumption is
* that when the process resumes it will get the interrupt,
* which will then be handled.
*/
/* ARGSUSED */
#ifndef _NO_PROTO
private void intr (int signo)
#else
private void intr (signo)
int signo;
#endif
{
signal(SIGINT, intr);
}
public void fixintr ()
{
signal(SIGINT, dbintr);
}
/*
* Resume execution.
*/
public resume (signo, last_bp)
int signo;
Address last_bp;
{
register Process p;
#ifdef K_THREADS
/* for thread_k_cont in case of all threads held */
extern Address save_last_bp;
save_last_bp = last_bp;
#endif /* K_THREADS */
p = process;
pcont(p, signo);
pc = reg(SYSREGNO(PROGCTR));
if (p->status != STOPPED)
{
/*
* Must unset all the breakpoints here since we won't be going
* back to continue from here. First we set status to STOPPED
* so that we can unset all the breakpoints, once we return we
* set it back so that we represent our true condition.
*/
p->status = STOPPED;
if (last_bp != 0)
unsetbp(last_bp);
else
unsetallbps();
p->status = FINISHED;
action_mask |= EXECUTION_COMPLETED;
if (p->signo != 0)
error( catgets(scmc_catd, MS_execute, MSG_113,
"program terminated by signal %d"), p->signo);
else if (not runfirst)
{
if (p->exitval == 0)
error( catgets(scmc_catd, MS_execute, MSG_114,
"program exited"));
else
error( catgets(scmc_catd, MS_execute, MSG_115,
"program exited with code %d"), p->exitval);
}
}
}
/*
* Continue execution up to the next source line.
*
* There are two ways to define the next source line depending on what
* is desired when a procedure or function call is encountered. Step
* stops at the beginning of the procedure or call; next skips over it.
*
* Stepc is what is called when the step command is given.
* It has to play with the "isstopped" information.
*/
public stepc ()
{
#ifdef K_THREADS
next_subcommand = true;
#endif /* K_THREADS */
if (just_started) {
pc = reg(SYSREGNO(PROGCTR));
just_started = false;
}
else if (not isstopped) {
error(catgets(scmc_catd, MS_execute, MSG_110,
"cannot continue execution"));
}
isstopped = false;
dostep(false);
isstopped = true;
#ifdef K_THREADS
next_subcommand = false;
#endif /* K_THREADS */
}
public next ()
{
Address oldfrp, newfrp, orgin;
Word instr;
register Breakpoint bp;
extern Address before_alloca_frp();
#ifdef K_THREADS
next_subcommand = true;
#endif /* K_THREADS */
orgin = reg(SYSREGNO(PROGCTR));
if (not isstopped && not just_started) {
error(catgets(scmc_catd, MS_execute, MSG_110,
"cannot continue execution"));
}
just_started = false;
isstopped = false;
oldfrp = reg(FRP);
iread(&instr,reg(SYSREGNO(PROGCTR)),4);
/* if $catchbp is set or doing nexti or inside a prolog */
if (catchbp || inst_tracing || isprolog(pc))
{
dostep(true);
pc = reg(SYSREGNO(PROGCTR));
}
else
{
do {
dostep(true);
pc = reg(SYSREGNO(PROGCTR));
/* test and reset if the frame pointer has be changed by alloca() */
newfrp = (newfrp = before_alloca_frp()) ? newfrp : reg(FRP);
} while (newfrp < oldfrp && newfrp != 0);
}
isstopped = true;
#ifdef K_THREADS
next_subcommand = false;
#endif /* K_THREADS */
}
/*
* Continue execution until the current function returns, or,
* if the given argument is non-nil, until execution returns to
* somewhere within the given function.
*/
public rtnfunc (f)
Symbol f;
{
Address addr;
Symbol t;
if (not isstopped) {
error(catgets(scmc_catd, MS_execute, MSG_110,
"cannot continue execution"));
} else if (f != nil and not isactive(f)) {
error(catgets(scmc_catd, MS_execute, MSG_153, "%s is not active"),
symname(f));
} else {
addr = return_addr();
if (addr == nil) {
error(catgets(scmc_catd, MS_execute, MSG_155,
"no place to return to"));
} else {
isstopped = false;
contto(addr);
if (f != nil) {
for (;;) {
t = whatblock(pc);
if (t == f)
break;
addr = return_addr();
if (addr == nil)
break;
contto(addr);
bpact(); /* do breakpoint commands, but don't stop */
}
}
if ((not bpact()) || (pc == addr)) {
isstopped = true;
printstatus();
}
}
}
}
/*
* Single-step over the current machine instruction.
*
* If we're single-stepping by source line we want to step to the
* next source line. Otherwise we're going to continue so there's
* no reason to do all the work necessary to single-step to the next
* source line.
*/
public stepover ()
{
Boolean b;
if (traceexec) {
(*rpt_output)(stdout, "!! stepping over 0x%x\n",
reg(SYSREGNO(PROGCTR)));
}
if (single_stepping) {
dostep(false);
} else {
b = inst_tracing;
inst_tracing = true;
dostep(false);
inst_tracing = b;
}
if (traceexec) {
(*rpt_output)(stdout, "!! stepped over to 0x%x\n",
reg(SYSREGNO(PROGCTR)));
}
}
/*
* Resume execution up to the given address. We can either ignore
* breakpoints (stepto) or catch them (contto).
*/
public stepto (addr)
Address addr;
{
#ifdef K_THREADS
Symbol save_running_thread;
save_running_thread = running_thread;
#endif /* K_THREADS */
xto(addr, false);
#ifdef K_THREADS
/* if next step or nexti subcommands we have to stay in same thread */
/* local break-point stop only if it's the good thread */
if (lib_type == KERNEL_THREAD && save_running_thread )
while (save_running_thread != running_thread)
{
if (!next_subcommand || catchbp) break;
if (traceexec) {
(*rpt_output)(stdout, "!! local break-point on next addr = %X \n",
addr);
}
/*we have to stepi the current instruction */
if (nb_k_threads_sig == 1)
stepover();
xto(addr,false);
}
#endif /* K_THREADS */
}
public contto (addr)
Address addr;
{
xto(addr, true);
}
private xto (addr, catchbps)
Address addr;
boolean catchbps;
{
Address curpc;
register Breakpoint bp;
extern Boolean isdbxbp();
if (catchbps) {
stepover();
}
curpc = reg(SYSREGNO(PROGCTR));
#ifndef K_THREADS
if (addr != curpc) {
#else
if (addr != curpc || nb_k_threads_sig > 1) {
#endif
if (traceexec) {
(*rpt_output)(stdout, "!! stepping from 0x%x to 0x%x\n",
curpc, addr);
}
if (catchbps) {
setallbps();
}
setbp(addr);
if ((varIsSet("$sigblock") or single_stepping)
and (contsig != DEFSIG)) {
int signo = contsig;
contsig = DEFSIG;
resume(signo, addr);
} else
resume(DEFSIG, addr);
unsetbp(addr);
if (catchbps) {
unsetallbps();
}
if ((!isdbxbp(addr) && single_stepping) || (!isbperr())) {
printstatus();
}
}
}
/*
* Enter a procedure by creating and executing a call instruction.
*/
#define CALLSIZE 4 /* size of call instruction */
public beginproc (p, argc, ptrtofuncaddr)
Symbol p;
int argc;
Address ptrtofuncaddr;
{
extern Boolean just_started;
#ifdef K_THREADS
extern Address addr_dbsubn;
#endif
Address addr, orig_pc;
extern Process process;
Symbol callpt, assemvar;
Name n;
int local_module;
Desclist *dptr;
orig_pc = reg(SYSREGNO(IAR));
if (ptrtofuncaddr) {
addr = ptrtofuncaddr;
}
else {
addr = prolloc(p);
}
if (textobj(addr) != (local_module = textobj(orig_pc))) {
for (dptr = p->symvalue.funcv.fcn_desc;
(dptr != nil) && (textobj(dptr->descaddr) != local_module);
dptr = dptr->next_desc);
#ifdef K_THREADS
/* if addr_dbsubn we are trying to hold the running thread */
/* the function __funcblock_np of libpthreads is not */
/* accessible with "linkage symbol" if the library is */
/* shared. In this case we can call directly __funcblock_np*/
/* the registers are saved and restored after. We can */
/* use the same toc. */
if (dptr == nil && !addr_dbsubn)
#else
if (dptr == nil)
#endif
error( catgets(scmc_catd, MS_execute, MSG_539,
"Unable to locate linkage symbol for out-of-module call"));
#ifdef K_THREADS
if (!addr_dbsubn || dptr != nil)
#endif
addr = dptr->descaddr;
n = identname("__dbsubg",true);
} else {
/* Linkage routines (GLUE codes) can be in the local module. */
/* Need __dbsubg to restore the TOC for them also. */
if (p->symvalue.funcv.islinkage && !ptrtofuncaddr)
n = identname("__dbsubg",true);
else
n = identname("__dbsubc",true);
}
find(callpt, n) where isroutine(callpt) endfind(callpt);
if (callpt == nil) {
error( catgets(scmc_catd, MS_execute, MSG_162,
"Failed establishing calling point (libg.a not linked)"));
}
pc = prolloc(callpt);
setreg(SYSREGNO(IAR), pc);
if (just_started) setreg(FRP, reg(STKP));
#if defined(_IBMRT) && !defined(KDBXRT)
/*
dwrite((char *)&addr,dbargs_addr-4,4);
setreg(GPR0, dbargs_addr-4);
*/
setreg(GPR0, addr);
#else
setreg(SYSREGNO(LR), addr);
#endif
pstep(process, DEFSIG);
pc = reg(SYSREGNO(IAR));
if (not isbperr()) {
printstatus();
}
}
/*
* Print the status of the process.
* This routine does not return.
*/
public printstatus ()
{
if ((process->status == FINISHED) && (!isXDE)) {
exit(0);
} else if ((process->status == FINISHED) &&
(isXDE) && (runfirst) &&
(action_mask & EXECUTION) &&
(action_mask & EXECUTION_COMPLETED)) {
exit(0);
} else if ((process->status == FINISHED) &&
(isXDE) && (runfirst) &&
!(action_mask & EXECUTION)) {
(*rpt_open)();
action_mask |= DBX_TERMINATED;
longjmp( envptr, 1 );
} else {
if (runfirst) {
(*rpt_error)(stderr, catgets(scmc_catd, MS_execute, MSG_163,
"\nEntering debugger ...\n"));
if (isXDE) (*rpt_open)();
printheading();
init();
}
setcurfunc(whatblock(pc));
getsrcpos();
if (process->signo == SIGINT) {
isstopped = true;
printerror();
}
else if (isbperr() and isstopped)
{
(*rpt_output)(stdout, "stopped ");
printloc();
(*rpt_output)(stdout, "\n" );
if (curline > 0)
{
printlines(curline, curline);
}
else
{
printinst(pc, pc);
}
if (call_command)
{
reset_subarray_vars();
bp_skips = 0;
if (initdone)
{
gobble();
longjmp( envptr, 1);
}
}
else {
extern char * process_char;
if (*process_char != ';')
erecover();
}
} else {
fixintr();
isstopped = true;
printerror();
}
}
}
/*
* Print out the current location in the debuggee.
*/
public printloc ()
{
extern Boolean heat_shrunk;
struct Frame frp;
int frame_found = false;
(*rpt_output)(stdout, "in ");
if (isinline(curfunc))
(*rpt_output)(stdout, "unnamed block ");
/* if this is a heat_shrunk executable and the function
name is in the traceback table */
if (heat_shrunk && (frame_found = (getcurframe(&frp) == 0))
&& (frp.tb.name_present && frp.name))
{
/* print the function name found in the traceback table */
(*rpt_output)(stdout, "%s", frp.name);
}
else
printname(rpt_output, stdout, curfunc, true);
(*rpt_output)(stdout, " ");
if (curline > 0 and not useInstLoc) {
printsrcpos();
} else {
useInstLoc = false;
curline = 0;
(*rpt_output)(stdout, "at 0x%x", pc);
/* if the current location is a fdpr relocated address */
if (frame_found && (frp.save_pc != frp.orig_loc))
{
/* print the original location of this location */
/* NOTE : frp.orig_loc was set by getcurframe */
(*rpt_output)(stdout, " = fdpr[0x%x]", frp.orig_loc);
}
}
#if defined (CMA_THREAD) || defined (K_THREADS)
/* indicate what thread is running if needed... */
if (current_thread)
(*rpt_output)(stdout, " (%s)", symname(current_thread));
#endif /* CMA_THREAD || K_THREADS */
}
/*
* Predicate to test if the reason the process stopped was because
* of a breakpoint. If so, as a side effect clear the local copy of
* signal handler associated with process. We must do this so as to
* not confuse future stepping or continuing by possibly concluding
* the process should continue with a SIGTRAP handler.
*/
public boolean isbperr ()
{
Process p;
boolean b;
p = process;
/* if we stopped because of a breakpoint */
if (p->status == STOPPED && p->is_bp) {
b = true;
p->sigstatus = 0;
} else {
b = false;
}
return b;
}
/*
* Some functions for testing the state of the process.
*/
public Boolean notstarted (p)
Process p;
{
return (Boolean) (p->status == NOTSTARTED);
}
public Boolean isfinished (p)
Process p;
{
return (Boolean) (p->status == FINISHED);
}
/*
* Return the signal number that stopped the process.
*/
public int errnum (p)
Process p;
{
return p->signo;
}
/*
* Return the signal code associated with the signal.
*/
public int errcode (p)
Process p;
{
return p->sigcode;
}
/*
* Return the termination code of the process.
*/
public int exitcode (p)
Process p;
{
return p->exitval;
}
/*
* These routines are used to access the debuggee process from
* outside this module.
*
* They invoke "pio" which eventually leads to a call to "ptrace".
* The system generates an I/O error when a ptrace fails. During reads
* these are ignored, during writes they are reported as an error, and
* for anything else they cause a fatal error.
*/
extern Intfunc onsyserr();
private badaddr;
public boolean badIO;
/*
* Read from the process' instruction area.
*/
public iread (buff, addr, nbytes)
char *buff;
Address addr;
int nbytes;
{
Intfunc f;
badIO = false;
f = onsyserr(EIO, read_err);
badaddr = addr;
if (coredump) {
coredump_readtext(buff, addr, nbytes);
} else if (noexec) {
objfile_readtext(buff, addr, nbytes);
} else {
pio(process, PREAD, TEXTSEGM, buff, addr, nbytes);
}
onsyserr(EIO, f);
return (badIO == true);
}
/*
* Write to the process' instruction area, usually in order to set
* or unset a breakpoint.
*/
public iwrite (buff, addr, nbytes)
char *buff;
Address addr;
int nbytes;
{
Intfunc f;
badIO = false;
if (coredump) {
error( catgets(scmc_catd, MS_execute, MSG_175,
"no process to write to"));
}
f = onsyserr(EIO, write_err);
badaddr = addr;
pio(process, PWRITE, TEXTSEGM, buff, addr, nbytes);
onsyserr(EIO, f);
return (badIO == true);
}
/*
* Read for the process' data area.
*/
public dread (buff, addr, nbytes)
char *buff;
Address addr;
int nbytes;
{
Intfunc f;
badaddr = addr;
badIO = false;
if (noexec) {
f = onsyserr(EIO, read_err);
objfile_readdata(buff, addr, nbytes);
onsyserr(EIO, f);
} else if (coredump && (fullcore || initdone)) {
f = onsyserr(EFAULT, read_err);
coredump_readdata(buff, addr, nbytes);
onsyserr(EFAULT, f);
} else {
f = onsyserr(EIO, read_err);
pio(process, PREAD, DATASEGM, buff, addr, nbytes);
onsyserr(EIO, f);
}
return (badIO == true);
}
/*
* Write to the process' data area.
*/
public dwrite (buff, addr, nbytes)
char *buff;
Address addr;
int nbytes;
{
Intfunc f;
badIO = false;
if (coredump) {
error( catgets(scmc_catd, MS_execute, MSG_175,
"no process to write to"));
}
f = onsyserr(EIO, write_err);
badaddr = addr;
pio(process, PWRITE, DATASEGM, buff, addr, nbytes);
onsyserr(EIO, f);
return (badIO == true);
}
/*
* Trap for errors in reading or writing to a process.
* The current approach is to "ignore" read errors and complain
* bitterly about write errors.
*/
private read_err ()
{
badIO = true;
/*
* Ignore.
*/
}
private write_err ()
{
badIO = true;
error(catgets(scmc_catd, MS_execute, MSG_215,
"cannot write to process (address 0x%x)"), badaddr);
}
/***************************************************
* Multi-process debugging for fork *
* *
* forks dbx and sets up new dbx if req *
* *
***************************************************/
ffork (pid)
int pid;
{
Node multcmd; /* Command to enable multi-process in child */
int nextdbx = 0, i,j,k,l,m,n;
char buff[32];
struct termio tty_old; /* save the original tty */
int rc;
register String dir; /* source path string for xde call */
char *argv[MAXNCMDARGS]; /* argv list for xde exec call */
char *arg0;
int z = 0;
(*rpt_output)(stdout, catgets(scmc_catd, MS_execute, MSG_216,
"application forked, child pid=%d, process stopped, awaiting input \n"),
pid);
/**************** if dbx is running under XDE *******************/
if (isXDE)
{
if ((nextdbx = fork()) == 0) { /* if it is the child xde */
/* allow multiple arguments separated by single spaces */
arg0 = graphical_debugger;
while ( arg0 != NULL )
{
argv[z++] = arg0;
arg0 = strchr(arg0, ' ');
if ( arg0 != NULL )
*arg0++ = '\0';
}
/** find the source files path and pass it along to the new xde */
foreach (String, dir, sourcepath)
sprintf(argv[z++]=(char *)(malloc(strlen(dir)+3)), "-I%s", dir);
endfor
/* Use '-A' option to reattach to the stopped debuggee */
sprintf(argv[z++]=(char *)(malloc(strlen(dir)+3)), "-A%d", pid);
/* Use '-child' option to alarm new xde that it is a forked child */
sprintf(argv[z++]=(char *)(malloc(7)), "-child");
argv[z]=nil;
execvp(graphical_debugger,argv); /* exec a new xde with needed info */
_exit(1);
}
else {
(*rpt_multiprocess)(pid);
newdbg = nextdbx;
}
}
/************* next xdb program when dbx forks *******/
else if (multproc == on) {
/****** next dbx program on fork *********/
int fd;
nextdbx = fork();
if (nextdbx == 0) { /* if new next dbx */
fd = screen(0); /* set up new virtual term */
if(fd == -1) {
char *sp, *tmp_file;
FILE *fp;
int dirlen = 0, i;
char *s;
tmp_file = mktemp("/tmp/dbxtmpfile.XXXXXX");
fp = fopen(tmp_file, "w");
/* for unknown reason, it needs next before detach */
fprintf(fp, "next\ndetach\n");
fclose(fp);
/** find the source files path and pass it along to the new xde */
foreach (String, dir, sourcepath)
sprintf(argv[z++]=(char *)(malloc(strlen(dir)+3)), "-I%s ", dir);
endfor
for(i=0; i < z; i++) {
dirlen += strlen(argv[i]);
}
s = (char *)malloc(dirlen+1);
*s = '\0';
for(i=0; i < z; i++) {
strcat(s,argv[i]);
}
/* allocate space big enough to hold the following sprintf for sp */
sp=(char *)(malloc(strlen(graphical_debugger)+60+dirlen));
sprintf(sp, "%s -c %s %s -A %d > /dev/null\n",
graphical_debugger, tmp_file, s, newdebuggee);
free(s);
system(sp);
free(sp);
remove(tmp_file);
exit(0);
}
rc = ptrace(REATT, pid, 0, 0, 0); /* re-attach to child proc */
if (rc < 0) /* error */
fatal( catgets(scmc_catd, MS_execute, MSG_217,
"Could not reattach to child process on fork"));
else {
norun = true; /* prevent use of run or rerun */
(*rpt_output)(stdout, catgets(scmc_catd, MS_execute, MSG_218,
"debugging child, pid=%d, process stopped, waiting input \n"), pid);
process->pid = pid;
reset_debuggee_fd(fd);
reset_pgrp_info();
/* Enable multi-process debugging. */
multcmd = build(O_MULTPROC, build(O_LCON, (int) multproc));
eval(multcmd);
}
}
else newdbg = nextdbx;
}
else if (multproc == child)
{
parent_pid new_parent_pid;
delete_all_bps();
rc = ptrace(REATT, pid, 0, 0, 0); /* re-attach to child proc */
if (rc < 0) /* error */
fatal( catgets(scmc_catd, MS_execute, MSG_217,
"Could not reattach to child process on fork"));
(*rpt_output)(stdout, catgets(scmc_catd, MS_execute, MSG_218,
"debugging child, pid=%d, process stopped, waiting input \n"), pid);
/* detach from the parent process */
detach(process->pid, 0);
/* Keep a list of parent process ids, in case they have
not finished executing when dbx is exited. We will
then kill them */
new_parent_pid = new(parent_pid);
new_parent_pid->pid = process->pid;
new_parent_pid->next = parent_pid_list;
parent_pid_list = new_parent_pid;
eval(build(O_CATCH, build(O_LCON, SIGHUP)));
process->pid = pid;
} /*not isXDE*/
else if (multproc == parent)
{
int temppid = process->pid;
/* temporarily make the child process the "current"
process */
process->pid = pid;
delete_all_bps();
process->pid = temppid;
/* detach from the child process */
detach(pid, 0);
}
}
void kill_parents()
{
parent_pid parent_pid_ptr = parent_pid_list;
while (parent_pid_ptr != NULL)
{
/* send a kill signal to all processes we detached
from while following the child */
kill(parent_pid_ptr->pid, 9);
parent_pid_ptr = parent_pid_ptr->next;
}
}
/***************************************************
* restart dbx to handle new process *
* used when application program execs *
* *
***************************************************/
dbxexec(pid)
int pid;
{
getprog(pid,true);
just_started = true;
cacheflush(process);
exec_init();
/* not reached */
}
/*
* Report process unload/load and update maps and tables.
*/
process_load ()
{
int prev_ldcnt;
struct ldinfo *prev_ldinfo;
struct fdinfo *prev_fdinfo;
extern int loadcnt;
if (!dynamic_loading) {
dynamic_loading = true;
}
update_load_maps();
action_mask |= LOADCALL;
}
/*
* Reorganize debuggee's address spaces and symbols after load/unload
*/
update_load_maps ()
{
int prev_ldcnt;
struct ldinfo *prev_ldinfo;
struct fdinfo *prev_fdinfo;
extern int loadcnt;
extern String prevsource;
int i;
/* Save previous loader information */
prev_ldcnt = loadcnt;
prev_ldinfo = loader_info;
prev_fdinfo = fd_info;
/* Get new loader information. */
getldrinfo(false);
/* Get new symbolic information and retain old information. */
sync_load_info(prev_ldinfo,prev_fdinfo,prev_ldcnt);
/* Clean up (close) previous opened file descriptors within prev_ldinfo */
for (i = 0; i < prev_ldcnt; i++)
{
extern LDFILE **ldentry;
extern boolean lazy;
int j = 0, needed = false;
/* if ldentry has not be ldclosed */
if (lazy || noexec || coredump)
while (!needed && j < loadcnt) {
if (prev_ldinfo[i].ldinfo_fd == ldentry[j]->ioptr->_file)
needed = true;
j++;
}
/* close if not needed and not input file */
if ((!needed) && (!(checkinput(prev_ldinfo[i].ldinfo_fd))))
close(prev_ldinfo[i].ldinfo_fd);
}
prevsource = nil; /* reset since previous fd could be deleted */
if (srcfp) { /* close open File from skimsource() */
fclose(srcfp);
errno = 0; /* fclose could fail due to previsous close() */
srcfp = nil;
}
}
/*
* Get the signal number associated with a given name.
* The name is first translated to upper case if necessary.
*/
public int siglookup (s)
String s;
{
register char *p, *q;
char buf[100];
int i;
p = s;
q = buf;
while (*p != '\0') {
if (*p >= 'a' and *p <= 'z') {
*q = (*p - 'a') + 'A';
} else {
*q = *p;
}
++p;
++q;
}
*q = '\0';
p = buf;
if (buf[0] == 'S' and buf[1] == 'I' and buf[2] == 'G') {
p += 3;
}
i = 1;
for (;;) {
if (i >= sizeof(signames) / sizeof(signames[0])) {
error(catgets(scmc_catd, MS_execute, MSG_623,
"signal \"%1$s\" unknown"), s);
i = 0;
break;
}
if (signames[i] != nil and streq(signames[i], p)) {
break;
}
++i;
}
return i;
}
/*
* Print all signals being ignored by the debugger.
* These signals are auotmatically
* passed on to the debugged process.
*/
public printsigsignored (p)
Process p;
{
printsigs(~p->sigset[0],~p->sigset[1]);
}
/*
* Print all signals being intercepted by
* the debugger for the specified process.
*/
public printsigscaught (p)
Process p;
{
printsigs(p->sigset[0],p->sigset[1]);
}
private printsigs (lowerset, upperset)
int lowerset, upperset;
{
int s;
int set = lowerset;
char separator[2];
separator[0] = '\0';
for (s = 1; s < sizeof(signames) / sizeof(signames[0]); s++) {
if (s > (8*sizeof(Word))) {
set = upperset;
}
if (set & setrep((s > (8*sizeof(Word)) ? s - (8*sizeof(Word)) : s))) {
if (signames[s] != nil) {
(*rpt_output)(stdout, "%s%s", separator, signames[s]);
separator[0] = ' ';
separator[1] = '\0';
}
}
}
if (separator[0] == ' ') {
(*rpt_output)(stdout, "\n" );
}
}
/* Return the current process id for the child. */
curpid()
{
return process->pid;
}
/*
* Stuff from here on down deals with handling non-executable object files.
*/
objfile_readin()
{
objfile = ldopen(objname, objfile);
if (objfile == NULL) {
fatal(catgets(scmc_catd, MS_execute, MSG_225,
"cannot read \"%s\""), objname);
}
}
objfile_readtext(buff, addr, nbytes)
char *buff;
Address addr;
int nbytes;
{
if (dataaddrs) {
objfile_readdata(buff, addr, nbytes);
return;
} else if (fileaddrs) {
objfile_readbytes(buff, addr, nbytes);
return;
}
if (textorg != 0)
addr += textorg;
if ((addr < text_scn_hdr.s_paddr) ||
(addr > text_scn_hdr.s_paddr + text_scn_hdr.s_size))
error( catgets(scmc_catd, MS_execute, MSG_226,
"Address out of bounds for text section."));
FSEEK(objfile, (long int) (text_scn_hdr.s_scnptr+addr-text_scn_hdr.s_paddr),
BEGINNING);
FREAD((void *) buff, (size_t) nbytes, (size_t) sizeof(Byte), objfile);
}
objfile_readdata(buff, addr, nbytes)
char *buff;
Address addr;
int nbytes;
{
if (textaddrs) {
objfile_readtext(buff, addr, nbytes);
return;
} else if (fileaddrs) {
objfile_readbytes(buff, addr, nbytes);
return;
}
if (dataorg != 0)
addr += dataorg;
if ((addr < data_scn_hdr.s_paddr) ||
(addr > data_scn_hdr.s_paddr + data_scn_hdr.s_size))
error( catgets(scmc_catd, MS_execute, MSG_227,
"Address out of bounds for data section."));
FSEEK(objfile, (long int)(data_scn_hdr.s_scnptr+addr-data_scn_hdr.s_paddr),
BEGINNING);
FREAD((void *) buff, (size_t) nbytes, (size_t) sizeof(Byte), objfile);
}
objfile_readbytes(buff, addr, nbytes)
char *buff;
Address addr;
int nbytes;
{
FSEEK(objfile, (long int) addr, BEGINNING);
FREAD((void *) buff, (size_t) nbytes, (size_t) sizeof(Byte), objfile);
}
objfile_close()
{
ldclose(objfile);
}