Fix parameters of signal handlers for linuxppc.
authorGeorge Koehler <xkernigh@netscape.net>
Sun, 22 Jan 2017 05:52:32 +0000 (00:52 -0500)
committerGeorge Koehler <xkernigh@netscape.net>
Sun, 22 Jan 2017 05:52:32 +0000 (00:52 -0500)
Linux passes the arguments in registers, but our compiler expects
arguments on the stack.  Signal handlers got garbage instead of the
signal number.  Some handlers, like the one in lang/m2/libm2/sigtrp.c,
need the correct signal number.

I write a "bridge" in PowerPC assembly that moves the arguments to the
stack.  I put the bridge in sigaction(), so I provide a signal() that
calls sigaction().  I remove the *.c glob or wildcard from build.lua,
so linuxppc only compiles its own signal.c, not the other signal.c for
linux386 and linux68k.

My bridge uses sigprocmask(), so I also add sigprocmask().  Because
linux386 and linux68k use globs, they also get sigprocmask().  I sync
the header files so all three Linux platforms declare execve(),
sigprocmask(), and unlink(), but not remove(), because we have
remove() in <stdio.h>.

I am using sigaction.s to test some features that we recently added to
our PowerPC assembler.  These are the "hi16[...]" and "lo16[...]"
syntax, and also the extended names like "beq", "cmpwi", "li", "subi".

plat/linux/libsys/sigprocmask.c [new file with mode: 0644]
plat/linux386/include/unistd.h
plat/linux68k/include/unistd.h
plat/linuxppc/include/unistd.h
plat/linuxppc/libsys/build.lua
plat/linuxppc/libsys/sigaction.s [new file with mode: 0644]
plat/linuxppc/libsys/signal.c [new file with mode: 0644]

diff --git a/plat/linux/libsys/sigprocmask.c b/plat/linux/libsys/sigprocmask.c
new file mode 100644 (file)
index 0000000..ad1b339
--- /dev/null
@@ -0,0 +1,7 @@
+#include <signal.h>
+#include "libsys.h"
+
+int sigprocmask(int flags, const sigset_t *new, sigset_t *old)
+{
+       return _syscall(__NR_sigprocmask, flags, (quad) new, (quad) old);
+}
index 5c6f31e..8c8637c 100644 (file)
@@ -56,7 +56,6 @@ extern int write(int fd, void* buffer, size_t count);
 extern off_t lseek(int fildes, off_t offset, int whence);
 extern int fcntl(int fd, int op, ...);
 extern int unlink(const char* path);
-extern int remove(const char* path);
 
 /* Special variables */
 
@@ -117,8 +116,16 @@ typedef int sig_atomic_t;
 
 #define _NSIG           32      /* Biggest signal number + 1
                                    (not including real-time signals).  */
+
+/* sigprocmask */
+#define SIG_BLOCK       0
+#define SIG_UNBLOCK     1
+#define SIG_SETMASK     2
+typedef unsigned long sigset_t;
+
 typedef void (*sighandler_t)(int);
 extern sighandler_t signal(int signum, sighandler_t handler);
+extern int sigprocmask(int, const sigset_t *, sigset_t *);
 extern int raise(int signum);
 
 
index 307192f..927a204 100644 (file)
@@ -55,6 +55,7 @@ extern int read(int fd, void* buffer, size_t count);
 extern int write(int fd, void* buffer, size_t count);
 extern off_t lseek(int fildes, off_t offset, int whence);
 extern int fcntl(int fd, int op, ...);
+extern int unlink(const char* path);
 
 /* Special variables */
 
@@ -67,6 +68,7 @@ extern pid_t getpid(void);
 extern int brk(void* ptr);
 extern void* sbrk(int increment);
 extern int isatty(int d);
+extern int execve(const char *path, char *const argv[], char *const envp[]);
 
 /* Signal handling */
 
@@ -114,8 +116,16 @@ typedef int sig_atomic_t;
 
 #define _NSIG           32      /* Biggest signal number + 1
                                    (not including real-time signals).  */
+
+/* sigprocmask */
+#define SIG_BLOCK       0
+#define SIG_UNBLOCK     1
+#define SIG_SETMASK     2
+typedef unsigned long sigset_t;
+
 typedef void (*sighandler_t)(int);
 extern sighandler_t signal(int signum, sighandler_t handler);
+extern int sigprocmask(int, const sigset_t *, sigset_t *);
 extern int raise(int signum);
 
 
index a31bd9f..f577053 100644 (file)
@@ -55,6 +55,7 @@ extern int read(int fd, void* buffer, size_t count);
 extern int write(int fd, void* buffer, size_t count);
 extern off_t lseek(int fildes, off_t offset, int whence);
 extern int fcntl(int fd, int op, ...);
+extern int unlink(const char* path);
 
 /* Special variables */
 
@@ -115,8 +116,34 @@ typedef int sig_atomic_t;
 
 #define _NSIG           32      /* Biggest signal number + 1
                                    (not including real-time signals).  */
