summaryrefslogtreecommitdiff
path: root/gnu/usr.bin/perl/mpeix/mpeix_setjmp.c
blob: 491c71664e52e9e175cbf2a45b2f490396d04480 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
/* Workaround for CR JAGab60546 setjmp/longjmp and
   JAGad55982 sigsetjmp/siglongjmp from shared libraries. */

/*
  * tabstop=4
  *
  * _setjmp/setjmp/sigsetjmp and
  *_longjmp/longjmp/siglongjmp.
  *
  * Written by Mark Klein, 10 October, 2000
  * Updated for gcc 3.x 6 October, 2005
  *
  * These routines are GCC specific and MUST BE COMPILED
  * WITH -O2
  *
  * The existing setjmp/longjmp code in both libc.a and XL.PUB.SYS
  * are not SR4 aware and cause problems when working with shared
  * libraries (XLs), especially when executing a longjmp between
  * XLs. This code preserves SR4 and will successfully handle
  * a cross space longjmp. However, the setjmp code must be
  * bound into each XL from which it will be called as well as
  * being bound into the main program.
  */

/*
  * The following macro takes the contents of the jmpbuf and
  * restores the registers from them. There is other code
  * elsewhere that ensures that __jmpbuf is %r26 at this
  * point in time. If it becomes some other register, that
  * register must be the last restored. At the end will
  * be a branch external that will cause a cross space
  * return if needed.
  */
#define RESTORE_REGS_AND_RETURN(__jmpbuf, __retval)                  \
({                                                                   \
         __asm__ __volatile__ (                                      \
             "   ldw    0(%%sr0, %0), %%rp\n"                        \
             "\t   ldw    4(%%sr0, %0), %%sp\n"                      \
             "\t   ldw   16(%%sr0, %0), %%r3\n"                      \
             "\t   ldw   20(%%sr0, %0), %%r4\n"                      \
             "\t   ldw   24(%%sr0, %0), %%r5\n"                      \
             "\t   ldw   28(%%sr0, %0), %%r6\n"                      \
             "\t   ldw   32(%%sr0, %0), %%r7\n"                      \
             "\t   ldw   36(%%sr0, %0), %%r8\n"                      \
             "\t   ldw   40(%%sr0, %0), %%r9\n"                      \
             "\t   ldw   44(%%sr0, %0), %%r10\n"                     \
             "\t   ldw   48(%%sr0, %0), %%r11\n"                     \
             "\t   ldw   52(%%sr0, %0), %%r12\n"                     \
             "\t   ldw   56(%%sr0, %0), %%r13\n"                     \
             "\t   ldw   60(%%sr0, %0), %%r14\n"                     \
             "\t   ldw   64(%%sr0, %0), %%r15\n"                     \
             "\t   ldw   68(%%sr0, %0), %%r16\n"                     \
             "\t   ldw   72(%%sr0, %0), %%r17\n"                     \
             "\t   ldw   76(%%sr0, %0), %%r18\n"                     \
             "\t   ldw   80(%%sr0, %0), %%r19\n"                     \
             "\t   ldw   84(%%sr0, %0), %%r20\n"                     \
             "\t   ldw   88(%%sr0, %0), %%r21\n"                     \
             "\t   ldw   92(%%sr0, %0), %%r22\n"                     \
             "\t   ldw   96(%%sr0, %0), %%r23\n"                     \
             "\t   ldw  100(%%sr0, %0), %%r24\n"                     \
             "\t   ldw  104(%%sr0, %0), %%r25\n"                     \
             "\t   ldw  112(%%sr0, %0), %%r27\n"                     \
             "\t   ldw  116(%%sr0, %0), %%r1\n"                      \
             "\t   mtsp %%r1, %%sr3\n"                               \
             "\t   ldw  120(%%sr0, %0), %%r1\n"                      \
             "\t   mtsp %%r1, %%sr1\n"                               \
             "\t   or,<>   %%r0, %1, %%r0\n"                         \
             "\t     ldi 1, %%r28\n"                                 \
             "\t   ldw  108(%%sr0, %0), %%r26\n"                     \
             "\t   be       0(%%sr1, %%rp)\n"                        \
             "\t   mtsp %%r1, %%sr4\n"                               \
                 : \
                 : "r" (__jmpbuf),                                   \
                   "r" (__retval));                                  \
})

/*
  * The following macro extracts the signal mask
  * from  __jmpbuf from the 3rd and 4th words and
  * if non-zero, calls sigprocmask with that value
  * to set the signal mask. This macro is usually
  * invoked before the registers are restored in
  * the longjmp routines and it can clobber things
  * without needing to spill them as a result.
  * A quick frame is built before making the
  * call and cut back just afterwards.
  * The ldi 2, %r26 is actually SIG_SETMASK from
  * /usr/include/signal.h.
  */
