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 <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".
This commit is contained in:
George Koehler 2017-01-22 00:52:32 -05:00
parent 5aa2ac2246
commit a585ddf578
7 changed files with 256 additions and 10 deletions

View file

@ -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);
}

View 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);

View 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);

View 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);

View 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"
}
}

View file

@ -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

View file

@ -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;
}