Return to BSD News archive
Newsgroups: comp.unix.bsd Path: sserve!manuel!munnari.oz.au!uunet!mcsun!sun4nl!eur.nl!pk From: pk@cs.few.eur.nl (Paul Kranenburg) Subject: Re: GDB under 386bsd 0.1 Message-ID: <1992Sep4.153554.4387@cs.few.eur.nl> Keywords: gdb, ptrace Sender: news@cs.few.eur.nl Reply-To: pk@cs.eur.nl Organization: Erasmus University Rotterdam References: <1992Sep4.005417.3876@gumby.dsd.trw.com> Date: Fri, 4 Sep 1992 15:35:54 GMT Lines: 89 In <1992Sep4.005417.3876@gumby.dsd.trw.com> gottloeb@eel.dsd.trw.com writes: >I have noticed a problem using gdb under 386bsd 0.1. >When at least one breakpoint has been set and the program terminates, >e.g. executes exit(), the breakpoint is not removed from the process's >text image. When the program is subsequently re-executed under gdb, >gdb remembers that a breakpoint is supposed to at the memory location >and puts one there again. However this time it shadows the breakpoint >instruction from the previous run rather than the original instruction. >When execution begins after reaching the breakpoint, various traps occur. >If the program is run after quitting gdb, a Trace/BPT trap occurs. >I think the problem is that gdb assumes that when ptrace modifies the >process's image the kernel either makes a private copy of the text image >or it will throw away the text image after the process terminates. >However, this is not the case - the kernel keeps the modified text image >around and executes it rather than a fresh copy from the original file. The 386 does not generate a page protection fault while it is executing in supervisor mode :-(, so copy on write handling never takes place when the kernel stuffs data into a process's text- or any other non-anonymous segment. So these cases must be explicitly checked for. Here's a patch for kern/sys_process.c: ------- sys_process.c ------- *** /tmp/da05975 Fri Sep 4 16:51:50 1992 --- sys_process.c Fri Sep 4 16:51:18 1992 *************** *** 278,286 **** break; case PT_WRITE_I: ! case PT_WRITE_D: ! ipc.error = copyout((char *)&ipc.data, (char *)ipc.addr, sizeof(ipc.data)); break; case PT_WRITE_U: if ((u_int)ipc.addr > UPAGES * NBPG - sizeof(int)) { --- 278,318 ---- break; case PT_WRITE_I: ! case PT_WRITE_D: { ! vm_prot_t prot; /* current protection of region */ ! int cow; /* ensure copy-on-write happens */ ! ! if (cow = (useracc(ipc.addr, sizeof(ipc.data), B_WRITE) == 0)) { ! vm_offset_t addr = (vm_offset_t)ipc.addr; ! vm_size_t size; ! vm_prot_t max_prot; ! vm_inherit_t inh; ! boolean_t shared; ! vm_object_t object; ! vm_offset_t objoff; ! ! if (vm_region(&p->p_vmspace->vm_map, &addr, &size, ! &prot, &max_prot, &inh, &shared, ! &object, &objoff) != KERN_SUCCESS || ! vm_protect(&p->p_vmspace->vm_map, ipc.addr, ! sizeof(ipc.data), FALSE, ! prot|VM_PROT_WRITE) != KERN_SUCCESS || ! vm_fault(&p->p_vmspace->vm_map,trunc_page(ipc.addr), ! VM_PROT_WRITE, FALSE) != KERN_SUCCESS) { ! ! ipc.error = EFAULT; ! break; ! } ! } ! ipc.error = copyout((char *)&ipc.data, ! (char *)ipc.addr, sizeof(ipc.data)); ! if (cow) ! if (vm_protect(&p->p_vmspace->vm_map, ipc.addr, ! sizeof(ipc.data), FALSE, ! prot) != KERN_SUCCESS) ! printf("ptrace: oops\n"); break; + } case PT_WRITE_U: if ((u_int)ipc.addr > UPAGES * NBPG - sizeof(int)) { ------------ EOP -------------------- I am not entirely happy about the call to `vm_fault', but at this moment I don't see another way to enforce a copy on write. -pk