#define RESTORE_SIGNAL_MASK(__jmpbuf)                                \
({                                                                   \
   __asm__ __volatile__ (                                            \
              "  ldw 8(%0), %%r26\n"                                 \
              "\t  comibt,=,n 0,%%r26,.+36\n"                        \
              "\t    ldo 64(%%sp), %%sp\n"                           \
              "\t    stw %0, -28(%%sp)\n"                            \
              "\t    ldi 0, %%r24\n"                                 \
              "\t    ldo 8(%0), %%r25\n"                             \
              "\t    .import sigprocmask,code\n"                     \
              "\t    bl sigprocmask,%%rp\n"                          \
              "\t    ldi 2, %%r26\n"                                 \
              "\t    ldw -28(%%sr0, %%sp), %0\n"                     \
              "\t    ldo -64(%%sp), %%sp\n"                          \
                     :                                               \
                     : "r" (__jmpbuf));                              \
})

/*
  * This macro saves the current contents of the
  * registers to __jmpbuf. Note that __jmpbuf is
  * guaranteed elsewhere to be in %r26. We do not
  * want it spilled, nor do we want a new frame
  * built.
  */
#define SAVE_REGS(__jmpbuf)                                          \
({                                                                   \
   __asm__ __volatile__ (                                            \
              "  stw %%rp,     0(%%sr0, %0)\n"                       \
              "\t  stw %%sp,     4(%%sr0, %0)\n"                     \
              "\t  stw %%r0,     8(%%sr0, %0)\n"                     \
              "\t  stw %%r3,    16(%%sr0, %0)\n"                     \
              "\t  stw %%r4,    20(%%sr0, %0)\n"                     \
              "\t  stw %%r5,    24(%%sr0, %0)\n"                     \
              "\t  stw %%r6,    28(%%sr0, %0)\n"                     \
              "\t  stw %%r7,    32(%%sr0, %0)\n"                     \
              "\t  stw %%r8,    36(%%sr0, %0)\n"                     \
              "\t  stw %%r9,    40(%%sr0, %0)\n"                     \
              "\t  stw %%r10,   44(%%sr0, %0)\n"                     \
              "\t  stw %%r11,   48(%%sr0, %0)\n"                     \
              "\t  stw %%r12,   52(%%sr0, %0)\n"                     \
              "\t  stw %%r13,   56(%%sr0, %0)\n"                     \
              "\t  stw %%r14,   60(%%sr0, %0)\n"                     \
              "\t  stw %%r15,   64(%%sr0, %0)\n"                     \
              "\t  stw %%r16,   68(%%sr0, %0)\n"                     \
              "\t  stw %%r17,   72(%%sr0, %0)\n"                     \
              "\t  stw %%r18,   76(%%sr0, %0)\n"                     \
              "\t  stw %%r19,   80(%%sr0, %0)\n"                     \
              "\t  stw %%r20,   84(%%sr0, %0)\n"                     \
              "\t  stw %%r21,   88(%%sr0, %0)\n"                     \
              "\t  stw %%r22,   92(%%sr0, %0)\n"                     \
              "\t  stw %%r23,   96(%%sr0, %0)\n"                     \
              "\t  stw %%r24,  100(%%sr0, %0)\n"                     \
              "\t  stw %%r25,  104(%%sr0, %0)\n"                     \
              "\t  stw %%r26,  108(%%sr0, %0)\n"                     \
              "\t  stw %%r27,  112(%%sr0, %0)\n"                     \
              "\t  mfsp %%sr3, %%r1\n"                               \
              "\t  stw %%r1,   116(%%sr0, %0)\n"                     \
              "\t  mfsp %%sr4, %%r1\n"                               \
              "\t  stw %%r1,   120(%%sr0, %0)\n"                     \
                   :                                                 \
                   : "r" (__jmpbuf));                                \
})

/*
  * This macro will save the signal mask to the
  * __jmpbuf if __savemask is non-zero. By this
  * point in time, the other resisters have been
  * saved into the __jmpbuf.
  * The ldi 0, %r26 is actually SIG_BLOCK from
  * /usr/include/signal.h. Since the block is
  * an OR of the bits, this does not change the
  * mask, but returns it into the double word at
  * the address in %r24.
  */
#define SAVE_SIGNAL_MASK(__jmpbuf,__savemask)                        \
({                                                                   \
   __asm__ __volatile__ (                                            \
              "  comibt,=,n 0,%1,.+36\n"                             \
              "\t    stw %%rp, -20(%%sr0, %%sp)\n"                   \
              "\t    ldo 64(%%sp), %%sp\n"                           \
              "\t    ldo 8(%0), %%r24\n"                             \
              "\t    ldi 0, %%r25\n"                                 \
              "\t    .import sigprocmask,code\n"                     \
              "\t    bl sigprocmask,%%rp\n"                          \
              "\t    ldi 0, %%r26\n"                                 \
              "\t    ldo -64(%%sp), %%sp\n"                          \
              "\t    ldw -20(%%sr0, %%sp), %%rp\n"                   \
                     :                                               \
                     : "r" (__jmpbuf),                               \
                       "r" (__savemask));                            \
})

