mirror of
				https://github.com/go-delve/delve.git
				synced 2025-10-30 10:17:03 +08:00 
			
		
		
		
	Begin thread code isolation
This commit is contained in:
		| @ -3,7 +3,6 @@ package proctl_test | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"path/filepath" | ||||
| 	"syscall" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/derekparker/delve/helper" | ||||
| @ -12,7 +11,7 @@ import ( | ||||
|  | ||||
| func dataAtAddr(pid int, addr uint64) ([]byte, error) { | ||||
| 	data := make([]byte, 1) | ||||
| 	_, err := syscall.PtracePeekData(pid, uintptr(addr), data) | ||||
| 	_, err := proctl.ReadMemory(pid, uintptr(addr), data) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
							
								
								
									
										315
									
								
								proctl/threads.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										315
									
								
								proctl/threads.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,315 @@ | ||||
| package proctl | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/binary" | ||||
| 	"fmt" | ||||
|  | ||||
| 	"syscall" | ||||
|  | ||||
| 	"github.com/derekparker/delve/dwarf/frame" | ||||
| ) | ||||
|  | ||||
| // ThreadContext represents a single thread of execution in the | ||||
| // traced program. | ||||
| type ThreadContext struct { | ||||
| 	Id      int | ||||
| 	Process *DebuggedProcess | ||||
| 	Status  *syscall.WaitStatus | ||||
| 	Regs    *syscall.PtraceRegs | ||||
| } | ||||
|  | ||||
| // Obtains register values from the debugged process. | ||||
| func (thread *ThreadContext) Registers() (*syscall.PtraceRegs, error) { | ||||
| 	err := syscall.PtraceGetRegs(thread.Id, thread.Regs) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("could not get registers %s", err) | ||||
| 	} | ||||
|  | ||||
| 	return thread.Regs, nil | ||||
| } | ||||
|  | ||||
| // Returns the current PC for this thread id. | ||||
| func (thread *ThreadContext) CurrentPC() (uint64, error) { | ||||
| 	regs, err := thread.Registers() | ||||
| 	if err != nil { | ||||
| 		return 0, err | ||||
| 	} | ||||
|  | ||||
| 	return regs.PC(), nil | ||||
| } | ||||
|  | ||||
| // PrintInfo prints out the thread status | ||||
| // including: PC, tid, file, line, and function. | ||||
| func (thread *ThreadContext) PrintInfo() error { | ||||
| 	pc, err := thread.CurrentPC() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	f, l, fn := thread.Process.GoSymTable.PCToLine(pc) | ||||
| 	if fn != nil { | ||||
| 		fmt.Printf("Thread %d at %#v %s:%d %s\n", thread.Id, pc, f, l, fn.Name) | ||||
| 	} else { | ||||
| 		fmt.Printf("Thread %d at %#v\n", thread.Id, pc) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Sets a software breakpoint at addr, and stores it in the process wide | ||||
| // break point table. Setting a break point must be thread specific due to | ||||
| // ptrace actions needing the thread to be in a signal-delivery-stop in order | ||||
| // to initiate any ptrace command. Otherwise, it really doesn't matter | ||||
| // as we're only dealing with threads. | ||||
| func (thread *ThreadContext) Break(addr uintptr) (*BreakPoint, error) { | ||||
| 	var ( | ||||
| 		int3         = []byte{0xCC} | ||||
| 		f, l, fn     = thread.Process.GoSymTable.PCToLine(uint64(addr)) | ||||
| 		originalData = make([]byte, 1) | ||||
| 	) | ||||
|  | ||||
| 	if fn == nil { | ||||
| 		return nil, InvalidAddressError{address: addr} | ||||
| 	} | ||||
|  | ||||
| 	_, err := ReadMemory(thread.Id, addr, originalData) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("PEEK ERR") | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	if bytes.Equal(originalData, int3) { | ||||
| 		return nil, BreakPointExistsError{f, l, addr} | ||||
| 	} | ||||
|  | ||||
| 	_, err = WriteMemory(thread.Id, addr, int3) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("POKE ERR") | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	breakpoint := &BreakPoint{ | ||||
| 		FunctionName: fn.Name, | ||||
| 		File:         f, | ||||
| 		Line:         l, | ||||
| 		Addr:         uint64(addr), | ||||
| 		OriginalData: originalData, | ||||
| 	} | ||||
|  | ||||
| 	thread.Process.BreakPoints[uint64(addr)] = breakpoint | ||||
|  | ||||
| 	return breakpoint, nil | ||||
| } | ||||
|  | ||||
| // Clears a software breakpoint, and removes it from the process level | ||||
| // break point table. | ||||
| func (thread *ThreadContext) Clear(pc uint64) (*BreakPoint, error) { | ||||
| 	bp, ok := thread.Process.BreakPoints[pc] | ||||
| 	if !ok { | ||||
| 		return nil, fmt.Errorf("No breakpoint currently set for %#v", pc) | ||||
| 	} | ||||
|  | ||||
| 	if _, err := WriteMemory(thread.Id, uintptr(bp.Addr), bp.OriginalData); err != nil { | ||||
| 		return nil, fmt.Errorf("could not clear breakpoint %s", err) | ||||
| 	} | ||||
|  | ||||
| 	delete(thread.Process.BreakPoints, pc) | ||||
|  | ||||
| 	return bp, nil | ||||
| } | ||||
|  | ||||
| func (thread *ThreadContext) Continue() error { | ||||
| 	// Check whether we are stopped at a breakpoint, and | ||||
| 	// if so, single step over it before continuing. | ||||
| 	regs, err := thread.Registers() | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("could not get registers %s", err) | ||||
| 	} | ||||
|  | ||||
| 	if _, ok := thread.Process.BreakPoints[regs.PC()-1]; ok { | ||||
| 		err := thread.Step() | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("could not step %s", err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return syscall.PtraceCont(thread.Id, 0) | ||||
| } | ||||
|  | ||||
| // Single steps this thread a single instruction, ensuring that | ||||
| // we correctly handle the likely case that we are at a breakpoint. | ||||
| func (thread *ThreadContext) Step() (err error) { | ||||
| 	regs, err := thread.Registers() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	bp, ok := thread.Process.BreakPoints[regs.PC()-1] | ||||
| 	if ok { | ||||
| 		// Clear the breakpoint so that we can continue execution. | ||||
| 		_, err = thread.Clear(bp.Addr) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		// Reset program counter to our restored instruction. | ||||
| 		regs.SetPC(bp.Addr) | ||||
| 		err = syscall.PtraceSetRegs(thread.Id, regs) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("could not set registers %s", err) | ||||
| 		} | ||||
|  | ||||
| 		// Restore breakpoint now that we have passed it. | ||||
| 		defer func() { | ||||
| 			_, err = thread.Break(uintptr(bp.Addr)) | ||||
| 		}() | ||||
| 	} | ||||
|  | ||||
| 	err = syscall.PtraceSingleStep(thread.Id) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("step failed: %s", err.Error()) | ||||
| 	} | ||||
|  | ||||
| 	_, _, err = wait(thread.Id, 0) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Step to next source line. Next will step over functions, | ||||
| // and will follow through to the return address of a function. | ||||
| // Next is implemented on the thread context, however during the | ||||
| // course of this function running, it's very likely that the | ||||
| // goroutine our M is executing will switch to another M, therefore | ||||
| // this function cannot assume all execution will happen on this thread | ||||
| // in the traced process. | ||||
| func (thread *ThreadContext) Next() (err error) { | ||||
| 	pc, err := thread.CurrentPC() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if _, ok := thread.Process.BreakPoints[pc-1]; ok { | ||||
| 		pc-- // Decrement PC to account for BreakPoint | ||||
| 	} | ||||
|  | ||||
| 	_, l, _ := thread.Process.GoSymTable.PCToLine(pc) | ||||
| 	fde, err := thread.Process.FrameEntries.FDEForPC(pc) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	step := func() (uint64, error) { | ||||
| 		err = thread.Step() | ||||
| 		if err != nil { | ||||
| 			return 0, err | ||||
| 		} | ||||
|  | ||||
| 		return thread.CurrentPC() | ||||
| 	} | ||||
|  | ||||
| 	ret := thread.ReturnAddressFromOffset(fde.ReturnAddressOffset(pc)) | ||||
| 	for { | ||||
| 		pc, err = step() | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		if !fde.Cover(pc) && pc != ret { | ||||
| 			err := thread.continueToReturnAddress(pc, fde) | ||||
| 			if err != nil { | ||||
| 				if _, ok := err.(InvalidAddressError); !ok { | ||||
| 					return err | ||||
| 				} | ||||
| 			} | ||||
| 			pc, _ = thread.CurrentPC() | ||||
| 		} | ||||
|  | ||||
| 		_, nl, _ := thread.Process.GoSymTable.PCToLine(pc) | ||||
| 		if nl != l { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (thread *ThreadContext) continueToReturnAddress(pc uint64, fde *frame.FrameDescriptionEntry) error { | ||||
| 	for !fde.Cover(pc) { | ||||
| 		// Our offset here is be 0 because we | ||||
| 		// have stepped into the first instruction | ||||
| 		// of this function. Therefore the function | ||||
| 		// has not had a chance to modify its' stack | ||||
| 		// and change our offset. | ||||
| 		addr := thread.ReturnAddressFromOffset(0) | ||||
| 		bp, err := thread.Break(uintptr(addr)) | ||||
| 		if err != nil { | ||||
| 			if _, ok := err.(BreakPointExistsError); !ok { | ||||
| 				return err | ||||
| 			} | ||||
| 		} | ||||
| 		bp.temp = true | ||||
| 		// Ensure we cleanup after ourselves no matter what. | ||||
| 		defer thread.clearTempBreakpoint(bp.Addr) | ||||
|  | ||||
| 		for { | ||||
| 			err = thread.Continue() | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			// We wait on -1 here because once we continue this | ||||
| 			// thread, it's very possible the scheduler could of | ||||
| 			// change the goroutine context on us, we there is | ||||
| 			// no guarantee that waiting on this tid will ever | ||||
| 			// return. | ||||
| 			wpid, _, err := trapWait(thread.Process, -1, 0) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			if wpid != thread.Id { | ||||
| 				thread = thread.Process.Threads[wpid] | ||||
| 			} | ||||
| 			pc, _ = thread.CurrentPC() | ||||
| 			if (pc - 1) == bp.Addr { | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Takes an offset from RSP and returns the address of the | ||||
| // instruction the currect function is going to return to. | ||||
| func (thread *ThreadContext) ReturnAddressFromOffset(offset int64) uint64 { | ||||
| 	regs, err := thread.Registers() | ||||
| 	if err != nil { | ||||
| 		panic("Could not obtain register values") | ||||
| 	} | ||||
|  | ||||
| 	retaddr := int64(regs.Rsp) + offset | ||||
| 	data := make([]byte, 8) | ||||
| 	syscall.PtracePeekText(thread.Id, uintptr(retaddr), data) | ||||
| 	return binary.LittleEndian.Uint64(data) | ||||
| } | ||||
|  | ||||
| func (thread *ThreadContext) clearTempBreakpoint(pc uint64) error { | ||||
| 	if bp, ok := thread.Process.BreakPoints[pc]; ok { | ||||
| 		_, err := thread.Clear(bp.Addr) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		// Reset program counter to our restored instruction. | ||||
| 		regs, err := thread.Registers() | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		regs.SetPC(bp.Addr) | ||||
| 		return syscall.PtraceSetRegs(thread.Id, regs) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
| @ -1,315 +1,11 @@ | ||||
| package proctl | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/binary" | ||||
| 	"fmt" | ||||
| import "syscall" | ||||
|  | ||||
| 	"syscall" | ||||
|  | ||||
| 	"github.com/derekparker/delve/dwarf/frame" | ||||
| ) | ||||
|  | ||||
| // ThreadContext represents a single thread of execution in the | ||||
| // traced program. | ||||
| type ThreadContext struct { | ||||
| 	Id      int | ||||
| 	Process *DebuggedProcess | ||||
| 	Status  *syscall.WaitStatus | ||||
| 	Regs    *syscall.PtraceRegs | ||||
| func WriteMemory(tid int, addr uintptr, data []byte) (int, error) { | ||||
| 	return syscall.PtracePokeData(tid, addr, data) | ||||
| } | ||||
|  | ||||
| // Obtains register values from the debugged process. | ||||
| func (thread *ThreadContext) Registers() (*syscall.PtraceRegs, error) { | ||||
| 	err := syscall.PtraceGetRegs(thread.Id, thread.Regs) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("could not get registers %s", err) | ||||
| 	} | ||||
|  | ||||
| 	return thread.Regs, nil | ||||
| } | ||||
|  | ||||
| // Returns the current PC for this thread id. | ||||
| func (thread *ThreadContext) CurrentPC() (uint64, error) { | ||||
| 	regs, err := thread.Registers() | ||||
| 	if err != nil { | ||||
| 		return 0, err | ||||
| 	} | ||||
|  | ||||
| 	return regs.PC(), nil | ||||
| } | ||||
|  | ||||
| // PrintInfo prints out the thread status | ||||
| // including: PC, tid, file, line, and function. | ||||
| func (thread *ThreadContext) PrintInfo() error { | ||||
| 	pc, err := thread.CurrentPC() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	f, l, fn := thread.Process.GoSymTable.PCToLine(pc) | ||||
| 	if fn != nil { | ||||
| 		fmt.Printf("Thread %d at %#v %s:%d %s\n", thread.Id, pc, f, l, fn.Name) | ||||
| 	} else { | ||||
| 		fmt.Printf("Thread %d at %#v\n", thread.Id, pc) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Sets a software breakpoint at addr, and stores it in the process wide | ||||
| // break point table. Setting a break point must be thread specific due to | ||||
| // ptrace actions needing the thread to be in a signal-delivery-stop in order | ||||
| // to initiate any ptrace command. Otherwise, it really doesn't matter | ||||
| // as we're only dealing with threads. | ||||
| func (thread *ThreadContext) Break(addr uintptr) (*BreakPoint, error) { | ||||
| 	var ( | ||||
| 		int3         = []byte{0xCC} | ||||
| 		f, l, fn     = thread.Process.GoSymTable.PCToLine(uint64(addr)) | ||||
| 		originalData = make([]byte, 1) | ||||
| 	) | ||||
|  | ||||
| 	if fn == nil { | ||||
| 		return nil, InvalidAddressError{address: addr} | ||||
| 	} | ||||
|  | ||||
| 	_, err := syscall.PtracePeekData(thread.Id, addr, originalData) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("PEEK ERR") | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	if bytes.Equal(originalData, int3) { | ||||
| 		return nil, BreakPointExistsError{f, l, addr} | ||||
| 	} | ||||
|  | ||||
| 	_, err = syscall.PtracePokeData(thread.Id, addr, int3) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("POKE ERR") | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	breakpoint := &BreakPoint{ | ||||
| 		FunctionName: fn.Name, | ||||
| 		File:         f, | ||||
| 		Line:         l, | ||||
| 		Addr:         uint64(addr), | ||||
| 		OriginalData: originalData, | ||||
| 	} | ||||
|  | ||||
| 	thread.Process.BreakPoints[uint64(addr)] = breakpoint | ||||
|  | ||||
| 	return breakpoint, nil | ||||
| } | ||||
|  | ||||
| // Clears a software breakpoint, and removes it from the process level | ||||
| // break point table. | ||||
| func (thread *ThreadContext) Clear(pc uint64) (*BreakPoint, error) { | ||||
| 	bp, ok := thread.Process.BreakPoints[pc] | ||||
| 	if !ok { | ||||
| 		return nil, fmt.Errorf("No breakpoint currently set for %#v", pc) | ||||
| 	} | ||||
|  | ||||
| 	if _, err := syscall.PtracePokeData(thread.Id, uintptr(bp.Addr), bp.OriginalData); err != nil { | ||||
| 		return nil, fmt.Errorf("could not clear breakpoint %s", err) | ||||
| 	} | ||||
|  | ||||
| 	delete(thread.Process.BreakPoints, pc) | ||||
|  | ||||
| 	return bp, nil | ||||
| } | ||||
|  | ||||
| func (thread *ThreadContext) Continue() error { | ||||
| 	// Check whether we are stopped at a breakpoint, and | ||||
| 	// if so, single step over it before continuing. | ||||
| 	regs, err := thread.Registers() | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("could not get registers %s", err) | ||||
| 	} | ||||
|  | ||||
| 	if _, ok := thread.Process.BreakPoints[regs.PC()-1]; ok { | ||||
| 		err := thread.Step() | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("could not step %s", err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return syscall.PtraceCont(thread.Id, 0) | ||||
| } | ||||
|  | ||||
| // Single steps this thread a single instruction, ensuring that | ||||
| // we correctly handle the likely case that we are at a breakpoint. | ||||
| func (thread *ThreadContext) Step() (err error) { | ||||
| 	regs, err := thread.Registers() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	bp, ok := thread.Process.BreakPoints[regs.PC()-1] | ||||
| 	if ok { | ||||
| 		// Clear the breakpoint so that we can continue execution. | ||||
| 		_, err = thread.Clear(bp.Addr) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		// Reset program counter to our restored instruction. | ||||
| 		regs.SetPC(bp.Addr) | ||||
| 		err = syscall.PtraceSetRegs(thread.Id, regs) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("could not set registers %s", err) | ||||
| 		} | ||||
|  | ||||
| 		// Restore breakpoint now that we have passed it. | ||||
| 		defer func() { | ||||
| 			_, err = thread.Break(uintptr(bp.Addr)) | ||||
| 		}() | ||||
| 	} | ||||
|  | ||||
| 	err = syscall.PtraceSingleStep(thread.Id) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("step failed: %s", err.Error()) | ||||
| 	} | ||||
|  | ||||
| 	_, _, err = wait(thread.Id, 0) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Step to next source line. Next will step over functions, | ||||
| // and will follow through to the return address of a function. | ||||
| // Next is implemented on the thread context, however during the | ||||
| // course of this function running, it's very likely that the | ||||
| // goroutine our M is executing will switch to another M, therefore | ||||
| // this function cannot assume all execution will happen on this thread | ||||
| // in the traced process. | ||||
| func (thread *ThreadContext) Next() (err error) { | ||||
| 	pc, err := thread.CurrentPC() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if _, ok := thread.Process.BreakPoints[pc-1]; ok { | ||||
| 		pc-- // Decrement PC to account for BreakPoint | ||||
| 	} | ||||
|  | ||||
| 	_, l, _ := thread.Process.GoSymTable.PCToLine(pc) | ||||
| 	fde, err := thread.Process.FrameEntries.FDEForPC(pc) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	step := func() (uint64, error) { | ||||
| 		err = thread.Step() | ||||
| 		if err != nil { | ||||
| 			return 0, err | ||||
| 		} | ||||
|  | ||||
| 		return thread.CurrentPC() | ||||
| 	} | ||||
|  | ||||
| 	ret := thread.ReturnAddressFromOffset(fde.ReturnAddressOffset(pc)) | ||||
| 	for { | ||||
| 		pc, err = step() | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		if !fde.Cover(pc) && pc != ret { | ||||
| 			err := thread.continueToReturnAddress(pc, fde) | ||||
| 			if err != nil { | ||||
| 				if _, ok := err.(InvalidAddressError); !ok { | ||||
| 					return err | ||||
| 				} | ||||
| 			} | ||||
| 			pc, _ = thread.CurrentPC() | ||||
| 		} | ||||
|  | ||||
| 		_, nl, _ := thread.Process.GoSymTable.PCToLine(pc) | ||||
| 		if nl != l { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (thread *ThreadContext) continueToReturnAddress(pc uint64, fde *frame.FrameDescriptionEntry) error { | ||||
| 	for !fde.Cover(pc) { | ||||
| 		// Our offset here is be 0 because we | ||||
| 		// have stepped into the first instruction | ||||
| 		// of this function. Therefore the function | ||||
| 		// has not had a chance to modify its' stack | ||||
| 		// and change our offset. | ||||
| 		addr := thread.ReturnAddressFromOffset(0) | ||||
| 		bp, err := thread.Break(uintptr(addr)) | ||||
| 		if err != nil { | ||||
| 			if _, ok := err.(BreakPointExistsError); !ok { | ||||
| 				return err | ||||
| 			} | ||||
| 		} | ||||
| 		bp.temp = true | ||||
| 		// Ensure we cleanup after ourselves no matter what. | ||||
| 		defer thread.clearTempBreakpoint(bp.Addr) | ||||
|  | ||||
| 		for { | ||||
| 			err = thread.Continue() | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			// We wait on -1 here because once we continue this | ||||
| 			// thread, it's very possible the scheduler could of | ||||
| 			// change the goroutine context on us, we there is | ||||
| 			// no guarantee that waiting on this tid will ever | ||||
| 			// return. | ||||
| 			wpid, _, err := trapWait(thread.Process, -1, 0) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			if wpid != thread.Id { | ||||
| 				thread = thread.Process.Threads[wpid] | ||||
| 			} | ||||
| 			pc, _ = thread.CurrentPC() | ||||
| 			if (pc - 1) == bp.Addr { | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Takes an offset from RSP and returns the address of the | ||||
| // instruction the currect function is going to return to. | ||||
| func (thread *ThreadContext) ReturnAddressFromOffset(offset int64) uint64 { | ||||
| 	regs, err := thread.Registers() | ||||
| 	if err != nil { | ||||
| 		panic("Could not obtain register values") | ||||
| 	} | ||||
|  | ||||
| 	retaddr := int64(regs.Rsp) + offset | ||||
| 	data := make([]byte, 8) | ||||
| 	syscall.PtracePeekText(thread.Id, uintptr(retaddr), data) | ||||
| 	return binary.LittleEndian.Uint64(data) | ||||
| } | ||||
|  | ||||
| func (thread *ThreadContext) clearTempBreakpoint(pc uint64) error { | ||||
| 	if bp, ok := thread.Process.BreakPoints[pc]; ok { | ||||
| 		_, err := thread.Clear(bp.Addr) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		// Reset program counter to our restored instruction. | ||||
| 		regs, err := thread.Registers() | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		regs.SetPC(bp.Addr) | ||||
| 		return syscall.PtraceSetRegs(thread.Id, regs) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| func ReadMemory(tid int, addr uintptr, data []byte) (int, error) { | ||||
| 	return syscall.PtracePeekData(tid, addr, data) | ||||
| } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Derek Parker
					Derek Parker