Question about setjmp on Itanium HPUX.

P

Peter Smithson

Hi,

I've read this page -

http://devrsrc1.external.hp.com/STK/impacts/i634.html

but don't understand it. Here's the text -

"Non-standard usage of setjmp() and longjmp() could result in
compatibility problems. The contents of the jmp_buf buffer are specific
to the architecture and compilation environment. For example, this
includes using jmp_buf in a user-defined structure and passing jmp_buf
structures across relocatable objects. Objects built using these
functions may not be supported for future architectures, such as IPF."

What does it mean about passing jmp_buf across a relocatable object?
We've got a library with several source modules in it. We compile with
position independent options as we sometimes make a shared library from
this library.

We're getting core dumps when we do a setjmp on a jmp_buf who's address
is returned in another module. Seems to work OK if we malloc each
buffer individually in the other module (rather than using them out of a
structure). I can see that it's saying this won't work on the web page
- it doesn't want us to have a jmp_buf in a user defined structure (for
some reason).

But I'm not sure about this "across relocatable objects" - what does
that mean? We can't re-create it in a simple test program. (Two
source modules compiled together).

Any ideas?

Thanks.

Peter
 
R

Richard Bos

Peter Smithson said:
I've read this page -

http://devrsrc1.external.hp.com/STK/impacts/i634.html

but don't understand it. Here's the text -

"Non-standard usage of setjmp() and longjmp() could result in
compatibility problems. The contents of the jmp_buf buffer are specific
to the architecture and compilation environment. For example, this
includes using jmp_buf in a user-defined structure and passing jmp_buf
structures across relocatable objects.

Neither of these restrictions are valid in ISO C. There's nothing in the
Standard that I can find that forbids using a jmp_buf as a member of a
struct, and "across relocatable objects" doesn't even make sense in an
ISO C context.
Objects built using these
functions may not be supported for future architectures, such as IPF."

If the implementation doesn't support putting a jmp_buf member in a
struct, it is not a compliant ISO C implementation.

Richard
 
C

Chris Torek

Peter Smithson said:
http://devrsrc1.external.hp.com/STK/impacts/i634.html [snippage - that page says, in part:]
"Non-standard usage of setjmp() and longjmp() could result in
compatibility problems. The contents of the jmp_buf buffer are specific
to the architecture and compilation environment. For example, this
includes using jmp_buf in a user-defined structure and passing jmp_buf
structures across relocatable objects.

Neither of these restrictions are valid in ISO C. There's nothing in the
Standard that I can find that forbids using a jmp_buf as a member of a
struct, and "across relocatable objects" doesn't even make sense in an
ISO C context.

Reading the original page (and then "reading between the lines" as
the saying goes), what they really mean is that compiling to .o files
using *different compiler flags*, then trying to link those .o files
together, may not work right:

% cc -do_setjmp_one_way -c foo.c
% cc -do_setjmp_another_way -c bar.c
% cc -o broken foo.o bar.o

could produce an executable that does not function correctly. But
note that this is no different in principle from, e.g.:

% foocorp_cc -c foo.c
% barcorp_cc -c bar.c
% bazlink -o broken foo.o bar.o

where the compilers from FooCorp and BarCorp are not compatible with
each other, so that no matter how clever the BazCorp linker is, the
final executable remains broken.
If the implementation doesn't support putting a jmp_buf member in a
struct, it is not a compliant ISO C implementation.

Indeed -- but again, the "between the lines" reading suggests that
different compiler flags cause the size of the data structure to
change:

% cat foo.c
#include <setjmp.h>
#include <stddef.h>
#include <stdio.h>

struct S { jmp_buf jb; int x; };
void bar(void);

int main(void) {
printf("offset of x in foo.c: %lu\n",
(unsigned long)offsetof(struct S, x));
bar();
return 0;
}
% cat bar.c
#include <setjmp.h>
#include <stddef.h>
#include <stdio.h>

struct S { jmp_buf jb; int x; };

void bar(void) {
printf("offset of x in bar.c: %lu\n",
(unsigned long)offsetof(struct S, x));
}
%

Compile these two files to "relocatable format" (.o files) using
various compiler flags, then link and run them, and the output will
be something like (total guesses at the offsets here):

% ./a.out
offset of x in foo.c: 24
offset of y in bar.c: 32

In other words, the contents of <setjmp.h> *change* depending on
how the compiler is invoked. Each set of compilation-flags (or
some sub-groups of compilation flags) thus amount to separate
implementations -- and you have to use one single implementation,
not several different ones, to compile all your source code.