/*
  * Construct a jump buffer and unconditionally save
  * the signal mask. Return a 0 unconditionally.
  * Care is taken here and in the macros to assume
  * the __jumpbuf is in %r26 and that the return
  * value will be in %r28. It is done this way to
  * prevent a frame from being built and any registers
  * from being spilled.
  */
int setjmp(register void *jmpbuf)
{
   register int __jmpbuf asm ("%r26");

   SAVE_REGS(__jmpbuf);
   SAVE_SIGNAL_MASK(__jmpbuf, 1);
   return 0;
}

/*
  * Construct a jump buffer but do not save the
  * signal mask.
  */
int _setjmp(register void *jmpbuf)
{
   register int __jmpbuf asm ("%r26");

   SAVE_REGS(__jmpbuf);
   return 0;
}

/*
  * Construct a jump buffer and conditionally save
  * the signal mask. The mask is saved if the
  * savemask parameter is non-zero.
  */
int sigsetjmp(register void *jmpbuf, register int savemask)
{
   register int __jmpbuf   asm ("%r26");
   register int __savemask asm ("%r25");

   SAVE_REGS(__jmpbuf);
   SAVE_SIGNAL_MASK(__jmpbuf, __savemask);
   return 0;
}

/*
  * Return to the location established in the jmpbuf,
  * and place the value in i2 in %r28. Registers
  * %r4 and %r5 are co-opted to save the address and
  * value of jmpbuf and the return value. The signal
  * mask is re-established if needed, then the
  * address of jmpbuf and value of retval are placed
  * into %r26 and %r28 correspondingly. This routine
  * will never return to its caller and the stack
  * will be cut back to whatever exists in the jmpbuf.
  */
void longjmp(register void *jmpbuf, register int i2)
{
   register int __jmpbuf        asm ("%r26");
   register int __retval        asm ("%r28");

   __asm__ __volatile__ (
              "  copy %0, %%r4\n"
              "\t  copy %1, %%r5\n"
                     :
                     : "r" (jmpbuf),
                       "r" (i2));

   RESTORE_SIGNAL_MASK (__jmpbuf);

   __asm__ __volatile__ (
              "  copy %%r4, %0\n"
              "\t  copy %%r5, %1\n"
                     : "=r" (__jmpbuf),
                       "=r" (__retval));

   RESTORE_REGS_AND_RETURN (__jmpbuf, __retval);
}

/*
  * Return to the location established in the jmpbuf,
  * but do not restore the signal mask.
  */
void _longjmp(register void *jmpbuf, register int i2)
{
   register int __retval         asm ("%r28");
   register int __jmpbuf         asm ("%r26");

   __jmpbuf = (int)jmpbuf;
   __retval = i2;

   RESTORE_REGS_AND_RETURN (__jmpbuf, __retval);
}

/*
  * Return to the location established in the jmpbuf,
  * and conditionally re-establish the signal mask.
  */
void siglongjmp(register void *jmpbuf, register int i2)
{
   register int __jmpbuf        asm ("%r26");
   register int __retval        asm ("%r28");

   __asm__ __volatile__ (
              "  copy %0, %%r4\n"
              "\t  copy %1, %%r5\n"
                     :
                     : "r" (jmpbuf),
                       "r" (i2));

   RESTORE_SIGNAL_MASK (__jmpbuf);

   __asm__ __volatile__ (
              "  copy %%r4, %0\n"
              "\t  copy %%r5, %1\n"
                     : "=r" (__jmpbuf),
                       "=r" (__retval));

   RESTORE_REGS_AND_RETURN (__jmpbuf, __retval);
}

#ifdef TEST
int buf1[50];
int buf2[50];

foo() {
   printf("In routine foo(). Doing Longjmp.\n");
   longjmp(buf1, 123);
   printf("This is in foo after the longjmp() call. Should not reach here.\n");
}

bar(int ret) {
   printf("In routine bar(%d). Doing siglongjmp.\n",ret);
   siglongjmp(buf2, ret);
   printf("This is in bar after the siglongjmp() call. Should not reach here.\n");
}

main() {
   int i;
   if ((i = setjmp(buf1)))
     {
           printf("This is the return from the longjmp. i: %d\n",i);
         }
   else
     {
           printf("Jump buffer established, i: %d. Calling foo()\n",i);
           foo();
           printf("This is in main after the foo() call. Should not reach here.\n ");
         }

   if ((i = sigsetjmp(buf2,0)))
     {
           printf("This is the return from the longjmp. i: %d\n",i);
         }
   else
     {
           printf("Jump buffer established, i: %d. Calling bar(456)\n",i);
           bar(456);
           printf("This is in main after the bar(456) call. Should not reach here.\n");
         }

   if ((i = sigsetjmp(buf2,1)))
     {
           printf("This is the return from the longjmp. i: %d\n",i);
         }
   else
     {
           printf("Jump buffer established, i: %d. Calling bar(789)\n",i);
           bar(789);
           printf("This is in main after the bar(789) call. Should not reach here.\n");
         }
}
#endif