Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Custom setjmp() and Unix signal handlers

80 views
Skip to first unread message

Marven Lee

unread,
Feb 10, 2012, 2:08:03 PM2/10/12
to
As I was skimming over the thread on exception propagation
in C it dawned on me that I have never used setjmp() & longjmp().

So does setjmp() merely store the register state in the jmp_buf
or is there some compiler magic going on? I'd like to know
if it's possible to write a custom version of setjmp()/longjmp().

What I had in mind was a version of setjmp() that could be
used to catch Unix signals, specifically SIGSEGV signals
on page faults. One of the uses I was thinking of was for
mapping messages from another process into the address
space and being able to have a try/catch like block around
the code that accessed this mapped area. It could be
that a section of a message doesn't have any mapped
pages for example, or they are marked read only.

It could be used something like this:

if ( 0 == SetJmpOnSignal (&sigsegv_jmp_buf))
{
// Copy or read parts of message here
}
else
{
// SIGSEGV exception caught, do some cleanup
}
ClearJmpOnSignal (&sigsegv_jmp_buf);


The Unix-like signal handler would look like:

void SigsegvHandler (int signo, siginfo_t *info, void *context)
{
// Copy register context stored in sigsegv_jmp_buf into the
// context stored by the signal handler wrapper. Set the
// value of context->eax to a non-zero value.
// This context is then passed to the system call
// __SigReturn(&context);
}

Returning from the signal handler acts like longjmp(). The
wrapper around the signal handler calls the system call
__SigReturn(&context). This clears the signal mask and loads
the registers with the values stored in the context before
returning, effectively doing the longjmp().

Another use I was thinking of was to emulate the kernel's
copyin / copy_from_user type functions in user-mode.
I was thinking of having 2 protection rings in user-mode
using address space tricks with 2 page directories and something
like the 4gb/4gb trampoline. The more privileged ring would
need some way of safely copying data from the less privileged
ring in a similar way to copyin/copyout/copyinstr. In the kernel
these functions internally have a try/catch mechanism when
a page fault can't be resolved.

So is it possible to do a custom setjmp/longjmp or does the
compiler use some internal trickery that I am not aware of,
perhaps due to optimizing variables on the stack ?


--
Marv

Marven Lee

unread,
Feb 10, 2012, 2:15:16 PM2/10/12
to
Or to put it more bluntly, is there a way to do a longjmp()
from a Unix signal handler. I assumed it would have
to make a system call to __SigReturn() as that is needed
to cleanup signal state inside the kernel prior to returning
from the signal handler.

--
Marv

CN

unread,
Feb 10, 2012, 2:48:41 PM2/10/12
to
It is possible, and is explicitly defined as possible. You need to take
care of the signal mask, though. When you are in a signal handler, the
current signal (which caused the handler invocation) is masked. There is
a special version of setjmp/longjmp -- sigsetjmp/siglongjmp -- which
saves and restores the signal mask.

Marven Lee

unread,
Feb 10, 2012, 4:30:58 PM2/10/12
to
I see. I didn't realise there was a sigsetjmp and siglongjmp which is just
what I wanted. I'm assuming the setjmp.h header is a part of GCC install
and that it won't have the sigsetjmp versions within, so I'd have to write
my own sigsetjmp based on it. I'll have a look through the sources.

Thanks.

--
Marv

van...@vsta.org

unread,
Feb 10, 2012, 4:32:43 PM2/10/12
to
Marven Lee <marv...@gmail.com> wrote:
> So does setjmp() merely store the register state in the jmp_buf
> or is there some compiler magic going on?

There's more to it. If you look at the PDP-11 code for setjmp/longjmp in V7
Unix (reading the Old Masters is one of the best ways to appreciate the
underlying principles), you'll see that the code has to explicitly concern
itself with the stack frame and register save state. This is because the
call frame of the setjmp() is long gone (it returned with a value of 0 when
the original setjmp() returned) and thus just restoring the old register
values will not necessarily get you back to a compatible C run-time state.

