summaryrefslogtreecommitdiff
path: root/src/signal/powerpc
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2015-04-17 21:54:42 -0400
committerRich Felker <dalias@aerifal.cx>2015-04-17 21:54:42 -0400
commit583e55122e767b1586286a0d9c35e2a4027998ab (patch)
tree369bc1f292ee44f7bdd702203aaf535d97569caf /src/signal/powerpc
parent81e18eb3cd61f7b68a8f99b3e6c728f2a4d79221 (diff)
downloadmusl-583e55122e767b1586286a0d9c35e2a4027998ab.tar.gz
redesign sigsetjmp so that signal mask is restored after longjmp
the conventional way to implement sigsetjmp is to save the signal mask then tail-call to setjmp; siglongjmp then restores the signal mask and calls longjmp. the problem with this approach is that a signal already pending, or arriving between unmasking of signals and restoration of the saved stack pointer, will have its signal handler run on the stack that was active before siglongjmp was called. this can lead to unbounded stack usage when siglongjmp is used to leave a signal handler. in the new design, sigsetjmp saves its own return address inside the extended part of the sigjmp_buf (outside the __jmp_buf part used by setjmp) then calls setjmp to save a jmp_buf inside its own execution. it then tail-calls to __sigsetjmp_tail, which uses the return value of setjmp to determine whether to save the current signal mask or restore a previously-saved mask. as an added bonus, this design makes it so that siglongjmp and longjmp are identical. this is useful because the __longjmp_chk function we need to add for ABI-compatibility assumes siglongjmp and longjmp are the same, but for different reasons -- it was designed assuming either can access a flag just past the __jmp_buf indicating whether the signal masked was saved, and act on that flag. however, early versions of musl did not have space past the __jmp_buf for the non-sigjmp_buf version of jmp_buf, so our setjmp cannot store such a flag without risking clobbering memory on (very) old binaries.
Diffstat (limited to 'src/signal/powerpc')
-rw-r--r--src/signal/powerpc/sigsetjmp.s46
1 files changed, 17 insertions, 29 deletions
diff --git a/src/signal/powerpc/sigsetjmp.s b/src/signal/powerpc/sigsetjmp.s
index 461b7372..78acf590 100644
--- a/src/signal/powerpc/sigsetjmp.s
+++ b/src/signal/powerpc/sigsetjmp.s
@@ -4,35 +4,23 @@
.type __sigsetjmp,%function
sigsetjmp:
__sigsetjmp:
- #int sigsetjmp(sigjmp_buf buf, int save)
- # r3 r4
- #0) store save into buf->__fl
- stw 4, 448(3)
- #1) compare save with 0
cmpwi cr7, 4, 0
- #2) if its 0, goto setjmp code
beq- cr7, 1f
- #3) else: we must call pthread_sigmask(SIG_SETMASK, 0, (sigset_t *)buf->__ss);
- # store non-volatile regs 30, 31 into the setjmp buf
- stw 30, 0(3)
- stw 31, 4(3)
- # use them to store the pointer to the jmpbuf and the link reg
- mr 30, 3
- mflr 31
-
- # put pointer to ss buf into r5 (3rd arg)
- addi 5, 3, 452
- # put "2" i.e. SIG_SETMASK in r3
- li 3, 2
- li 4, 0
- bl pthread_sigmask
-
- #restore jmpbuf pointer and link reg
- mr 3, 30
- mtlr 31
- #resore non-volatile regs
- lwz 30, 0(3)
- lwz 31, 4(3)
-1:
- b setjmp
+ mflr 5
+ stw 5, 448(3)
+ stw 16, 448+4+8(3)
+ mr 16, 3
+
+ bl setjmp
+
+ mr 4, 3
+ mr 3, 16
+ lwz 5, 448(3)
+ mtlr 5
+ lwz 16, 448+4+8(3)
+
+.hidden __sigsetjmp_tail
+ b __sigsetjmp_tail
+
+1: b setjmp