+
+/* sigprocmask */
+#define SIG_BLOCK       0
+#define SIG_UNBLOCK     1
+#define SIG_SETMASK     2
+typedef unsigned long sigset_t;
+
+/* sa_flags */
+#define SA_NODEFER      0x40000000UL
+#define SA_RESETHAND    0x80000000UL
+
+struct __siginfo;
+struct sigaction {
+       union {
+               void (*__sa_handler)(int);
+               void (*__sa_sigaction)(int, struct __siginfo *, void *);
+       } __sigaction_u;
+       sigset_t sa_mask;
+       unsigned long sa_flags;
+       void (*sa_restorer)(void);
+};
+#define sa_handler __sigaction_u.__sa_handler
+#define sa_sigaction __sigaction_u.__sa_sigaction
+
 typedef void (*sighandler_t)(int);
+extern int sigaction(int, const struct sigaction *, struct sigaction *);
 extern sighandler_t signal(int signum, sighandler_t handler);
+extern int sigprocmask(int, const sigset_t *, sigset_t *);
 extern int raise(int signum);
 
 
index e74f3f4..f7b16b3 100644 (file)
@@ -1,16 +1,36 @@
 acklibrary {
-    name = "lib",
-    srcs = {
-        "./*.s",
-        "plat/linux/libsys/*.c",
-        "plat/linux/libsys/*.s",
-    },
+       name = "lib",
+       srcs = {
+               "./_syscall.s",
+               "./sigaction.s",
+               "./signal.c",
+               "./trap.s",
+               "plat/linux/libsys/_exit.c",
+               "plat/linux/libsys/_hol0.s",
+               "plat/linux/libsys/close.c",
+               "plat/linux/libsys/creat.c",
+               "plat/linux/libsys/errno.s",
+               "plat/linux/libsys/execve.c",
+               "plat/linux/libsys/getpid.c",
+               "plat/linux/libsys/gettimeofday.c",
+               "plat/linux/libsys/ioctl.c",
+               "plat/linux/libsys/isatty.c",
+               "plat/linux/libsys/kill.c",
+               "plat/linux/libsys/lseek.c",
+               "plat/linux/libsys/open.c",
+               "plat/linux/libsys/read.c",
+               "plat/linux/libsys/sbrk.c",
+               -- omit signal.c
+               "plat/linux/libsys/sigprocmask.c",
+               "plat/linux/libsys/unlink.c",
+               "plat/linux/libsys/write.c",
+       },
        deps = {
                "lang/cem/libcc.ansi/headers+headers",
                "plat/linuxppc/include+headers",
        },
-    vars = {
-        plat = "linuxppc"
-    }
+       vars = {
+               plat = "linuxppc"
+       }
 }
 
