2014-05-21 15:23:14 +00:00
|
|
|
// Package proctl provides functions for attaching to and manipulating
|
|
|
|
// a process during the debug session.
|
2014-05-20 18:23:35 +00:00
|
|
|
package proctl
|
|
|
|
|
|
|
|
import (
|
2014-07-11 19:52:55 +00:00
|
|
|
"bytes"
|
2014-05-23 14:42:06 +00:00
|
|
|
"debug/gosym"
|
2014-07-10 23:07:39 +00:00
|
|
|
"encoding/binary"
|
2014-05-20 18:23:35 +00:00
|
|
|
"fmt"
|
|
|
|
"os"
|
2014-07-30 00:00:24 +00:00
|
|
|
"strconv"
|
2014-10-07 19:32:22 +00:00
|
|
|
"strings"
|
2014-08-06 16:14:23 +00:00
|
|
|
"sync"
|
2014-05-20 18:23:35 +00:00
|
|
|
"syscall"
|
2014-08-04 18:53:15 +00:00
|
|
|
"unsafe"
|
2014-06-29 16:52:21 +00:00
|
|
|
|
|
|
|
"github.com/derekparker/dbg/dwarf/frame"
|
|
|
|
"github.com/derekparker/dbg/dwarf/line"
|
2014-07-30 00:00:24 +00:00
|
|
|
"github.com/derekparker/dbg/dwarf/op"
|
2014-09-13 17:28:46 +00:00
|
|
|
"github.com/derekparker/dbg/vendor/dwarf"
|
|
|
|
"github.com/derekparker/dbg/vendor/elf"
|
2014-05-20 18:23:35 +00:00
|
|
|
)
|
|
|
|
|
2014-05-21 15:23:14 +00:00
|
|
|
// Struct representing a debugged process. Holds onto pid, register values,
|
|
|
|
// process struct and process state.
|
2014-05-20 18:23:35 +00:00
|
|
|
type DebuggedProcess struct {
|
2014-10-04 23:13:20 +00:00
|
|
|
Pid int
|
|
|
|
Regs *syscall.PtraceRegs
|
|
|
|
Process *os.Process
|
|
|
|
ProcessState *syscall.WaitStatus
|
|
|
|
Executable *elf.File
|
|
|
|
Symbols []elf.Symbol
|
|
|
|
GoSymTable *gosym.Table
|
|
|
|
FrameEntries *frame.FrameDescriptionEntries
|
|
|
|
DebugLine *line.DebugLineInfo
|
|
|
|
BreakPoints map[uint64]*BreakPoint
|
2014-05-24 16:22:06 +00:00
|
|
|
}
|
|
|
|
|
2014-05-27 18:20:10 +00:00
|
|
|
// Represents a single breakpoint. Stores information on the break
|
2014-06-27 03:43:30 +00:00
|
|
|
// point including the byte of data that originally was stored at that
|
2014-05-27 18:20:10 +00:00
|
|
|
// address.
|
2014-05-24 16:22:06 +00:00
|
|
|
type BreakPoint struct {
|
|
|
|
FunctionName string
|
2014-05-27 15:43:47 +00:00
|
|
|
File string
|
2014-05-24 16:22:06 +00:00
|
|
|
Line int
|
|
|
|
Addr uint64
|
2014-05-27 15:43:47 +00:00
|
|
|
OriginalData []byte
|
2014-05-20 18:23:35 +00:00
|
|
|
}
|
|
|
|
|
2014-07-30 00:00:24 +00:00
|
|
|
type Variable struct {
|
|
|
|
Name string
|
|
|
|
Value string
|
|
|
|
Type string
|
|
|
|
}
|
|
|
|
|
2014-08-15 20:43:14 +00:00
|
|
|
type BreakPointExistsError struct {
|
|
|
|
file string
|
|
|
|
line int
|
|
|
|
addr uintptr
|
|
|
|
}
|
|
|
|
|
2014-07-11 19:52:55 +00:00
|
|
|
func (bpe BreakPointExistsError) Error() string {
|
|
|
|
return fmt.Sprintf("Breakpoint exists at %s:%d at %x", bpe.file, bpe.line, bpe.addr)
|
|
|
|
}
|
|
|
|
|
2014-05-21 15:23:14 +00:00
|
|
|
// Returns a new DebuggedProcess struct with sensible defaults.
|
2014-05-20 18:23:35 +00:00
|
|
|
func NewDebugProcess(pid int) (*DebuggedProcess, error) {
|
2014-05-23 14:42:06 +00:00
|
|
|
proc, err := os.FindProcess(pid)
|
2014-05-20 18:23:35 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2014-05-23 14:42:06 +00:00
|
|
|
err = syscall.PtraceAttach(pid)
|
2014-05-20 18:23:35 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2014-08-27 22:47:04 +00:00
|
|
|
ps, err := wait(proc.Pid)
|
2014-05-20 18:23:35 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2014-05-20 18:23:36 +00:00
|
|
|
debuggedProc := DebuggedProcess{
|
2014-10-04 23:13:20 +00:00
|
|
|
Pid: pid,
|
|
|
|
Regs: new(syscall.PtraceRegs),
|
|
|
|
Process: proc,
|
|
|
|
ProcessState: ps,
|
|
|
|
BreakPoints: make(map[uint64]*BreakPoint),
|
2014-05-20 18:23:36 +00:00
|
|
|
}
|
|
|
|
|
2014-05-23 14:42:06 +00:00
|
|
|
err = debuggedProc.LoadInformation()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2014-05-20 18:23:35 +00:00
|
|
|
return &debuggedProc, nil
|
|
|
|
}
|
|
|
|
|
2014-05-27 18:20:10 +00:00
|
|
|
// Finds the executable from /proc/<pid>/exe and then
|
2014-07-17 01:16:49 +00:00
|
|
|
// uses that to parse the following information:
|
|
|
|
// * Dwarf .debug_frame section
|
|
|
|
// * Dwarf .debug_line section
|
|
|
|
// * Go symbol table.
|
2014-05-23 14:42:06 +00:00
|
|
|
func (dbp *DebuggedProcess) LoadInformation() error {
|
2014-08-06 16:14:23 +00:00
|
|
|
var (
|
|
|
|
wg sync.WaitGroup
|
|
|
|
err error
|
|
|
|
)
|
2014-05-23 14:42:06 +00:00
|
|
|
|
2014-08-06 16:14:23 +00:00
|
|
|
err = dbp.findExecutable()
|
2014-06-29 16:52:21 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2014-08-06 16:14:23 +00:00
|
|
|
wg.Add(3)
|
|
|
|
go dbp.parseDebugFrame(&wg)
|
|
|
|
go dbp.parseDebugLine(&wg)
|
|
|
|
go dbp.obtainGoSymbols(&wg)
|
2014-06-29 16:52:21 +00:00
|
|
|
|
2014-08-06 16:14:23 +00:00
|
|
|
wg.Wait()
|
2014-05-23 14:42:06 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-05-21 15:23:14 +00:00
|
|
|
// Obtains register values from the debugged process.
|
2014-05-20 18:23:35 +00:00
|
|
|
func (dbp *DebuggedProcess) Registers() (*syscall.PtraceRegs, error) {
|
|
|
|
err := syscall.PtraceGetRegs(dbp.Pid, dbp.Regs)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("Registers():", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return dbp.Regs, nil
|
|
|
|
}
|
|
|
|
|
2014-05-24 00:44:54 +00:00
|
|
|
// Sets a breakpoint in the running process.
|
2014-05-27 23:15:18 +00:00
|
|
|
func (dbp *DebuggedProcess) Break(addr uintptr) (*BreakPoint, error) {
|
2014-05-24 00:44:54 +00:00
|
|
|
var (
|
2014-05-29 15:19:42 +00:00
|
|
|
int3 = []byte{0xCC}
|
|
|
|
f, l, fn = dbp.GoSymTable.PCToLine(uint64(addr))
|
|
|
|
originalData = make([]byte, 1)
|
2014-05-24 00:44:54 +00:00
|
|
|
)
|
|
|
|
|
2014-10-11 22:29:10 +00:00
|
|
|
if fn == nil {
|
|
|
|
return nil, fmt.Errorf("could not set breakpoint")
|
|
|
|
}
|
|
|
|
|
2014-05-29 15:19:42 +00:00
|
|
|
_, err := syscall.PtracePeekData(dbp.Pid, addr, originalData)
|
2014-05-27 15:43:47 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2014-05-24 16:22:06 +00:00
|
|
|
}
|
|
|
|
|
2014-07-11 19:52:55 +00:00
|
|
|
if bytes.Equal(originalData, int3) {
|
|
|
|
return nil, BreakPointExistsError{f, l, addr}
|
|
|
|
}
|
|
|
|
|
2014-05-27 15:43:47 +00:00
|
|
|
_, err = syscall.PtracePokeData(dbp.Pid, addr, int3)
|
2014-05-24 00:44:54 +00:00
|
|
|
if err != nil {
|
2014-05-24 16:22:06 +00:00
|
|
|
return nil, err
|
2014-05-24 00:44:54 +00:00
|
|
|
}
|
|
|
|
|
2014-05-24 16:22:06 +00:00
|
|
|
breakpoint := &BreakPoint{
|
|
|
|
FunctionName: fn.Name,
|
2014-05-27 15:43:47 +00:00
|
|
|
File: f,
|
|
|
|
Line: l,
|
2014-05-27 23:15:18 +00:00
|
|
|
Addr: uint64(addr),
|
2014-05-29 15:19:42 +00:00
|
|
|
OriginalData: originalData,
|
2014-05-24 16:22:06 +00:00
|
|
|
}
|
|
|
|
|
2014-09-17 03:37:48 +00:00
|
|
|
dbp.BreakPoints[uint64(addr)] = breakpoint
|
2014-05-24 16:22:06 +00:00
|
|
|
|
|
|
|
return breakpoint, nil
|
2014-05-24 00:44:54 +00:00
|
|
|
}
|
|
|
|
|
2014-05-27 18:33:49 +00:00
|
|
|
// Clears a breakpoint.
|
2014-05-27 23:15:18 +00:00
|
|
|
func (dbp *DebuggedProcess) Clear(pc uint64) (*BreakPoint, error) {
|
|
|
|
bp, ok := dbp.PCtoBP(pc)
|
2014-05-27 18:33:49 +00:00
|
|
|
if !ok {
|
2014-09-05 19:20:12 +00:00
|
|
|
return nil, fmt.Errorf("No breakpoint currently set for %#v", pc)
|
2014-05-27 18:33:49 +00:00
|
|
|
}
|
|
|
|
|
2014-05-29 15:26:42 +00:00
|
|
|
_, err := syscall.PtracePokeData(dbp.Pid, uintptr(bp.Addr), bp.OriginalData)
|
2014-05-27 18:33:49 +00:00
|
|
|
if err != nil {
|
2014-05-27 23:15:18 +00:00
|
|
|
return nil, err
|
2014-05-27 18:33:49 +00:00
|
|
|
}
|
|
|
|
|
2014-09-17 03:37:48 +00:00
|
|
|
delete(dbp.BreakPoints, pc)
|
2014-05-27 18:33:49 +00:00
|
|
|
|
2014-05-27 23:15:18 +00:00
|
|
|
return bp, nil
|
2014-05-27 18:33:49 +00:00
|
|
|
}
|
|
|
|
|
2014-05-21 15:23:14 +00:00
|
|
|
// Steps through process.
|
2014-05-29 16:18:28 +00:00
|
|
|
func (dbp *DebuggedProcess) Step() (err error) {
|
2014-05-27 15:43:47 +00:00
|
|
|
regs, err := dbp.Registers()
|
2014-05-23 14:42:06 +00:00
|
|
|
if err != nil {
|
2014-05-27 15:43:47 +00:00
|
|
|
return err
|
2014-05-23 14:42:06 +00:00
|
|
|
}
|
|
|
|
|
2014-05-29 14:34:37 +00:00
|
|
|
bp, ok := dbp.PCtoBP(regs.PC() - 1)
|
2014-05-27 15:43:47 +00:00
|
|
|
if ok {
|
2014-05-29 16:18:28 +00:00
|
|
|
// Clear the breakpoint so that we can continue execution.
|
2014-05-29 14:34:37 +00:00
|
|
|
_, err = dbp.Clear(bp.Addr)
|
2014-05-27 15:51:38 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2014-05-29 15:27:03 +00:00
|
|
|
// Reset program counter to our restored instruction.
|
|
|
|
regs.SetPC(bp.Addr)
|
|
|
|
err = syscall.PtraceSetRegs(dbp.Pid, regs)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-05-29 16:18:28 +00:00
|
|
|
|
|
|
|
// Restore breakpoint now that we have passed it.
|
|
|
|
defer func() {
|
|
|
|
_, err = dbp.Break(uintptr(bp.Addr))
|
|
|
|
}()
|
2014-05-27 15:43:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
err = dbp.handleResult(syscall.PtraceSingleStep(dbp.Pid))
|
2014-05-23 14:42:06 +00:00
|
|
|
if err != nil {
|
2014-05-27 15:43:47 +00:00
|
|
|
return fmt.Errorf("step failed: ", err.Error())
|
2014-05-23 14:42:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2014-05-20 18:23:35 +00:00
|
|
|
}
|
2014-05-20 18:23:36 +00:00
|
|
|
|
2014-09-05 19:19:03 +00:00
|
|
|
// Step over function calls.
|
|
|
|
func (dbp *DebuggedProcess) Next() error {
|
|
|
|
pc, err := dbp.CurrentPC()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2014-10-04 02:44:43 +00:00
|
|
|
if _, ok := dbp.BreakPoints[pc-1]; ok {
|
2014-10-07 12:57:34 +00:00
|
|
|
// Decrement the PC to be before
|
|
|
|
// the breakpoint instruction.
|
2014-10-04 02:44:43 +00:00
|
|
|
pc--
|
2014-09-17 03:37:48 +00:00
|
|
|
}
|
|
|
|
|
2014-10-08 03:35:57 +00:00
|
|
|
f, l, _ := dbp.GoSymTable.PCToLine(pc)
|
2014-10-04 02:44:43 +00:00
|
|
|
fde, err := dbp.FrameEntries.FDEForPC(pc)
|
2014-09-17 03:37:48 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2014-10-11 02:00:07 +00:00
|
|
|
step := func() (uint64, error) {
|
|
|
|
err = dbp.Step()
|
|
|
|
if err != nil {
|
|
|
|
return 0, fmt.Errorf("next stepping failed: ", err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
return dbp.CurrentPC()
|
|
|
|
}
|
|
|
|
|
2014-10-08 03:35:57 +00:00
|
|
|
loc := dbp.DebugLine.NextLocation(pc, f, l)
|
2014-10-11 05:52:05 +00:00
|
|
|
if !fde.Cover(loc.Address) {
|
2014-10-11 02:00:07 +00:00
|
|
|
// Step once to ensure we're not going to step
|
|
|
|
// into another function before returning.
|
|
|
|
pc, err = step()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-10-04 02:44:43 +00:00
|
|
|
|
2014-10-11 05:52:05 +00:00
|
|
|
if fde.Cover(pc) {
|
2014-10-11 02:00:07 +00:00
|
|
|
// Unconditionally step out of current function
|
|
|
|
// Don't bother looking up ret addr, next line is
|
|
|
|
// outside of current fn, should only be a few
|
|
|
|
// instructions left to RET
|
2014-10-11 05:52:05 +00:00
|
|
|
for fde.Cover(pc) {
|
2014-10-11 02:00:07 +00:00
|
|
|
pc, err = step()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-10-07 17:25:33 +00:00
|
|
|
}
|
2014-10-11 02:00:07 +00:00
|
|
|
return nil
|
2014-10-04 02:44:43 +00:00
|
|
|
}
|
2014-10-11 02:00:07 +00:00
|
|
|
|
2014-10-04 02:44:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for {
|
2014-10-11 02:00:07 +00:00
|
|
|
pc, err = step()
|
2014-10-04 02:44:43 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2014-10-11 05:52:05 +00:00
|
|
|
if !fde.Cover(pc) {
|
2014-10-11 22:29:10 +00:00
|
|
|
err = dbp.continueToReturnAddress(pc, fde)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
pc, _ = dbp.CurrentPC()
|
2014-10-07 17:25:33 +00:00
|
|
|
}
|
|
|
|
|
2014-10-08 03:35:57 +00:00
|
|
|
_, nl, _ := dbp.GoSymTable.PCToLine(pc)
|
|
|
|
if nl != l {
|
2014-10-07 12:57:11 +00:00
|
|
|
break
|
2014-10-04 02:44:43 +00:00
|
|
|
}
|
2014-07-21 23:20:16 +00:00
|
|
|
}
|
|
|
|
|
2014-09-17 03:37:48 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-10-11 22:29:10 +00:00
|
|
|
func (dbp *DebuggedProcess) 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 := dbp.ReturnAddressFromOffset(0)
|
|
|
|
bp, err := dbp.Break(uintptr(addr))
|
|
|
|
if err != nil {
|
|
|
|
if _, ok := err.(BreakPointExistsError); !ok {
|
|
|
|
for !fde.Cover(pc) {
|
|
|
|
err = dbp.Step()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
pc, err = dbp.CurrentPC()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
err = dbp.Continue()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = dbp.clearTempBreakpoint(bp.Addr)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
pc, _ = dbp.CurrentPC()
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-05-21 15:23:14 +00:00
|
|
|
// Continue process until next breakpoint.
|
2014-05-20 18:23:36 +00:00
|
|
|
func (dbp *DebuggedProcess) Continue() error {
|
2014-05-27 15:43:47 +00:00
|
|
|
// Stepping first will ensure we are able to continue
|
|
|
|
// past a breakpoint if that's currently where we are stopped.
|
|
|
|
err := dbp.Step()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2014-05-21 15:23:45 +00:00
|
|
|
return dbp.handleResult(syscall.PtraceCont(dbp.Pid, 0))
|
2014-05-20 20:15:52 +00:00
|
|
|
}
|
|
|
|
|
2014-06-29 16:52:21 +00:00
|
|
|
func (dbp *DebuggedProcess) CurrentPC() (uint64, error) {
|
|
|
|
regs, err := dbp.Registers()
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return regs.Rip, nil
|
|
|
|
}
|
|
|
|
|
2014-10-04 23:13:20 +00:00
|
|
|
func (dbp *DebuggedProcess) clearTempBreakpoint(pc uint64) error {
|
2014-10-07 17:25:33 +00:00
|
|
|
if bp, ok := dbp.PCtoBP(pc); ok {
|
|
|
|
regs, err := dbp.Registers()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-09-25 22:20:09 +00:00
|
|
|
|
|
|
|
// Reset program counter to our restored instruction.
|
2014-10-07 17:25:33 +00:00
|
|
|
bp, err = dbp.Clear(bp.Addr)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2014-09-25 22:20:09 +00:00
|
|
|
regs.SetPC(bp.Addr)
|
|
|
|
return syscall.PtraceSetRegs(dbp.Pid, regs)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-10-07 19:32:22 +00:00
|
|
|
// Returns the value of the named symbol.
|
|
|
|
func (dbp *DebuggedProcess) EvalSymbol(name string) (*Variable, error) {
|
|
|
|
data, err := dbp.Executable.DWARF()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
reader := data.Reader()
|
|
|
|
|
|
|
|
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if entry.Tag != dwarf.TagVariable {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
n, ok := entry.Val(dwarf.AttrName).(string)
|
|
|
|
if !ok || n != name {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
offset, ok := entry.Val(dwarf.AttrType).(dwarf.Offset)
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
t, err := data.Type(offset)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
instructions, ok := entry.Val(dwarf.AttrLocation).([]byte)
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
val, err := dbp.extractValue(instructions, 0, t)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &Variable{Name: n, Type: t.String(), Value: val}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, fmt.Errorf("could not find symbol value for %s", name)
|
|
|
|
}
|
|
|
|
|
2014-07-30 00:00:24 +00:00
|
|
|
// Extracts the value from the instructions given in the DW_AT_location entry.
|
|
|
|
// We execute the stack program described in the DW_OP_* instruction stream, and
|
|
|
|
// then grab the value from the other processes memory.
|
2014-10-07 19:32:22 +00:00
|
|
|
func (dbp *DebuggedProcess) extractValue(instructions []byte, off int64, typ interface{}) (string, error) {
|
2014-07-30 00:00:24 +00:00
|
|
|
regs, err := dbp.Registers()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
fde, err := dbp.FrameEntries.FDEForPC(regs.PC())
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
fctx := fde.EstablishFrame(regs.PC())
|
|
|
|
cfaOffset := fctx.CFAOffset()
|
|
|
|
|
2014-10-07 19:32:22 +00:00
|
|
|
offset := off
|
|
|
|
if off == 0 {
|
|
|
|
offset, err = op.ExecuteStackProgram(cfaOffset, instructions)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
offset = int64(regs.Rsp) + offset
|
2014-07-30 00:00:24 +00:00
|
|
|
}
|
|
|
|
|
2014-10-07 21:22:26 +00:00
|
|
|
// If we have a user defined type, find the
|
|
|
|
// underlying concrete type and use that.
|
|
|
|
if tt, ok := typ.(*dwarf.TypedefType); ok {
|
|
|
|
typ = tt.Type
|
|
|
|
}
|
|
|
|
|
2014-10-07 19:32:22 +00:00
|
|
|
offaddr := uintptr(offset)
|
2014-08-04 20:21:35 +00:00
|
|
|
switch t := typ.(type) {
|
2014-10-07 21:22:26 +00:00
|
|
|
case *dwarf.PtrType:
|
|
|
|
addr, err := dbp.readMemory(offaddr, 8)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
adr := binary.LittleEndian.Uint64(addr)
|
|
|
|
val, err := dbp.extractValue(nil, int64(adr), t.Type)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
retstr := fmt.Sprintf("*%s", val)
|
|
|
|
return retstr, nil
|
2014-08-04 18:53:15 +00:00
|
|
|
case *dwarf.StructType:
|
2014-08-06 14:50:15 +00:00
|
|
|
switch t.StructName {
|
2014-08-04 20:21:35 +00:00
|
|
|
case "string":
|
2014-10-07 19:32:22 +00:00
|
|
|
return dbp.readString(offaddr)
|
2014-08-04 20:41:09 +00:00
|
|
|
case "[]int":
|
2014-10-07 19:32:22 +00:00
|
|
|
return dbp.readIntSlice(offaddr)
|
|
|
|
default:
|
|
|
|
// Here we could recursively call extractValue to grab
|
|
|
|
// the value of all the members of the struct.
|
|
|
|
fields := make([]string, 0, len(t.Field))
|
|
|
|
for _, field := range t.Field {
|
|
|
|
val, err := dbp.extractValue(nil, field.ByteOffset+offset, field.Type)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
fields = append(fields, fmt.Sprintf("%s: %s", field.Name, val))
|
|
|
|
}
|
|
|
|
retstr := fmt.Sprintf("%s {%s}", t.StructName, strings.Join(fields, ", "))
|
|
|
|
return retstr, nil
|
2014-08-04 20:21:35 +00:00
|
|
|
}
|
|
|
|
case *dwarf.ArrayType:
|
2014-10-07 19:32:22 +00:00
|
|
|
return dbp.readIntArray(offaddr, t)
|
2014-07-30 00:00:24 +00:00
|
|
|
case *dwarf.IntType:
|
2014-10-07 19:32:22 +00:00
|
|
|
return dbp.readInt(offaddr)
|
2014-08-04 18:25:00 +00:00
|
|
|
case *dwarf.FloatType:
|
2014-10-07 19:32:22 +00:00
|
|
|
return dbp.readFloat64(offaddr)
|
2014-08-04 18:25:00 +00:00
|
|
|
}
|
2014-07-30 00:00:24 +00:00
|
|
|
|
2014-08-04 18:25:00 +00:00
|
|
|
return "", fmt.Errorf("could not find value for type %s", typ)
|
|
|
|
}
|
2014-07-30 00:00:24 +00:00
|
|
|
|
2014-08-04 18:53:15 +00:00
|
|
|
func (dbp *DebuggedProcess) readString(addr uintptr) (string, error) {
|
|
|
|
val, err := dbp.readMemory(addr, 8)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
// deref the pointer to the string
|
|
|
|
addr = uintptr(binary.LittleEndian.Uint64(val))
|
|
|
|
val, err = dbp.readMemory(addr, 16)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
i := bytes.IndexByte(val, 0x0)
|
|
|
|
val = val[:i]
|
|
|
|
str := *(*string)(unsafe.Pointer(&val))
|
|
|
|
return str, nil
|
|
|
|
}
|
2014-09-30 13:49:30 +00:00
|
|
|
|
2014-08-04 20:41:09 +00:00
|
|
|
func (dbp *DebuggedProcess) readIntSlice(addr uintptr) (string, error) {
|
|
|
|
var number uint64
|
|
|
|
|
|
|
|
val, err := dbp.readMemory(addr, uintptr(24))
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
a := binary.LittleEndian.Uint64(val[:8])
|
|
|
|
l := binary.LittleEndian.Uint64(val[8:16])
|
|
|
|
c := binary.LittleEndian.Uint64(val[16:24])
|
|
|
|
|
|
|
|
val, err = dbp.readMemory(uintptr(a), uintptr(8*l))
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
members := make([]uint64, 0, l)
|
|
|
|
buf := bytes.NewBuffer(val)
|
|
|
|
for {
|
|
|
|
err := binary.Read(buf, binary.LittleEndian, &number)
|
|
|
|
if err != nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
members = append(members, number)
|
|
|
|
}
|
|
|
|
|
|
|
|
str := fmt.Sprintf("len: %d cap: %d %d", l, c, members)
|
|
|
|
|
|
|
|
return str, err
|
|
|
|
}
|
2014-08-04 18:53:15 +00:00
|
|
|
|
2014-08-04 20:21:35 +00:00
|
|
|
func (dbp *DebuggedProcess) readIntArray(addr uintptr, t *dwarf.ArrayType) (string, error) {
|
|
|
|
var (
|
|
|
|
number uint64
|
2014-09-13 17:28:46 +00:00
|
|
|
members = make([]uint64, 0, t.ByteSize)
|
2014-08-04 20:21:35 +00:00
|
|
|
)
|
|
|
|
|
2014-09-13 17:28:46 +00:00
|
|
|
val, err := dbp.readMemory(addr, uintptr(t.ByteSize))
|
2014-08-04 20:21:35 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
buf := bytes.NewBuffer(val)
|
|
|
|
for {
|
|
|
|
err := binary.Read(buf, binary.LittleEndian, &number)
|
|
|
|
if err != nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
members = append(members, number)
|
|
|
|
}
|
|
|
|
|
2014-09-13 17:28:46 +00:00
|
|
|
str := fmt.Sprintf("[%d]int %d", t.ByteSize/8, members)
|
2014-08-04 20:21:35 +00:00
|
|
|
|
2014-09-13 17:28:46 +00:00
|
|
|
return str, nil
|
2014-08-04 20:21:35 +00:00
|
|
|
}
|
|
|
|
|
2014-08-04 18:25:00 +00:00
|
|
|
func (dbp *DebuggedProcess) readInt(addr uintptr) (string, error) {
|
|
|
|
val, err := dbp.readMemory(addr, 8)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
n := binary.LittleEndian.Uint64(val)
|
2014-08-04 18:20:20 +00:00
|
|
|
|
2014-08-04 18:25:00 +00:00
|
|
|
return strconv.Itoa(int(n)), nil
|
|
|
|
}
|
2014-08-06 16:14:23 +00:00
|
|
|
|
2014-08-04 18:25:00 +00:00
|
|
|
func (dbp *DebuggedProcess) readFloat64(addr uintptr) (string, error) {
|
|
|
|
var n float64
|
|
|
|
val, err := dbp.readMemory(addr, 8)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
2014-07-30 00:00:24 +00:00
|
|
|
}
|
2014-08-04 18:25:00 +00:00
|
|
|
buf := bytes.NewBuffer(val)
|
|
|
|
binary.Read(buf, binary.LittleEndian, &n)
|
2014-07-30 00:00:24 +00:00
|
|
|
|
2014-08-04 18:25:00 +00:00
|
|
|
return strconv.FormatFloat(n, 'f', -1, 64), nil
|
2014-07-30 00:00:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (dbp *DebuggedProcess) readMemory(addr uintptr, size uintptr) ([]byte, error) {
|
|
|
|
buf := make([]byte, size)
|
|
|
|
|
|
|
|
_, err := syscall.PtracePeekData(dbp.Pid, addr, buf)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return buf, nil
|
|
|
|
}
|
|
|
|
|
2014-05-21 15:23:45 +00:00
|
|
|
func (dbp *DebuggedProcess) handleResult(err error) error {
|
2014-05-20 18:23:36 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2014-08-27 22:47:04 +00:00
|
|
|
ps, err := wait(dbp.Process.Pid)
|
2014-09-17 03:37:48 +00:00
|
|
|
if err != nil && err != syscall.ECHILD {
|
2014-05-20 18:23:36 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2014-09-17 03:37:48 +00:00
|
|
|
if ps != nil {
|
|
|
|
dbp.ProcessState = ps
|
|
|
|
if ps.TrapCause() == -1 && !ps.Exited() {
|
|
|
|
regs, err := dbp.Registers()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Printf("traced program %s at: %#v\n", ps.StopSignal(), regs.PC())
|
|
|
|
}
|
|
|
|
}
|
2014-05-20 18:23:36 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2014-05-23 14:42:06 +00:00
|
|
|
|
|
|
|
func (dbp *DebuggedProcess) findExecutable() error {
|
|
|
|
procpath := fmt.Sprintf("/proc/%d/exe", dbp.Pid)
|
|
|
|
|
2014-09-13 17:28:46 +00:00
|
|
|
f, err := os.OpenFile(procpath, 0, os.ModePerm)
|
2014-05-23 14:42:06 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
elffile, err := elf.NewFile(f)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
dbp.Executable = elffile
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-08-06 16:14:23 +00:00
|
|
|
func (dbp *DebuggedProcess) parseDebugLine(wg *sync.WaitGroup) {
|
|
|
|
defer wg.Done()
|
|
|
|
|
2014-06-29 16:52:21 +00:00
|
|
|
debugLine, err := dbp.Executable.Section(".debug_line").Data()
|
|
|
|
if err != nil {
|
2014-08-06 16:14:23 +00:00
|
|
|
fmt.Println("could not get .debug_line section", err)
|
|
|
|
os.Exit(1)
|
2014-06-29 16:52:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
dbp.DebugLine = line.Parse(debugLine)
|
|
|
|
}
|
2014-07-17 01:13:01 +00:00
|
|
|
|
2014-08-06 16:14:23 +00:00
|
|
|
func (dbp *DebuggedProcess) parseDebugFrame(wg *sync.WaitGroup) {
|
|
|
|
defer wg.Done()
|
|
|
|
|
2014-06-29 16:52:21 +00:00
|
|
|
debugFrame, err := dbp.Executable.Section(".debug_frame").Data()
|
|
|
|
if err != nil {
|
2014-08-06 16:14:23 +00:00
|
|
|
fmt.Println("could not get .debug_frame section", err)
|
|
|
|
os.Exit(1)
|
2014-06-29 16:52:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
dbp.FrameEntries = frame.Parse(debugFrame)
|
|
|
|
}
|
|
|
|
|
2014-08-06 16:14:23 +00:00
|
|
|
func (dbp *DebuggedProcess) obtainGoSymbols(wg *sync.WaitGroup) {
|
|
|
|
defer wg.Done()
|
|
|
|
|
2014-08-07 22:57:58 +00:00
|
|
|
var (
|
|
|
|
symdat []byte
|
|
|
|
pclndat []byte
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
|
|
|
|
if sec := dbp.Executable.Section(".gosymtab"); sec != nil {
|
|
|
|
symdat, err = sec.Data()
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println("could not get .gosymtab section", err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
2014-05-23 14:42:06 +00:00
|
|
|
}
|
|
|
|
|
2014-08-07 22:57:58 +00:00
|
|
|
if sec := dbp.Executable.Section(".gopclntab"); sec != nil {
|
|
|
|
pclndat, err = sec.Data()
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println("could not get .gopclntab section", err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
2014-05-23 14:42:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pcln := gosym.NewLineTable(pclndat, dbp.Executable.Section(".text").Addr)
|
|
|
|
tab, err := gosym.NewTable(symdat, pcln)
|
|
|
|
if err != nil {
|
2014-08-06 16:14:23 +00:00
|
|
|
fmt.Println("could not get initialize line table", err)
|
|
|
|
os.Exit(1)
|
2014-05-23 14:42:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
dbp.GoSymTable = tab
|
|
|
|
}
|
2014-05-27 15:43:47 +00:00
|
|
|
|
2014-05-27 18:20:10 +00:00
|
|
|
// Converts a program counter value into a breakpoint, if one was set
|
|
|
|
// for the function containing pc.
|
2014-05-27 15:43:47 +00:00
|
|
|
func (dbp *DebuggedProcess) PCtoBP(pc uint64) (*BreakPoint, bool) {
|
2014-09-17 03:37:48 +00:00
|
|
|
bp, ok := dbp.BreakPoints[pc]
|
2014-05-27 15:43:47 +00:00
|
|
|
return bp, ok
|
|
|
|
}
|
2014-07-10 23:07:39 +00:00
|
|
|
|
|
|
|
// Takes an offset from RSP and returns the address of the
|
|
|
|
// instruction the currect function is going to return to.
|
|
|
|
func (dbp *DebuggedProcess) ReturnAddressFromOffset(offset int64) uint64 {
|
|
|
|
regs, err := dbp.Registers()
|
|
|
|
if err != nil {
|
|
|
|
panic("Could not obtain register values")
|
|
|
|
}
|
|
|
|
|
|
|
|
retaddr := int64(regs.Rsp) + offset
|
|
|
|
data := make([]byte, 8)
|
|
|
|
syscall.PtracePeekText(dbp.Pid, uintptr(retaddr), data)
|
|
|
|
return binary.LittleEndian.Uint64(data)
|
|
|
|
}
|
2014-08-27 22:47:04 +00:00
|
|
|
|
|
|
|
func wait(pid int) (*syscall.WaitStatus, error) {
|
|
|
|
var status syscall.WaitStatus
|
|
|
|
var rusage syscall.Rusage
|
|
|
|
|
|
|
|
_, e := syscall.Wait4(pid, &status, 0, &rusage)
|
|
|
|
if e != nil {
|
|
|
|
return nil, e
|
|
|
|
}
|
|
|
|
|
|
|
|
return &status, nil
|
|
|
|
}
|