What this really implies is a versioning nightmare, in which
apparently-compatible object files are not in fact compatible after
all -- and there is no way to tell simply by inspecting the .o
files. (What is needed is a compiler directive -- a #pragma or
equivalent -- that drops version information into the object files,
so that various tools can tell you that foo.o uses "setjmp version
A" while bar.o uses "longjmp version B", for instance. Of course,
the .o-file format needs to have room for such information as well,
and then the tools need to look for it and diagnose mismatches.)
 
D

Dennis Handly

Peter Smithson ([email protected]) wrote:
: I've read this page -
: http://devrsrc1.external.hp.com/STK/impacts/i634.html
: but don't understand it.

: "Non-standard usage of setjmp() and longjmp() could result in
: compatibility problems. The contents of the jmp_buf buffer are specific
: to the architecture and compilation environment. For example, this
: includes using jmp_buf in a user-defined structure and passing jmp_buf
: structures across relocatable objects. Objects built using these
: functions may not be supported for future architectures, such as IPF."
: What does it mean about passing jmp_buf across a relocatable object?

I don't remember quite what we were trying to say wasn't portable.
Or why relocatable objects are mentioned?

It could be referring to the fact that the user-defined struct must be
allocated on a larger alignment on IPF. And jmp_buf must be larger.
And you can't use the pack pragma on the struct.

: We've got a library with several source modules in it. We compile with
: position independent options as we sometimes make a shared library from
: this library.

PIC code is the default on IPF.

: We're getting core dumps when we do a setjmp on a jmp_buf who's address
: is returned in another module.

Abort on the setjmp? Or the longjmp? What's the signal?

If you have an abort on setjmp, you probably aren't allocating the
right size or alignment for jmp_buf. jmp_buf must be 16 byte aligned.

: Seems to work OK if we malloc each
: buffer individually in the other module (rather than using them out of a
: structure). I can see that it's saying this won't work on the web page
: - it doesn't want us to have a jmp_buf in a user defined structure

It doesn't want you to misalign it. malloc aligns on 16 bytes.

: But I'm not sure about this "across relocatable objects" - what does
: that mean?
: Peter

This concern may be obsolete. Possibly a concern before the IPF
implementation was finalized?
From: (e-mail address removed) (Richard Bos)
Neither of these restrictions are valid in ISO C. There's nothing in the
Standard that I can find that forbids using a jmp_buf as a member of a
struct, and "across relocatable objects" doesn't even make sense in an
ISO C context.

True. I think it was more in terms of compatibility and a porting issue.
If the implementation doesn't support putting a jmp_buf member in a
struct, it is not a compliant ISO C implementation.
Richard

But Peter's program doesn't work. So he is violating something, probably
with some evil cast or ignored warning.
From: Chris Torek <[email protected]>
Reading the original page (and then "reading between the lines" as
the saying goes), what they really mean is that compiling to .o files
using *different compiler flags*, then trying to link those .o files
together, may not work right:

Probably something like that. Using the pack pragma will mess things up
too.
be something like (total guesses at the offsets here):