diff --git a/plat/linuxppc/libsys/sigaction.s b/plat/linuxppc/libsys/sigaction.s
new file mode 100644 (file)
index 0000000..0509c8e
--- /dev/null
@@ -0,0 +1,156 @@
+#define __NR_sigaction         67
+#define SIG_BLOCK              0
+#define SIG_SETMASK            2
+#define MAXSIG                 32
+
+/* offsets into our stack frame */
+#define mynew  16      /* new sigaction */
+#define mynset 32      /* new signal set */
+#define myoset 36      /* old signal set */
+#define mysave 40
+#define mysize 56
+
+.sect .text; .sect .rodata; .sect .data; .sect .bss
+
+/*
+ * Linux calls signal handlers with arguments in registers, but the
+ * ACK expects arguments on the stack.  This sigaction() uses a
+ * "bridge" to move the arguments.
+ */
+.sect .text
+.define _sigaction
+_sigaction:
+       mflr    r0
+       subi    r1, r1, mysize
+       stw     r31, mysave+8(r1)
+       stw     r30, mysave+4(r1)
+       stw     r29, mysave(r1)
+       stw     r0, mysave+12(r1)
+       li      r3, 0
+       stw     r3, mynset(r1)          ! mynset = 0
+       lwz     r29, mysize(r1)         ! r29 = signal number
+       lwz     r30, mysize+4(r1)       ! r30 = new action
+       lwz     r31, mysize+8(r1)       ! r31 = old action
+       /*
+        * If the new action is non-NULL, the signal number is in
+        * range 1 to MAXSIG, and the new handler is not SIG_DFL 0
+        * or SIG_IGN 1, then we interpose our bridge.
+        */
+       cmpwi   cr0, r30, 0
+       subi    r7, r29, 1              ! r7 = index in handlers
+       cmplwi  cr7, r7, MAXSIG         ! unsigned comparison
+       beq     cr0, kernel
+       bge     cr7, kernel
+       lwz     r3, 0(r30)              ! r3 = new handler
+       clrrwi. r3, r3, 1
+       beq     cr0, kernel
+       /*
+        * Block the signal while we build the bridge.  Prevents a
+        * race if a signal arrives after we change the bridge but
+        * before we change the action in the kernel.
+        */
+       li      r4, 1
+       slw     r4, r4, r7
+       stw     r4, mynset(r1)          ! mynmask = 1 << (signal - 1)
+       li      r3, SIG_BLOCK
+       la      r4, mynset(r1)
+       la      r5, myoset(r1)
+       stw     r3, 0(r1)
+       stw     r4, 4(r1)
+       stw     r5, 8(r1)
+       bl      _sigprocmask
+       /*
+        * Point our bridge to the new signal handler.  Then copy the
+        * new sigaction but point it to our bridge.
+        */
+       lis     r6, hi16[handlers]
+       ori     r6, r6, lo16[handlers]
+       subi    r7, r29, 1
+       slwi    r7, r7, 2
+       lwz     r3, 0(r30)              ! r3 = new handler
+       stwx    r3, r6, r7              ! put it in array of handlers
+       lis     r3, hi16[bridge]
+       ori     r3, r3, lo16[bridge]
+       lwz     r4, 4(r30)
+       lwz     r5, 8(r30)
+       lwz     r6, 12(r30)
+       stw     r3, mynew(r1)           ! sa_handler or sa_sigaction
+       stw     r4, mynew+4(r1)         ! sa_mask
+       stw     r5, mynew+8(r1)         ! sa_flags
+       stw     r6, mynew+12(r1)        ! sa_restorer
+       la      r30, mynew(r1)
+kernel:
+       li      r3, __NR_sigaction
+       stw     r3, 0(r1)
+       stw     r29, 4(r1)
+       stw     r30, 8(r1)
+       stw     r31, 12(r1)
+       bl      __syscall
+       /*
+        * If we blocked the signal, then restore the old signal mask.
+        */
+       lwz     r3, mynset(r1)
+       cmpwi   cr0, r3, 0
+       beq     cr0, fixold
+       li      r3, SIG_SETMASK
+       la      r4, myoset(r1)
+       li      r5, 0
+       stw     r3, 0(r1)
+       stw     r4, 4(r1)
+       stw     r5, 8(r1)
+       bl      _sigprocmask
+       /*
+        * If the old sigaction is non-NULL and points to our bridge,
+        * then point it to the signal handler.
+        */
+fixold:
+       cmpwi   cr0, r31, 0
+       beq     cr0, leave
+       lis     r3, hi16[bridge]
+       ori     r3, r3, lo16[bridge]
+       lwz     r4, 0(r31)
+       cmpw    cr0, r3, r4
+       bne     cr0, leave
+       lis     r6, hi16[handlers]
+       ori     r6, r6, lo16[handlers]
+       subi    r7, r29, 1
+       slwi    r7, r7, 2
+       lwzx    r3, r6, r7      ! get it from array of handlers
+       stw     r3, 0(r31)      ! put it in old sigaction
+leave:
+       lwz     r0, mysave+12(r1)
+       lwz     r29, mysave(r1)
+       lwz     r30, mysave+4(r1)
+       lwz     r31, mysave+8(r1)
+       addi    r1, r1, mysize
+       mtlr    r0
+       blr                     ! return from sigaction
+
+/*
+ * Linux calls bridge(signum) or bridge(signum, info, context) with
+ * arguments in registers r3, r4, r5.
+ */
+bridge:
+       mflr    r0
+       subi    r1, r1, 16
+       stw     r0, 12(r1)
+       stw     r3, 0(r1)       ! signal number
+       stw     r4, 4(r1)       ! info
+       stw     r5, 8(r1)       ! context
+
+       lis     r6, hi16[handlers]
+       ori     r6, r6, lo16[handlers]
+       subi    r7, r3, 1
+       slwi    r7, r7, 2
+       lwzx    r6, r6, r7
+       mtctr   r6
+       bctrl                   ! call our signal handler
+
+       lwz     r0, 12(r1)
+       addi    r1, r1, 16
+       mtlr    r0
+       blr                     ! return from bridge
+
+.sect .bss
+handlers:
+       .space 4 * MAXSIG       ! array of signal handlers
diff --git a/plat/linuxppc/libsys/signal.c b/plat/linuxppc/libsys/signal.c
new file mode 100644 (file)
index 0000000..0ed1918
--- /dev/null
@@ -0,0 +1,19 @@
+#include <signal.h>
+
+/*
+ * Uses our bridge in sigaction.s when calling the signal handler.
+ * Mimics Linux __NR_signal by using SA_NODEFER | SA_RESETHAND.
+ */
+sighandler_t signal(int signum, sighandler_t handler) {
+       struct sigaction new, old;
+       int i;
+
+       new.sa_handler = handler;
+       new.sa_mask = 0; /* empty set */
+       new.sa_flags = SA_NODEFER | SA_RESETHAND;
+
+       i = sigaction(signum, &new, &old);
+       if (i < 0)
+               return SIG_ERR;
+       return old.sa_handler;
+}