From 824d6619b500a86ff2fc680268357f0215d59b0c Mon Sep 17 00:00:00 2001 From: "mostang.com!davidm" Date: Sat, 8 Feb 2003 10:10:59 +0000 Subject: [PATCH] (Logical change 1.45) --- doc/libunwind-setjmp.man | 136 ++++++++++++++++++ doc/libunwind-setjmp.tex | 90 ++++++++++++ doc/unw_resume.man | 107 ++++++++++++++ doc/unw_resume.tex | 74 ++++++++++ src/elf32.c | 2 + src/elf32.h | 7 + src/elf64.c | 2 + src/elf64.h | 7 + src/elfxx.c | 168 ++++++++++++++++++++++ src/elfxx.h | 71 +++++++++ src/ia64/mk_cursor_i-ia64.c | 104 ++++++++++++++ src/ia64/setjmp-ia64.S | 45 ++++++ src/ia64/siglongjmp-ia64.S | 64 +++++++++ src/ia64/sigsetjmp-ia64.S | 64 +++++++++ src/longjmp.c | 109 ++++++++++++++ src/os-linux.c | 63 ++++++++ src/os-linux.h | 69 +++++++++ src/setjmp.c | 52 +++++++ src/siglongjmp.c | 108 ++++++++++++++ src/sigsetjmp.c | 47 ++++++ tests/test-setjmp.c | 278 ++++++++++++++++++++++++++++++++++++ 21 files changed, 1667 insertions(+) diff --git a/doc/libunwind-setjmp.man b/doc/libunwind-setjmp.man index e69de29b..3c112cd9 100644 --- a/doc/libunwind-setjmp.man +++ b/doc/libunwind-setjmp.man @@ -0,0 +1,136 @@ +'\" t +.\" Manual page created with latex2man on Sat Feb 8 01:31:56 PST 2003 +.\" NOTE: This file is generated, DO NOT EDIT. +.de Vb +.ft CW +.nf +.. +.de Ve +.ft R + +.fi +.. +.TH "LIBUNWIND\-SETJMP" "3" "08 February 2003" "Programming Library " "Programming Library " +.SH NAME + +libunwind\-setjmp \-\- libunwind\-based non\-local gotos +.PP +.SH SYNOPSIS + +.PP +#include +.br +.PP +int +setjmp(jmp_buf env); +.br +void +longjmp(jmp_buf env, +int val); +.br +int +_setjmp(jmp_buf env); +.br +void +_longjmp(jmp_buf env, +int val); +.br +int +setjmp(sigjmp_buf env, +int savemask); +.br +void +siglongjmp(sigjmp_buf env, +int val); +.br +.PP +.SH DESCRIPTION + +.PP +The unwind\-setjmp +library offers a libunwind\-based +implementation of non\-local gotos. This implementation is intended to +be a drop\-in replacement for the normal, system\-provided routines of +the same name. The main advantage of using the unwind\-setjmp +library is that setting up a non\-local goto via one of the +setjmp() +routines is very fast. Typically, just 2 or 3 words +need to be saved in the jump\-buffer (plus one call to +sigprocmask(2), +in the case of sigsetjmp). +On the +other hand, executing a non\-local goto by calling one of the +longjmp() +routines tends to be much slower than with the +system\-provided routines. In fact, the time spent on a +longjmp() +will be proportional to the number of call frames +that exist between the points where setjmp() +and +longjmp() +were called. For this reason, the +unwind\-setjmp +library is beneficial primarily in applications +that frequently call setjmp() +but only rarely call +longjmp(). +.PP +.SH CAVEATS + +.PP +.TP +.B * +The correct operation of this library depends on the presence of +correct unwind information. On newer platforms, this is rarely an +issue. On older platforms, care needs to be taken to +ensure that each of the functions whose stack frames may have to be +unwound during a longjmp() +have correct unwind information +(on those platforms, there is usually a compiler\-switch, such as +\fB\-funwind\-tables\fP, +to request the generation of unwind +information). +.TP +.B * +The contents of jmp_buf and sigjmp_buf as setup +and used by these routines is completely different from the ones +used by the system\-provided routines. Thus, a jump\-buffer created +by the libunwind\-based setjmp()/_setjmp +may only be +used in a call to the libunwind\-based +longjmp()/_longjmp(). +The analogous applies for +sigjmp_buf +with sigsetjmp() +and siglongjmp(). +.PP +.SH FILES + +.PP +.TP +\fB\-l\fPunwind\-setjmp + The library an application should +be linked against to ensure it uses the libunwind\-based non\-local +goto routines. +.PP +.SH SEE ALSO + +.PP +libunwind(3), +setjmp(3), longjmp(3), +_setjmp(3), _longjmp(3), +sigsetjmp(3), siglongjmp(3) +.PP +.SH AUTHOR + +.PP +David Mosberger\-Tang +.br +Hewlett\-Packard Labs +.br +Palo\-Alto, CA 94304 +.br +Email: \fBdavidm@hpl.hp.com\fP +.br +WWW: \fBhttp://www.hpl.hp.com/research/linux/libunwind/\fP\&. +.\" NOTE: This file is generated, DO NOT EDIT. diff --git a/doc/libunwind-setjmp.tex b/doc/libunwind-setjmp.tex index e69de29b..efaf9570 100644 --- a/doc/libunwind-setjmp.tex +++ b/doc/libunwind-setjmp.tex @@ -0,0 +1,90 @@ +\documentclass{article} +\usepackage[fancyhdr,pdf]{latex2man} + +\input{common.tex} + +\begin{document} + +\begin{Name}{3}{libunwind-setjmp}{David Mosberger-Tang}{Programming Library}{libunwind-based non-local gotos} + libunwind-setjmp -- libunwind-based non-local gotos +\end{Name} + +\section{Synopsis} + +\File{\#include $<$setjmp.h$>$}\\ + +\noindent +\Type{int} \Func{setjmp}(\Type{jmp\_buf}~\Var{env});\\ +\Type{void} \Func{longjmp}(\Type{jmp\_buf}~\Var{env}, \Type{int}~\Var{val});\\ +\Type{int} \Func{\_setjmp}(\Type{jmp\_buf}~\Var{env});\\ +\Type{void} \Func{\_longjmp}(\Type{jmp\_buf}~\Var{env}, \Type{int}~\Var{val});\\ +\Type{int} \Func{setjmp}(\Type{sigjmp\_buf}~\Var{env}, \Type{int}~\Var{savemask});\\ +\Type{void} \Func{siglongjmp}(\Type{sigjmp\_buf}~\Var{env}, \Type{int}~\Var{val});\\ + +\section{Description} + +The \Prog{unwind-setjmp} library offers a \Prog{libunwind}-based +implementation of non-local gotos. This implementation is intended to +be a drop-in replacement for the normal, system-provided routines of +the same name. The main advantage of using the \Prog{unwind-setjmp} +library is that setting up a non-local goto via one of the +\Func{setjmp}() routines is very fast. Typically, just 2 or 3 words +need to be saved in the jump-buffer (plus one call to +\Func{sigprocmask}(2), in the case of \Func{sigsetjmp}). On the +other hand, executing a non-local goto by calling one of the +\Func{longjmp}() routines tends to be much slower than with the +system-provided routines. In fact, the time spent on a +\Func{longjmp}() will be proportional to the number of call frames +that exist between the points where \Func{setjmp}() and +\Func{longjmp}() were called. For this reason, the +\Prog{unwind-setjmp} library is beneficial primarily in applications +that frequently call \Func{setjmp}() but only rarely call +\Func{longjmp}(). + +\section{Caveats} + +\begin{itemize} +\item The correct operation of this library depends on the presence of + correct unwind information. On newer platforms, this is rarely an + issue. On older platforms, care needs to be taken to + ensure that each of the functions whose stack frames may have to be + unwound during a \Func{longjmp}() have correct unwind information + (on those platforms, there is usually a compiler-switch, such as + \Opt{-funwind-tables}, to request the generation of unwind + information). +\item The contents of \Type{jmp\_buf} and \Type{sigjmp\_buf} as setup + and used by these routines is completely different from the ones + used by the system-provided routines. Thus, a jump-buffer created + by the libunwind-based \Func{setjmp}()/\Func{\_setjmp} may only be + used in a call to the libunwind-based + \Func{longjmp}()/\Func{\_longjmp}(). The analogous applies for + \Type{sigjmp\_buf} with \Func{sigsetjmp}() and \Func{siglongjmp}(). +\end{itemize} + +\section{Files} + +\begin{Description} +\item[\Opt{-l}\File{unwind-setjmp}] The library an application should + be linked against to ensure it uses the libunwind-based non-local + goto routines. +\end{Description} + + +\section{See Also} + +\SeeAlso{libunwind(3)}, +setjmp(3), longjmp(3), +\_setjmp(3), \_longjmp(3), +sigsetjmp(3), siglongjmp(3) + +\section{Author} + +\noindent +David Mosberger-Tang\\ +Hewlett-Packard Labs\\ +Palo-Alto, CA 94304\\ +Email: \Email{davidm@hpl.hp.com}\\ +WWW: \URL{http://www.hpl.hp.com/research/linux/libunwind/}. +\LatexManEnd + +\end{document} diff --git a/doc/unw_resume.man b/doc/unw_resume.man index e69de29b..4adf8da7 100644 --- a/doc/unw_resume.man +++ b/doc/unw_resume.man @@ -0,0 +1,107 @@ +'\" t +.\" Manual page created with latex2man on Sat Feb 8 01:21:05 PST 2003 +.\" NOTE: This file is generated, DO NOT EDIT. +.de Vb +.ft CW +.nf +.. +.de Ve +.ft R + +.fi +.. +.TH "UNW\\_RESUME" "3" "08 February 2003" "Programming Library " "Programming Library " +.SH NAME + +.PP +unw_resume \-\- resume execution in a particular stack frame +.PP +.SH SYNOPSIS + +.PP +#include +.br +.PP +int +unw_resume(unw_cursor_t *cursor); +.br +.PP +.SH DESCRIPTION + +.PP +The unw_resume() +routine resumes execution at the stack frame +identified by cursor\&. +Normally, this is accomplished by +restoring the ``preserved\&'' (callee\-saved) machine state. However, if +execution in any of the stack frames younger (more deeply nested) than +the one identified by cursor +was interrupted by a signal, then +unw_resume() +will restore the entire machine state, including +the ``preserved\&'' and ``scratch\&'' (caller\-saved) registers, as well as +the signal mask. +.PP +Most platforms reserve some registers to pass arguments to exception +handlers (e.g., IA\-64 uses r15\-r18 +for this +purpose). These registers are normally treated like ``scratch\&'' +registers. However, if libunwind +is used to define an +exception argument register, e.g., by calling unw_set_reg(), +then unw_resume() +will always install the new value as the +contents of that register. In other words, the exception handling +arguments are installed even in cases where normally only the +``preserved\&'' registers are restored. +.PP +.SH RETURN VALUE + +.PP +For local unwinding, unw_resume() +does not return on success. +For remote unwinding, it returns 0 on success. On failure, the +negative value of one of the errors below is returned. +.PP +.SH ERRORS + +.PP +.TP +UNW_EUNSPEC + An unspecified error occurred. +.TP +UNW_EBADREG + A register needed by unw_resume() +wasn\&'t +accessible. +.TP +UNW_EINVALIDIP + The instruction pointer identified by +cursor +is not valid. +.TP +UNW_BADFRAME + The stack frame identified by +cursor +is not valid. +.PP +.SH SEE ALSO + +.PP +libunwind(3), +unw_set_reg(3), +sigprocmask(2) +.PP +.SH AUTHOR + +.PP +David Mosberger\-Tang +.br +Hewlett\-Packard Labs +.br +Palo\-Alto, CA 94304 +.br +Email: \fBdavidm@hpl.hp.com\fP +.br +WWW: \fBhttp://www.hpl.hp.com/research/linux/libunwind/\fP\&. +.\" NOTE: This file is generated, DO NOT EDIT. diff --git a/doc/unw_resume.tex b/doc/unw_resume.tex index e69de29b..f7f458a7 100644 --- a/doc/unw_resume.tex +++ b/doc/unw_resume.tex @@ -0,0 +1,74 @@ +\documentclass{article} +\usepackage[fancyhdr,pdf]{latex2man} + +\input{common.tex} + +\begin{document} + +\begin{Name}{3}{unw\_resume}{David Mosberger-Tang}{Programming Library}{unw\_resume} + + unw\_resume -- resume execution in a particular stack frame +\end{Name} + +\section{Synopsis} + +\File{\#include $<$libunwind.h$>$}\\ + +\Type{int} \Func{unw\_resume}(\Type{unw\_cursor\_t~*}\Var{cursor});\\ + +\section{Description} + +The \Func{unw\_resume}() routine resumes execution at the stack frame +identified by \Var{cursor}. Normally, this is accomplished by +restoring the ``preserved'' (callee-saved) machine state. However, if +execution in any of the stack frames younger (more deeply nested) than +the one identified by \Var{cursor} was interrupted by a signal, then +\Func{unw\_resume}() will restore the entire machine state, including +the ``preserved'' and ``scratch'' (caller-saved) registers, as well as +the signal mask. + +Most platforms reserve some registers to pass arguments to exception +handlers (e.g., IA-64 uses \texttt{r15}-\texttt{r18} for this +purpose). These registers are normally treated like ``scratch'' +registers. However, if \Prog{libunwind} is used to define an +exception argument register, e.g., by calling \Func{unw\_set\_reg}(), +then \Func{unw\_resume}() will always install the new value as the +contents of that register. In other words, the exception handling +arguments are installed even in cases where normally only the +``preserved'' registers are restored. + +\section{Return Value} + +For local unwinding, \Func{unw\_resume}() does not return on success. +For remote unwinding, it returns 0 on success. On failure, the +negative value of one of the errors below is returned. + +\section{Errors} + +\begin{Description} +\item[\Const{UNW\_EUNSPEC}] An unspecified error occurred. +\item[\Const{UNW\_EBADREG}] A register needed by \Func{unw\_resume}() wasn't + accessible. +\item[\Const{UNW\_EINVALIDIP}] The instruction pointer identified by + \Var{cursor} is not valid. +\item[\Const{UNW\_BADFRAME}] The stack frame identified by + \Var{cursor} is not valid. +\end{Description} + +\section{See Also} + +\SeeAlso{libunwind(3)}, +\SeeAlso{unw\_set\_reg(3)}, +sigprocmask(2) + +\section{Author} + +\noindent +David Mosberger-Tang\\ +Hewlett-Packard Labs\\ +Palo-Alto, CA 94304\\ +Email: \Email{davidm@hpl.hp.com}\\ +WWW: \URL{http://www.hpl.hp.com/research/linux/libunwind/}. +\LatexManEnd + +\end{document} diff --git a/src/elf32.c b/src/elf32.c index e69de29b..f1c21e6d 100644 --- a/src/elf32.c +++ b/src/elf32.c @@ -0,0 +1,2 @@ +#include "elf32.h" +#include "elfxx.c" diff --git a/src/elf32.h b/src/elf32.h index e69de29b..d27646cc 100644 --- a/src/elf32.h +++ b/src/elf32.h @@ -0,0 +1,7 @@ +#ifndef elf32_h +#define elf32_h + +#define ELF_CLASS ELFCLASS32 +#include "elfxx.h" + +#endif /* elf32_h */ diff --git a/src/elf64.c b/src/elf64.c index e69de29b..1154179b 100644 --- a/src/elf64.c +++ b/src/elf64.c @@ -0,0 +1,2 @@ +#include "elf64.h" +#include "elfxx.c" diff --git a/src/elf64.h b/src/elf64.h index e69de29b..8f22384a 100644 --- a/src/elf64.h +++ b/src/elf64.h @@ -0,0 +1,7 @@ +#ifndef elf64_h +#define elf64_h + +#define ELF_CLASS ELFCLASS64 +#include "elfxx.h" + +#endif /* elf64_h */ diff --git a/src/elfxx.c b/src/elfxx.c index e69de29b..68985b5f 100644 --- a/src/elfxx.c +++ b/src/elfxx.c @@ -0,0 +1,168 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2003 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#include +#include +#include + +#include + + +extern HIDDEN int +elfW (valid_object) (struct elf_image *ei) +{ + if (ei->size <= EI_CLASS) + return 0; + + return (memcmp (ei->image, ELFMAG, SELFMAG) == 0 + && ((uint8_t *) ei->image)[EI_CLASS] == ELF_CLASS); +} + + +static int +elfW (lookup_symbol) (unw_word_t ip, struct elf_image *ei, + ElfW (Addr) load_offset, + char *buf, size_t buf_len, unw_word_t *offp) +{ + size_t syment_size, str_size; + ElfW (Ehdr) *ehdr = ei->image; + ElfW (Sym) *sym, *symtab, *symtab_end; + ElfW (Off) soff, str_soff; + ElfW (Shdr) *shdr, *str_shdr; + ElfW (Addr) val, min_dist = ~(ElfW (Addr))0; + char *strtab; + int i; + + if (!elfW (valid_object) (ei)) + return -1; + + soff = ehdr->e_shoff; + if (soff + ehdr->e_shnum * ehdr->e_shentsize > ei->size) + { + debug (1, "%s: section table outside of image? (%lu > %lu)\n", + __FUNCTION__, soff + ehdr->e_shnum * ehdr->e_shentsize, + ei->size); + return -1; + } + + shdr = (ElfW (Shdr) *) ((char *) ei->image + soff); + + for (i = 0; i < ehdr->e_shnum; ++i) + { + switch (shdr->sh_type) + { + case SHT_SYMTAB: + case SHT_DYNSYM: + symtab = (ElfW (Sym) *) ((char *) ei->image + shdr->sh_offset); + symtab_end = (ElfW (Sym) *) ((char *) symtab + shdr->sh_size); + syment_size = shdr->sh_entsize; + + str_soff = soff + (shdr->sh_link * ehdr->e_shentsize); + if (str_soff + ehdr->e_shentsize >= ei->size) + { + debug (1, "%s: string table outside of image? (%lu >= %lu)\n", + __FUNCTION__, str_soff + ehdr->e_shentsize, ei->size); + break; + } + str_shdr = (ElfW (Shdr) *) ((char *) ei->image + str_soff); + str_size = str_shdr->sh_size; + strtab = (char *) ei->image + str_shdr->sh_offset; + + debug (10, "symtab=0x%lx[%d], strtab=0x%lx\n", shdr->sh_offset, + shdr->sh_type, str_shdr->sh_offset); + + for (sym = symtab; + sym < symtab_end; + sym = (ElfW (Sym) *) ((char *) sym + syment_size)) + { + if (ELFW (ST_TYPE) (sym->st_info) == STT_FUNC + && sym->st_shndx != SHN_UNDEF) + { + val = sym->st_value; + if (sym->st_shndx != SHN_ABS) + val += load_offset; + debug (100, "0x%016lx info=0x%02x %s\n", + val, sym->st_info, strtab + sym->st_name); + + if ((ElfW (Addr)) (ip - val) < min_dist) + { + min_dist = (ElfW (Addr)) (ip - val); + buf[buf_len - 1] = 'x'; + strncpy (buf, strtab + sym->st_name, buf_len); + buf[buf_len - 1] = '\0'; + } + } + } + break; + + default: + break; + } + shdr = (Elf64_Shdr *) (((char *) shdr) + ehdr->e_shentsize); + } + if (min_dist >= ei->size) + return -1; /* not found */ + if (offp) + *offp = min_dist; + return 0; +} + +/* Find the ELF image that contains IP and return the "closest" + procedure name, if there is one. With some caching, this could be + sped up greatly, but until an application materializes that's + sensitive to the performance of this routine, why bother... */ + +HIDDEN int +elfW (get_proc_name) (unw_word_t ip, char *buf, size_t buf_len, + unw_word_t *offp) +{ + unsigned long segbase, mapoff; + ElfW (Addr) load_offset = 0; + struct elf_image ei; + ElfW (Ehdr) *ehdr; + ElfW (Phdr) *phdr; + int i, ret; + + ret = tdep_get_elf_image (&ei, getpid (), ip, &segbase, &mapoff); + if (ret < 0) + return ret; + + ehdr = ei.image; + phdr = (Elf64_Phdr *) ((char *) ei.image + ehdr->e_phoff); + + for (i = 0; i < ehdr->e_phnum; ++i) + if (phdr[i].p_type == PT_LOAD && phdr[i].p_offset == mapoff) + { + load_offset = segbase - phdr[i].p_vaddr; + break; + } + + ret = elfW (lookup_symbol) (ip, &ei, load_offset, buf, buf_len, offp); + + munmap (ei.image, ei.size); + ei.image = NULL; + + return ret; +} diff --git a/src/elfxx.h b/src/elfxx.h index e69de29b..b92253bb 100644 --- a/src/elfxx.h +++ b/src/elfxx.h @@ -0,0 +1,71 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2003 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#include +#include +#include + +#include + +#include "internal.h" + +#if ELF_CLASS == ELFCLASS32 +# define ELFW(x) ELF32_##x +# define ElfW(x) Elf32_##x +# define elfW(x) _Uelf32_##x +#else +# define ELFW(x) ELF64_##x +# define ElfW(x) Elf64_##x +# define elfW(x) _Uelf64_##x +#endif + +static inline int +elf_map_image (struct elf_image *ei, char *path) +{ + struct stat stat; + int fd; + + fd = open (path, O_RDONLY); + if (fd < 0) + return -1; + + if (fstat (fd, &stat) < 0) + { + close (fd); + return -1; + } + + ei->size = stat.st_size; + ei->image = mmap (NULL, ei->size, PROT_READ, MAP_PRIVATE, fd, 0); + close (fd); + if (ei->image == MAP_FAILED) + return -1; + + return 0; +} + +extern HIDDEN int elfW (valid_object) (struct elf_image *ei); +extern HIDDEN int elfW (get_proc_name) (unw_word_t ip, char *buf, size_t len, + unw_word_t *offp); diff --git a/src/ia64/mk_cursor_i-ia64.c b/src/ia64/mk_cursor_i-ia64.c index e69de29b..aa70a270 100644 --- a/src/ia64/mk_cursor_i-ia64.c +++ b/src/ia64/mk_cursor_i-ia64.c @@ -0,0 +1,104 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2003 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +/* Utility to generate cursor_i.h. */ + +#include +#include + +#include "internal.h" + +#ifdef offsetof +# undef offsetof +#endif + +#define offsetof(type,field) ((char *) &((type *) 0)->field - (char *) 0) + +struct + { + const char name[256]; + unsigned long value; + } +tab[] = + { + { "IP_OFF", offsetof (struct cursor, ip) }, + { "PR_OFF", offsetof (struct cursor, pr) }, + { "BSP_OFF", offsetof (struct cursor, bsp) }, + { "PSP_OFF", offsetof (struct cursor, psp) }, + { "PFS_LOC_OFF", offsetof (struct cursor, pfs_loc) }, + { "RNAT_LOC_OFF", offsetof (struct cursor, rnat_loc) }, + { "UNAT_LOC_OFF", offsetof (struct cursor, unat_loc) }, + { "LC_LOC_OFF", offsetof (struct cursor, lc_loc) }, + { "FPSR_LOC_OFF", offsetof (struct cursor, fpsr_loc) }, + { "B1_LOC_OFF", offsetof (struct cursor, b1_loc) }, + { "B2_LOC_OFF", offsetof (struct cursor, b2_loc) }, + { "B3_LOC_OFF", offsetof (struct cursor, b3_loc) }, + { "B4_LOC_OFF", offsetof (struct cursor, b4_loc) }, + { "B5_LOC_OFF", offsetof (struct cursor, b5_loc) }, + { "F2_LOC_OFF", offsetof (struct cursor, f2_loc) }, + { "F3_LOC_OFF", offsetof (struct cursor, f3_loc) }, + { "F4_LOC_OFF", offsetof (struct cursor, f4_loc) }, + { "F5_LOC_OFF", offsetof (struct cursor, f5_loc) }, + { "FR_LOC_OFF", offsetof (struct cursor, fr_loc) }, + { "SIGCONTEXT_LOC_OFF", offsetof (struct cursor, sigcontext_loc) }, +}; + +static const char *tabs = "\t\t\t\t\t\t\t\t\t\t"; + +int +main (int argc, char **argv) +{ + const char *space; + int i, num_tabs; + size_t len; + + printf ("#ifndef cursor_i_h\n"); + printf ("#define cursor_i_h\n\n"); + + printf ("/*\n * DO NOT MODIFY\n *\n * This file was generated by " + "print_offsets.\n *\n */\n\n"); + + for (i = 0; i < (int) (sizeof (tab) / sizeof (tab[0])); ++i) + { + if (tab[i].name[0] == '\0') + printf ("\n"); + else + { + len = strlen (tab[i].name); + + num_tabs = (40 - len) / 8; + if (num_tabs <= 0) + space = " "; + else + space = strchr(tabs, '\0') - (40 - len) / 8; + + printf ("#define %s%s%lu\t/* 0x%lx */\n", + tab[i].name, space, tab[i].value, tab[i].value); + } + } + + printf ("\n#endif /* cursor_i_h */\n"); + return 0; +} diff --git a/src/ia64/setjmp-ia64.S b/src/ia64/setjmp-ia64.S index e69de29b..52ecd53b 100644 --- a/src/ia64/setjmp-ia64.S +++ b/src/ia64/setjmp-ia64.S @@ -0,0 +1,45 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2003 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + + .align 32 + + .global _setjmp + + .proc _setjmp + +_setjmp: + mov r2 = ar.bsp + st8 [r32] = r12 // jmp_buf[0] = sp + mov r3 = rp + + adds r16 = 8, r32 + adds r17 = 16, r32 + mov r8 = 0 + ;; + st8 [r16] = r3 // jmp_buf[1] = rp + st8 [r17] = r2 // jmp_buf[2] = bsp + br.ret.sptk.many rp + + .endp _setjmp diff --git a/src/ia64/siglongjmp-ia64.S b/src/ia64/siglongjmp-ia64.S index e69de29b..3feccf24 100644 --- a/src/ia64/siglongjmp-ia64.S +++ b/src/ia64/siglongjmp-ia64.S @@ -0,0 +1,64 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2003 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#define SIG_SETMASK 2 + + .globl _UI_siglongjmp_cont + + .align 32 + .proc siglongjmp_continuation +siglongjmp_continuation: +_UI_siglongjmp_cont: // non-function label for {sig,}longjmp.c + .prologue + .save rp, r15 + .body + nop 0 + nop 0 + br.call.sptk.many b6 = 1f + ;; + .prologue + .save ar.pfs, r33 +1: alloc loc1 = ar.pfs, 0, 3, 3, 0 + /* + * Note: we can use the scratch stack are because the caller + * of sigsetjmp() by definition is not a leaf-procedure. + */ + st8 [sp] = r17 // store signal mask + .save rp, loc0 + mov loc0 = r15 // final continuation point + ;; + .body + mov loc2 = r16 // value to return in r8 + + mov out0 = SIG_SETMASK + mov out1 = sp + mov out2 = r0 + br.call.sptk.many rp = sigprocmask + ;; + mov rp = loc0 + mov ar.pfs = loc1 + mov r8 = loc2 + br.ret.sptk.many rp + .endp siglongjmp_continuation diff --git a/src/ia64/sigsetjmp-ia64.S b/src/ia64/sigsetjmp-ia64.S index e69de29b..271d29d9 100644 --- a/src/ia64/sigsetjmp-ia64.S +++ b/src/ia64/sigsetjmp-ia64.S @@ -0,0 +1,64 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2003 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#define SIG_BLOCK 0 + + .align 32 + + .global __sigsetjmp + + .proc __sigsetjmp + +__sigsetjmp: + .prologue + .save ar.pfs, r35 + alloc loc1 = ar.pfs, 2, 3, 3, 0 + add r16 = 16, in0 + add out2 = 24, in0 + + st8 [in0] = sp, 8 // sigjmp_buf[0] = sp + mov out0 = SIG_BLOCK + .save rp, loc0 + mov loc0 = rp + .body + ;; + st8 [in0] = loc0, 24 // sigjmp_buf[1] = rp + st8 [r16] = in1 // sigjmp_buf[2] = savemask + cmp.ne p6, p0 = in1, r0 + + mov out1 = r0 + mov loc2 = ar.bsp +(p6) br.call.sptk.many rp = sigprocmask // sigjmp_buf[3] = sigmask + ;; + + st8 [in0] = loc2 // sigjmp_buf[4] = bsp + mov rp = loc0 + nop 0 + + mov r8 = 0 + mov.i ar.pfs = loc1 + br.ret.sptk.many rp + + .endp __sigsetjmp diff --git a/src/longjmp.c b/src/longjmp.c index e69de29b..69bfeba1 100644 --- a/src/longjmp.c +++ b/src/longjmp.c @@ -0,0 +1,109 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2003 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#define UNW_LOCAL_ONLY + +#include +#include +#include +#include + +#if UNW_TARGET_IA64 +# include "ia64/rse.h" +#endif + +void +_longjmp (jmp_buf env, int val) +{ + extern int _UI_siglongjmp_cont; + sigset_t current_mask; + unw_context_t uc; + unw_cursor_t c; + unw_word_t sp; + unw_word_t *wp = (unw_word_t *) env; + + if (unw_getcontext (&uc) < 0 || unw_init_local (&c, &uc) < 0) + abort (); + + do + { + if (unw_get_reg (&c, UNW_REG_SP, &sp) < 0) + abort (); + if (sp != wp[0]) + continue; + +#if UNW_TARGET_IA64 + { + unw_word_t bsp, pfs, sol; + + if (unw_get_reg (&c, UNW_IA64_BSP, &bsp) < 0 + || unw_get_reg (&c, UNW_IA64_AR_PFS, &pfs) < 0) + abort (); + + /* simulate the effect of "br.call setjmp" on ar.bsp: */ + sol = (pfs >> 7) & 0x7f; + bsp = ia64_rse_skip_regs (bsp, sol); + + if (bsp != wp[2]) + continue; + } +#endif + + /* found the right frame: */ + + if (sigprocmask (SIG_BLOCK, NULL, ¤t_mask) < 0) + abort (); + + if (unw_set_reg (&c, UNW_REG_EH_ARG0, wp[1]) < 0 + || unw_set_reg (&c, UNW_REG_EH_ARG1, val) < 0 + || unw_set_reg (&c, UNW_REG_EH_ARG2, + ((unw_word_t *) ¤t_mask)[0]) < 0 + || unw_set_reg (&c, UNW_REG_IP, + (unw_word_t) &_UI_siglongjmp_cont)) + abort (); + + if (_NSIG > 8 * sizeof (unw_word_t)) + { + if (_NSIG > 16 * sizeof (unw_word_t)) + abort (); + if (unw_set_reg (&c, UNW_REG_EH_ARG3, + ((unw_word_t *) ¤t_mask)[1]) < 0) + abort (); + } + + unw_resume (&c); + + abort (); + } + while (unw_step (&c) >= 0); + + abort (); +} + +void +longjmp (jmp_buf env, int val) +{ + _longjmp (env, val); +} diff --git a/src/os-linux.c b/src/os-linux.c index e69de29b..b4b8d713 100644 --- a/src/os-linux.c +++ b/src/os-linux.c @@ -0,0 +1,63 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2003 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#ifndef UNW_REMOTE_ONLY + +#include +#include + +#include "internal.h" +#include "os-linux.h" + +/* Here, it doesn't matter whether we include elf64.h or elf32.h. + Both define "struct elf_image" and elf_map_image() in an identical + fashion. */ +#include "elf64.h" + +HIDDEN int +tdep_get_elf_image (struct elf_image *ei, pid_t pid, unw_word_t ip, + unsigned long *segbase, unsigned long *mapoff) +{ + struct map_iterator mi; + char path[PATH_MAX]; + int found = 0; + unsigned long hi; + + maps_init (&mi, pid); + while (maps_next (&mi, segbase, &hi, mapoff, path)) + if (ip >= *segbase && ip < hi) + { + found = 1; + break; + } + maps_close (&mi); + + if (!found) + return -1; + + return elf_map_image (ei, path); +} + +#endif /* UNW_REMOTE_ONLY */ diff --git a/src/os-linux.h b/src/os-linux.h index e69de29b..88ba4ac2 100644 --- a/src/os-linux.h +++ b/src/os-linux.h @@ -0,0 +1,69 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2003 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#ifndef os_linux_h +#define os_linux_h + +struct map_iterator + { + FILE *fp; + }; + +static inline void +maps_init (struct map_iterator *mi, pid_t pid) +{ + char path[PATH_MAX]; + + snprintf (path, sizeof (path), "/proc/%d/maps", pid); + mi->fp = fopen (path, "r"); +} + +static inline int +maps_next (struct map_iterator *mi, + unsigned long *low, unsigned long *high, unsigned long *offset, + char *path) +{ + char line[256+PATH_MAX]; + + if (!mi->fp) + return 0; + + while (fgets (line, sizeof (line), mi->fp)) + { + if (sscanf (line, "%lx-%lx %*4c %lx %*x:%*x %*d %s\n", + low, high, offset, path) == 4) + return 1; + } + return 0; +} + +static inline void +maps_close (struct map_iterator *mi) +{ + fclose (mi->fp); + mi->fp = NULL; +} + +#endif /* os_linux_h */ diff --git a/src/setjmp.c b/src/setjmp.c index e69de29b..8901071b 100644 --- a/src/setjmp.c +++ b/src/setjmp.c @@ -0,0 +1,52 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2003 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#include +#include + +/* Why use K&R syntax here? setjmp() is often a macro and that + expands into a call to, say, __setjmp() and we need to define the + libunwind-version of setjmp() with the name of the actual function. + Using K&R syntax lets us keep the setjmp() macro while keeping the + syntax valid... This trick works provided setjmp() doesn't do + anything other than a function call. */ + +int +setjmp (env) + jmp_buf env; +{ + void **wp = (void **) env; + +#if UNW_TARGET_IA64 + wp[0] = __builtin_dwarf_cfa () - 16; + wp[1] = __builtin_ia64_bsp (); +#else + /* this should work on most platforms, but may not be + performance-optimal; check the code! */ + wp[0] = __builtin_frame_address (0); + wp[1] = (void *) (uintptr_t) 0; +#endif + return 0; +} diff --git a/src/siglongjmp.c b/src/siglongjmp.c index e69de29b..97624108 100644 --- a/src/siglongjmp.c +++ b/src/siglongjmp.c @@ -0,0 +1,108 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2003 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#define UNW_LOCAL_ONLY + +#include +#include +#include +#include + +#if UNW_TARGET_IA64 +# include "ia64/rse.h" +#endif + +void +siglongjmp (sigjmp_buf env, int val) +{ + unw_word_t *mp, *wp = (unw_word_t *) env; + extern int _UI_siglongjmp_cont; + sigset_t current_mask; + unw_context_t uc; + unw_cursor_t c; + unw_word_t sp; + + if (unw_getcontext (&uc) < 0 || unw_init_local (&c, &uc) < 0) + abort (); + + do + { + if (unw_get_reg (&c, UNW_REG_SP, &sp) < 0) + abort (); + if (sp != wp[0]) + continue; + +#if UNW_TARGET_IA64 + { + unw_word_t bsp, pfs, sol; + + if (unw_get_reg (&c, UNW_IA64_BSP, &bsp) < 0 + || unw_get_reg (&c, UNW_IA64_AR_PFS, &pfs) < 0) + abort (); + + /* simulate the effect of "br.call sigsetjmp" on ar.bsp: */ + sol = (pfs >> 7) & 0x7f; + bsp = ia64_rse_skip_regs (bsp, sol); + + if (bsp != wp[4]) + continue; + } +#endif + + /* found the right frame: */ + + if (wp[2]) + mp = wp + 3; + else + { + /* sigmask wasn't saved; get it now so we can leave it unchanged. */ + if (sigprocmask (SIG_BLOCK, NULL, ¤t_mask) < 0) + abort (); + mp = (unw_word_t *) ¤t_mask; + } + + if (unw_set_reg (&c, UNW_REG_EH_ARG0, wp[1]) < 0 + || unw_set_reg (&c, UNW_REG_EH_ARG1, val) < 0 + || unw_set_reg (&c, UNW_REG_EH_ARG2, mp[0]) < 0 + || unw_set_reg (&c, UNW_REG_IP, + (unw_word_t) &_UI_siglongjmp_cont)) + abort (); + + if (_NSIG > 8 * sizeof (unw_word_t)) + { + if (_NSIG > 16 * sizeof (unw_word_t)) + abort (); + if (unw_set_reg (&c, UNW_REG_EH_ARG3, mp[1]) < 0) + abort (); + } + + unw_resume (&c); + + abort (); + } + while (unw_step (&c) >= 0); + + abort (); +} diff --git a/src/sigsetjmp.c b/src/sigsetjmp.c index e69de29b..73782879 100644 --- a/src/sigsetjmp.c +++ b/src/sigsetjmp.c @@ -0,0 +1,47 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2003 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#include +#include +#include + +int +sigsetjmp (sigjmp_buf env, int savemask) +{ + unw_word_t *wp = (unw_word_t *) env; + + /* This should work on most platforms, but may not be + performance-optimal; check the code! */ + + wp[0] = (unw_word_t) __builtin_frame_address (0); + wp[1] = (unw_word_t) __builtin_return_address (0); + wp[2] = savemask; + + /* Note: we assume here that "wp" has same or better alignment as + sigset_t. */ + if (savemask && sigprocmask (SIG_BLOCK, NULL, (sigset_t *) (wp + 3)) < 0) + abort (); + return 0; +} diff --git a/tests/test-setjmp.c b/tests/test-setjmp.c index e69de29b..7a3ddb46 100644 --- a/tests/test-setjmp.c +++ b/tests/test-setjmp.c @@ -0,0 +1,278 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2003 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +/* The setjmp()/longjmp(), sigsetjmp()/siglongjmp(). */ + +#include +#include +#include +#include +#include +#include + +int nerrors; +int verbose; + +static jmp_buf jbuf; +static sigjmp_buf sigjbuf; +static sigset_t sigset4; + +void +raise_longjmp (jmp_buf jbuf, int i, int n) +{ + while (i < n) + raise_longjmp (jbuf, i + 1, n); + + longjmp (jbuf, n); +} + +void +test_setjmp (void) +{ + volatile int i; + jmp_buf jbuf; + int ret; + + for (i = 0; i < 10; ++i) + { + if ((ret = setjmp (jbuf))) + { + if (verbose) + printf ("%s: secondary setjmp () return, ret=%d\n", + __FUNCTION__, ret); + if (ret != i + 1) + { + fprintf (stderr, "%s: setjmp() returned %d, expected %d\n", + __FUNCTION__, ret, i + 1); + ++nerrors; + } + continue; + } + if (verbose) + printf ("%s.%d: done with setjmp(); calling children\n", + __FUNCTION__, i + 1); + + raise_longjmp (jbuf, 0, i + 1); + + fprintf (stderr, "%s: raise_longjmp() returned unexpected\n", + __FUNCTION__); + ++nerrors; + } +} + + +void +raise_siglongjmp (sigjmp_buf jbuf, int i, int n) +{ + while (i < n) + raise_siglongjmp (jbuf, i + 1, n); + + siglongjmp (jbuf, n); +} + +void +test_sigsetjmp (void) +{ + sigjmp_buf jbuf; + int i, ret; + + for (i = 0; i < 10; ++i) + { + if ((ret = sigsetjmp (jbuf, 1))) + { + if (verbose) + printf ("%s: secondary sigsetjmp () return, ret=%d\n", + __FUNCTION__, ret); + if (ret != i + 1) + { + fprintf (stderr, "%s: sigsetjmp() returned %d, expected %d\n", + __FUNCTION__, ret, i + 1); + ++nerrors; + } + continue; + } + if (verbose) + printf ("%s.%d: done with sigsetjmp(); calling children\n", + __FUNCTION__, i + 1); + + raise_siglongjmp (jbuf, 0, i + 1); + + fprintf (stderr, "%s: raise_siglongjmp() returned unexpected\n", + __FUNCTION__); + ++nerrors; + } +} + +void +sighandler (int signal) +{ + if (verbose) + printf ("%s: got signal %d\n", __FUNCTION__, signal); + + sigprocmask (SIG_BLOCK, NULL, (sigset_t *) &sigset4); + siglongjmp (sigjbuf, 1); +} + +int +main (int argc, char **argv) +{ + volatile sigset_t sigset1, sigset2, sigset3; + volatile struct sigaction act; + + if (argc > 1) + verbose = 1; + + sigemptyset ((sigset_t *) &sigset1); + sigaddset ((sigset_t *) &sigset1, SIGUSR1); + sigemptyset ((sigset_t *) &sigset2); + sigaddset ((sigset_t *) &sigset2, SIGUSR2); + + memset ((void *) &act, 0, sizeof (act)); + act.sa_handler = sighandler; + sigaction (SIGTERM, (struct sigaction *) &act, NULL); + + test_setjmp (); + test_sigsetjmp (); + + /* _setjmp() MUST NOT change signal mask: */ + sigprocmask (SIG_SETMASK, (sigset_t *) &sigset1, NULL); + if (_setjmp (jbuf)) + { + sigemptyset ((sigset_t *) &sigset3); + sigprocmask (SIG_BLOCK, NULL, (sigset_t *) &sigset3); + if (memcmp ((sigset_t *) &sigset3, (sigset_t *) &sigset2, + sizeof (sigset_t)) != 0) + { + fprintf (stderr, "FAILURE: _longjmp() manipulated signal mask!\n"); + ++nerrors; + } + else if (verbose) + printf ("OK: _longjmp() seems not to change signal mask\n"); + } + else + { + sigprocmask (SIG_SETMASK, (sigset_t *) &sigset2, NULL); + _longjmp (jbuf, 1); + } + + /* sigsetjmp(jbuf, 1) MUST preserve signal mask: */ + sigprocmask (SIG_SETMASK, (sigset_t *) &sigset1, NULL); + if (sigsetjmp (sigjbuf, 1)) + { + sigemptyset ((sigset_t *) &sigset3); + sigprocmask (SIG_BLOCK, NULL, (sigset_t *) &sigset3); + if (memcmp ((sigset_t *) &sigset3, (sigset_t *) &sigset1, + sizeof (sigset_t)) != 0) + { + fprintf (stderr, + "FAILURE: siglongjmp() didn't restore signal mask!\n"); + ++nerrors; + } + else if (verbose) + printf ("OK: siglongjmp() restores signal mask when asked to\n"); + } + else + { + sigprocmask (SIG_SETMASK, (sigset_t *) &sigset2, NULL); + siglongjmp (sigjbuf, 1); + } + + /* sigsetjmp(jbuf, 0) MUST NOT preserve signal mask: */ + sigprocmask (SIG_SETMASK, (sigset_t *) &sigset1, NULL); + if (sigsetjmp (sigjbuf, 0)) + { + sigemptyset ((sigset_t *) &sigset3); + sigprocmask (SIG_BLOCK, NULL, (sigset_t *) &sigset3); + if (memcmp ((sigset_t *) &sigset3, (sigset_t *) &sigset2, + sizeof (sigset_t)) != 0) + { + fprintf (stderr, + "FAILURE: siglongjmp() changed signal mask!\n"); + ++nerrors; + } + else if (verbose) + printf ("OK: siglongjmp() leaves signal mask along when asked to\n"); + } + else + { + sigprocmask (SIG_SETMASK, (sigset_t *) &sigset2, NULL); + siglongjmp (sigjbuf, 1); + } + + /* sigsetjmp(jbuf, 1) MUST preserve signal mask: */ + sigprocmask (SIG_SETMASK, (sigset_t *) &sigset1, NULL); + if (sigsetjmp (sigjbuf, 1)) + { + sigemptyset ((sigset_t *) &sigset3); + sigprocmask (SIG_BLOCK, NULL, (sigset_t *) &sigset3); + if (memcmp ((sigset_t *) &sigset3, (sigset_t *) &sigset1, + sizeof (sigset_t)) != 0) + { + fprintf (stderr, + "FAILURE: siglongjmp() didn't restore signal mask!\n"); + ++nerrors; + } + else if (verbose) + printf ("OK: siglongjmp() restores signal mask when asked to\n"); + } + else + { + sigprocmask (SIG_SETMASK, (sigset_t *) &sigset2, NULL); + kill (getpid (), SIGTERM); + fprintf (stderr, "FAILURE: unexpected return from kill()\n"); + ++nerrors; + } + + /* sigsetjmp(jbuf, 0) MUST NOT preserve signal mask: */ + sigprocmask (SIG_SETMASK, (sigset_t *) &sigset1, NULL); + if (sigsetjmp (sigjbuf, 0)) + { + sigemptyset ((sigset_t *) &sigset3); + sigprocmask (SIG_BLOCK, NULL, (sigset_t *) &sigset3); + if (memcmp ((sigset_t *) &sigset3, (sigset_t *) &sigset4, + sizeof (sigset_t)) != 0) + { + fprintf (stderr, + "FAILURE: siglongjmp() changed signal mask!\n"); + ++nerrors; + } + else if (verbose) + printf ("OK: siglongjmp() leaves signal mask along when asked to\n"); + } + else + { + sigprocmask (SIG_SETMASK, (sigset_t *) &sigset2, NULL); + kill (getpid (), SIGTERM); + fprintf (stderr, "FAILURE: unexpected return from kill()\n"); + ++nerrors; + } + + if (nerrors > 0) + { + fprintf (stderr, "FAILURE: detected %d failures\n", nerrors); + exit (-1); + } + if (verbose) + printf ("SUCCESS\n"); + return 0; +}