/***************************************************************/ /* mgdb: midipix-specific bits for gdb */ /* Copyright (C) 2019 Z. Gilboa */ /* Released under GPLv2 and GPLv3; see COPYING.MGDB. */ /***************************************************************/ #include "defs.h" #include "osabi.h" #include "config.h" #include "target.h" #include "utils.h" #include "inferior.h" #include "gdbthread.h" #include "inf-child.h" #include "x86-nat.h" #include #include #include #include #include #include #include #define WINNT_OUTBUF_SIZE 65536 #define winnt_error(msg) \ error("%s(): %s.",__FUNCTION__,msg) #define winnt_perror(msg,pid) \ error("%s(): %s (pid %d).",__FUNCTION__,msg,pid) struct winnt_process { int pfd; pid_t pid; pid_t syspid; pid_t systid; pid_t flags; }; static size_t pcnt; static winnt_process * plist; static char * outbuf; static winnt_process * winnt_plist_expand (void) { struct winnt_process * pnew; struct winnt_process * pold; size_t idx; if (!(pnew = (struct winnt_process *)calloc(pcnt+32, sizeof(*pnew)))) return 0; memcpy( pnew,plist, pcnt*sizeof(*plist)); pold = plist; plist = pnew; pcnt += 32; free(pold); return pnew; } static int winnt_plist_add (pid_t pid, int pfd) { struct winnt_process * pdbg; struct winnt_process * pcap; for (pdbg=plist, pcap=&plist[pcnt]; pdbgpid) { pdbg->pid = pid; pdbg->pfd = pfd; return 0; } } return winnt_plist_expand() ? winnt_plist_add(pid,pfd) : -1; } static void winnt_plist_remove (pid_t pid) { struct winnt_process * pdbg; struct winnt_process * pcap; for (pdbg=plist, pcap=&plist[pcnt]; pdbgpid == pid) { pdbg->pid = 0; return; } } } static struct winnt_process * winnt_process_record(pid_t pid) { winnt_process * pdbg; winnt_process * pcap; for (pdbg=plist, pcap=&plist[pcnt]; pdbgpid == pid) return pdbg; return 0; } static char * winnt_pid_to_exec_file (struct target_ops * t, int pid) { (void)t; (void)pid; outbuf[0] = 0; return outbuf; } static enum target_xfer_status winnt_xfer_partial( struct target_ops * t, enum target_object object, const char * annex, gdb_byte * readbuf, const gdb_byte * writebuf, ULONGEST offset, ULONGEST len, ULONGEST * nxfered) { (void)t; (void)object; (void)annex; (void)offset; (void)len; if (readbuf && writebuf) winnt_error("internal error: both readbuf and writebuf are non-null."); if (!readbuf && !writebuf) winnt_error("internal error: both readbuf and writebuf are null."); if (!nxfered) winnt_error("internal error: nxfered is null."); return TARGET_XFER_E_IO; } static nfds_t winnt_poll_one_init (struct pollfd * pfds, pid_t pid) { winnt_process * pdbg; winnt_process * pcap; for (pdbg=plist, pcap=&plist[pcnt]; pdbgpid == pid) { pfds->fd = pdbg->pfd; pfds->events = POLLIN; return 1; } } return 0; } static nfds_t winnt_poll_init (struct pollfd * pfds) { winnt_process * pdbg; winnt_process * pcap; struct pollfd * pfd; for (pfd=pfds, pdbg=plist, pcap=&plist[pcnt]; pdbgpid) { pfd->fd = pdbg->pfd; pfd->events = POLLIN; pfd++; } } return pfd - pfds; } static void winnt_close (struct target_ops * t) { } static void winnt_xclose (struct target_ops * t) { } static void winnt_respond (int pfd, struct __dbg_event * event, int response) { ptid_t ptid; event->eresponse = response; switch (event->evttype) { case __DBG_STATE_CREATE_PROCESS: ptid = ptid_build(event->syspid,0,0); add_thread(ptid); ptid = ptid_build(event->syspid,0,event->systid); add_thread(ptid); break; case __DBG_STATE_CREATE_THREAD: ptid = ptid_build(event->syspid,0,event->systid); add_thread(ptid); break; default: break; } if (__dbg_event_respond(pfd,event) < 0) winnt_perror("failed to respond to debug event",pfd); } static void winnt_prepare (struct target_ops * t, pid_t pid, int pfd, int attached) { int ret; struct inferior * cinf; struct __dbg_event event; struct winnt_process * pidinfo; if (!target_is_pushed(t)) push_target(t); clear_proceed_status(0); init_wait_for_inferior(); if (!(cinf = current_inferior())) winnt_error("failed to obtain current inferior"); if (!(pidinfo = winnt_process_record(pid))) winnt_perror("internal error: record not found",pid); inferior_ptid = pid_to_ptid (pid); cinf->attach_flag = attached; inferior_appeared(cinf,pid); init_thread_list(); target_terminal_init(); target_terminal_inferior(); while (1) { do { ret = __dbg_event_acquire(pfd,&event); } while ((ret < 0) && (errno == EAGAIN)); if (ret < 0) winnt_perror("failed to acquire preliminary debug event",pid); if ((event.evttype == __DBG_STATE_CREATE_PROCESS) && !attached) if (__dbg_resume_thread(pfd,event.systid) < 0) winnt_perror("failed to resume first thread",pid); switch (event.evttype) { case __DBG_STATE_EXCEPTION: case __DBG_STATE_BREAKPOINT: case __DBG_STATE_SINGLE_STEP: pidinfo->syspid = event.syspid; pidinfo->systid = event.systid; target_terminal_ours(); return; case __DBG_STATE_CREATE_PROCESS: case __DBG_STATE_CREATE_THREAD: case __DBG_STATE_IDLE: case __DBG_STATE_REPLY_PENDING: case __DBG_STATE_DLL_LOAD: case __DBG_STATE_DLL_UNLOAD: case __DBG_STATE_EXIT_THREAD: case __DBG_STATE_EXIT_PROCESS: winnt_respond(pfd,&event,__DBG_RESPONSE_CONTINUE); } } } static void winnt_attach (struct target_ops * t, const char * args, int from_tty) { pid_t pid; int pfd; if ((pid = parse_pid_to_attach(args)) < 0) { winnt_error ("cannot parse pid to attach"); } if ((pfd = __dbg_attach(pid)) < 0) { winnt_perror ("cannot attach to process",pid); } if (__dbg_rbreak(pfd) < 0) { __dbg_detach(pfd); close(pfd); winnt_perror ("could not issue a breakpoint in process",pid); } if (winnt_plist_add(pid,pfd) < 0) { __dbg_detach(pfd); close(pfd); winnt_error ("could not expand debuggee list"); } winnt_prepare(t,pid,pfd,from_tty); } static void winnt_abandon (struct target_ops * t, winnt_process * pidinfo) { inferior_ptid = null_ptid; x86_cleanup_dregs(); inf_child_maybe_unpush_target(t); winnt_plist_remove(pidinfo->pid); } static void winnt_detach (struct target_ops * t, const char * args, int from_tty) { pid_t pid; winnt_process * pidinfo; if ((pid = ptid_get_pid(inferior_ptid)) < 0) winnt_error ("cannot determine pid to detach"); if (!(pidinfo = winnt_process_record(pid))) winnt_perror ("debuggee record does not exist",pid); if (__dbg_detach(pidinfo->pfd) < 0) winnt_perror ("could not detach from process",pid); if (close(pidinfo->pfd) < 0) winnt_perror ("failed to close process file descriptor",pid); detach_inferior(pidinfo->syspid); winnt_abandon(t,pidinfo); } static ptid_t winnt_wait( struct target_ops * t, ptid_t ptid, struct target_waitstatus * waitstatus, int target_options) { struct __dbg_event dbgevent; struct pollfd pollfdbuf[512]; struct pollfd * pfds; struct pollfd * pfd; nfds_t nfds,i; if (pcnt*sizeof(*pfds) < sizeof(pollfdbuf)) pfds = pollfdbuf; else if (!(pfds = (struct pollfd *)calloc(pcnt,sizeof(*pfds)))) return null_ptid; nfds = (ptid.pid > 0) ? winnt_poll_one_init(pfds,ptid.pid) : winnt_poll_init(pfds); if (poll(pfds,nfds,-1) < 0) return null_ptid; for (i=0, pfd=0; ifd,&dbgevent) < 0) return null_ptid; return ptid_build( dbgevent.cpid ? dbgevent.cpid : dbgevent.syspid, 0, dbgevent.ctid ? dbgevent.ctid : dbgevent.systid); } static void winnt_resume ( struct target_ops * t, ptid_t ptid, int step, enum gdb_signal sig) { } static void winnt_kill (struct target_ops * t) { struct inferior * cinf; struct winnt_process * pidinfo; if (!(cinf = current_inferior())) winnt_error("failed to obtain current inferior"); if (!(pidinfo = winnt_process_record(cinf->pid))) winnt_perror("internal error: record not found",cinf->pid); if (__dbg_kill(pidinfo->pfd) < 0) winnt_perror("failed to kill current inferior",cinf->pid); winnt_abandon(t,pidinfo); } static void winnt_mourn_inferior (struct target_ops * t) { /* update plist, etc. */ } static void winnt_create_inferior( struct target_ops * t, char * exec_file, char * args, char ** envp, int from_tty) { int pfd; pid_t pid; size_t arglen; char * argbuf; char ** argv; /* validate */ if (!exec_file) winnt_error("Executable not set; use `target exec'."); /* assume no folded white space, add final null termination */ arglen = strlen(args); arglen++; if (!(argbuf = (char *)calloc(arglen,1))) winnt_error("Failed to allocate argument string buffer."); /* extra pointer for exec_file (argv[0]) */ arglen++; if (!(argv = (char **)calloc(arglen,sizeof(char *)))) winnt_error("Failed to allocate argument vector buffer."); arglen--; /* argv */ argv[0] = exec_file; if (__cmd_args_to_argv(args,argbuf,arglen,&argv[1],arglen) < 0) winnt_error("Failed to parse command-line arguments."); /* spawn */ if ((pfd = __dbg_spawn(exec_file,argv,envp,0)) < 0) winnt_error("Failed to spawn executable."); /* syspid */ if ((pid = __dbg_query_syspid(pfd)) < 0) { __dbg_kill(pfd); close(pfd); winnt_error("Failed to obtain syspid (should never happen)."); } /* debuggee list */ if (winnt_plist_add(pid,pfd) < 0) { __dbg_kill(pfd); close(pfd); winnt_error ("could not expand debuggee list"); } /* init loop */ winnt_prepare(t,pid,pfd,0); } static char * winnt_pid_to_str (struct target_ops * t, ptid_t ptid) { if (ptid.tid) sprintf(outbuf,"Thread %d:%ld",ptid.pid,ptid.tid); else sprintf(outbuf,"Process %d",ptid.pid); return outbuf; } static target_ops * winnt_target_alloc (void) { target_ops * t = inf_child_target(); t->to_attach_no_wait = 1; t->to_has_thread_control = 1; t->to_close = winnt_close; t->to_xclose = winnt_xclose; t->to_attach = winnt_attach; t->to_detach = winnt_detach; t->to_wait = winnt_wait; t->to_resume = winnt_resume; t->to_kill = winnt_kill; t->to_mourn_inferior = winnt_mourn_inferior; t->to_create_inferior = winnt_create_inferior; t->to_pid_to_str = winnt_pid_to_str; t->to_pid_to_exec_file = winnt_pid_to_exec_file; t->to_xfer_partial = winnt_xfer_partial; x86_use_watchpoints(t); return ((outbuf = (char *)calloc(1,WINNT_OUTBUF_SIZE))) ? t : 0; } extern initialize_file_ftype _initialize_winnt_nat; void _initialize_winnt_nat(void) { add_target(winnt_target_alloc()); }