mirror of
				https://github.com/go-delve/delve.git
				synced 2025-10-31 10:47:27 +08:00 
			
		
		
		
	 f6e8fb37a4
			
		
	
	f6e8fb37a4
	
	
	
		
			
			* proc: changed windows backend to deal with simultaneous breakpoints * bugfix: forgot to add windowsPrologue3 to the prologues list in e4c7df1 * Tolerate errors returned by Stacktrace in TestStacktraceGoroutine. * bugfix: proc: propagate debug events we don't cause back to the target process Fixes: #594 * proc: fixed TestStepConcurrentPtr Implementation of nextInProgress was wrong.
		
			
				
	
	
		
			166 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			166 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package proc
 | |
| 
 | |
| import (
 | |
| 	"debug/gosym"
 | |
| 	"encoding/binary"
 | |
| 	"rsc.io/x86/x86asm"
 | |
| )
 | |
| 
 | |
| var maxInstructionLength uint64 = 15
 | |
| 
 | |
| type ArchInst x86asm.Inst
 | |
| 
 | |
| func asmDecode(mem []byte, pc uint64) (*ArchInst, error) {
 | |
| 	inst, err := x86asm.Decode(mem, 64)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	patchPCRel(pc, &inst)
 | |
| 	r := ArchInst(inst)
 | |
| 	return &r, nil
 | |
| }
 | |
| 
 | |
| func (inst *ArchInst) Size() int {
 | |
| 	return inst.Len
 | |
| }
 | |
| 
 | |
| // converts PC relative arguments to absolute addresses
 | |
| func patchPCRel(pc uint64, inst *x86asm.Inst) {
 | |
| 	for i := range inst.Args {
 | |
| 		rel, isrel := inst.Args[i].(x86asm.Rel)
 | |
| 		if isrel {
 | |
| 			inst.Args[i] = x86asm.Imm(int64(pc) + int64(rel) + int64(inst.Len))
 | |
| 		}
 | |
| 	}
 | |
| 	return
 | |
| }
 | |
| 
 | |
| func (inst *AsmInstruction) Text(flavour AssemblyFlavour) string {
 | |
| 	if inst.Inst == nil {
 | |
| 		return "?"
 | |
| 	}
 | |
| 
 | |
| 	var text string
 | |
| 
 | |
| 	switch flavour {
 | |
| 	case GNUFlavour:
 | |
| 		text = x86asm.GNUSyntax(x86asm.Inst(*inst.Inst))
 | |
| 	case IntelFlavour:
 | |
| 		fallthrough
 | |
| 	default:
 | |
| 		text = x86asm.IntelSyntax(x86asm.Inst(*inst.Inst))
 | |
| 	}
 | |
| 
 | |
| 	if inst.IsCall() && inst.DestLoc != nil && inst.DestLoc.Fn != nil {
 | |
| 		text += " " + inst.DestLoc.Fn.Name
 | |
| 	}
 | |
| 
 | |
| 	return text
 | |
| }
 | |
| 
 | |
| func (inst *AsmInstruction) IsCall() bool {
 | |
| 	return inst.Inst.Op == x86asm.CALL || inst.Inst.Op == x86asm.LCALL
 | |
| }
 | |
| 
 | |