The details vary from compiler to compiler, but in general you have to know
what the compiler made the environment look like on *return* from the setjmp,
so that you can make your longjmp state match it.

--
Andy Valencia
Home page: http://www.vsta.org/andy/
To contact me: http://www.vsta.org/contact/andy.html

Rod Pemberton

unread,
Feb 10, 2012, 6:38:49 PM2/10/12
to
"Marven Lee" <marv...@gmail.com> wrote in message
news:9pl89c...@mid.individual.net...
> As I was skimming over the thread on exception propagation
> in C it dawned on me that I have never used setjmp() & longjmp().

I haven't used them in a long time. I haven't used them for anything
serious. AIR, they can be awkard to use correctly. Basically, using
longjmp() "resets" the program to a known location, i.e., by "exiting"
setjmp().

> So does setjmp() merely store the register state in the jmp_buf
> or is there some compiler magic going on?

They are both custom implementations for the current C compiler. From the
ones I've looked at *just* the registers needed to resume are saved into the
jump buffer. But, you also need to have the correct parameters on the stack
when you resume. IIRC, that's why you usually use them up a level or two of
nested procedures ... I.e., the local stack for the current procedure gets
cleared out.

> I'd like to know if it's possible to write a custom version
> of setjmp()/longjmp().

1) Does your C compiler support inline assembly?
2) Is this only for one C compiler?

If both are yes, I'd say: "Yes." If multiple C compilers, you'll need to
write one for each. If inline assembly support is a "No", then you
may have some work ahead.

> [ideas]

...

> So is it possible to do a custom setjmp/longjmp or does the
> compiler use some internal trickery that I am not aware of,
> perhaps due to optimizing variables on the stack ?
>

The ones I've seen *DO NOT* save stack state. It's up to the programmer
to use setjmp()/longjmp() in a safe manner.


Summary from Harbison & Steele, "C: A Reference Manual", 3rd, 1991:

a) setjmp() saves caller's environment in an implementation defined array
b) calling longjmp() causes it to return at setjmp()
c) longjmp() passes a status value to setjmp()
d) some implementations allow zero as a status
e) and some prohibit zero returning one instead
f) static variables are preserved
g) for ANSI C, automatic variables local to setjmp() only have their correct
values if i) they weren't modified ii) they are marked as volatile
h) ANSI C restricts the syntax use of setjmp() to four situations (I've not
posted them)
i) ANSI C requires longjmp() to work correctly within non-nested signal
handlers
j) the behavior is undefined if setjmp() wasn't called prior to longjmp()
k) the behavior is undefined if a longjmp() wasn't called prior to exiting
the function that contains setjmp()

The manpages in Posix environments and ISO C drafts should have info too.


Rod Pemberton




Marven Lee

unread,
Feb 13, 2012, 9:16:13 AM2/13/12
to

Rod Pemberton wrote:
> Marven Lee wrote:
>> I'd like to know if it's possible to write a custom version
>> of setjmp()/longjmp().
>
> 1) Does your C compiler support inline assembly?
> 2) Is this only for one C compiler?
>
> If both are yes, I'd say: "Yes." If multiple C compilers, you'll need to
> write one for each. If inline assembly support is a "No", then you
> may have some work ahead.

I thought that setjmp and longjmp would be part of libgcc and
be created along with a few machine dependent header files, but I
looked through Newlib and found it was implemented there. I had
forgotten that I once looked at Newlib's setjmp source as it
included macros to enable and disable interrupts during a longjmp.
This was the cause of a general protection exception I got in some
program I ported. There was a cflags switch that removed the
cli and sti instructions,
-D_I386MACH_ALLOW_HW_INTERRUPTS

Well it looks like sigsetjmp/siglongjmp is the solution. I had assumed
you couldn't use longjmp from signal handlers and that signals had
to always return through something like _SigReturn(context) which
clears the signal mask and reloads the context.

--
Marv
0 new messages