Pointer arithmetics in Solaris

S

stefano.verna

I would be grateful to anybody who can explain what on earth is going
wrong. I have this tiny little piece of code:

void sendOperation16 (int fd, int16_t o1, int16_t o2) {
unsigned char response[3] = { '=', 0 };
int16_t *p = (int16_t *) (response+1);
/* ..some overflow error management here.. */
o1+=o2;
memcpy (p, &o1, 2);
Writen (fd, response, 3);
}

On Linux/Cygwin it works as is should. On Solaris, the memcpy
statement will cause the program to be terminated. I really don't know
what to think about, as at these lines are perfectly normal and
compliant to me.

If i simply change it to
memcpy (response+1, &o1, sizeof (o1));
it works again.
 
I

Ian Collins

I would be grateful to anybody who can explain what on earth is going
wrong. I have this tiny little piece of code:

void sendOperation16 (int fd, int16_t o1, int16_t o2) {
unsigned char response[3] = { '=', 0 };
int16_t *p = (int16_t *) (response+1);
/* ..some overflow error management here.. */
o1+=o2;
memcpy (p, &o1, 2);
Writen (fd, response, 3);
}

On Linux/Cygwin it works as is should. On Solaris, the memcpy
statement will cause the program to be terminated. I really don't know
what to think about, as at these lines are perfectly normal and
compliant to me.

If i simply change it to
memcpy (response+1, &o1, sizeof (o1));
it works again.
Then why don't you? What's all the nonsense with int16_t pointers doing?
 
S

stefano.verna

Then why don't you? What's all the nonsense with int16_t pointers doing?

I could agree with you about the nonsense-ness, but the topic of this
message is not solving the problem, as I already found a solution, but
trying to understand why that intermediate passage it's wrong and how
gets interpreted by Solaris.
 
D

Doug

I would be grateful to anybody who can explain what on earth is going
wrong. I have this tiny little piece of code:

void sendOperation16 (int fd, int16_t o1, int16_t o2) {
unsigned char response[3] = { '=', 0 };
int16_t *p = (int16_t *) (response+1);
/* ..some overflow error management here.. */
o1+=o2;
memcpy (p, &o1, 2);
Writen (fd, response, 3);

}

On Linux/Cygwin it works as is should. On Solaris, the memcpy
statement will cause the program to be terminated. I really don't know
what to think about, as at these lines are perfectly normal and
compliant to me.

If i simply change it to
memcpy (response+1, &o1, sizeof (o1));
it works again.

Hi there,

My guess is that it's an alignment issue. I think Solaris (on non-
x86) will want to align int16_t on 2-byte boundaries. So the cast
returns an invalid pointer.

What I don't know, but I'm sure others will be able to tell us, is why
it fails. The call to memcpy should convert the int16_t * to a void *
- so I don't really understand why it fails since it never actually
dereferences the bad int16_t *. I think the standard says it's
undefined behaviour because the pointer is invalid, but I'm surprised
it actually breaks. Can someone enlighten us?

I think the reason this would work on Linux/Cygwin is that you
probably ran on x86 hardware, which usually doesn't have these
alignment issues.

Doug
 
J

Jens Thoms Toerring

I would be grateful to anybody who can explain what on earth is going
wrong. I have this tiny little piece of code:
void sendOperation16 (int fd, int16_t o1, int16_t o2) {
unsigned char response[3] = { '=', 0 };
int16_t *p = (int16_t *) (response+1);
/* ..some overflow error management here.. */
o1+=o2;
memcpy (p, &o1, 2);
Writen (fd, response, 3);
}
On Linux/Cygwin it works as is should. On Solaris, the memcpy
statement will cause the program to be terminated. I really don't know
what to think about, as at these lines are perfectly normal and
compliant to me.
If i simply change it to
memcpy (response+1, &o1, sizeof (o1));
it works again.

My best guess is that the line

int16_t *p = ( int16_t * ) ( response + 1 );