| func (thread *Thread) resolveCallArg(inst *ArchInst, currentGoroutine bool, regs Registers) *Location {
 | |
| 	if inst.Op != x86asm.CALL && inst.Op != x86asm.LCALL {
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	var pc uint64
 | |
| 	var err error
 | |
| 
 | |
| 	switch arg := inst.Args[0].(type) {
 | |
| 	case x86asm.Imm:
 | |
| 		pc = uint64(arg)
 | |
| 	case x86asm.Reg:
 | |
| 		if !currentGoroutine || regs == nil {
 | |
| 			return nil
 | |
| 		}
 | |
| 		pc, err = regs.Get(int(arg))
 | |
| 		if err != nil {
 | |
| 			return nil
 | |
| 		}
 | |
| 	case x86asm.Mem:
 | |
| 		if !currentGoroutine || regs == nil {
 | |
| 			return nil
 | |
| 		}
 | |
| 		if arg.Segment != 0 {
 | |
| 			return nil
 | |
| 		}
 | |
| 		regs, err := thread.Registers()
 | |
| 		if err != nil {
 | |
| 			return nil
 | |
| 		}
 | |
| 		base, err1 := regs.Get(int(arg.Base))
 | |
| 		index, err2 := regs.Get(int(arg.Index))
 | |
| 		if err1 != nil || err2 != nil {
 | |
| 			return nil
 | |
| 		}
 | |
| 		addr := uintptr(int64(base) + int64(index*uint64(arg.Scale)) + arg.Disp)
 | |
| 		//TODO: should this always be 64 bits instead of inst.MemBytes?
 | |
| 		pcbytes, err := thread.readMemory(addr, inst.MemBytes)
 | |
| 		if err != nil {
 | |
| 			return nil
 | |
| 		}
 | |
| 		pc = binary.LittleEndian.Uint64(pcbytes)
 | |
| 	default:
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	file, line, fn := thread.dbp.PCToLine(pc)
 | |
| 	if fn == nil {
 | |
| 		return nil
 | |
| 	}
 | |
| 	return &Location{PC: pc, File: file, Line: line, Fn: fn}
 | |
| }
 | |
| 
 | |
| type instrseq []x86asm.Op
 | |
| 
 | |
| var windowsPrologue = instrseq{x86asm.MOV, x86asm.MOV, x86asm.LEA, x86asm.CMP, x86asm.JBE}
 | |
| var windowsPrologue2 = instrseq{x86asm.MOV, x86asm.MOV, x86asm.CMP, x86asm.JBE}
 | |
| var windowsPrologue3 = instrseq{x86asm.MOV, x86asm.MOV, x86asm.MOV, x86asm.CMP, x86asm.JE}
 | |
| var unixPrologue = instrseq{x86asm.MOV, x86asm.LEA, x86asm.CMP, x86asm.JBE}
 | |
| var unixPrologue2 = instrseq{x86asm.MOV, x86asm.CMP, x86asm.JBE}
 | |
| var unixPrologue3 = instrseq{x86asm.MOV, x86asm.MOV, x86asm.CMP, x86asm.JE}
 | |
| var prologues = []instrseq{windowsPrologue, windowsPrologue2, windowsPrologue3, unixPrologue, unixPrologue2, unixPrologue3}
 | |
| 
 | |
| // FirstPCAfterPrologue returns the address of the first instruction after the prologue for function fn
 | |
| // If sameline is set FirstPCAfterPrologue will always return an address associated with the same line as fn.Entry
 | |
| func (dbp *Process) FirstPCAfterPrologue(fn *gosym.Func, sameline bool) (uint64, error) {
 | |
| 	text, err := dbp.CurrentThread.Disassemble(fn.Entry, fn.End, false)
 | |
| 	if err != nil {
 | |
| 		return fn.Entry, err
 | |
| 	}
 | |
| 
 | |
| 	if len(text) <= 0 {
 | |
| 		return fn.Entry, nil
 | |
| 	}
 | |
| 
 | |
| 	for _, prologue := range prologues {
 | |
| 		if len(prologue) >= len(text) {
 | |
| 			continue
 | |
| 		}
 | |
| 		if checkPrologue(text, prologue) {
 | |
| 			r := &text[len(prologue)]
 | |
| 			if sameline {
 | |
| 				if r.Loc.Line != text[0].Loc.Line {
 | |
| 					return fn.Entry, nil
 | |
| 				}
 | |
| 			}
 | |
| 			return r.Loc.PC, nil
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return fn.Entry, nil
 | |
| }
 | |
| 
 | |
| func checkPrologue(s []AsmInstruction, prologuePattern instrseq) bool {
 | |
| 	for i, op := range prologuePattern {
 | |
| 		if s[i].Inst.Op != op {
 | |
| 			return false
 | |
| 		}
 | |
| 	}
 | |
| 	return true
 | |
| }
 |