Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions cmd/bingo/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import (
"log"
"os"
"os/signal"
"syscall"

"golang.org/x/sys/unix"

"github.com/bingosuite/bingo/config"
websocket "github.com/bingosuite/bingo/internal/ws"
Expand All @@ -28,7 +29,7 @@ func main() {

// Set up signal handling for graceful shutdown
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
signal.Notify(sigChan, os.Interrupt, unix.SIGTERM)

// Wait for shutdown signal
<-sigChan
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ require (
golang.org/x/mod v0.27.0 // indirect
golang.org/x/net v0.43.0 // indirect
golang.org/x/sync v0.17.0 // indirect
golang.org/x/sys v0.35.0 // indirect
golang.org/x/sys v0.41.0 // indirect
golang.org/x/term v0.34.0
golang.org/x/text v0.29.0 // indirect
golang.org/x/tools v0.36.0 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,8 @@ golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ import (
"path/filepath"
"runtime"
"strings"
"syscall"
"time"

"github.com/bingosuite/bingo/internal/debuginfo"

"golang.org/x/sys/unix"
)

var (
Expand Down Expand Up @@ -115,7 +116,7 @@ func (d *Debugger) StartWithDebug(path string) {
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.SysProcAttr = &syscall.SysProcAttr{Ptrace: true}
cmd.SysProcAttr = &unix.SysProcAttr{Ptrace: true}

if err := cmd.Start(); err != nil {
log.Printf("[Debugger] Failed to start target: %v", err)
Expand All @@ -130,7 +131,7 @@ func (d *Debugger) StartWithDebug(path string) {
log.Printf("[Debugger] Started process with PID: %d and PGID: %d\n", dbInf.Target.PID, dbInf.Target.PGID)

// Enable tracking threads spawned from target and killing target once Bingo exits
if err := syscall.PtraceSetOptions(dbInf.Target.PID, syscall.PTRACE_O_TRACECLONE|ptraceOExitKill); err != nil {
if err := unix.PtraceSetOptions(dbInf.Target.PID, unix.PTRACE_O_TRACECLONE|ptraceOExitKill); err != nil {
log.Printf("[Debugger] Failed to set TRACECLONE and EXITKILL options on target: %v", err)
panic(err)
}
Expand All @@ -140,8 +141,8 @@ func (d *Debugger) StartWithDebug(path string) {
// We want to catch the initial SIGTRAP sent by process creation. When this is caught, we know that the target just started and we can ask the user where they want to set their breakpoints
// The message we print to the console will be removed in the future, it's just for debugging purposes for now.

var waitStatus syscall.WaitStatus
if _, status := syscall.Wait4(d.DebugInfo.Target.PID, &waitStatus, 0, nil); status != nil {
var waitStatus unix.WaitStatus
if _, status := unix.Wait4(d.DebugInfo.Target.PID, &waitStatus, 0, nil); status != nil {
log.Printf("[Debugger] Received SIGTRAP from process creation: %v", status)
}

Expand All @@ -167,8 +168,8 @@ func (d *Debugger) StartWithDebug(path string) {

func (d *Debugger) Continue(pid int) {
// Read registers
var regs syscall.PtraceRegs
if err := syscall.PtraceGetRegs(pid, &regs); err != nil {
var regs unix.PtraceRegs
if err := unix.PtraceGetRegs(pid, &regs); err != nil {
log.Printf("[Debugger] Failed to get registers: %v", err)
panic(err)
}
Expand All @@ -184,22 +185,22 @@ func (d *Debugger) Continue(pid int) {
regs.Rip = bpAddr

// Rewind Rip by 1
if err := syscall.PtraceSetRegs(pid, &regs); err != nil {
if err := unix.PtraceSetRegs(pid, &regs); err != nil {
log.Printf("[Debugger] Failed to restore registers: %v", err)
panic(err)
}

// Step over the instruction we previously removed to put the breakpoint
// TODO: decide if we want to call debugger.SingleStep() for this or just the system
if err := syscall.PtraceSingleStep(pid); err != nil {
if err := unix.PtraceSingleStep(pid); err != nil {
log.Printf("[Debugger] Failed to single-step: %v", err)
panic(err)
}

// TODO: only trigger for step over signal
var waitStatus syscall.WaitStatus
var waitStatus unix.WaitStatus
// Wait until the program lets us know we stepped over (handle cases where we get another signal which Wait4 would consume)
if _, err := syscall.Wait4(pid, &waitStatus, 0, nil); err != nil {
if _, err := unix.Wait4(pid, &waitStatus, 0, nil); err != nil {
log.Printf("[Debugger] Failed to wait for the single-step: %v", err)
panic(err)
}
Expand All @@ -212,7 +213,7 @@ func (d *Debugger) Continue(pid int) {

// Resume execution
// TODO: decide if we want to call debugger.Continue() for this or just the system call
if err := syscall.PtraceCont(pid, 0); err != nil {
if err := unix.PtraceCont(pid, 0); err != nil {
log.Printf("[Debugger] Failed to resume target execution: %v", err)
panic(err)
}
Expand All @@ -221,7 +222,7 @@ func (d *Debugger) Continue(pid int) {

func (d *Debugger) SingleStep(pid int) {

if err := syscall.PtraceSingleStep(pid); err != nil {
if err := unix.PtraceSingleStep(pid); err != nil {
log.Printf("[Debugger] Failed to single-step: %v", err)
panic(err)
}
Expand All @@ -232,7 +233,7 @@ func (d *Debugger) StopDebug() {
// Detach from the target process, letting it continue running
if d.DebugInfo.Target.PID > 0 {
log.Printf("[Debugger] Detaching from target process (PID: %d)", d.DebugInfo.Target.PID)
if err := syscall.PtraceDetach(d.DebugInfo.Target.PID); err != nil {
if err := unix.PtraceDetach(d.DebugInfo.Target.PID); err != nil {
log.Printf("[Debugger] Failed to detach from target process: %v (might have already exited)", err)
panic(err)
}
Expand All @@ -253,10 +254,10 @@ func (d *Debugger) SetBreakpoint(pid int, line int) error {
}

original := make([]byte, len(bpCode))
if _, err := syscall.PtracePeekData(pid, uintptr(pc), original); err != nil {
if _, err := unix.PtracePeekData(pid, uintptr(pc), original); err != nil {
return fmt.Errorf("failed to read original machine code into memory: %v for PID: %d", err, pid)
}
if _, err := syscall.PtracePokeData(pid, uintptr(pc), bpCode); err != nil {
if _, err := unix.PtracePokeData(pid, uintptr(pc), bpCode); err != nil {
return fmt.Errorf("failed to write breakpoint into memory: %v", err)
}
d.Breakpoints[pc] = original
Expand All @@ -269,7 +270,7 @@ func (d *Debugger) ClearBreakpoint(pid int, line int) error {
if err != nil {
return fmt.Errorf("failed to get PC of line %v: %v", line, err)
}
if _, err := syscall.PtracePokeData(pid, uintptr(pc), d.Breakpoints[pc]); err != nil {
if _, err := unix.PtracePokeData(pid, uintptr(pc), d.Breakpoints[pc]); err != nil {
return fmt.Errorf("failed to write breakpoint into memory: %v", err)
}
return nil
Expand All @@ -288,8 +289,8 @@ func (d *Debugger) mainDebugLoop() {
}

// Wait until any of the child processes of the target is interrupted or ends
var waitStatus syscall.WaitStatus
wpid, err := syscall.Wait4(-1*d.DebugInfo.Target.PGID, &waitStatus, syscall.WNOHANG, nil)
var waitStatus unix.WaitStatus
wpid, err := unix.Wait4(-1*d.DebugInfo.Target.PGID, &waitStatus, unix.WNOHANG, nil)
if err != nil {
log.Printf("[Debugger] Failed to wait for the target or any of its threads: %v", err)
// Don't panic, just exit gracefully
Expand Down Expand Up @@ -317,13 +318,13 @@ func (d *Debugger) mainDebugLoop() {
}
} else {
// Only stop on breakpoints caused by our debugger, ignore any other event like spawning of new threads
if waitStatus.StopSignal() == syscall.SIGTRAP && waitStatus.TrapCause() != syscall.PTRACE_EVENT_CLONE {
if waitStatus.StopSignal() == unix.SIGTRAP && waitStatus.TrapCause() != unix.PTRACE_EVENT_CLONE {
//TODO: improve error handling and messages

d.breakpointHit(wpid)

} else {
if err := syscall.PtraceCont(wpid, 0); err != nil {
if err := unix.PtraceCont(wpid, 0); err != nil {
log.Printf("[Debugger] Failed to resume target execution: %v for PID: %d", err, wpid)
// Don't panic, might have been detached
return
Expand Down Expand Up @@ -364,7 +365,7 @@ func (d *Debugger) initialBreakpointHit() {
}
case "continue":
log.Println("[Debugger] Continuing from initial breakpoint")
if err := syscall.PtraceCont(d.DebugInfo.Target.PID, 0); err != nil {
if err := unix.PtraceCont(d.DebugInfo.Target.PID, 0); err != nil {
log.Printf("[Debugger] Failed to resume target execution: %v", err)
panic(err)
}
Expand All @@ -386,8 +387,8 @@ func (d *Debugger) initialBreakpointHit() {

func (d *Debugger) breakpointHit(pid int) {
// Get register information to determine location
var regs syscall.PtraceRegs
if err := syscall.PtraceGetRegs(pid, &regs); err != nil {
var regs unix.PtraceRegs
if err := unix.PtraceGetRegs(pid, &regs); err != nil {
log.Printf("[Debugger] Failed to get registers: %v", err)
panic(err)
}
Expand Down
5 changes: 3 additions & 2 deletions internal/debuginfo/debug_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import (
"debug/elf"
"debug/gosym"
"fmt"
"syscall"

"golang.org/x/sys/unix"
)

type Target struct {
Expand Down Expand Up @@ -48,7 +49,7 @@ func NewDebugInfo(path string, pid int) (*DebugInfo, error) {
sourceFile, _, _ := symTable.PCToLine(symTable.LookupFunc("main.main").Entry)

// Need this to wait on threads
pgid, err := syscall.Getpgid(pid)
pgid, err := unix.Getpgid(pid)
if err != nil {
return nil, fmt.Errorf("error getting PGID: %v", err)
}
Expand Down