diff --git a/GetRuntimeAddresses/symbolyze/gdb_test.go b/GetRuntimeAddresses/symbolyze/gdb_test.go new file mode 100644 index 0000000..36eac45 --- /dev/null +++ b/GetRuntimeAddresses/symbolyze/gdb_test.go @@ -0,0 +1,106 @@ +package symbolyze + +import ( + "bufio" + "fmt" + "os" + "os/exec" + "regexp" + "strconv" + "testing" +) + +func buildSimple() error { + cmd := exec.Command("make") + cmd.Dir = "testdata" + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + return cmd.Run() +} + +func TestSimpleGDB(t *testing.T) { + ourResults := map[int]uint64{} + gdbResults := map[int]uint64{} + + err := buildSimple() + if err != nil { + t.Fatal(err) + } + + scanner := NewScanner("_PyRuntime", "*python3*") + scanner.OnFound(func(pid int, offset uint64) error { + t.Logf("scanner setting pid %d to offset %x", pid, offset) + ourResults[pid] = offset + return nil + }) + + for i := 0; i < 5; i++ { + simple := exec.Command("testdata/simple") + if err := simple.Start(); err != nil { + t.Fatal(err) + } + + defer func() { + simple.Process.Kill() + go simple.Wait() + }() + + t.Logf("asking Gdb about PID %d", simple.Process.Pid) + + offset, err := extractOffsetWithGdb(simple.Process.Pid, t) + if err != nil { + t.Fatalf("extractOffsetWithGdb: %v", err) + } + + t.Logf("Found offset at 0x%x", offset) + gdbResults[simple.Process.Pid] = offset + } + + if err = scanner.Run(); err != nil { + t.Logf("Scanner failure: %v", err) + } + + for pid, off := range gdbResults { + if off2, ok := ourResults[pid]; !ok { + t.Fatalf("Scanner has no offset for pid %d", pid) + } else if off != off2 { + t.Fatalf("Scanner has found offset %d while gdb has found offset %d for pid %d", off2, off, pid) + } + } +} + +var searchRX = regexp.MustCompile(`Symbol "_PyRuntime" is static storage at address 0x([0-9a-fA-F]+)`) + +func extractOffsetWithGdb(pid int, t *testing.T) (offset uint64, err error) { + cmd := exec.Command("gdb", + "--batch", + "-p", strconv.Itoa(pid), + "-ex", "info address _PyRuntime", + "-ex", "quit") + + output, err := cmd.StdoutPipe() + if err != nil { + return 0, err + } + + if err := cmd.Start(); err != nil { + return 0, err + } + + scanner := bufio.NewScanner(output) + for scanner.Scan() { + line := scanner.Text() + // t.Logf("[gdb]: %v", line) + match := searchRX.FindStringSubmatch(line) + if len(match) > 1 { + return strconv.ParseUint(match[1], 16, 64) + } + } + + if err := cmd.Wait(); err != nil { + return 0, err + } + + return 0, fmt.Errorf("symbol not found with gdb") +} diff --git a/GetRuntimeAddresses/symbolyze/testdata/.gitignore b/GetRuntimeAddresses/symbolyze/testdata/.gitignore new file mode 100644 index 0000000..2f17ffb --- /dev/null +++ b/GetRuntimeAddresses/symbolyze/testdata/.gitignore @@ -0,0 +1,2 @@ +simple +simple.o diff --git a/GetRuntimeAddresses/symbolyze/testdata/Makefile b/GetRuntimeAddresses/symbolyze/testdata/Makefile new file mode 100644 index 0000000..0c30885 --- /dev/null +++ b/GetRuntimeAddresses/symbolyze/testdata/Makefile @@ -0,0 +1,12 @@ +CC=gcc +PC=python3.7dm-config +PIE=-fPIE # this was necessary on my machine +CFLAGS=$(shell $(PC) --cflags) +LDFLAGS=$(shell $(PC) --ldflags) + +simple: simple.o + $(CC) -o $@ $< $(LDFLAGS) + +simple.o: simple.c + $(CC) -c $(CFLAGS) $(PIE) $< + diff --git a/GetRuntimeAddresses/symbolyze/testdata/simple.c b/GetRuntimeAddresses/symbolyze/testdata/simple.c new file mode 100644 index 0000000..437019f --- /dev/null +++ b/GetRuntimeAddresses/symbolyze/testdata/simple.c @@ -0,0 +1,22 @@ +#define PY_SSIZE_T_CLEAN +#include + +static char script[] = "import time\n" + "import os\n" + "\n" + "print(\"Process ID: {}\".format(os.getpid()))\n" + "print(\"Entering infinite loop ...\")\n" + "while 1:\n" + " time.sleep(2)\n" + "\n"; + +int +main(int argc, char *argv[]) +{ + Py_Initialize(); + PyRun_SimpleString(script); + if (Py_FinalizeEx() < 0) { + exit(120); + } + return 0; +}