is at the root of the problem - you cast a char pointer to a
pointer of an incompatible type, incompatible because a char
pointer may have different alignment requirements than a
pointer to int16_t (the later probably being required to be
even on Solaris while no such constraint exists for a char
pointer).

This, in turn, leads the compiler to optimize the code a
bit too aggressively (but, considering that it has to
assume that you copy between int16_t types, in principle
correctly), replacing

memcpy( p, &o1, 2 );

by something equivalent to a simple

*p = o1;

and, since what 'p' is pointing to has at least a 50% chance of
being not properly aligned for an int16_t, this may lead to the
crash with a SIGBUS. On the other hand, with

memcpy( response + 1, &o1, sizeof o1 )

the compiler probably will have to do a byte-by-byte copy and
that doesn't lead any issues with alignments.

You probably will also get rid of the problem if you use

void *p = response + 1;

instead of casting to an int16_t.

Regards, Jens
 
R

Richard Tobin

int16_t *p = (int16_t *) (response+1);

That's your problem. You can't cast arbitrary addresses to arbitrary
types. You have to make sure they satisfy the alignment requriements
of the system you're using. Presumably on your system 16-bit words
have to be aligned on 16-bit boundaries. On most systems merely
setting the value won't cause a problem, and it only shows up when you
dereference the pointer. In this case, you do that here:
memcpy (p, &o1, 2);

Many systems won't show the problem even here, because memcpy() has to
be able to copy between arbitrarily-aligned blocks of memory. But
presumably in this case the memcpy() has been optimised to a single
move instruction, which is a perfectly legal optimisation since p can
only legally point to a suitably aligned location. Unfortunately your
earlier error means that it doesn't.

-- Richard
 
I

Ian Collins

I could agree with you about the nonsense-ness, but the topic of this
message is not solving the problem, as I already found a solution, but
trying to understand why that intermediate passage it's wrong and how
gets interpreted by Solaris.
The issue would appear to be alignment requirements of the Sparc
processor, not Solaris.
 
C

CBFalconer

I could agree with you about the nonsense-ness, but the topic of this
message is not solving the problem, as I already found a solution, but
trying to understand why that intermediate passage it's wrong and how
gets interpreted by Solaris.

Then you should be asking on a newsgroup that deals with Solaris.
 
K

Kenneth Brody

I would be grateful to anybody who can explain what on earth is going
wrong. I have this tiny little piece of code:

void sendOperation16 (int fd, int16_t o1, int16_t o2) {
unsigned char response[3] = { '=', 0 };
int16_t *p = (int16_t *) (response+1);
/* ..some overflow error management here.. */
o1+=o2;
memcpy (p, &o1, 2);
Writen (fd, response, 3);
}

On Linux/Cygwin it works as is should. On Solaris, the memcpy
statement will cause the program to be terminated. I really don't know
what to think about, as at these lines are perfectly normal and
compliant to me.

If i simply change it to
memcpy (response+1, &o1, sizeof (o1));
it works again.

First, is sizeof(int16_t)==2?

Second, it may be an alignment issue. The address "response+1" may
not be a legal address for int16_t on the Solaris box's hardware.
Perhaps the compiler has done some optimization for you, given the
assumption that p is a valid int16_t* value, and inlines the memcpy
from
memcpy(p,&o1,2);
to
*p = o1;

(These are, after all, equivalent, if p is properly aligned, and the
sizeof int16_t is indeed 2.)

You can test this theory, perhaps, by changing p to "char *" instead,
and seeing if the crash goes away.


Question: Is the compiler allowed to make such an assumption? Given
that p is of type "int16_t*", can it assume that it is properly
aligned, and replace the memcpy() as I have stated?


--
+-------------------------+--------------------+-----------------------+
| Kenneth J. Brody | www.hvcomputer.com | #include |
| kenbrody/at\spamcop.net | www.fptech.com | <std_disclaimer.h> |
+-------------------------+--------------------+-----------------------+
Don't e-mail me at: <mailto:[email protected]>
 

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,776
Messages
2,569,603
Members
45,198
Latest member
JaimieWan8

Latest Threads

Top