Reputation: 987
I am trying to intercept syscalls when calling a program from Go, however I'm running into two issues.
The child seems to hang, which hangs the parent process as well. It seems wait4(2)
is blocking which seems weird, wouldn't the child finally call exit(2)
to exit?
The syscalls I get to stdout
are not consistent, sometimes the last syscall is 3
, other times it's 6
or 192
. Do I have a race condition in my code? Why does this happen?
I tried listening for signals on the parent, but I don't receive anything..
I've substituted the program I usually run with /bin/ls
.
package main
import (
"syscall"
"fmt"
"os/signal"
"os"
)
func main() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, os.Kill)
go SignalListener(c)
attr := new(syscall.ProcAttr)
attr.Sys = new(syscall.SysProcAttr)
attr.Sys.Ptrace = true
pid, err := syscall.ForkExec("/bin/ls", nil, attr)
if err != nil {
panic(err)
}
var wstat syscall.WaitStatus
var regs syscall.PtraceRegs
for {
fmt.Println("Waiting..")
_, err := syscall.Wait4(pid, &wstat, 0, nil)
fmt.Printf("Exited: %d\n", wstat.Exited())
if err != nil {
fmt.Println(err)
break
}
syscall.PtraceGetRegs(pid, ®s);
fmt.Printf("syscall: %d\n", regs.Orig_eax)
syscall.PtraceSyscall(pid, 0)
}
}
func SignalListener(c <-chan os.Signal) {
s := <-c
fmt.Printf("Got signal %d\n", s)
}
Upvotes: 3
Views: 605
Reputation: 5720
The short answer is that intercepting syscalls with Go is going to be very difficult, and anything ptrace will probably not work.
Go has a runtime which multiplexes go-routines onto OS threads. A syscall is a scheduling point, so after the syscall returns you could be on a different thread, whereas I think ptrace follows a single thread.
Say the thread you are ptrace-ing is running your main go-routine. Then you call fmt.Println (which does syscall.Write), so the Go runtime takes your go-routine off that thread, and runs the syscall in a different os thread (syscalls always run in different threads). When the syscall returns, your main go-routine is put back on the schedulers list of runnable routines, and it will continue on whichever os thread is available, which may not be the one you are ptrace-ing.
This is also the reason you cannot step through a Go program with gdb
.
If you just wanted to execute an external program (like /bin/ls) you could use os/exec from the standard library.
The program that comes closes to what you are trying to do is probably delve. I think that sets a breakpoint on every thread at each single-step, then tries to find which thread your go-routine is now on based on the go-routine id.
Upvotes: 1