Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > x86 binary runs; x86_64 binary throws segfault

Reply
Thread Tools

x86 binary runs; x86_64 binary throws segfault

 
 
Don
Guest
Posts: n/a
 
      01-07-2010
Hello, folks. I have two boxes, both running Debian GNU/Linux 5,
kernel 2.6.26, and gcc version 4.3.2. One box is an AMD64 (obviously,
the x86_64 architecture), and one is an Intel i686.

I have a very simple program designed to a base string, which is a
numeral in base 12, supplied at the command line or from standard
input, into decimal, then send it back to the calling function as a
double. Very simple, under 200 lines.

Before converting the string to a decimal double, I test whether the
string is written in exponential notation, and if so, send it to a
function to convert it out of exponential notation. This consists of
juggling the integral marker (hard-coded as a semicolon, at this
point) back or forward in the string depending on the value of the
exponent, cutting off the exponent in the process. It was a hairier
problem than I expected, and it's not pretty, but it works *almost*
every time. I know that the problem is in this function because no
errors occur when the string is *not* in exponential notation, and
this function is therefore never called.

It works properly on all tested strings sent from the command line.

When I supply the strings from standard input, however, some strange
things happen. They only happen on the second string sent through the
function. The errors do *not* occur if I send a string that isn't in
exponential notation, which makes me think the problem is in the
exponential-handling function.

If the first two strings sent through stdin are "X44;4" (decimal
1492.3repeating) and "X;444e2" (the same number), the first is
converted properly and the second is converted to 17908.0000, which
about the proper number multiplied by twelve (not quite, but very
close). All subsequent passes work fine, and the same input in any
place but the second time works fine. "X44;4" does *not* fail when
passed through second.

If the first two strings sent through stdin are "X;3" (10.25 decimal)
and "0;x3e1" (the same number), the first is converted properly and
the second throws a segfault. Entering "0;x3e1" in any other place
(as in, not second from standard input) works perfectly fine,
producing "10.25" as output predictably. "X;3" does *not* fail when
passed through second.

I stared at this for some time, then in desperation sshed over to my
i686 box, recompiled the same code, and tried it there. Both of these
situations worked properly, without any issues.

I'm compiling with the following command line:

gcc -lm -o dec dec.c

