[libc] Rewrite setjmp() and longjmp()

Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Michael Brown
2015-03-02 15:21:38 +00:00
parent d1f0e89e4e
commit 8ab4b00442
2 changed files with 81 additions and 49 deletions

View File

@@ -1,42 +1,64 @@
/* setjmp and longjmp. Use of these functions is deprecated. */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
FILE_LICENCE ( GPL2_OR_LATER )
.text .text
.arch i386 .arch i386
.code32 .code32
/************************************************************************** /* Must match jmp_buf structure layout */
SETJMP - Save stack context for non-local goto .struct 0
**************************************************************************/ env_retaddr: .long 0
env_stack: .long 0
env_ebx: .long 0
env_esi: .long 0
env_edi: .long 0
env_ebp: .long 0
.previous
/*
* Save stack context for non-local goto
*/
.globl setjmp .globl setjmp
setjmp: setjmp:
movl 4(%esp),%ecx /* jmpbuf */ /* Get jmp_buf pointer in %edx */
movl 0(%esp),%edx /* return address */ movl 4(%esp),%edx
movl %edx,0(%ecx) /* Save return address */
movl %ebx,4(%ecx) movl 0(%esp),%eax
movl %esp,8(%ecx) movl %eax, env_retaddr(%edx)
movl %ebp,12(%ecx) /* Save stack pointer */
movl %esi,16(%ecx) movl %esp, env_stack(%edx)
movl %edi,20(%ecx) /* Save other registers */
movl $0,%eax movl %ebx, env_ebx(%edx)
movl %esi, env_esi(%edx)
movl %edi, env_edi(%edx)
movl %ebp, env_ebp(%edx)
/* Return 0 when returning as setjmp() */
xorl %eax, %eax
ret ret
.size setjmp, . - setjmp
/************************************************************************** /*
LONGJMP - Non-local jump to a saved stack context * Non-local jump to a saved stack context
**************************************************************************/ */
.globl longjmp .globl longjmp
longjmp: longjmp:
movl 4(%esp),%edx /* jumpbuf */ /* Get jmp_buf pointer in %edx */
movl 8(%esp),%eax /* result */ movl 4(%esp),%edx
movl 0(%edx),%ecx /* Get result in %eax */
movl 4(%edx),%ebx movl 8(%esp),%eax
movl 8(%edx),%esp /* Force result to non-zero */
movl 12(%edx),%ebp testl %eax, %eax
movl 16(%edx),%esi jnz 1f
movl 20(%edx),%edi incl %eax
cmpl $0,%eax 1: /* Restore stack pointer */
jne 1f movl env_stack(%edx), %esp
movl $1,%eax /* Restore other registers */
1: movl %ecx,0(%esp) movl env_ebx(%edx), %esp
movl env_esi(%edx), %esp
movl env_edi(%edx), %esp
movl env_ebp(%edx), %ebp
/* Replace return address on the new stack */
popl %ecx /* discard */
pushl env_retaddr(%edx)
/* Return to setjmp() caller */
ret ret
.size longjmp, . - longjmp

View File

@@ -1,40 +1,50 @@
#ifndef ETHERBOOT_SETJMP_H #ifndef _SETJMP_H
#define ETHERBOOT_SETJMP_H #define _SETJMP_H
FILE_LICENCE ( GPL2_OR_LATER ); FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h> #include <stdint.h>
#include <realmode.h> #include <realmode.h>
/** A jump buffer */ /** A jump buffer */
typedef struct { typedef struct {
/** Saved return address */
uint32_t retaddr; uint32_t retaddr;
/** Saved stack pointer */
uint32_t stack;
/** Saved %ebx */
uint32_t ebx; uint32_t ebx;
uint32_t esp; /** Saved %esi */
uint32_t ebp;
uint32_t esi; uint32_t esi;
/** Saved %edi */
uint32_t edi; uint32_t edi;
/** Saved %ebp */
uint32_t ebp;
} jmp_buf[1]; } jmp_buf[1];
/** A real-mode-extended jump buffer */ /** A real-mode-extended jump buffer */
typedef struct { typedef struct {
/** Jump buffer */
jmp_buf env; jmp_buf env;
uint16_t rm_ss; /** Real-mode stack pointer */
uint16_t rm_sp; segoff_t rm_stack;
} rmjmp_buf[1]; } rmjmp_buf[1];
extern int __asmcall setjmp ( jmp_buf env ); extern int __asmcall __attribute__ (( returns_twice ))
extern void __asmcall longjmp ( jmp_buf env, int val ); setjmp ( jmp_buf env );
#define rmsetjmp( _env ) ( { \ extern void __asmcall __attribute__ (( noreturn ))
(_env)->rm_ss = rm_ss; \ longjmp ( jmp_buf env, int val );
(_env)->rm_sp = rm_sp; \
setjmp ( (_env)->env ); } ) \
#define rmlongjmp( _env, _val ) do { \ #define rmsetjmp( _env ) ( { \
rm_ss = (_env)->rm_ss; \ (_env)->rm_stack.segment = rm_ss; \
rm_sp = (_env)->rm_sp; \ (_env)->rm_stack.offset = rm_sp; \
longjmp ( (_env)->env, (_val) ); \ setjmp ( (_env)->env ); } ) \
#define rmlongjmp( _env, _val ) do { \
rm_ss = (_env)->rm_stack.segment; \
rm_sp = (_env)->rm_stack.offset; \
longjmp ( (_env)->env, (_val) ); \
} while ( 0 ) } while ( 0 )
#endif /* ETHERBOOT_SETJMP_H */ #endif /* _SETJMP_H */