mirror of
				https://github.com/go-delve/delve.git
				synced 2025-10-31 10:47:27 +08:00 
			
		
		
		
	pkg/proc: Refactor Disassemble
This commit is contained in:
		 Derek Parker
					Derek Parker
				
			
				
					committed by
					
						 Alessandro Arzilli
						Alessandro Arzilli
					
				
			
			
				
	
			
			
			 Alessandro Arzilli
						Alessandro Arzilli
					
				
			
						parent
						
							583d335ffe
						
					
				
				
					commit
					9963458d77
				
			| @ -24,39 +24,24 @@ const ( | |||||||
| 	GoFlavour | 	GoFlavour | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // Disassemble disassembles target memory between startPC and endPC, marking | // Disassemble disassembles target memory between startAddr and endAddr, marking | ||||||
| // the current instruction being executed in goroutine g. | // the current instruction being executed in goroutine g. | ||||||
| // If currentGoroutine is set and thread is stopped at a CALL instruction Disassemble will evaluate the argument of the CALL instruction using the thread's registers | // If currentGoroutine is set and thread is stopped at a CALL instruction Disassemble | ||||||
| // Be aware that the Bytes field of each returned instruction is a slice of a larger array of size endPC - startPC | // will evaluate the argument of the CALL instruction using the thread's registers. | ||||||
| func Disassemble(dbp Process, g *G, startPC, endPC uint64) ([]AsmInstruction, error) { | // Be aware that the Bytes field of each returned instruction is a slice of a larger array of size startAddr - endAddr. | ||||||
| 	if _, err := dbp.Valid(); err != nil { | func Disassemble(mem MemoryReadWriter, regs Registers, breakpoints *BreakpointMap, bi *BinaryInfo, startAddr, endAddr uint64) ([]AsmInstruction, error) { | ||||||
| 		return nil, err | 	return disassemble(mem, regs, breakpoints, bi, startAddr, endAddr, false) | ||||||
| 	} |  | ||||||
| 	if g == nil { |  | ||||||
| 		ct := dbp.CurrentThread() |  | ||||||
| 		regs, _ := ct.Registers(false) |  | ||||||
| 		return disassemble(ct, regs, dbp.Breakpoints(), dbp.BinInfo(), startPC, endPC, false) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	var regs Registers |  | ||||||
| 	var mem MemoryReadWriter = dbp.CurrentThread() |  | ||||||
| 	if g.Thread != nil { |  | ||||||
| 		mem = g.Thread |  | ||||||
| 		regs, _ = g.Thread.Registers(false) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return disassemble(mem, regs, dbp.Breakpoints(), dbp.BinInfo(), startPC, endPC, false) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func disassemble(memrw MemoryReadWriter, regs Registers, breakpoints *BreakpointMap, bi *BinaryInfo, startPC, endPC uint64, singleInstr bool) ([]AsmInstruction, error) { | func disassemble(memrw MemoryReadWriter, regs Registers, breakpoints *BreakpointMap, bi *BinaryInfo, startAddr, endAddr uint64, singleInstr bool) ([]AsmInstruction, error) { | ||||||
| 	mem := make([]byte, int(endPC-startPC)) | 	mem := make([]byte, int(endAddr-startAddr)) | ||||||
| 	_, err := memrw.ReadMemory(mem, uintptr(startPC)) | 	_, err := memrw.ReadMemory(mem, uintptr(startAddr)) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	r := make([]AsmInstruction, 0, len(mem)/15) | 	r := make([]AsmInstruction, 0, len(mem)/15) | ||||||
| 	pc := startPC | 	pc := startAddr | ||||||
|  |  | ||||||
| 	var curpc uint64 | 	var curpc uint64 | ||||||
| 	if regs != nil { | 	if regs != nil { | ||||||
|  | |||||||
| @ -115,33 +115,6 @@ func FindFunctionLocation(p Process, funcName string, lineOffset int) (uint64, e | |||||||
| 	return breakAddr, err | 	return breakAddr, err | ||||||
| } | } | ||||||
|  |  | ||||||
| // FunctionReturnLocations will return a list of addresses corresponding |  | ||||||
| // to 'ret' or 'call runtime.deferreturn'. |  | ||||||
| func FunctionReturnLocations(p Process, funcName string) ([]uint64, error) { |  | ||||||
| 	const deferReturn = "runtime.deferreturn" |  | ||||||
|  |  | ||||||
| 	g := p.SelectedGoroutine() |  | ||||||
| 	fn, ok := p.BinInfo().LookupFunc[funcName] |  | ||||||
| 	if !ok { |  | ||||||
| 		return nil, fmt.Errorf("unable to find function %s", funcName) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	instructions, err := Disassemble(p, g, fn.Entry, fn.End) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	var addrs []uint64 |  | ||||||
| 	for _, instruction := range instructions { |  | ||||||
| 		if instruction.IsRet() { |  | ||||||
| 			addrs = append(addrs, instruction.Loc.PC) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	addrs = append(addrs, findDeferReturnCalls(instructions)...) |  | ||||||
|  |  | ||||||
| 	return addrs, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Next continues execution until the next source line. | // Next continues execution until the next source line. | ||||||
| func Next(dbp Process) (err error) { | func Next(dbp Process) (err error) { | ||||||
| 	if _, err := dbp.Valid(); err != nil { | 	if _, err := dbp.Valid(); err != nil { | ||||||
|  | |||||||
| @ -2545,7 +2545,7 @@ func TestStepOnCallPtrInstr(t *testing.T) { | |||||||
| 			regs, err := p.CurrentThread().Registers(false) | 			regs, err := p.CurrentThread().Registers(false) | ||||||
| 			assertNoError(err, t, "Registers()") | 			assertNoError(err, t, "Registers()") | ||||||
| 			pc := regs.PC() | 			pc := regs.PC() | ||||||
| 			text, err := proc.Disassemble(p, nil, pc, pc+maxInstructionLength) | 			text, err := proc.Disassemble(p.CurrentThread(), regs, p.Breakpoints(), p.BinInfo(), pc, pc+maxInstructionLength) | ||||||
| 			assertNoError(err, t, "Disassemble()") | 			assertNoError(err, t, "Disassemble()") | ||||||
| 			if text[0].IsCall() { | 			if text[0].IsCall() { | ||||||
| 				found = true | 				found = true | ||||||
| @ -3629,7 +3629,8 @@ func TestIssue1145(t *testing.T) { | |||||||
| func TestDisassembleGlobalVars(t *testing.T) { | func TestDisassembleGlobalVars(t *testing.T) { | ||||||
| 	withTestProcess("teststepconcurrent", t, func(p proc.Process, fixture protest.Fixture) { | 	withTestProcess("teststepconcurrent", t, func(p proc.Process, fixture protest.Fixture) { | ||||||
| 		mainfn := p.BinInfo().LookupFunc["main.main"] | 		mainfn := p.BinInfo().LookupFunc["main.main"] | ||||||
| 		text, err := proc.Disassemble(p, nil, mainfn.Entry, mainfn.End) | 		regs, _ := p.CurrentThread().Registers(false) | ||||||
|  | 		text, err := proc.Disassemble(p.CurrentThread(), regs, p.Breakpoints(), p.BinInfo(), mainfn.Entry, mainfn.End) | ||||||
| 		assertNoError(err, t, "Disassemble") | 		assertNoError(err, t, "Disassemble") | ||||||
| 		found := false | 		found := false | ||||||
| 		for i := range text { | 		for i := range text { | ||||||
|  | |||||||
| @ -225,7 +225,7 @@ func next(dbp Process, stepInto, inlinedStepOut bool) error { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if !csource { | 	if !csource { | ||||||
| 		deferreturns := findDeferReturnCalls(text) | 		deferreturns := FindDeferReturnCalls(text) | ||||||
|  |  | ||||||
| 		// Set breakpoint on the most recently deferred function (if any) | 		// Set breakpoint on the most recently deferred function (if any) | ||||||
| 		var deferpc uint64 | 		var deferpc uint64 | ||||||
| @ -325,7 +325,7 @@ func next(dbp Process, stepInto, inlinedStepOut bool) error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func findDeferReturnCalls(text []AsmInstruction) []uint64 { | func FindDeferReturnCalls(text []AsmInstruction) []uint64 { | ||||||
| 	const deferreturn = "runtime.deferreturn" | 	const deferreturn = "runtime.deferreturn" | ||||||
| 	deferreturns := []uint64{} | 	deferreturns := []uint64{} | ||||||
|  |  | ||||||
|  | |||||||
| @ -218,12 +218,42 @@ func (d *Debugger) LastModified() time.Time { | |||||||
| 	return d.target.BinInfo().LastModified() | 	return d.target.BinInfo().LastModified() | ||||||
| } | } | ||||||
|  |  | ||||||
|  | const deferReturn = "runtime.deferreturn" | ||||||
|  |  | ||||||
| // FunctionReturnLocations returns all return locations | // FunctionReturnLocations returns all return locations | ||||||
| // for the given function. See the documentation for the | // for the given function, a list of addresses corresponding | ||||||
| // function of the same name within the `proc` package for | // to 'ret' or 'call runtime.deferreturn'. | ||||||
| // more information. |  | ||||||
| func (d *Debugger) FunctionReturnLocations(fnName string) ([]uint64, error) { | func (d *Debugger) FunctionReturnLocations(fnName string) ([]uint64, error) { | ||||||
| 	return proc.FunctionReturnLocations(d.target, fnName) | 	var ( | ||||||
|  | 		p = d.target | ||||||
|  | 		g = p.SelectedGoroutine() | ||||||
|  | 	) | ||||||
|  |  | ||||||
|  | 	fn, ok := p.BinInfo().LookupFunc[fnName] | ||||||
|  | 	if !ok { | ||||||
|  | 		return nil, fmt.Errorf("unable to find function %s", fnName) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var regs proc.Registers | ||||||
|  | 	var mem proc.MemoryReadWriter = p.CurrentThread() | ||||||
|  | 	if g.Thread != nil { | ||||||
|  | 		mem = g.Thread | ||||||
|  | 		regs, _ = g.Thread.Registers(false) | ||||||
|  | 	} | ||||||
|  | 	instructions, err := proc.Disassemble(mem, regs, p.Breakpoints(), p.BinInfo(), fn.Entry, fn.End) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var addrs []uint64 | ||||||
|  | 	for _, instruction := range instructions { | ||||||
|  | 		if instruction.IsRet() { | ||||||
|  | 			addrs = append(addrs, instruction.Loc.PC) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	addrs = append(addrs, proc.FindDeferReturnCalls(instructions)...) | ||||||
|  |  | ||||||
|  | 	return addrs, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // Detach detaches from the target process. | // Detach detaches from the target process. | ||||||
| @ -1121,9 +1151,9 @@ func (d *Debugger) FindLocation(scope api.EvalScope, locStr string) ([]api.Locat | |||||||
| 	return locs, err | 	return locs, err | ||||||
| } | } | ||||||
|  |  | ||||||
| // Disassemble code between startPC and endPC | // Disassemble code between startPC and endPC. | ||||||
| // if endPC == 0 it will find the function containing startPC and disassemble the whole function | // if endPC == 0 it will find the function containing startPC and disassemble the whole function. | ||||||
| func (d *Debugger) Disassemble(scope api.EvalScope, startPC, endPC uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error) { | func (d *Debugger) Disassemble(goroutineID int, addr1, addr2 uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error) { | ||||||
| 	d.processMutex.Lock() | 	d.processMutex.Lock() | ||||||
| 	defer d.processMutex.Unlock() | 	defer d.processMutex.Unlock() | ||||||
|  |  | ||||||
| @ -1131,21 +1161,27 @@ func (d *Debugger) Disassemble(scope api.EvalScope, startPC, endPC uint64, flavo | |||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if endPC == 0 { | 	if addr2 == 0 { | ||||||
| 		_, _, fn := d.target.BinInfo().PCToLine(startPC) | 		_, _, fn := d.target.BinInfo().PCToLine(addr1) | ||||||
| 		if fn == nil { | 		if fn == nil { | ||||||
| 			return nil, fmt.Errorf("Address 0x%x does not belong to any function", startPC) | 			return nil, fmt.Errorf("address %#x does not belong to any function", addr1) | ||||||
| 		} | 		} | ||||||
| 		startPC = fn.Entry | 		addr1 = fn.Entry | ||||||
| 		endPC = fn.End | 		addr2 = fn.End | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	g, err := proc.FindGoroutine(d.target, scope.GoroutineID) | 	g, err := proc.FindGoroutine(d.target, goroutineID) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	insts, err := proc.Disassemble(d.target, g, startPC, endPC) | 	var regs proc.Registers | ||||||
|  | 	var mem proc.MemoryReadWriter = d.target.CurrentThread() | ||||||
|  | 	if g.Thread != nil { | ||||||
|  | 		mem = g.Thread | ||||||
|  | 		regs, _ = g.Thread.Registers(false) | ||||||
|  | 	} | ||||||
|  | 	insts, err := proc.Disassemble(mem, regs, d.target.Breakpoints(), d.target.BinInfo(), addr1, addr2) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -317,6 +317,6 @@ type DisassembleRequest struct { | |||||||
|  |  | ||||||
| func (c *RPCServer) Disassemble(args DisassembleRequest, answer *api.AsmInstructions) error { | func (c *RPCServer) Disassemble(args DisassembleRequest, answer *api.AsmInstructions) error { | ||||||
| 	var err error | 	var err error | ||||||
| 	*answer, err = c.debugger.Disassemble(args.Scope, args.StartPC, args.EndPC, args.Flavour) | 	*answer, err = c.debugger.Disassemble(args.Scope.GoroutineID, args.StartPC, args.EndPC, args.Flavour) | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
|  | |||||||
| @ -607,7 +607,7 @@ type DisassembleOut struct { | |||||||
| // Disassemble will also try to calculate the destination address of an absolute indirect CALL if it happens to be the instruction the selected goroutine is stopped at. | // Disassemble will also try to calculate the destination address of an absolute indirect CALL if it happens to be the instruction the selected goroutine is stopped at. | ||||||
| func (c *RPCServer) Disassemble(arg DisassembleIn, out *DisassembleOut) error { | func (c *RPCServer) Disassemble(arg DisassembleIn, out *DisassembleOut) error { | ||||||
| 	var err error | 	var err error | ||||||
| 	out.Disassemble, err = c.debugger.Disassemble(arg.Scope, arg.StartPC, arg.EndPC, arg.Flavour) | 	out.Disassemble, err = c.debugger.Disassemble(arg.Scope.GoroutineID, arg.StartPC, arg.EndPC, arg.Flavour) | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
|  |  | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user