I've googled the issue; in some places I'm told to try compiling with
the -arch x86_64 flag, and in other places I'm told that gcc should
automatically compile for x86_64 on an x86_64 system. I can't try it
for eight or nine more hours (I'm at work), so I thought I'd ask here
and see if perhaps the code itself is bad.

Here's the function at issue: it takes a pointer to the string to be
converted, plus an integer representing the point of the string where
the "e" is, computed at call time (I loop through the string looking
for an "e" or "E" to determine whether it needs to be called at all;
since I already had the loop index set for that when I called the
function, I thought it made sense to send it to the function rather
than let the function do the same loop again itself). Error checking
for the input has already occurred at this point, but it's proper
input that's causing this problem anyway. It calls "doztodec(char
*s)" to convert the part of the string that represents the exponent to
decimal; this function works fine on all input, and typically returns
a double. I'm putting it in a char because the exponent has to be
below 127 (my precision doesn't get anywhere like that high here), but
I understood that a cast would be done automatically in this case.
Should I make it explicit? I've sprinkled a lot of extra comments for
this posting to make it clearer what's going on.
*******
int expkill (char *s, int expspot)
{
int i,j;
char zensuf[3]; /* the base-12 exponent suffix */
char exp; /* the value of the suffix */
int semi; /* where semicolon is in original string */

for (i = expspot+1,j=0; *(s+i) != '\0'; ++i,++j)
*(zensuf+j) = *(s+i); /* put the exponent in zensuf */
exp = doztodec(zensuf); /* convert the exponent to decimal char */
for (semi=0; *(s+semi) != ';' && *(s+semi) != '\0'; ++semi); /* set
semi = semicolon's location */
*(s+expspot) = '\0'; /* cut off the "e" and the exponent */
if (semi >= strlen(s))
return 0; /* if the semicolon is already at the end of the string,
end processing */
if (exp > 0) { /* if the semicolon needs to be moved to the right */
for (i=semi; *(s+i+1) != '\0'; ++i)
*(s+i) = *(s+i+1); /* move everything over to get rid of original
semicolon */
*(s+i) = '\0'; /* move string terminator to new end of string */
for (i=strlen(s); i > semi+exp; --i)
*(s+i) = *(s+i-1); /* move the rest of string over to make room for
new semi */
*(s+semi+exp) = ';'; /* insert the new semicolon */
} else if (exp < 0) { /* if the semicolon needs to be moved to the
left */
exp = -exp, ++exp;
for (i=strlen(s)+exp; i >= 0; --i)
*(s+i) = *(s+i-exp); /* move string to right to make room for new
digits */
for (i=0; i < exp; ++i)
*(s+i) = '0'; /* pad left side of string with zeroes */
for (semi=0; *(s+semi) != ';'; ++semi); /* find location of
semicolon */
for (i=semi; *(s+i) != '\0'; ++i)
*(s+i) = *(s+i+1); /* move over to eliminate original semicolon */
*(s+i) = '\0'; /* insert new end of string */
*(s+1) = ';'; /* put in new semicolon */
}
if (*(s+strlen(s)-1) == ';')
*(s+strlen(s)-1) = '\0'; /* if there's no fractional part now, don't
have semicolon */
return 0;
}
*******

The contents of the file that produces erroneous input is:
X44;4
X;444e2

The contents of the file that throws a segfault is:
X;3
0;X3e1

Thanks to anyone who can help me figure this out.
 
Reply With Quote
 
 
 
 
Eric Sosman
Guest
Posts: n/a
 
      01-07-2010
On 1/7/2010 11:04 AM, Don wrote:
> Hello, folks.[...]


Could you post a short, complete program that demonstrates
the problem? Based on your report that "it works" with strings
entered on the command line but fails when input is read from a
stream, I have a sneaking suspicion that the problem is in the
way the strings are read. (If you're using fgets(), did you
remember that the '\n' at the end of the line is still there?)

Also, a little more information on your duodecimal notation
would be welcome. It seems that 'X' or 'x' stands for the
duodecimal digit ten, and I'm going to guess that 'Y'/'y' mean
eleven -- but that doesn't seem to be spelled out anywhere.

--
Eric Sosman
http://www.velocityreviews.com/forums/(E-Mail Removed)lid
 
Reply With Quote
 
 
 
 
Don
Guest
Posts: n/a
 
      01-07-2010
On Jan 7, 11:37*am, Eric Sosman <(E-Mail Removed)> wrote:
> On 1/7/2010 11:04 AM, Don wrote:
>
> > Hello, folks.[...]

>
> * * *Could you post a short, complete program that demonstrates
> the problem?


Hmm. Not for several more hours, at least; I don't have a compiler
here at work, so I couldn't ensure that what I posted would work.
Plus, to get it really working I'd probably have to include all the
code.

I'm not using fgets(), though; I'm using a home-rolled getline(),
because I'm working through K&R and it's comfortably familiar to me.

Yes, 'X' or 'x' would work for ten; also 'T', 't', 'A', or 'a'. I
tend to use 'X' myself. Eleven is 'E', 'B', or 'b' (not 'e', as that
would conflict with the exponents). I didn't think it would be
relevant since whenever this expkill() function is not called,
everything works great; and indeed, whenever expkill() *is* called, it
all works great except for the second item read in.

I'll go ahead and humiliate myself and post the entire code. I
haven't worked in error checking yet, but since it's good input that's
breaking it I don't suppose that matters at this point.

*******
int main(int argc, char *argv[])
{
int c;
char doznum[MAXLINE];

while (*(++argv) != NULL && *argv[0] == '-') {
while (c = *++argv[0])
switch (c) {
default:
fprintf(stderr,"dec: illegal option \"%c\"\n",c);
break;
}
}
if (*(++argv) == NULL) {
printf("%f\n",doztodec(*(argv-1)));
return 0;
}
while (getline(doznum,MAXLINE) != EOF) {
printf("%f\n",doztodec(doznum));
}
return 0;
}

/* convert exponential notation to normal b/f processing */
int expkill (char *s, int expspot)
{
int i,j;
char zensuf[3];
char exp;
int semi; /* where semicolon is */

for (i = expspot+1,j=0; *(s+i) != '\0'; ++i,++j)
*(zensuf+j) = *(s+i);
exp = doztodec(zensuf);
for (semi=0; *(s+semi) != ';' && *(s+semi) != '\0'; ++semi);
*(s+expspot) = '\0';
if (semi >= strlen(s))
return 0;
if (exp > 0) {
for (i=semi; *(s+i+1) != '\0'; ++i)
*(s+i) = *(s+i+1);
*(s+i) = '\0';
for (i=strlen(s); i > semi+exp; --i)
*(s+i) = *(s+i-1);
*(s+semi+exp) = ';';
} else if (exp < 0) {
exp = -exp, ++exp;
for (i=strlen(s)+exp; i >= 0; --i)
*(s+i) = *(s+i-exp);
for (i=0; i < exp; ++i)
*(s+i) = '0';
for (semi=0; *(s+semi) != ';'; ++semi);
for (i=semi; *(s+i) != '\0'; ++i)
*(s+i) = *(s+i+1);
*(s+i) = '\0';
*(s+1) = ';';
}
if (*(s+strlen(s)-1) == ';')
*(s+strlen(s)-1) = '\0';
return 0;
}

double doztodec(char *s)
{
char frac[MAXDIGS];
int i, j;
int endpoint;
double decnum = 0.0;

for (i=0; *(s+i) != '\0'; ++i)
if (*(s+i) == 'e' || *(s+i) == 'E')
expkill(s,i);
for (i=0; *(s+i) != ';'; ++i);
endpoint = i;
if (endpoint < (strlen(s) - 1)) {
for (i=++i,j=0; *(s+i) != '\0'; ++i,++j)
*(frac+j) = *(s+i);
*(s+endpoint) = *(frac+j) = '\0';
decnum += fracdec(frac);
}
decnum += wholedec(s);
return decnum;
}

double wholedec(char *s)
{
int addnum = 0; /* added to decnum each loop */
int i;
int multiple = 1; /* provides multiple for loop */
double decnum = 0.0; /* final result to return */
char sign = 0;

reverse(s);
if (*(s+strlen(s)-1) == '-') {
sign = 1;
*(s+strlen(s)-1) = '\0';
}
for (i=0; *(s+i) != '\0'; ++i) {
addnum = decmify(*(s+i));
addnum *= multiple;
decnum += addnum;
multiple *= 12;
}
return (sign == 0) ? decnum : decnum * -1;
}

double fracdec(char *s)
{
int i;
double divisor = 1.0;
double fracval = 0.0;

for (i=0; *(s+i) != '\0'; ++i)
divisor *= 12.0;
fracval = wholedec(s) / divisor;
return fracval;
}

char decmify(char c)
{
switch (c) {
case '0': return 0; break;
case '1': return 1; break;
case '2': return 2; break;
case '3': return 3; break;
case '4': return 4; break;
case '5': return 5; break;
case '6': return 6; break;
case '7': return 7; break;
case '8': return 8; break;
case '9': return 9; break;
case 'X': case 'x': case 'T': case 't': case 'A': case
'a':
return 10; break;
case 'E': case 'B': case 'b':
return 11; break;
default: return 0; break;
}
}

void reverse(char *s)
{
int i, j;
char tmp;

for (i=0, j=strlen(s)-1; i<j; ++i, --j) {
tmp = *(s+i);
*(s+i) = *(s+j);
*(s+j) = tmp;
}
}

int getline(char *s, int lim)
{
int c, i;

for (i=0; (c=getchar())!=EOF && c!='\n' && c!='\t' && c!=' '; ++i) {
if (i < lim-1)
*(s+i) = c;
else if (i == lim)
*(s+i) = '\0';
}
if (c == EOF)
return EOF;
*(s+i) = '\0';
return i;
}
*******
Input file that returns wrong output:
*******
X44;4
X;444e2
*******
Input file that throws a segfault:
*******
X;3
0;X3e1
*******
These errors only show up on my AMD64 box, not on my x86 box. The
same numbers that throw errors when on the second line of the input
file do not when on any other line.

Thanks again.
 
Reply With Quote
 
Don
Guest
Posts: n/a
 
      01-07-2010
Naturally, forgot to post all the pre-main stuff:
*******
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>

#define MAXDIGS 30
#define MAXLINE 30

int getline(char *s, int lim);
void reverse(char *s);
char decmify(char c);
double wholedec(char *s);
double doztodec(char *s);
double fracdec(char *s);
int expkill (char *s, int expspot);
*******
That, plus the rest of it. Thanks again.
 
Reply With Quote
 
James Dow Allen
Guest
Posts: n/a
 
      01-07-2010
On Jan 7, 11:57*pm, Don <(E-Mail Removed)> wrote:
> I'll go ahead and humiliate myself and post the entire code.


The foolishest question is the unasked one.
Nevertheless I'd suggest, as a later exercise,
trying to simplify this code.

> /* convert exponential notation to normal b/f processing */
> int expkill (char *s, int expspot)
> {
> * * * * int i,j;
> * * * * char zensuf[3];
> * * * * char exp;
> * * * * int semi; /* where semicolon is */
>
> * * * * for (i = expspot+1,j=0; *(s+i) != '\0'; ++i,++j)
> * * * * * * * * *(zensuf+j) = *(s+i);
> * * * * exp = doztodec(zensuf);


I'd not be surprised if your error is right here! The for loop
does NOT copy the '\0' character, but doztodec() depends on
it. Since uninitialized bytes in zensuf[] will vary based
on "irrelevant" details, this would tend to explain
your symptoms.

By the way, when in doubt, use asserts or printf's to check
what's going on. For example a
fprintf(stderr, "Expkill %d %d\n", strlen(s), expspot);
at the beginning of expkill() would error-check its args
(though not the actual problem here).

> * * * * [several more lines snipped]


I didn't study the whole code. I just assumed that, were I
to find the error before patience ran outit would
be at beginning of examined code.

James Dow Allen
 
Reply With Quote
 
Ben Bacarisse
Guest
Posts: n/a
 
      01-07-2010
Don <(E-Mail Removed)> writes:

> On Jan 7, 11:37*am, Eric Sosman <(E-Mail Removed)> wrote:
>> On 1/7/2010 11:04 AM, Don wrote:
>>
>> > Hello, folks.[...]

>>
>> * * *Could you post a short, complete program that demonstrates
>> the problem?

<snip>
> I'll go ahead and humiliate myself and post the entire code. I
> haven't worked in error checking yet, but since it's good input that's
> breaking it I don't suppose that matters at this point.
>
> *******
> int main(int argc, char *argv[])
> {
> int c;
> char doznum[MAXLINE];
>
> while (*(++argv) != NULL && *argv[0] == '-') {
> while (c = *++argv[0])
> switch (c) {
> default:
> fprintf(stderr,"dec: illegal option \"%c\"\n",c);
> break;
> }
> }
> if (*(++argv) == NULL) {
> printf("%f\n",doztodec(*(argv-1)));
> return 0;
> }
> while (getline(doznum,MAXLINE) != EOF) {
> printf("%f\n",doztodec(doznum));
> }
> return 0;
> }
>
> /* convert exponential notation to normal b/f processing */
> int expkill (char *s, int expspot)
> {
> int i,j;
> char zensuf[3];
> char exp;
> int semi; /* where semicolon is */
>
> for (i = expspot+1,j=0; *(s+i) != '\0'; ++i,++j)
> *(zensuf+j) = *(s+i);


The string in zensuf is not null terminated. You stop when "see" the
null but you never put it into the array.

> exp = doztodec(zensuf);


This call expects a proper string -- one the is null terminated.

> for (semi=0; *(s+semi) != ';' && *(s+semi) != '\0'; ++semi);
> *(s+expspot) = '\0';
> if (semi >= strlen(s))
> return 0;
> if (exp > 0) {
> for (i=semi; *(s+i+1) != '\0'; ++i)
> *(s+i) = *(s+i+1);
> *(s+i) = '\0';
> for (i=strlen(s); i > semi+exp; --i)
> *(s+i) = *(s+i-1);
> *(s+semi+exp) = ';';
> } else if (exp < 0) {
> exp = -exp, ++exp;
> for (i=strlen(s)+exp; i >= 0; --i)
> *(s+i) = *(s+i-exp);
> for (i=0; i < exp; ++i)
> *(s+i) = '0';
> for (semi=0; *(s+semi) != ';'; ++semi);
> for (i=semi; *(s+i) != '\0'; ++i)
> *(s+i) = *(s+i+1);
> *(s+i) = '\0';
> *(s+1) = ';';
> }
> if (*(s+strlen(s)-1) == ';')
> *(s+strlen(s)-1) = '\0';
> return 0;
> }
>
> double doztodec(char *s)
> {
> char frac[MAXDIGS];
> int i, j;
> int endpoint;
> double decnum = 0.0;
>
> for (i=0; *(s+i) != '\0'; ++i)


See? There many be no null.

> if (*(s+i) == 'e' || *(s+i) == 'E')
> expkill(s,i);
> for (i=0; *(s+i) != ';'; ++i);
> endpoint = i;
> if (endpoint < (strlen(s) - 1)) {
> for (i=++i,j=0; *(s+i) != '\0'; ++i,++j)
> *(frac+j) = *(s+i);
> *(s+endpoint) = *(frac+j) = '\0';
> decnum += fracdec(frac);
> }
> decnum += wholedec(s);
> return decnum;
> }


This is not magic or quick eyes. I compiled with -g, ran it under
valgrind and followed the logic to see what was wrong. i don't know
how I managed without valgrind! There may be other errors, but deal
with them one at a time.

BTW, a quick tip: Your code will loads easier to read is every *(x+e)
was replaced by x[e]. Most people expect to see pointers indexed with
[] and it is much easier to read.

--
Ben.
 
Reply With Quote
 
Don
Guest
Posts: n/a
 
      01-07-2010
On Jan 7, 12:18*pm, James Dow Allen <(E-Mail Removed)> wrote:
> The foolishest question is the unasked one.
> Nevertheless I'd suggest, as a later exercise,
> trying to simplify this code.


Yes! I'm actually fairly happy with most of it, other than the expkill
() mess. But that is definitely beyond ugly. There must be a better
way to do that.

> I'd not be surprised if your error is right here! *The for loop
> does NOT copy the '\0' character, but doztodec() depends on
> it. *Since uninitialized bytes in zensuf[] will vary based
> on "irrelevant" details, this would tend to explain
> your symptoms.


Hmm. Yes, I'll fix that; funny I missed it, when generally I end up
putting '\0's where they already are just to be sure. But would that
really explain why it fails consistently on the second call, and only
the second call, on x86_64 and not on i686?

@Ben Bacarisse:

I'd never heard of valgrind before today; I'll be sure to look at it.
I just recently discovered Beej's Quick Guide to GDB, which looks like
it'll help me a lot, too.

> BTW, a quick tip: Your code will loads easier to read is every *(x+e)
> was replaced by x[e]. Most people expect to see pointers indexed with
> [] and it is much easier to read.


Hmm. When I went through the pointers chapter in K&R, it advised me
to go through my old programs with the array indexing and change them
to pointers, as it would be faster that way, though harder to read "at
least for the uninitiated."

Because you are clearly initiated, I question this now. Is array
indexing more normal? That would be annoying for me, since I've
finally gotten into the habit of using the pointer notation, but I'd
rather the code be maintainable.
 
Reply With Quote
 
Mark
Guest
Posts: n/a
 
      01-07-2010
Ben Bacarisse <(E-Mail Removed)> wrote:
>
> This is not magic or quick eyes. I compiled with -g, ran it under
> valgrind and followed the logic to see what was wrong. i don't know
> how I managed without valgrind! There may be other errors, but deal
> with them one at a time.
>
> BTW, a quick tip: Your code will loads easier to read is every *(x+e)
> was replaced by x[e]. Most people expect to see pointers indexed with
> [] and it is much easier to read.


To add to Ben's good advice, and despite the fact you may get away
with it, this isn't a good idea:

>> for (i=++i,j=0; *(s+i) != '\0'; ++i,++j)

^^^^^
 
Reply With Quote
 
Fred
Guest
Posts: n/a
 
      01-07-2010
On Jan 7, 9:18*am, James Dow Allen <(E-Mail Removed)> wrote:
> On Jan 7, 11:57*pm, Don <(E-Mail Removed)> wrote:
>
> > I'll go ahead and humiliate myself and post the entire code.

>
> The foolishest question is the unasked one.
> Nevertheless I'd suggest, as a later exercise,
> trying to simplify this code.
>
> > /* convert exponential notation to normal b/f processing */
> > int expkill (char *s, int expspot)
> > {
> > * * * * int i,j;
> > * * * * char zensuf[3];


Note the size for zensuf


> > * * * * char exp;
> > * * * * int semi; /* where semicolon is */

>
> > * * * * for (i = expspot+1,j=0; *(s+i) != '\0'; ++i,++j)
> > * * * * * * * * *(zensuf+j) = *(s+i);


and what happens above if j > 2 ?

--
Fred K
 
Reply With Quote
 
lawrence.jones@siemens.com
Guest
Posts: n/a
 
      01-07-2010
Don <(E-Mail Removed)> wrote:
>
> Hmm. When I went through the pointers chapter in K&R, it advised me
> to go through my old programs with the array indexing and change them
> to pointers, as it would be faster that way, though harder to read "at
> least for the uninitiated."
>
> Because you are clearly initiated, I question this now. Is array
> indexing more normal? That would be annoying for me, since I've
> finally gotten into the habit of using the pointer notation, but I'd
> rather the code be maintainable.


You misunderstood K&R's advice. What they were suggesting is that it's
usually faster to walk a pointer through an array rather than using
subscripts. For example, instead of:

for (i = 0; i < n; i++)
a[i] += 10;

do:

for (p = a; p < a + n; p++)
*p += 10;

The notation is irrelevant -- *(a + i) is exactly equivalent to a[i],
but a[i] is usually much easier to read.
--
Larry Jones

You can never really enjoy Sundays because in the back of your
mind you know you have to go to school the next day. -- Calvin
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
invoking a segfault within a segfault handler - is this undefinedbehavior? Andrey Vul C Programming 8 07-30-2010 02:14 PM
stack pointer alignment on x86 and x86_64 omkarenator C Programming 2 05-01-2009 01:53 PM
Why does:system("./ati-driver-installer-8.40.4-x86.x86_64.run --buildpkg Ubuntu/feisty")not work ? It always fails without any error? kazaam Ruby 6 08-27-2007 02:04 PM
Integer#to_s(2) works on x86, fails on x86_64 Suraj Kurapati Ruby 5 12-05-2006 02:37 PM
Why is there an x86 emu if a processor is x86-64? =?Utf-8?B?RWxsaW90IEh1ZGdpbnM=?= Windows 64bit 4 07-23-2006 11:52 PM



Advertisments