From a585ddf578479853c73b4948fbf143306ff4b142 Mon Sep 17 00:00:00 2001 From: George Koehler Date: Sun, 22 Jan 2017 00:52:32 -0500 Subject: [PATCH] Fix parameters of signal handlers for linuxppc. 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 . 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 | 7 ++ plat/linux386/include/unistd.h | 9 +- plat/linux68k/include/unistd.h | 10 ++ plat/linuxppc/include/unistd.h | 27 ++++++ plat/linuxppc/libsys/build.lua | 38 ++++++-- plat/linuxppc/libsys/sigaction.s | 156 +++++++++++++++++++++++++++++++ plat/linuxppc/libsys/signal.c | 19 ++++ 7 files changed, 256 insertions(+), 10 deletions(-) create mode 100644 plat/linux/libsys/sigprocmask.c create mode 100644 plat/linuxppc/libsys/sigaction.s create mode 100644 plat/linuxppc/libsys/signal.c diff --git a/plat/linux/libsys/sigprocmask.c b/plat/linux/libsys/sigprocmask.c new file mode 100644 index 000000000..ad1b339c2 --- /dev/null +++ b/plat/linux/libsys/sigprocmask.c @@ -0,0 +1,7 @@ +#include +#include "libsys.h" + +int sigprocmask(int flags, const sigset_t *new, sigset_t *old) +{ + return _syscall(__NR_sigprocmask, flags, (quad) new, (quad) old); +} diff --git a/plat/linux386/include/unistd.h b/plat/linux386/include/unistd.h index 5c6f31ef4..8c8637c09 100644 --- a/plat/linux386/include/unistd.h +++ b/plat/linux386/include/unistd.h @@ -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); diff --git a/plat/linux68k/include/unistd.h b/plat/linux68k/include/unistd.h index 307192f77..927a20459 100644 --- a/plat/linux68k/include/unistd.h +++ b/plat/linux68k/include/unistd.h @@ -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); diff --git a/plat/linuxppc/include/unistd.h b/plat/linuxppc/include/unistd.h index a31bd9f0d..f57705365 100644 --- a/plat/linuxppc/include/unistd.h +++ b/plat/linuxppc/include/unistd.h @@ -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); diff --git a/plat/linuxppc/libsys/build.lua b/plat/linuxppc/libsys/build.lua index e74f3f416..f7b16b378 100644 --- a/plat/linuxppc/libsys/build.lua +++ b/plat/linuxppc/libsys/build.lua @@ -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 index 000000000..0509c8e72 --- /dev/null +++ b/plat/linuxppc/libsys/sigaction.s @@ -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 index 000000000..0ed1918e1 --- /dev/null +++ b/plat/linuxppc/libsys/signal.c @@ -0,0 +1,19 @@ +#include + +/* + * 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; +} -- 2.34.1