From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Brian Gianforcaro <b.gianfo@gmail.com>
Date: Sun, 20 Feb 2022 00:32:51 -0800
Subject: [PATCH] minerva: Implement custom wait override for the
 minerva_nat_target

While troubleshooting why gdb wasn't working when attempting to debug
minerva programs I noticed two things:

 - The contract of minerva's `waitpid(..)` appears to be slightly
   different than the generic ptrace target expects. We need to make
   sure we pass `WSTOPPED`, and it can return different errno values
   that we would want to re-try on.

-  The contract of minerva's `ptrace(..)` implementation appears to
   diverge as well, as we are expected to call `PT_ATTACH` before we
   call `PT_CONTINUE`, otherwise `ptrace(..)` will just error out.

Allow gdb to understand these differences, I've overloaded the
minerva_nat_target::wait(..) method and added the logic there.
---
 gdb/minerva-nat.c | 62 +++++++++++++++++++++++++++++++++++++++++++++-
 gdb/minerva-nat.h |  4 +++
 2 files changed, 65 insertions(+), 1 deletion(-)

diff --git a/gdb/minerva-nat.c b/gdb/minerva-nat.c
index ff740d4..b1d39d4 100644
--- a/gdb/minerva-nat.c
+++ b/gdb/minerva-nat.c
@@ -10,4 +10,64 @@
 #include "gdbsupport/gdb_wait.h"
 
 #include "inf-child.h"
-#include "minerva-nat.h"
\ No newline at end of file
+#include "minerva-nat.h"
+
+ptid_t minerva_nat_target::wait (ptid_t ptid, struct target_waitstatus* ourstatus, target_wait_flags)
+{
+    int pid;
+    int status, save_errno;
+
+    do
+    {
+        set_sigint_trap ();
+
+        do
+        {
+            errno = 0;
+            pid = waitpid (ptid.pid (), &status, WSTOPPED);
+            if (errno != 0)
+            {
+                save_errno = errno;
+                perror_with_name (("waitpid"));
+            }
+        }
+        while (pid == -1 && (save_errno == EINTR || save_errno == EAGAIN));
+
+        clear_sigint_trap ();
+
+        if (pid == -1)
+        {
+            fprintf_unfiltered (gdb_stderr,
+                    _("Child process unexpectedly missing: %s.\n"),
+                    safe_strerror (save_errno));
+
+            /* Claim it exited with unknown signal.  */
+            ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
+            ourstatus->value.sig = GDB_SIGNAL_UNKNOWN;
+            return inferior_ptid;
+        }
+
+        /* Ignore terminated detached child processes.  */
+        if (!WIFSTOPPED (status) && find_inferior_pid (this, pid) == nullptr)
+            pid = -1;
+    }
+    while (pid == -1);
+
+    store_waitstatus (ourstatus, status);
+
+    /* Minerva requires us to PT_ATTACH before we PT_CONTINUE, however GDB doesn't
+     * provide a hook for us to do that before we issue the PT_CONTINUE, so we are
+     * forced to do it here.
+     */
+    if (!m_attach_before_continue_called) {
+        errno = 0;
+        ptrace (PT_ATTACH, pid, (PTRACE_TYPE_ARG3)0, 0);
+        if (errno != 0) {
+            save_errno = errno;
+            printf_unfiltered (_("PT_ATTACH failed: %d - %s \n"), save_errno, safe_strerror (save_errno));
+        }
+        m_attach_before_continue_called = true;
+    }
+
+    return ptid_t (pid);
+}
diff --git a/gdb/minerva-nat.h b/gdb/minerva-nat.h
index dcd24ce..9ae3b87 100644
--- a/gdb/minerva-nat.h
+++ b/gdb/minerva-nat.h
@@ -11,6 +11,10 @@
 
 class minerva_nat_target : public inf_ptrace_target
 {
+  ptid_t wait (ptid_t, struct target_waitstatus *, target_wait_flags) override;
+
+private:
+  bool m_attach_before_continue_called { false };
 };
 
 #endif /* minerva-nat.h */