Try: typedef __float80 jmp_buf[320/4];
(What is needed is a compiler directive -- a #pragma or
equivalent -- that drops version information into the object files,

Yes.
 
R

Richard Bos

Chris Torek said:
Peter Smithson said:
http://devrsrc1.external.hp.com/STK/impacts/i634.html [snippage - that page says, in part:]
"Non-standard usage of setjmp() and longjmp() could result in
compatibility problems. The contents of the jmp_buf buffer are specific
to the architecture and compilation environment. For example, this
includes using jmp_buf in a user-defined structure and passing jmp_buf
structures across relocatable objects.

Neither of these restrictions are valid in ISO C. There's nothing in the
Standard that I can find that forbids using a jmp_buf as a member of a
struct, and "across relocatable objects" doesn't even make sense in an
ISO C context.

Reading the original page (and then "reading between the lines" as
the saying goes), what they really mean is that compiling to .o files
using *different compiler flags*, then trying to link those .o files
together, may not work right:

% cc -do_setjmp_one_way -c foo.c
% cc -do_setjmp_another_way -c bar.c
% cc -o broken foo.o bar.o

could produce an executable that does not function correctly.

Oh, good grief... "No shit, Sherlock." They could've said that, then.
But note that this is no different in principle from, e.g.:

% foocorp_cc -c foo.c
% barcorp_cc -c bar.c
% bazlink -o broken foo.o bar.o

Nor is setjmp() any different from many other Standard functions, or
indeed your own. Try creating a struct in an function compiled with
#pragma packed, and then filling it in a function compiled without...
great way to smash the stack.
Indeed -- but again, the "between the lines" reading suggests that
different compiler flags cause the size of the data structure to
change:

Again, no kidding. They really could've phrased that a bit more clearly.
And again, in what way is jmp_buf different from, say, FILE?
What this really implies is a versioning nightmare, in which
apparently-compatible object files are not in fact compatible after
all -- and there is no way to tell simply by inspecting the .o
files. (What is needed is a compiler directive

Or proper project management. Granted, in the case where you can be
called upon to use someone else's .os, that may imply a bit of
paperwork, but really...

Richard
 
P

Peter Smithson

Thanks for that reply and all the others.

I think the executive summary is that we are fine as long as we align on
16 byte boundaries.

So we must be doing something silly. The guy who looked at this in more
detail says he changed our allocation code to align to 16 bytes but I
wonder if he made a mistake.
 
P

Peter Smithson

Thanks for that reply and all the others.

I think the executive summary is that we are fine as long as we align on
16 byte boundaries.

So we must be doing something silly. The guy who looked at this in more
detail says he changed our allocation code to align to 16 bytes but I
wonder if he made a mistake.

Yep, I just changed our #define ALLIGN 8 to read #define ALLIGN 16 and
it all worked fine. (something used in our memory allocation code).

Still, it's good to know that we're not going to come across some other
problem due to the thing about relocatable objects which we didn't
understand.

Thanks again.

Peter
 
K

Keith Thompson

Peter Smithson ([email protected]) wrote: [...]
: Seems to work OK if we malloc each
: buffer individually in the other module (rather than using them out of a
: structure). I can see that it's saying this won't work on the web page
: - it doesn't want us to have a jmp_buf in a user defined structure

It doesn't want you to misalign it. malloc aligns on 16 bytes.

Then the implementation needs to define jmp_buf so that any declared
object of that type will be aligned properly, even if it's a member of
a struct. It's free to use compiler magic if necessary (such as an
alignment #pragma).
From: (e-mail address removed) (Richard Bos) [...]
If the implementation doesn't support putting a jmp_buf member in a
struct, it is not a compliant ISO C implementation.
Richard

But Peter's program doesn't work. So he is violating something, probably
with some evil cast or ignored warning.

Either Peter's program is violating something, or the implementation
is buggy.
 
P

Peter Smithson

Then the implementation needs to define jmp_buf so that any declared
object of that type will be aligned properly, even if it's a member of
a struct. It's free to use compiler magic if necessary (such as an
alignment #pragma).

It seems to do that for us. Now that I've looked at the application in
detail it's embarissingly obvious when you write a test program to
demonstrate. The first setjmp below works, the 2nd one doesn't - we
don't always use malloc directly and our allocation code was using 8
bytes to align to.

#include <stdio.h>
#include <setjmp.h>
#include <malloc.h>

struct mystruct {
char abc;
jmp_buf s_jbuf;
};

#define ALLIGN 8

int main()
{
struct mystruct s_x;
struct mystruct *p_x = (void *)
((char *)malloc(sizeof(struct mystruct)+ALLIGN)+ALLIGN);

printf("Doing setjmp 1...\n");
setjmp(s_x.s_jbuf);
printf("Doing setjmp 2...\n");
setjmp(p_x->s_jbuf);
}
 
D

Dennis Handly

Keith Thompson ([email protected]) wrote:
: Then the implementation needs to define jmp_buf so that any declared
: object of that type will be aligned properly, even if it's a member of
: a struct. It's free to use compiler magic if necessary (such as an
: alignment #pragma).

Unfortunately there is no such magic. pack overrides. :-(
 
K

Keith Thompson

Keith Thompson ([email protected]) wrote:
: Then the implementation needs to define jmp_buf so that any declared
: object of that type will be aligned properly, even if it's a member of
: a struct. It's free to use compiler magic if necessary (such as an
: alignment #pragma).

Unfortunately there is no such magic. pack overrides. :-(

I'm not sure what you mean by "pack", but I'm guessing you're
referring to an implementation-defined #pragma that causes struct
members to be packed more tightly than they normally would be. For
example, given something like

struct foo { char c; double d; };
#pragma pack struct foo /* or whatever the syntax is */
struct foo obj;

I would expect the compiler to do whatever it needs to to make obj.d
accessible as a double. If obj.d is byte-aligned because of the
pragma, but the hardware requires stricter alignment, the compiler
probably needs to do something like the equivalent of a memcpy() to a
properly aligned chunk of memory to access the value. Perhaps it
handles this except in the special case of type jmp_buf, which
apparently requires 16-byte alignment.

But as it turns out this doesn't necessarily mean the compiler is
broken. C99 6.10.6p1 says:

A preprocessing directive of the form

# pragma pp-tokensopt new-line

where the preprocessing token STDC does not immediately follow
pragma in the directive (prior to any macro replacement) causes
the implementation to behave in an implementation-defined
manner. The behavior might cause translation to fail or cause the
translator or the resulting program to behave in a non-conforming
manner. Any such pragma that is not recognized by the
implementation is ignored.

If that's not what you meant by "pack", please clarify.
 
R

Richard Bos

Peter Smithson said:
#include <stdio.h>
#include <setjmp.h>
#include <malloc.h>

No such header in ISO C. Use said:
struct mystruct {
char abc;
jmp_buf s_jbuf;
};

#define ALLIGN 8

int main()
{
struct mystruct s_x;

This is correct.
struct mystruct *p_x = (void *)
((char *)malloc(sizeof(struct mystruct)+ALLIGN)+ALLIGN);

*Kablooie!* Yes, that was the sound of the alignment of your struct
being blown to bits. You do not know that a jmp_buf needs to be aligned
on 8 bytes or a whole fraction of 8 bytes. You do know that the pointer
returned from malloc() is correctly aligned for all types, including
jmp_buf and your struct. Therefore, you do not know that (that pointer+8
bytes) is correctly aligned for either. And _that_ means that...
printf("Doing setjmp 2...\n");
setjmp(p_x->s_jbuf);

....passing this quite probably incorrectly aligned pointer to setjmp()
causes undefined behaviour.

You can solve this by either not adding anything to malloc()'s return
value, or adding a number of bytes which you know will preserve correct
alignment.
In the first case, you write just

struct mystruct *p_x = malloc(sizeof *p_x + ALLIGN);

and use the last ALLIGN (btw, the correct spelling is ALIGN) bytes for
whatever extra data you need instead of the first ones. However, do note
that if you use them for anything but chars, you end up with the same
potential alignment problems as you have now.

In the second case, you need to find out a safe value for the addition.
The one value which you are guaranteed is safe for struct mystruct is
sizeof(struct mystruct); this _must_ be good enough, since in an array,
each element is sizeof(struct mystruct) beyond the previous. So in this
case, you write

#define ALIGN sizeof(struct mystruct)
struct mystruct *p_x=
(void *)((char *)malloc(sizeof *p_x+ALIGN) + ALIGN);

or even, more simply,

struct mystruct *p_x=malloc(2*sizeof *p_x)+1;

since you're now in effect dealing with an array of two struct elements.
Note that in this case, as in your original code, you need to remember
to subtract the extra amount of memory when you free() it.

This problem is not specific to either setjmp() or your implementation,
btw. Your code is simply not conforming; it would cause problems for any
type with a strict enough alignment, on any implementation that cares
about alignment.

Richard
 
P

Peter Smithson

rlb@hoekstra- said:
*Kablooie!* Yes, that was the sound of the alignment of your struct
being blown to bits. You do not know that a jmp_buf needs to be aligned
<snip>

Yes I know. As I said, it's obvious when I write it like that but in the
real app there was a structure set-up in some other module which called
another function which called another etc. etc. Even when we fixed our
own memory allocation routine to allocate to 16 bytes, there was a bit
of code elsewhere where we allocated the memory, then put two structures
in there. The first structure used up a multiple of 8 bytes, the 2nd
one had the jmp buffer in it. (lucky that the 1st structure was that
size - we could have had this problem on a machine requiring 8 byte
alignment!)

This has been fixed by using the same little bit of code used in the
memory allocation routine to ensure alignment in this area too. (similar
to what you posted)

It was a small example program based on an application consisting of
100's of source modules developed by about 10 or 20 developers over 10
years. It's quite hard to track it down and be sure your example
program matches the code - by then you've cracked it so there's not much
point in talking about the example code.
(btw, the correct spelling is ALIGN)

Thanks - I thought it was me! I couldn't find the code on an initial
search as I looked for ALIGN and then found it spelt ALLIGN!
#define ALIGN sizeof(struct mystruct)

That works only if your memory allocation routine is used just for the
one structure.

What would be nice is a way of figuring out what we should define ALIGN
as. We must have found 8 to be enough before but now we need 16.

How could we define it such that it works for any platform/OS? The code
run's on virtualy any current (and not so current) UNIX system and
Windows NT/2000/XP.

I'm sure the proper answer is to re-write the allocation routine so that
it calls malloc for every allocation and generaly re-write it so that it
can keep track of memory the same way it does now. But that's not
really practical.

Cheers

Peter
 
C

Chris Torek

... What would be nice is a way of figuring out what we should define ALIGN
as. We must have found 8 to be enough before but now we need 16.

How could we define it such that it works for any platform/OS?

You cannot.

I consider this a flaw in the C standards -- they do not provide
enough information to the implementation's user to allow him (or
her, or it) to write "malloc-like" routines.

One workaround is to rely on something "above and beyond" the C
standard, such as "POSIX" or "Unix" or "Linux" (are there any actual
Linux standards yet?) or "defines we supply ourselves every time
we port our code to another machine". The last of these gives you
the most control, but requires the most work.
The code run's on virtualy any current (and not so current) UNIX system and
Windows NT/2000/XP.

Note that x86 boxes now sometimes require 16-byte alignment too
(for SSE instructions). There may be systems that require 32-byte
alignment, or even larger, either now, or in the future. (Powers
of two seem likely to remain popular for the next century at least
though. :) )
 
S

Stephen Sprunk

Dennis Handly said:
Peter Smithson ([email protected]) wrote:
: I've read this page -
: http://devrsrc1.external.hp.com/STK/impacts/i634.html
: but don't understand it.

: "Non-standard usage of setjmp() and longjmp() could result in
: compatibility problems. The contents of the jmp_buf buffer are specific
: to the architecture and compilation environment. For example, this
: includes using jmp_buf in a user-defined structure and passing jmp_buf
: structures across relocatable objects. Objects built using these
: functions may not be supported for future architectures, such as IPF."
: What does it mean about passing jmp_buf across a relocatable object?

I don't remember quite what we were trying to say wasn't portable.
Or why relocatable objects are mentioned?

It's possible the compiler folks knew their implementation had problems, and
they were able to work around them somehow in static objects and within a
single relocatable object, but not across different relocatable objects.

Exactly what kind of implementation glitch this might be, particularly when
it's specific to a single compiler on a single platform, is beyond me. But
it'd be interesting to hear from the HP-UX compiler guys lurking here what
it is...

S
 
P

Peter Smithson

[email protected] says... said:
Note that x86 boxes now sometimes require 16-byte alignment too
(for SSE instructions). There may be systems that require 32-byte
alignment, or even larger, either now, or in the future. (Powers
of two seem likely to remain popular for the next century at least
though. :) )

I didn't know that - interesting.

I had an idea for the alignment question though it isn't a #define -

struct mystruct
{
char firstbyte;
jmp_buf jmpbuf;
};
int GetAlignment()
{
struct mystruct a;

return &a.jmpbuf - &a.firstbyte;
}

I've not compiled that so there might be a mistake but you get the idea.
It assumes that a jump buffer requires the most alignment.
 
R

Richard Bos

Peter Smithson said:
That works only if your memory allocation routine is used just for the
one structure.

You mean there could be different structs following your 8 byte header?
That's dreadful. It's a miracle it didn't break earlier.
What would be nice is a way of figuring out what we should define ALIGN
as. We must have found 8 to be enough before but now we need 16.

How could we define it such that it works for any platform/OS?

The only way I can think of is to take all possible structs that you
could put in that bit of memory, compute the LCM of all their sizes, and
use that. Best done at run-time, so you won't get caught when one of the
structs changes size due to a new compiler or something like that. It's
still hardly recommendable.

Richard
 
P

Peter Smithson

rlb@hoekstra- said:
You mean there could be different structs following your 8 byte header?
That's dreadful. It's a miracle it didn't break earlier.

Well the routine is general. One example had a structure at the start
with a size divisible by 8 which was very lucky that it was a nice size.
We've not had to align to more than 8 bytes so far. I agree though -
pretty dreadful. If someone had added a 4 byte integer to that
structure then it would have broken the code as we needed 8 byte
alignment for some other platform (who knows which one now!).

Other places the allocation routine is called, there is no header
structure and the structure will arbritrary. This is more normal. I
think the programmer of the other code was just trying to avoid two
calls to the allocation code out of laziness (possibly - I've not looked
at the code but it's been dealt with).

Thanks for your replies.

Peter
 
D

Dennis Handly

Keith Thompson ([email protected]) wrote:
: I'm not sure what you mean by "pack", but I'm guessing you're
: referring to an implementation-defined #pragma that causes struct
: members to be packed more tightly than they normally would be.

Exactly

: I would expect the compiler to do whatever it needs to to make obj.d
: accessible as a double.

Yes for the compiled code that sees that pragma.
But not for libc, which doesn't know about the pragma.
Though a warning would be nice.

: But as it turns out this doesn't necessarily mean the compiler is broken.

Right, the user code is broken. ;-)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,051
Latest member
CarleyMcCr

Latest Threads

Top