ack/plat/linuxppc/libsys/sigaction.s
George Koehler a585ddf578 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".
2017-01-22 00:52:32 -05:00

157 lines
3.5 KiB
ArmAsm

#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