This is NeXT-inferior.m in view mode; [Download] [Up]
#import "gdb.h" #import "objfiles.h" #import "symfile.h" #import "TellDisplayer.h" #import <mach-o/nlist.h> #import <mach-o/dyld_debug.h> #import "LazyRegionManager.h" #import "terminal.h" #import <mach/cthreads.h> #import <mach/notify.h> #import <mach/exception.h> #import <mach/mach_exception.h> #import <objc/objc-runtime.h> #import <sys/ptrace.h> #import <sys/signal.h> #import <sys/wait.h> struct target_task_state { port_t debug_port; thread_t debug_thread; unsigned int debug_thread_resume_count; unsigned int task_resume_count; thread_array_t threads; unsigned int thread_count; }; extern struct symbol *step_start_function; extern int stop_print_frame; extern int stop_soon_quietly; extern int breakpoints_inserted; extern int breakpoints_failed; static cthread_t signalThread = 0; static port_t signalPort = PORT_NULL; static mutex_t childrenMutex = 0; static condition_t childrenCondition; static BOOL childrenExist; static port_t gdb_notify_port; static port_t inferior_exception_port, inferior_old_exception_port; static int signalSuspendCount = 0; static int lazyRead = 0; static int debug_dyld = 0; static BOOL handle_exception; static BOOL areStepping = 0; const static char breakpoint_insn[] = BREAKPOINT; static char shadow[sizeof(breakpoint_insn)]; #ifdef IMPRECISE_EXECEPTIONS static CORE_ADDR breakPC = 0; #endif /* IMPRECISE_EXECEPTIONS */ port_set_name_t gdb_ports; /* MVS: make external; gdb_dyld_port must be inserted and removed repeatedly. */ thread_t catch_thread; int catch_exception; int catch_code; int catch_subcode; task_t catch_task; struct breakpoint *catch_data_break = NULL; RegionManager *regionManager; task_t inferior_task; thread_t current_thread = 0, step_thread = 0, last_current_thread = 0; int kernel_attached = NO; boolean_t stopped_in_ptrace; int read_memory_out_of_bounds_ok = 0; port_t gdb_dyld_port = PORT_NULL; /* MVS: null will mean needs to be initialized */ void create_inferior_for_task(task_t task, BOOL isLazy); void create_inferior_hook(char *args, int pid); void NeXT_mourn_inferior (); static void setToZero(pInt) int *pInt; { *pInt = 0; } /* find symbol name from address in dylib stub table */ const static char * get_symbol_stub_function_name (pc) CORE_ADDR pc; { char *name; struct objfile *obj; /* find the relevant objfile... */ ALL_OBJFILES(obj) { if (pc >= obj->start && pc < obj->end) { /* in this obj */ SegmentManager *manager = (SegmentManager*) obj->manager; const struct section *sec = [manager getSeg:"__TEXT" sect:"__picsymbol_stub"]; /* find obj and sec that has a (pic)symbol stub */ if (sec == 0) sec = [manager getSeg:"__TEXT" sect:"__symbol_stub"]; if (sec != 0) { unsigned long slide = [manager getSlide]; CORE_ADDR start = slide + sec->addr ; CORE_ADDR end = start + sec->size; if (pc >= start && pc < end) { /* is pc in one of it's stubs? */ struct symtab_command* symCmd = [manager symCmd]; struct dysymtab_command* dysymCmd = [manager dysymCmd]; if (dysymCmd && symCmd) { /* if we find both... */ const char *base = (const char*) [manager getMachHeader]; struct nlist *symbols = (struct nlist*) (base + symCmd->symoff); int *indirects = (int*) (base + dysymCmd->indirectsymoff); const char *strings = (const char*) (base + symCmd->stroff); unsigned long stub_size = sec->reserved2; int index; index = indirects[sec->reserved1 + (pc - start) / stub_size]; return (strings + symbols[index].n_un.n_strx); } else error ("couldn't find symbol tables..."); } } } } return 0; } /* Given an address, if the address is in a dylib stub table, * return the address of the actual function (or NULL). * If the name parameter is non-null, use it to return the name * of the function in question. */ CORE_ADDR get_symbol_stub_real_address (pc, name) CORE_ADDR pc; const char **name; { struct symbol *sym; struct minimal_symbol *msym; const char *lname = get_symbol_stub_function_name (pc); if (name) *name = lname; if (lname == NULL) return 0; pc = 0; /* found a name, now find a symbol and address */ sym = lookup_symbol (lname, 0, VAR_NAMESPACE, 0, 0); if (sym == NULL && lname[0] == '_') sym = lookup_symbol (lname + 1, 0, VAR_NAMESPACE, 0, 0); if (sym) { pc = BLOCK_START(SYMBOL_BLOCK_VALUE(sym)); } else { /* no sym, try for an msym */ msym = lookup_minimal_symbol (lname, NULL, NULL); if (msym == 0 && lname[0] == '_') msym = lookup_minimal_symbol (lname + 1, NULL, NULL); if (msym) { pc = SYMBOL_VALUE_ADDRESS (msym); } } return pc; } /* This function simply calls ptrace with the given arguments. It exists so that all calls to ptrace are isolated in this machine-dependent file. */ int call_ptrace (request, pid, arg3, arg4) int request, pid, arg3, arg4; { return ptrace (request, pid, arg3, arg4); } CORE_ADDR getImp(pc) CORE_ADDR pc; { unsigned object, sel; if (getFirstTwoIntArgs(&object, &sel)) { CORE_ADDR imp = find_implementation(object, sel); return imp; } else return 0; } CORE_ADDR getImpForMsgSuper(pc) CORE_ADDR pc; { unsigned sel, super; if (getFirstTwoIntArgs(&super, &sel)) { struct objc_super *mySuper = WARP((struct objc_super *)super); Class class = mySuper ? mySuper->class : NULL; CORE_ADDR imp = class ? find_implementation_from_class(class, sel) : 0; return imp; } else return 0; } /* isLibPrefix returns true if the symbol name begins with "_lib" or ".lib" */ static BOOL isLibPrefix(name) const char *name; { return (name[0] == '.' || name[0] == '_') && name[1] == 'l' && name[2] == 'i' && name[3] == 'b'; } /* The following data structure, "Ignore", is used to detect method calls * (thru ObjC runtime lib functions objc_msgSend, objc_msgSendSuper, etc.), * and ultimately find the method being called. */ typedef struct _Ignore { char *name; CORE_ADDR (*stop_at)(CORE_ADDR); CORE_ADDR begin; CORE_ADDR end; } Ignore; static Ignore ignored[] = { {"_objc_msgSend", &getImp, 0, 0}, {"_objc_msgSendSuper", &getImpForMsgSuper, 0, 0}, {"_objc_getClass", NULL, 0, 0}, {"_objc_getMetaClass", NULL, 0, 0} }; static int numIgnored = 0; #define NUMIGNORED (sizeof(ignored) / sizeof(*ignored)) static int foundIndices = NO; /* The following function, "find_objc_msgsend", fills in the data structure * "objc_msgs" by finding the addresses of each of the (currently four) * functions that it holds (of which objc_msgSend is the first). This * must be called each time symbols are loaded, in case the functions * have moved for some reason. */ void find_objc_msgsend () { for (numIgnored = 0; numIgnored < NUMIGNORED; numIgnored++) { struct minimal_symbol *func = lookup_minimal_symbol(ignored[numIgnored].name, NULL, NULL); /* maybe no underscore? */ if (func == 0 && ignored[numIgnored].name[0] == '_') func = lookup_minimal_symbol(ignored[numIgnored].name + 1, NULL, NULL); if (func) { ignored[numIgnored].begin = SYMBOL_VALUE_ADDRESS (func); do { ignored[numIgnored].end = SYMBOL_VALUE_ADDRESS (++ func); } while (ignored[numIgnored].begin == ignored[numIgnored].end); } } foundIndices = YES; } /* * find_objc_msgcall (replaces pc_off_limits) * * ALL that this function now does is to determine whether the input * address ("pc") is the address of one of the Objective C message * dispatch functions (mainly objc_msgSend or objc_msgSendSuper), and * if so, it returns the address of the method that will be called. * * The old function "pc_off_limits" used to do a lot of other things * in addition, such as detecting shared library jump stubs and * returning the address of the shlib function that would be called. * That functionality has been moved into the SKIP_TRAMPOLINE_CODE * and IN_SOLIB_TRAMPOLINE macros, which are resolved in the target- * dependent modules. */ int find_objc_msgcall (pc, new_pc) CORE_ADDR pc; CORE_ADDR *new_pc; { int i; for (i = 0; i < numIgnored; i++) { if ((ignored[i].begin <= pc) && (pc < ignored[i].end)) { if (ignored[i].stop_at && new_pc) *new_pc = (*ignored[i].stop_at)(pc); return YES; } } *new_pc = 0; return NO; } int process_notify_msg(msg) notification_t *msg; { if ((msg->notify_header.msg_id == NOTIFY_PORT_DELETED) && (msg->notify_port == inferior_task)) return 1; else return 0; } kern_return_t catch_exception_raise(port, thread, task, exception, code, subcode) port_t port; thread_t thread; task_t task; { port_t replyPort; /* mach_NeXT_exception("catch_exception_raise", exception, code, subcode); */ catch_task = task; catch_thread = thread; catch_exception = exception; catch_code = code; catch_subcode = subcode; if (task == inferior_task) { kern_return_t ret = task_suspend(task); handle_exception = YES; mach_call(ret, "task_suspend", "catch_exception_raise"); return ret; } else { handle_exception = NO; mach_NeXT_exception("gdb: forwarding exception ", exception, code, subcode); return KERN_FAILURE; } } struct msg { msg_header_t header; int data[20]; }; typedef struct _SignalMessage { msg_header_t header; msg_type_t type; int pid; int status; } SignalMsg; #define SIGNAL_MSG 0 #define STOP_MSG 1 void putExceptionPortBack(dummy) { port_set_add(task_self(), gdb_ports, inferior_exception_port); } void drain_exceptions() { struct msg inMsg, outMsg; int noMore, ret; struct cleanup *oldChain; mach_call_no_inferior(port_set_remove(task_self(), inferior_exception_port), "port_set_remove", "drain_exceptions"); oldChain = make_cleanup(putExceptionPortBack, 0); for (noMore = NO; !noMore;) { inMsg.header.msg_local_port = inferior_exception_port; inMsg.header.msg_size = sizeof(inMsg); ret = msg_receive(&inMsg.header, RCV_TIMEOUT, 0); if (ret == KERN_SUCCESS) { debugPrintf("Before exc_server, incoming remote port is %x.\n", inMsg.header.msg_remote_port); ret = exc_server(&inMsg, &outMsg); debugPrintf("exc_server returned %d.\n", ret); debugPrintf("After exc_server, outgoing remote port is %x.\n", outMsg.header.msg_remote_port); if (ret && handle_exception) { msg_send(&outMsg.header, MSG_OPTION_NONE, 0); task_resume(inferior_task); if (catch_exception != EXC_BREAKPOINT) { char exception_string[100]; strcpy(exception_string, mach_NeXT_exception_string (catch_exception, catch_code, catch_subcode)); terminal_ours_for_output (); printf("Program generated(%d): %s.\n", catch_exception, exception_string); } } else if (!handle_exception) { inMsg.header.msg_local_port = inMsg.header.msg_remote_port ; inMsg.header.msg_remote_port = inferior_old_exception_port; mach_call_no_error(msg_send(&inMsg.header, MSG_OPTION_NONE, 0), "msg_send", "drain_exceptions"); } } else noMore = YES; } mach_call_no_inferior(port_set_add(task_self(), gdb_ports, inferior_exception_port), "port_set_add", "drain_exceptions"); discard_cleanups(oldChain); } #if 0 void child_create_inferior(exec_file, allargs, env) char *exec_file, *allargs, **env; { int pid; char *shell_command; extern int sys_nerr; extern char *sys_errlist[]; extern struct target_ops child_ops; int len; int pending_execs; int debug_setpgrp; int fildes[2]; char dummy = 0; len = 5 + strlen (exec_file) + 1 + strlen (allargs) + 1 + /*slop*/ 10; shell_command = (char *) alloca (len); sprintf(shell_command, "exec %s %s", exec_file, allargs); pipe(fildes); pid = vfork (); if (pid < 0) perror_with_name ("vfork"); if (pid == 0) { #ifdef TIOCGPGRP /* Run inferior in a separate process group. */ debug_setpgrp = setpgrp (getpid (), getpid ()); if (0 != debug_setpgrp) perror("setpgrp failed in child"); #endif /* TIOCGPGRP */ /* Tell the terminal handling subsystem what tty we plan to run on; it will now switch to that one if non-null. */ new_tty (); /* Changing the signal handlers for the inferior after a vfork can also change them for the superior, so we don't mess with signals here. See comments in initialize_signals for how we get the right signal handlers for the inferior. */ read(fildes[0], &dummy, 1); close(fildes[0]); call_ptrace (0, 0, 0, 0); /* "Trace me, Dr. Memory!" */ execle ("/bin/sh", "/bin/sh", "-c", shell_command, (char *)0, env); fprintf (stderr, "Cannot exec %s: %s.\n", "/bin/sh", errno < sys_nerr ? sys_errlist[errno] : "unknown error"); fflush (stderr); _exit (0177); } /* Now that we have a child process, make it our target. */ push_target (&child_ops); create_inferior_hook(NULL, pid); /* The process was started by the fork that created it, but it will have stopped one instruction after execing the shell. Here we must get it up to actual execution of the real program. */ inferior_pid = pid; /* Needed for wait_for_inferior stuff below */ clear_proceed_status (); /* We will get a trace trap after one instruction. Continue it automatically. Eventually (after shell does an exec) it will get another trace trap. Then insert breakpoints and continue. */ pending_execs = 2; init_wait_for_inferior (); /* Set up the "saved terminal modes" of the inferior based on what modes we are starting it with. */ target_terminal_init (); /* Install inferior's terminal modes. */ target_terminal_inferior (); write(fildes[1], &dummy, 1); close(fildes[1]); task_resume(inferior_task); while (1) { stop_soon_quietly = 1; /* Make wait_for_inferior be quiet */ wait_for_inferior (); if (stop_signal != SIGTRAP) { /* Let shell child handle its own signals in its own way */ /* FIXME, what if child has exit()ed? Must exit loop somehow */ resume (0, stop_signal); } else { /* We handle SIGTRAP, however; it means child did an exec. */ if (0 == --pending_execs) break; resume (0, 0); /* Just make it go on */ } } stop_soon_quietly = 0; initLoaded(); /* Should this perhaps just be a "proceed" call? FIXME */ insert_step_breakpoint (); insertLoadBreakpoint(); if (gdb_dyld_port == PORT_NULL) { /* MVS: dyld port needs to be initialized */ mach_call (port_allocate (task_self (), &gdb_dyld_port), "port_allocate", "child_create_inferior"); mach_call(port_set_add(task_self(), gdb_ports, gdb_dyld_port), "port_set_add", "child_create_inferior"); } _dyld_debug_add_event_subscriber(inferior_task, 2000, 2000, 0, gdb_dyld_port); breakpoints_failed = insert_breakpoints (); if (!breakpoints_failed) { breakpoints_inserted = 1; target_terminal_inferior(); /* Start the child program going on its first instruction, single- stepping if we need to. */ resume (bpstat_should_step (), 0); wait_for_inferior (); normal_stop (); } } void NeXT_create_inferior(execFile, allArgs, env) char *execFile, *allArgs, **env; { child_create_inferior(execFile, allArgs, env); } #endif 0 int mach_oh_wait(pid, status) struct target_wait_status *status; { int ret, gotMsg = NO, isGone = NO; struct msg inMsg, outMsg; SignalMsg *signalMsg; struct breakpoint *db = NULL; BOOL stepping = areStepping; static CORE_ADDR retPC = 0; extern BOOL safetiesInserted; if (pid == -1) pid = inferior_pid; stopped_in_ptrace = NO; while (!gotMsg) { inMsg.header.msg_local_port = gdb_ports; inMsg.header.msg_size = sizeof(inMsg); ret = msg_receive(&inMsg.header, 0, 0); if (ret == KERN_SUCCESS) { if (inMsg.header.msg_local_port == gdb_notify_port) { if (process_notify_msg((notification_t *)&inMsg)) { if (attach_flag) { gotMsg = YES; isGone = PROCESS_DISAPPEARED; } } } else if (inMsg.header.msg_local_port == inferior_exception_port) { debugPrintf("Before exc_server, incoming remote port is %x.\n", inMsg.header.msg_remote_port); ret = exc_server(&inMsg, &outMsg); debugPrintf("exc_server returned %d.\n", ret); debugPrintf("After exc_server, outgoing remote port is %x.\n", outMsg.header.msg_remote_port); if (ret && handle_exception) { if (catch_thread != current_thread) { last_current_thread = current_thread; current_thread = catch_thread; registers_changed(); flush_cached_frames(); } ret = mach_call_no_error(msg_send(&outMsg.header, MSG_OPTION_NONE, 0), "msg_send", "mach_oh_wait 1"); prepare_threads_after_stop(); if (catch_exception == EXC_BAD_ACCESS) { db = is_data_break(catch_subcode); #ifdef IMPRECISE_EXECEPTIONS if (db) breakPC = read_pc(); #endif /* IMPRECISE_EXECEPTIONS */ if (db == (struct breakpoint *)0xFFFFFFFF) { remove_breakpoints (); breakpoints_inserted = 0; registers_changed(); child_resume(pid, 1, 0); } else if (db != 0) { catch_data_break = db; gotMsg = YES; } else gotMsg = YES; } else if (catch_exception == EXC_BREAKPOINT) { #ifdef IMPRECISE_EXECEPTIONS if (breakPC) { [regionManager putDataAt: (void *)breakPC for: sizeof(breakpoint_insn) from: (void *)shadow]; write_pc(read_pc() - DECR_PC_AFTER_BREAK); breakPC = 0; } #endif /* IMPRECISE_EXECEPTIONS */ if (db) { db = NULL; registers_changed(); if (stepping) child_resume(pid, 1, 0); else { insert_breakpoints (); breakpoints_inserted = 1; child_resume(pid, 0, 0); } } else if (safetiesInserted && isSafetyBreakpoint(read_pc() - DECR_PC_AFTER_BREAK)) { retPC = INITIAL_RET_PC(); removeSafetyBreakpoints(); [regionManager flushMarkedPages]; remove_breakpoints (); breakpoints_inserted = 0; [regionManager getDataAt: (void *)retPC for: sizeof(breakpoint_insn) into: (void *)shadow]; [regionManager putDataAt: (void *)retPC for: sizeof(breakpoint_insn) from: (void *)breakpoint_insn]; write_pc(read_pc() - DECR_PC_AFTER_BREAK); registers_changed(); child_resume(pid, 0, 0); } else if (retPC && (read_pc() - DECR_PC_AFTER_BREAK == retPC)) { [regionManager putDataAt: (void *)retPC for: sizeof(breakpoint_insn) from: (void *)shadow]; retPC = 0; insert_breakpoints (); breakpoints_inserted = 1; write_pc(read_pc() - DECR_PC_AFTER_BREAK); registers_changed(); child_resume(pid, 0, 0); } else gotMsg = YES; } else gotMsg = YES; if (gotMsg && (catch_thread != current_thread)) { last_current_thread = current_thread; current_thread = catch_thread; registers_changed(); flush_cached_frames(); } } else if (!handle_exception) { inMsg.header.msg_local_port = inMsg.header.msg_remote_port ; inMsg.header.msg_remote_port = inferior_old_exception_port; mach_call_no_error(msg_send(&inMsg.header, MSG_OPTION_NONE, 0), "msg_send", "mach_oh_wait 2"); } } else if (inMsg.header.msg_local_port == signalPort) { signalMsg = (SignalMsg *)&inMsg; if (signalMsg->pid == pid) { if (signalMsg->header.msg_id == SIGNAL_MSG) { union wait *w = (union wait *) &signalMsg->status; store_waitstatus(status, signalMsg->status); /* printf("Heard about signal %d for pid %d.\n", w->w_stopsig, */ /* inferior_pid); */ gotMsg = YES; if (WIFEXITED(*w)) isGone = PROCESS_EXITED; else if (!WIFSTOPPED(*w)) isGone = PROCESS_TERMINATED; /* else */ /* prepare_threads_after_stop(); */ stopped_in_ptrace = YES; } else if (signalMsg->header.msg_id == STOP_MSG){ gotMsg = YES; mach_call(task_suspend(inferior_task), "task_suspend", "mach_oh_wait 3"); drain_exceptions(); prepare_threads_after_stop(); catch_exception = -1; } } } else if (inMsg.header.msg_local_port == gdb_dyld_port) { struct _dyld_event_message_request *request = (struct _dyld_event_message_request *)&inMsg; struct _dyld_event_message_reply reply; registers_changed (); _dyld_event_server(request, &reply); registers_changed (); mach_call (msg_send(&reply.head, MSG_OPTION_NONE, 0), "msg_send", "mach_oh_wait 4"); } } } return (int)isGone; } void drainDyldEvents () { struct _dyld_event_message_request request; struct _dyld_event_message_reply reply; int gotAll = NO; kern_return_t ret; struct target_task_state state; mach_call (port_set_remove (task_self (),gdb_dyld_port), "port_set_remove", "drainDyldEvents"); Xprepare_msg_send (inferior_task, &state); while (!gotAll) { request.head.msg_local_port = gdb_dyld_port; request.head.msg_size = sizeof(request); mach_call (msg_receive(&request.head, 0, 0), "msg_receive", "drainDyldEvents"); if (request.event.type == DYLD_PAST_EVENTS_END) { gotAll = YES; } _dyld_event_server(&request, &reply); mach_call (msg_send(&reply.head, MSG_OPTION_NONE, 0), "msg_send", "drainDyldEvents"); } Xfinish_msg_send (inferior_task, &state); mach_call(port_set_add(task_self(), gdb_ports, gdb_dyld_port), "port_set_add", "drainDyldEvents"); registers_changed (); } static CORE_ADDR dyld_address = 0; static CORE_ADDR dyld_slide = 0; void _dyld_event_server_callback(subscriber, event) port_t subscriber; struct dyld_event event; { if (event.type == DYLD_IMAGE_ADDED) { CORE_ADDR header_addr = (CORE_ADDR) event.arg[0].header; struct mach_header *header; unsigned slide = event.arg[0].vmaddr_slide; int add = 0; SegmentManager *manager; BOOL foundIt = NO; int i; struct load_command *loadCmd; struct dylib_command *newCommand = NULL; struct segment_command *newSegCommand = NULL; [regionManager invalidate]; header = WARP ((struct mach_header*)header_addr); switch (header->filetype) { case MH_DYLIB: for (i = 0, loadCmd = (struct load_command *)(header + 1); i < header->ncmds; i++, ((void *)loadCmd) += loadCmd->cmdsize) { if (loadCmd->cmd == LC_ID_DYLIB) newCommand = (struct dylib_command *)loadCmd; if (loadCmd->cmd == LC_SEGMENT && !strcmp (((struct segment_command*)loadCmd)->segname, SEG_TEXT)) newSegCommand = ((struct segment_command*)loadCmd); } if (newCommand) printf("%s", newCommand->dylib.name.offset + (char *)newCommand); add = 1; break; case MH_DYLINKER: dyld_address = header_addr; dyld_slide = slide; printf ("Dynamic Linkeditor"); break; case MH_FVMLIB: printf ("Fixed Virtual Memory Shared Library"); break; case MH_PRELOAD: printf ("Preloaded Executable"); break; case MH_EXECUTE: printf ("Executable"); break; case MH_BUNDLE: printf ("Bundle"); add = 1; break; default: printf ("Ignored Object Module"); } printf (" at 0x%x offset 0x%x\n", header_addr, slide); if (add) { if (newCommand) { struct objfile *obj; ALL_OBJFILES (obj) { SegmentManager *manager = (SegmentManager*) obj->manager; struct mach_header *oldHeader = [manager getMachHeader]; struct dylib_command *oldCommand = NULL; struct segment_command *oldSegCommand = NULL; for (i = 0, loadCmd = (struct load_command *)(oldHeader + 1); i < oldHeader->ncmds; i++, ((void *)loadCmd) += loadCmd->cmdsize) { if (loadCmd->cmd == LC_ID_DYLIB) oldCommand = (struct dylib_command *) loadCmd; if (loadCmd->cmd == LC_SEGMENT && !strcmp(((struct segment_command *) loadCmd)->segname, SEG_TEXT)) oldSegCommand = ((struct segment_command*)loadCmd); } if (oldCommand && (strcmp(newCommand->dylib.name.offset + (char *) newCommand, oldCommand->dylib.name.offset + (char *) oldCommand) == 0)) { if ((newCommand->dylib.timestamp == oldCommand->dylib.timestamp) && oldSegCommand && newSegCommand && (oldSegCommand->vmaddr + [manager getSlide] == newSegCommand->vmaddr + slide)) { extern void dyld_breakpoint_re_set (); dyld_breakpoint_re_set (); add = 0; } else { invalidate_breakpoints_between(obj->start, obj->end); free_objfile(obj); removeExecManager(manager); } break; } } } if (add) { extern int forceRegionManager; struct cleanup *oldChain; struct objfile *obj; header = [regionManager copyLoadedImage: (struct mach_header*) header_addr withSlide:slide]; manager = [SegmentManager newHeader:header withSlide:0]; forceRegionManager = 1; oldChain = make_cleanup(setToZero, &forceRegionManager); addExecManager(manager); obj = symfile_add (manager, 0, 0, 0, 0, 0); obj->flags |= OBJF_DYLIB; discard_cleanups(oldChain); forceRegionManager = 0; } } } #ifdef DYLD_DEBUG else if (event.type == DYLD_MODULE_BOUND) printf("DYLD Module bound: 0x%08x -- 0x%08x -- %4d\n", event.arg[0].header, event.arg[0].vmaddr_slide, event.arg[0].module_index); else if (event.type == DYLD_MODULE_REMOVED) printf("DYLD Module removed.\n"); else if (event.type == DYLD_MODULE_REPLACED) printf("DYLD Module replaced.\n"); else if (event.type == DYLD_PAST_EVENTS_END) printf("DYLD past events end.\n"); #endif /* DYLD_DEBUG */ } #ifdef __DYNAMIC__ /* * The following symbol is referenced by libsys symbolically (instead of * through undefined reference). To get strip(1) to know this symbol is not * to be stripped it needs to have the REFERENCED_DYNAMICALLY bit * (0x10) set. This would have been done automaticly by ld(1) if this symbol * was referenced through an undefined symbol. */ asm(".desc __dyld_event_server_callback, 0x10"); #endif /* __DYNAMIC__ */ static void unloadDylibs() { struct objfile *obj, *nxt = 0; ALL_OBJFILES_SAFE (obj,nxt) { SegmentManager *manager = (SegmentManager*) obj->manager; struct mach_header *oldHeader = [manager getMachHeader]; if (oldHeader->filetype == MH_DYLIB) { CORE_ADDR headerAddr = (CORE_ADDR) [manager originalPointerFor: oldHeader]; invalidate_breakpoints_between(headerAddr, [manager getMaximumTextAddress]); } else if (oldHeader->filetype == MH_BUNDLE) { free_objfile(obj); removeExecManager(manager); } } } maybeAddDyld () { if (debug_dyld) { SegmentManager *manager; struct mach_header *header; header = WARP ((struct mach_header*)dyld_address); if (dyld_address && header && header->magic == MH_MAGIC && header->filetype == MH_DYLINKER) { printf ("Adding dynamic linker...\n"); header = [regionManager copyLoadedImage:(struct mach_header*)dyld_address withSlide:dyld_slide]; manager = [SegmentManager newHeader:header withSlide:0]; addExecManager(manager); addLoadedObjectModule (symfile_add (manager, 0, 0, 0, 0, 0)); } else { printf ("Rerun to enable dyld debugging\n"); } } } void buildSignalMsg(signalMsg, msg_id) SignalMsg *signalMsg; int msg_id; { signalMsg->header.msg_simple = TRUE; signalMsg->header.msg_size = sizeof(SignalMsg); signalMsg->header.msg_type = MSG_TYPE_NORMAL; signalMsg->header.msg_local_port = PORT_NULL; signalMsg->header.msg_remote_port = signalPort; signalMsg->header.msg_id = msg_id; signalMsg->type.msg_type_name = MSG_TYPE_INTEGER_32; signalMsg->type.msg_type_size = 32; signalMsg->type.msg_type_number = 2; signalMsg->type.msg_type_inline = TRUE; signalMsg->type.msg_type_longform = FALSE; signalMsg->type.msg_type_deallocate = FALSE; } void waitForSignal(dummy) void *dummy; { union wait w; int waitPid; SignalMsg signalMsg; buildSignalMsg(&signalMsg, SIGNAL_MSG); while (1) { /* printf("Waiting for signals.\n"); */ waitPid = wait(&w); if (waitPid == -1) { /* printf("There are no children to wait on.\n"); */ mutex_lock(childrenMutex); childrenExist = NO; while (!childrenExist) condition_wait(childrenCondition, childrenMutex); mutex_unlock(childrenMutex); } else { /* printf("Got signal %d for pid %d.\n", w.w_stopsig, waitPid); */ signalMsg.pid = waitPid; signalMsg.status = w.w_status; msg_send(&signalMsg.header, 0, 0); } } } void inferiorSigInt() { SignalMsg signalMsg; buildSignalMsg(&signalMsg, STOP_MSG); signalMsg.pid = inferior_pid; msg_send(&signalMsg.header, 0, 0); } void kill_inferior_fast () { kern_return_t ret; struct target_waitstatus w; if (inferior_pid == 0) return; else if (inferior_pid == -1) { unloadStatesFrom(0); unloadDylibs(); if (mach_call_no_error(task_terminate(inferior_task), "task_terminate", "kill_inferior_fast")) return; } else { unloadStatesFrom(0); unloadDylibs(); #ifdef i386 /* * BKM - XXX 4/20/92 * This is needed to prevent a race condition between the child's * death and possible arrival of a breakpoint exception (which would * make our exception handler suspend the task). This is caused * by a bug in the 386 kernel which needs to be fixed (i.e. PT_KILL * allows some threads to return to user mode). */ clear_trace_bit(current_thread); #endif if (attach_flag) { attach_flag = 0; kill (inferior_pid, SIGHUP); if (mach_call_no_error(task_resume(inferior_task), "task_resume", "kill_inferior_fast 1")) return; } else { if (!stopped_in_ptrace) { kill (inferior_pid, SIGHUP); if (mach_call_no_error(task_resume(inferior_task), "task_resume", "kill_inferior_fast 2")) return; if (!mach_oh_wait(-1, &w)) { ptrace(PT_KILL, inferior_pid, 0, 0); mach_oh_wait(-1, &w); } } else { ptrace(PT_KILL, inferior_pid, 0, 0); mach_oh_wait(-1, &w); } } } } void kill_inferior () { kill_inferior_fast (); NeXT_mourn_inferior (); tell_displayer_state_changed(DBG_STATE_INFERIOR_EXITED); } void child_resume (pid, step, signal) int pid, step; enum target_signal signal; { kern_return_t ret; if (pid == -1) pid = inferior_pid; /* use default pid */ areStepping = step; errno = 0; [regionManager invalidate]; #ifdef IMPRECISE_EXECEPTIONS if (breakPC) { [regionManager getDataAt: (void *)breakPC for: sizeof(breakpoint_insn) into: (void *)shadow]; [regionManager putDataAt: (void *)breakPC for: sizeof(breakpoint_insn) from: (void *)breakpoint_insn]; prepare_threads_before_run(0); } else prepare_threads_before_run(step); #else /* IMPRECISE_EXECEPTIONS */ prepare_threads_before_run(step); #endif /* IMPRECISE_EXECEPTIONS */ if (stopped_in_ptrace) { call_ptrace (step ? PTRACE_SINGLESTEP : PTRACE_CONT, pid, 1, target_signal_to_host (signal)); if (errno) perror_with_name ("ptrace"); else { tell_displayer_state_changed(DBG_STATE_INFERIOR_RUNNING); } } else { if ((ret = task_resume (inferior_task)) != KERN_SUCCESS) { fprintf(stderr, "Error resuming inferior.\n"); mach_error("task_resume", ret); error("resume: task_resume"); } tell_displayer_state_changed(DBG_STATE_INFERIOR_RUNNING); } } /* Start debugging the process whose number is PID. */ int attach (pid) int pid; { errno = 0; attach_flag = 1; return pid; } /* Stop debugging the process whose number is PID and continue it with signal number SIGNAL. SIGNAL = 0 means just continue it. */ /* ARGSUSED */ void detach (signal) int signal; { unloadStatesFrom(0); removeLoadBreakpoint(); if (gdb_dyld_port != PORT_NULL) { /* MVS: the dyld port must be removed on detaching */ /* Otherwise the inferior will hang, trying to talk to this port */ /* We set the port to null, so that it will be re-initialized */ /* with a new inferior later. */ port_deallocate (task_self (), gdb_dyld_port); gdb_dyld_port = PORT_NULL; } if (kernel_attached) { mach_call(task_detach(inferior_task), "task_resume", "detach 1"); kernel_attached = NO; } else mach_call(task_resume(inferior_task), "task_resume", "detach 2"); attach_flag = 0; } /* Copy LEN bytes to or from inferior's memory starting at MEMADDR to debugger memory starting at MYADDR. Copy to inferior if WRITE is nonzero. Returns the length copied, which is either the LEN argument or zero. This xfer function does not do partial moves, since child_ops doesn't allow memory operations to cross below us in the target stack anyway. */ int child_xfer_memory (memaddr, myaddr, len, write, target) CORE_ADDR memaddr; char *myaddr; int len, write; struct target_ops *target; { if (write) return [regionManager putDataAt: (void *)memaddr for: len from: myaddr]; else { int ret = [regionManager getDataAt: (void *)memaddr for: len into: myaddr]; if (!ret && read_memory_out_of_bounds_ok) { /* printf ("Bad read attempt at %p for %d bytes.\n", memaddr, len); */ bzero(myaddr, len); ret = 1; } return ret; } } static void badTaskFromRegionManager() { NeXT_mourn_inferior(); error("The program you were running has been terminated."); } void NeXT_mourn_inferior () { static int inMourning = 0; if (inMourning) { /* This means that we got an error while mourning. Don't try it again. */ inMourning = 0; } else { inMourning = 1; unloadStatesFrom(0); unloadDylibs(); inferior_task = TASK_NULL; regionManager = [regionManager free]; clearPrintStream(); child_mourn_inferior (); inMourning = 0; stopped_in_ptrace = 0; } } void start_inferior_hook() { } void create_inferior_for_task(task, isLazy) task_t task; BOOL isLazy; { thread_array_t thread_list; unsigned int thread_count; extern int stop_print_frame; inferior_task = task; mach_call(port_allocate(task_self(), &inferior_exception_port), "port_allocate", "create_inferior_for_task"); mach_call(task_get_exception_port(inferior_task, &inferior_old_exception_port), "task_get_special_port", "create_inferior_for_task"); mach_call(task_set_exception_port(inferior_task, inferior_exception_port), "task_set_special_port", "create_inferior_for_task"); mach_call_no_inferior(port_set_add(task_self(), gdb_ports, inferior_exception_port), "port_set_add", "create_inferior_for_task"); mach_call(task_threads(inferior_task, &thread_list, &thread_count), "task_threads", "create_inferior_for_task"); last_current_thread = current_thread = thread_list[0]; mach_call(vm_deallocate(task_self(), (vm_address_t)thread_list, (vm_size_t)(thread_count * sizeof(int))), "vm_deallocate", "create_inferior_for_task"); regionManager = [(lazyRead || isLazy) ? [LazyRegionManager class] : [RegionManager class] newTask: inferior_task readInRegions: NO]; [regionManager setTaskGoneCallBack: &badTaskFromRegionManager]; if (kernel_attached) [regionManager combineRegions: NO]; current_target.to_wait = &mach_oh_wait; current_target.to_load = NULL; current_target.to_mourn_inferior = &NeXT_mourn_inferior; // insertLoadBreakpoint(); clearThreadState(); } task_t findPidServer(host) char *host; { task_t pidServer = TASK_NULL; if (netname_look_up(name_server_port, host, "PIDSERVER", &pidServer) != KERN_SUCCESS) { if (vfork()) { int tries, found; for (tries = 0, found = NO; (tries < 20) && !found; tries++) { usleep(500000); found = (netname_look_up(name_server_port, host, "PIDSERVER", &pidServer) == KERN_SUCCESS); } } else execl("/usr/ucb/rsh", "/usr/ucb/rsh", host, "-n", "/usr/lib/pidServer", NULL); } return pidServer; } void create_inferior_hook(args, pid) char *args; int pid; { task_t infTask; char *space, *host, *colon; struct cleanup *oldChain; oldChain = make_cleanup(setToZero, &inferior_pid); if (pid == -1) { colon = strchr(args, ':'); if (colon) { task_t pidServer; *colon = '\0'; host = args; pidServer = findPidServer(host); if (!pidServer) error("Problem finding pid server on %s.", host); else { int remotePid = atoi(colon + 1); if (task_by_unix_pid(pidServer, remotePid, &infTask) == KERN_SUCCESS) lazyRead = 1; else error("Problem finding task for pid %d on %s", remotePid, host); } } else { space = strchr(args, ' '); if (space) { for (host = space; *host && (*host == ' '); host++); *space = '\0'; } else host = ""; if (netname_look_up(name_server_port, host, args, &infTask) != KERN_SUCCESS) error("Problem finding task named %s on %s", args, *host ? host : "localhost"); } } else { if (task_by_unix_pid(task_self(), pid, &infTask) != KERN_SUCCESS) error("Problem attaching to pid %d.", pid); } kernel_attached = NO; create_inferior_for_task(infTask, NO); discard_cleanups(oldChain); if (attach_flag) { prepare_threads_after_stop(); getLoadedSymbols(); insertLoadBreakpoint(); flush_cached_frames(); registers_changed (); task_suspend(inferior_task); stop_pc = read_pc(); step_start_function = find_pc_function(stop_pc); stop_print_frame = 1; reread_exec(); /* MVS: see if any symbols need updating */ } else { mutex_lock(childrenMutex); childrenExist = YES; condition_signal(childrenCondition); mutex_unlock(childrenMutex); } } static char *remoteHost = NULL; int isTask(task) task_t task; { struct task_basic_info taskInfo; unsigned int taskInfoCount; taskInfoCount = TASK_BASIC_INFO_COUNT; return ((task_info(task, TASK_BASIC_INFO, (task_info_t)&taskInfo, &taskInfoCount) == KERN_SUCCESS) ? YES : NO); } void suspendSignalThread() { if (signalThread) { if (!signalSuspendCount) thread_suspend(cthread_thread(signalThread)); signalSuspendCount++; } } void resumeSignalThread() { if (signalThread) { if (signalSuspendCount) thread_resume(cthread_thread(signalThread)); signalSuspendCount--; } } void _initialize_next_inferior() { mach_call(port_set_allocate(task_self(), &gdb_ports), "port_set_allocate", "_initialize_next_inferior"); mach_call(port_allocate(task_self(), &gdb_notify_port), "port_allocate", "_initialize_next_inferior"); mach_call(task_set_notify_port(task_self(), gdb_notify_port), "task_set_notify_port", "_initialize_next_inferior"); mach_call(port_set_add(task_self(), gdb_ports, gdb_notify_port), "port_set_add", "_initialize_next_inferior"); mach_call(port_allocate(task_self(), &signalPort), "port_allocate", "_initialize_next_inferior"); mach_call(port_set_backlog(task_self(), signalPort, PORT_BACKLOG_MAX), "port_set_backlog", "_initialize_next_inferior"); mach_call(port_set_add(task_self(), gdb_ports, signalPort), "port_set_add", "_initialize_next_inferior"); /* Note: gdb_dyld_port no longer initialized here. MVS */ childrenCondition = condition_alloc(); childrenMutex = mutex_alloc(); signalThread = cthread_fork((cthread_fn_t)&waitForSignal, NULL); cthread_set_name(signalThread, "signal thread"); cthread_detach(signalThread); #if 0 add_show_from_set(add_set_cmd("host", class_run, var_string_noescape, (char *)&remoteHost, "Set name of host to run program on.\n" "Currently only works for mach tasks.", &setlist), &showlist); #endif 0 add_show_from_set(add_set_cmd("lazy-read", class_run, var_boolean, (char *)&lazyRead, "Set if inferior's memory is read lazily.", &setlist), &showlist); add_show_from_set(add_set_cmd("debug-dyld", class_files, var_boolean, (char *)&debug_dyld, "Set dynamic link-editor debugging.", &setlist), &showlist); if (foundIndices) /* de-initialize Ignored[] */ foundIndices = numIgnored = 0; } void save_inferior_status_hook(inf_status) struct inferior_status *inf_status; { inf_status->extra_data[0] = current_thread; inf_status->extra_data[1] = step_thread; } void restore_inferior_status_hook(inf_status) struct inferior_status *inf_status; { current_thread = inf_status->extra_data[0]; step_thread = inf_status->extra_data[1]; } int mach_set_execute (start, size) CORE_ADDR start; unsigned size; { vm_address_t addr = start; vm_size_t siz = size; vm_prot_t prot; vm_prot_t max_protection; vm_inherit_t inheritance; boolean_t shared; port_t object_name; vm_offset_t offset; mach_call (vm_region (inferior_task, &addr, &siz, &prot, &max_protection, &inheritance, &shared, &object_name, &offset), "vm_region", "mach_set_execute"); mach_call (vm_protect (inferior_task, start, size, 0, prot | VM_PROT_EXECUTE), "vm_protect", "mach_set_execute"); return (int)prot; } void mach_unset_execute (start, size, prev_prot) CORE_ADDR start; unsigned size; vm_prot_t prev_prot; { mach_call (vm_protect (inferior_task, start, size, 0, prev_prot), "vm_protect", "mach_unset_execute"); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.