home *** CD-ROM | disk | FTP | other *** search
- From: cary@hpcuhe.cup.hp.com (Cary Coutant)
- Date: Fri, 18 Dec 1992 00:15:12 GMT
- Subject: Re: problems with setjmp(3C),longjmp(3C)
- Message-ID: <31480302@hpcuhe.cup.hp.com>
- Organization: Hewlett Packard, Cupertino
- Path: sparky!uunet!cs.utexas.edu!sdd.hp.com!hpscit.sc.hp.com!hplextra!hpfcso!hpcss01!hpcuhe!cary
- Newsgroups: comp.sys.hp
- References: <1992Dec17.131523.11740@Informatik.TU-Muenchen.DE>
- Lines: 340
-
- > I'm trying to port a distributed system to a HP9000/710 running
- > HP-UX 8.07. This system uses setjmp and longjmp for task switching.
- > I need to access stackpointer and framepointer through the jmp_buf
- > structure but nowhere in the HP documentation and also not in
- > /usr/include/setjmp.h there is information on the position in
- > jmp_buf where the contents of the dumped registers are stored.
-
- On PA, there is (usually) no framepointer. Instead, stack frames
- are a fixed size, and are created and destroyed on entry and exit.
- I don't know if this is a problem for the package you're trying
- to port.
-
- > In the porting notes of the system is also read:
- > "It is not possible to use setjmp or longjmp on some systems
- > either because they contain bugs and fail to restore state
- > properly, or because they check that longjmp only returns to
- > a previously encountered stack frame, i.e setjmp was previously
- > executed on the current stack."
-
- No problems here, though.
-
- > So my questions are:
- > - What registers are stored in what order in jmp_buf ?
-
- rp and sp are saved in the first two words, respectively,
- followed by the signal mask and an on-signal-stack flag.
- Then the entry save registers (gr3-18) are saved.
- Then gr19, sr3, fr12-15, a one-word flag indicating
- whether the signal mask should be restored, a one-word
- hole, fr16-21, rp-prime, and external-dp.
-
- > - If not how would assembly routines look like that could be
- > used instead ?
-
- I've attached below a simple user-level thread package that you might
- be able to use.
-
-
- Cary Coutant
- Hewlett-Packard
- California Language Lab
-
-
- # This is a shell archive. Remove anything before this line,
- # then unpack it by saving it in a file and typing "sh file".
- #
- # Wrapped by Cary Coutant <cary@hpclpax> on Thu Dec 17 16:16:02 1992
- #
- # This archive contains:
- # threads.s forks.c Makefile
- #
-
- LANG=""; export LANG
- PATH=/bin:/usr/bin:$PATH; export PATH
-
- echo x - threads.s
- cat >threads.s <<'@EOF'
- .copyright "Hewlett Packard, 1992"
-
- ; Thread Control Block structure
- ;
- ; struct tcb {
- ; long tcb_sp;
- ; long tcb_stackbase;
- ; long tcb_stacklimit;
- ; /* anything else you may want */
- ; }
-
- TCB_SP .equ 0
- TCB_STACKBASE .equ 4
- TCB_STACKLIMIT .equ 8
-
-
- psp .equ 64 ; pseudo-register for previous sp
-
-
-
- ; long *init_thread_stack(
- ; void (*code)(void),
- ; void *base,
- ; long size)
- ;
- ; Initializes a new thread's stack and returns the value of the
- ; stack pointer that the thread should initially use.
- ;
- ; <code> is the address of the function where the new thread will start.
- ;
- ; <base> is the base of the stack that has been allocated for the thread.
- ;
- ; <size> is the size of the stack.
- ;
- ; The stack frame is arranged so that it looks like the new thread
- ; has suspended execution at the beginning of __thread_init. When
- ; the new thread is resumed, __thread_init (below) will then call
- ; the function <code>.
-
- .code
-
- .proc
-
- ; ------------------------NOTE--------------------------
- ; Use the first .callinfo for PA-RISC 1.0 machines.
- ; Use the second for PA-RISC 1.1 machines.
- ; .callinfo calls,save_rp,entry_gr=18,entry_fr=15,entry_sr=3
- ; .callinfo calls,save_rp,entry_gr=18,entry_fr=21,entry_sr=3
- ; ------------------------------------------------------
-
- .callinfo calls,save_rp,entry_gr=18,entry_fr=15,entry_sr=3
- .export init_thread_stack,entry
-
- init_thread_stack
- .enter
-
- ; Calculate far end of stack and align to 64-byte boundary
- add %arg1,%arg2,%r22
- depi 0,31,6,%r22
-
- ; Align base of stack to 64-byte boundary
- ; and allocate a 64-byte stack frame
- ; (This will be __thread_init's stack frame)
- ldo 64+63(%arg1),%arg1
- depi 0,31,6,%arg1
-
- ; Store "magic number" at far end of stack for overflow detection
- mfctl %cr16,%r21 ; use interval timer as random no.
- uaddcm %r0,%r21,%r20
- stw %r21,-32(%r22) ; Store Cr16 to AlgnLimit-32
- stw %r20,-16(%r22) ; Store ~Cr16 to AlgnLimit-16
-
- ; Mark the bottom of the new stack by storing zero in
- ; the saved sp location (sp-4 of the new stack)
- stw %r0,-4(%arg1)
-
- ; Set the saved rp in __thread_init's stack frame so that execution
- ; begins in the new thread at __thread_init
- ldil L'__thread_init,%r1
- ldo R'__thread_init(%r1),%r1
- stw %r1,-20(%arg1) ; store at -20(new psp)
-
- ; Save the <code> parameter in the new stack frame
- ; (in __thread_init's parameter area)
- stw %arg0,-36(%arg1)
-
- ; Create a second stack frame on the new stack the same size
- ; as our stack frame
- ; (This will look like switch_thread's stack frame)
- ; (The register save area will be garbage, but it doesn't matter)
- ldo 0(psp),%r19
- sub %sp,%r19,%r20
- add %r20,%arg1,%arg1
-
- ; return the top of stack for the new thread
- copy %arg1,%ret0
-
- .leave
-
- .procend
-
-
-
- ; __thread_init(void (*code)(void))
- ;
- ; Thread execution begins here upon return from switch_thread.
- ; The <code> parameter was saved in the parameter save area by
- ; init_thread_stack, so it must be picked up again from there.
- ;
- ; This is an internal routine. It is never really "called";
- ; instead, it is "returned into" from switch_thread the first
- ; time that a thread is resumed.
-
- .proc
- .callinfo calls,save_sp
- .export __thread_init,entry
-
- __thread_init
- ; No entry sequence -- the stack frame was already initialized
- ; for us by init_thread_stack
-
- ; Retrieve the <code> parameter from the stack frame
- ldw -36(%sp),%arg0
-
- ; Call <code> (in r3) to initiate new thread
- bl .+8,%rp
- bv,n 0(%arg0)
-
- ; control should never return here
- break 0,0
-
- .procend
-
-
-
- ; void switch_thread(struct tcb *new_thread)
- ;
- ; Transfers control from one thread to another.
- ; Saves minimal state of the old thread and restores
- ; minimal state for the new thread.
- ;
- ; init_thread_stack and switch_thread must have identical frame sizes.
-
- .proc
-
- ; ------------------------NOTE--------------------------
- ; Use the first .callinfo for PA-RISC 1.0 machines.
- ; Use the second for PA-RISC 1.1 machines.
- ; .callinfo calls,save_rp,entry_gr=18,entry_fr=15,entry_sr=3
- ; .callinfo calls,save_rp,entry_gr=18,entry_fr=21,entry_sr=3
- ; ------------------------------------------------------
-
- .callinfo calls,save_rp,entry_gr=18,entry_fr=15,entry_sr=3
- .export switch_thread,entry
-
- switch_thread
- .enter
-
- ; Get address of current thread's TCB from <cur_thread>
- .import cur_thread,data
- .import $global$,data
- addil L'cur_thread-$global$,%dp
- ldw R'cur_thread-$global$(%r1),%r19
-
- ; Check for stack overflow
- ldw TCB_STACKLIMIT(%r19),%r20
- depi 0,31,6,%r20
- ldw -16(%r20),%r22
- ldw -32(%r20),%r21
- uaddcm %r0,%r22,%r22
- xor,= %r22,%r21,%r0
- break 0,0
-
- ; Switch stacks
- stw %sp,TCB_SP(%r19) ; save old thread's sp in its TCB
- ldw TCB_SP(%arg0),%sp ; get new thread's sp from <new_thread>
-
- ; We continue here, now on the new thread's stack.
- ; Put address of new TCB in <cur_thread>
- ;addil L'cur_thread-$global$,%dp ; (r1 is still live from above)
- stw %arg0,R'cur_thread-$global$(%r1)
-
- .leave
-
- .procend
-
- .end
- @EOF
-
- chmod 644 threads.s
-
- echo x - forks.c
- cat >forks.c <<'@EOF'
- #define STACKSIZE 65536
-
- struct tcb {
- char *sp;
- char *base;
- char *limit;
- };
-
- struct tcb tcb[3] = {0};
- struct tcb *cur_thread = &tcb[0];
-
- char stack1[STACKSIZE] = {0};
- char stack2[STACKSIZE] = {0};
- char stack3[STACKSIZE] = {0};
-
- extern char *init_thread_stack(void (*proc)(void), char *base, int size);
- extern void switch_thread(struct tcb *new_thread);
-
- #pragma align 64
- int limit[16] = {0};
-
- int main(int argc, char **argv)
- {
- int i, ub;
- extern void fork1(void), fork2(void);
-
- if (argc > 1)
- ub = atoi(argv[1]);
- else
- ub = 20;
-
- tcb[0].limit = (char *)&limit[16];
- limit[12] = -1;
-
- tcb[1].sp = init_thread_stack(fork1, stack1, STACKSIZE);
- tcb[1].base = stack1;
- tcb[1].limit = stack1 + STACKSIZE;
-
- tcb[2].sp = init_thread_stack(fork2, stack2, STACKSIZE);
- tcb[2].base = stack2;
- tcb[2].limit = stack2 + STACKSIZE;
-
- for (i = 0; i < ub; i++) {
- switch_thread(&tcb[1]);
- switch_thread(&tcb[2]);
- }
- }
-
- void fork1(void)
- {
- int i;
- extern void fork1b(void);
-
- for (i = 0; ; i++) {
- printf("%d\n", i);
- switch_thread(&tcb[0]);
- }
- }
-
- void fork2(void)
- {
- int i, j, k;
- i = 0;
- j = 1;
- for (;;) {
- printf("\t%d\n", j);
- switch_thread(&tcb[0]);
- k = i + j;
- i = j;
- j = k;
- }
- }
- @EOF
-
- chmod 644 forks.c
-
- echo x - Makefile
- cat >Makefile <<'@EOF'
- CFLAGS = -O -Aa
- LDFLAGS = -Wl,-a,archive
-
- OBJS = forks.o threads.o
-
- forks: $(OBJS)
- $(CC) -o forks $(LDFLAGS) $(OBJS)
- @EOF
-
- chmod 644 Makefile
-
- exit 0
-