/***************************************************************/ /* 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 regcache; struct winnt_process { int pfd; pid_t pid; pid_t syspid; pid_t systid; pid_t flags; int nmodules; struct __dbg_module_info * modules; struct __dbg_event event; }; static size_t pcnt; static winnt_process * plist; static char * outbuf; extern void amd64_winnt_fetch_registers( int pfd, struct regcache * rcache, int regnum, pid_t tid); extern void i386_winnt_fetch_registers( int pfd, struct regcache * rcache, int regnum, pid_t tid); 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_free_modules(struct winnt_process * pidinfo) { struct __dbg_module_info * module; struct __dbg_module_info * modules_base; struct __dbg_module_info * modules_cap; if (!pidinfo->modules) return; modules_base = pidinfo->modules; modules_cap = &modules_base[pidinfo->nmodules]; for (module=modules_base; modulemodule_name) free(module->module_name); free(pidinfo->modules); pidinfo->modules = 0; pidinfo->nmodules = 0; } static void winnt_plist_remove (pid_t pid) { struct winnt_process * pdbg; struct winnt_process * pcap; for (pdbg=plist, pcap=&plist[pcnt]; pdbgpid == pid) { winnt_free_modules(pdbg); pdbg->pid = 0; pdbg->pfd = 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 ptid_t winnt_get_ada_task_ptid (struct target_ops * t, long lwp, long tid) { ptid_t ptid; (void)t; ptid.pid = inferior_ptid.pid; ptid.lwp = 0; ptid.tid = lwp; return ptid; } static char * winnt_pid_to_exec_file (struct target_ops * t, int pid) { ssize_t ret; struct winnt_process * pidinfo; if (!(pidinfo = winnt_process_record(pid))) winnt_perror("internal error: record not found",pid); ret = __dbg_info_get( pidinfo->pfd,0, __DBG_INFO_IMAGE_RPATH, outbuf,WINNT_OUTBUF_SIZE); if (ret < 0) ret = __dbg_info_get( pidinfo->pfd,0, __DBG_INFO_REPORTED_IMAGE_RPATH, outbuf,WINNT_OUTBUF_SIZE); if (ret < 0) outbuf[0] = 0; return outbuf; } static void winnt_get_modules(struct winnt_process * pidinfo) { void * addr; struct __dbg_module_info * module; struct __dbg_module_info * modules; struct __dbg_module_info * modules_cap; ssize_t nbytes; size_t nmodules; winnt_free_modules(pidinfo); if (!(addr = calloc(2048,sizeof(*modules)))) return; nbytes = __dbg_info_get( pidinfo->pfd,0, __DBG_INFO_CACHED_MODULE_LIST, addr,2048*sizeof(*modules)); if (nbytes < 0) { free(addr); return; }; nmodules = nbytes / sizeof(*modules); modules = (struct __dbg_module_info *)calloc(nmodules,sizeof(*modules)); if (!modules) { free(addr); return; } memcpy(modules,addr,nmodules*sizeof(*modules)); free(addr); module = modules; modules_cap = &modules[nmodules]; for (; modulepfd,module->module_key, __DBG_INFO_MODULE_RPATH, outbuf,WINNT_OUTBUF_SIZE); if (nbytes > 0) module->module_name = strdup(outbuf); module++; } pidinfo->modules = modules; pidinfo->nmodules = nmodules; } static void winnt_fetch_registers ( struct target_ops * t, struct regcache * rcache, int regnum) { pid_t pid; pid_t tid; struct winnt_process * pidinfo; pid = inferior_ptid.pid; tid = inferior_ptid.tid; if (!(pidinfo = winnt_process_record(pid))) winnt_perror("internal error: record not found",pid); if (sizeof(uintptr_t) == 8) amd64_winnt_fetch_registers( pidinfo->pfd, rcache,regnum, tid); else i386_winnt_fetch_registers( pidinfo->pfd, rcache,regnum, tid); } static void winnt_store_registers ( struct target_ops * t, struct regcache * rcache, int regnum) { (void)t; (void)rcache; (void)regnum; } 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; ssize_t ret; pid_t pid; struct winnt_process * pidinfo; pid = inferior_ptid.pid; if (!(pidinfo = winnt_process_record(pid))) winnt_perror("internal error: record not found",pid); 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."); ret = (readbuf) ? __dbg_vm_read(pidinfo->pfd,readbuf,len,offset) : __dbg_vm_write(pidinfo->pfd,writebuf,len,offset); if (ret < 0) return TARGET_XFER_E_IO; *nxfered = ret; return TARGET_XFER_OK; } static int winnt_thread_alive (struct target_ops * t, ptid_t ptid) { (void)t; (void)ptid; return true; } static void winnt_interrupt (struct target_ops * t, ptid_t ptid) { (void)t; (void)ptid; } static void winnt_files_info (struct target_ops * t) { (void)t; } 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,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); event->syspid = 0; event->systid = 0; } 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); event = &pidinfo->event; 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); inferior_ptid.pid = event->syspid; inferior_ptid.tid = event->systid; 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); winnt_get_modules(winnt_process_record(pid)); } static void winnt_mourn_inferior (struct target_ops * t) { x86_cleanup_dregs(); inf_child_maybe_unpush_target(t); inf_child_mourn_inferior(t); delete_inferior(current_inferior()); } static void winnt_abandon (winnt_process * pidinfo) { inferior_ptid = null_ptid; 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(pidinfo); winnt_mourn_inferior(t); } static struct __dbg_event * winnt_wait_event(ptid_t ptid) { struct __dbg_event event; struct winnt_process * pdbg; struct winnt_process * pcap; struct winnt_process * pidinfo; struct pollfd pollfdbuf[512]; struct pollfd * pfds; struct pollfd * pfd; nfds_t nfds,i; if (ptid.pid < 0) for (pdbg=plist, pcap=&plist[pcnt]; pdbgevent.systid) return &pdbg->event; if (ptid.pid > 0) { if (!(pidinfo = winnt_process_record(event.syspid))) winnt_perror("internal error: record not found",event.syspid); if (pidinfo->event.systid) return &pidinfo->event; } if (pcnt*sizeof(*pfds) < sizeof(pollfdbuf)) pfds = pollfdbuf; else if (!(pfds = (struct pollfd *)calloc(pcnt,sizeof(*pfds)))) return 0; nfds = (ptid.pid > 0) ? winnt_poll_one_init(pfds,ptid.pid) : winnt_poll_init(pfds); if (poll(pfds,nfds,-1) < 0) return 0; for (i=0, pfd=0; ifd,&event) < 0) return 0; if (!(pidinfo = winnt_process_record(event.syspid))) winnt_perror("internal error: record not found",event.syspid); memcpy(&pidinfo->event,&event,sizeof(event)); return &pidinfo->event; } static ptid_t winnt_wait( struct target_ops * t, ptid_t ptid, struct target_waitstatus * waitstatus, int target_options) { struct __dbg_event * event; if (!(event = winnt_wait_event(ptid))) return null_ptid; switch (event->evttype) { case __DBG_STATE_EXCEPTION: case __DBG_STATE_BREAKPOINT: case __DBG_STATE_SINGLE_STEP: waitstatus->kind = TARGET_WAITKIND_STOPPED; waitstatus->value.sig = GDB_SIGNAL_TRAP; break; 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: waitstatus->kind = TARGET_WAITKIND_SPURIOUS; waitstatus->value.integer = 0; break; } inferior_ptid.pid = event->syspid; inferior_ptid.tid = event->systid; target_terminal_ours(); return inferior_ptid; } 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(pidinfo); winnt_mourn_inferior(t); } 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); winnt_get_modules(winnt_process_record(pid)); } 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_fetch_registers = winnt_fetch_registers; t->to_store_registers = winnt_store_registers; t->to_xfer_partial = winnt_xfer_partial; t->to_files_info = winnt_files_info; t->to_thread_alive = winnt_thread_alive; t->to_interrupt = winnt_interrupt; t->to_get_ada_task_ptid = winnt_get_ada_task_ptid; 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()); }