C. To segmentation fault or not to segmentation fault, that is the question.

Oddities with gcc, 2.95.3 for the AMIGA and 4.2.1 for MY current OSX 10.14.1...

I am creating a basic calculator for the AMIGA ADE *NIX emulator in C as it does not have one.

Below are two very condensed snippets of which I have added the results inside the each code section.

IMPORTANT! This will NOT even compile on gcc 2.95.3 for the AMIGA, but WORKS correctly on OSX 10.14.1, gcc 4.2.1.

/* No_error gcc 4.2.1 demo. */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int NUM_1;
int NUM_2;
char CHARACTER;

int main(int argc, char *argv[])
{
    /* Conditional BEFORE _variables_! */
    if (argc <= 3)
    {
        printf("ERROR!\n\n");
        printf("Not enough arguments!\n");
        exit(1);
    }

    int NUM_1 = strtod(argv[1], NULL);
    int NUM_2 = strtod(argv[3], NULL);
    CHARACTER = *argv[2];

    printf("\n%i, %i, %c\n\n", NUM_1, NUM_2, CHARACTER);

    return(0);
}


/* ****************************************
     Results OSX 10.14.1, gcc 4.2.1...

Last login: Fri Mar 15 14:45:03 on ttys000
AMIGA:amiga~> cd Desktop/Code/C
AMIGA:amiga~/Desktop/Code/C> gcc noerror.c
AMIGA:amiga~/Desktop/Code/C> ./a.out
ERROR!

Not enough arguments!
AMIGA:amiga~/Desktop/Code/C> ./a.out 1 v
ERROR!

Not enough arguments!
AMIGA:amiga~/Desktop/Code/C> ./a.out 1 v 3

1, 3, v

AMIGA:amiga~/Desktop/Code/C> _

**************************************** */

ALSO IMPORTANT! This compiles and WORKS perfectly on the AMIGA but gives a segmentation fault on OSX 10.14.1, gcc 4.2.1.

/* Error gcc 4.2.1 demo. */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int NUM_1;
int NUM_2;
char CHARACTER;

int main(int argc, char *argv[])
{
    int NUM_1 = strtod(argv[1], NULL);
    int NUM_2 = strtod(argv[3], NULL);
    CHARACTER = *argv[2];

    /* Conditional AFTER _variables_! */
    if (argc <= 3)
    {
        printf("ERROR!\n\n");
        printf("Not enough arguments!\n");
        exit(1);
    }

    printf("\n%i, %i, %c\n\n", NUM_1, NUM_2, CHARACTER);

    return(0);
}

/* ****************************************

Last login: Fri Mar 15 15:48:37 on ttys000
AMIGA:amiga~> cd Desktop/Code/C
AMIGA:amiga~/Desktop/Code/C> gcc error.c
AMIGA:amiga~/Desktop/Code/C> ./a.out
Segmentation fault: 11
AMIGA:amiga~/Desktop/Code/C> ./a.out 1 r
Segmentation fault: 11
AMIGA:amiga~/Desktop/Code/C> ./a.out 1 r 6

1, 6, r

AMIGA:amiga~/Desktop/Code/C> _

**************************************** */

As you can see the second code gives a Segmentation fault: 11 on OSX 10.14.1, gcc 4.2.1 but compiles and works correctly on the AMIGA.

Can anyone explain why there is a SEGMENTATION FAULT when the if conditional statement is inside the 'main()' function to start with and the assignments set?

Hi,
Both systems are 64 bit?
Maybe this is due to the red zone in the stack?

--- Post updated at 19:55 ---

Or maybe on the second system the usual garbage enters this area of the stack?

Hi nezabudk...

64 bit?
No, AMIGA A1200 is Motorola 68020 CPU 32 bit, newest gcc 2.95.3.
Yes, APPLE MBP is Intel 64 bit, newest gcc 4.2.1.

argc is _defined_ as an integer in the 'main()' function.
As for the stack quite possibly, but why? I am only comparing and argc integer against a fixed number.
Why should ANY assignments BEFORE the unrelated conditional statement crash out on this MBP?

There is little or nothing on the web about it?
And it compiles and works on the ancient gcc 2.95.3 for the equally ancient AMIGA A1200.

I understood correctly?
On a 64-bit system without parameters, the code gives "Segmentation fault".
Is there no " Segmentation fault" on the 32-bit system when started without parameters?

If my assumptions are correct, when creating a stack in the "main" function of the 64-bit system, a layer in the form of a red zone from the previous stack is filled with machine zeros and the parameters get no value, which leads to segmentation. And when creating a frame in the 32-bit system of this red zone is not! And the garbage from the old frame gets into the parameters and therefore there is no segmentation. But this is still not correct.

P.S. Thanks "Auto Save Recovery" accidentally closed the tab.

--- Post updated at 21:03 ---

I apologize this line should be read like this

It crashes when you overrun the end of argv[].

int main(int argc, char *argv[])
{
    int NUM_1 = strtod(argv[1], NULL); // Does argv[1] exist?  If not, crash.
    int NUM_2 = strtod(argv[3], NULL); // Does argv[3] exist?  If not, crash.
    CHARACTER = *argv[2]; // Does argv[2] exist?  If not, crash.

    /* Conditional AFTER _variables_! */
    if (argc <= 3)
    {
        printf("ERROR!\n\n");
        printf("Not enough arguments!\n");
        exit(1);
    }

    printf("\n%i, %i, %c\n\n", NUM_1, NUM_2, CHARACTER);

    return(0);
}

The system is actually supposed to crash when you do this, rather than let you get away with mangling data outside of bounds, but some systems are better at catching it than others.

1 Like

@nez...

(Just seen Corona688 post.)

The code was written for a real stock A1200 with HDD, Motorola 68020 32 bit, no FPU nor MMU.
This is a screenshot of the FS-UAE AMIGA emulator for THIS 64 bit MBP, OSX 10.14.1, using 68040 with FPU and MMU, and much more than the stock 2MB RAM, compiling the 'error.c' code that is in the OP using the AMIGA's gcc 2.95.3.

As you can see it IS working!

EDIT:
@C688...

That makes sense but I didn't see it. However the OP's first code block will not compile in gcc 2.95.3 as it crashes out so I am stuck with an 'illegal' operation for my fav' OS.

1 Like

Working by sheer coincidence. Going beyond the end of the array means you can't predict what data you'll find there. To avoid out of bounds errors, stay in bounds.

And now the way you're supposed to do it:

int main(int argc, char *argv[])
{
    int NUM_1, NUM_2, CHARACTER;

    if (argc <= 3)
    {
        printf("ERROR!\n\n");
        printf("Not enough arguments!\n");
        exit(1);
    }
    NUM_1 = strtod(argv[1], NULL);
    NUM_2 = strtod(argv[3], NULL);
    CHARACTER=*argv[2];
    printf("\n%i, %i, %c\n\n", NUM_1, NUM_2, CHARACTER);

    return(0);
}

Should work on older compilers.

1 Like

I've seen similar things in lots of code ported to different architectures. Quite a few different gotchas where something you can ignore harmlessly on one system, explodes on another, because something you took for granted has changed.

If you forget to include stdlib.h and math.h, your code may still work in 32-bit. It will assume these undeclared functions take all-integers and fill in the blanks. On a 64-bit system, where pointers are twice as large, that assumption is catastrophically wrong and causes memory addresses to get mangled, causing a crash.

Also, some systems let you do this, but don't depend on it, because most don't:

char *stuff="abcdefg";
stuff[2]='Q';
1 Like

Code that has problems like that is NOT working, despite your personal point of view. It is undefined behavior. So you understand: undefined behavior means the program is no longer following any rules. It went into lala land. It could limp forward until it hits an exit point, it could format all disks, it could contact the New Horizons spacecraft and order a pizza.

I would say the compiler implementers for the box's C compiler code decided to do some goof-proofing. The limp along option. They likely have a SIGSEGV signal trap? I do not know. If you have a truss equivalent on the box, you can find out if the segmentation fault has been blocked somehow.

I am not trying to be a grumpy old problem monger. But undefined behavior is never "working". Ask Don Cragun or Corona688.

3 Likes

Another way that looks nicer and should still work on older compilers:

int main(int argc, char *argv[])
{
    if (argc <= 3)
    {
        printf("ERROR!\n\n");
        printf("Not enough arguments!\n");
        exit(1);
    }

    /* Older compilers only let you put variables at the top of a code block. */
    /* But you can put code blocks wherever you want. */
    {
        int NUM_1 = strtod(argv[1], NULL);
        int NUM_2 = strtod(argv[3], NULL);
        CHARACTER = *argv[2];
        printf("\n%i, %i, %c\n\n", NUM_1, NUM_2, CHARACTER);
    }

    return(0);
}
1 Like

Hi all...

All cleaned up and working in AMIGA mode too now.
Thanks C688 for the code snippet.

I honestly thought that setting the parameters globally would suffice but it turns out I am wrong.

Thanks all for your input.
This embarrassment I will never forget.

Don't be embarassed! C is very different from most languages. It's a high-level language which writes pure assembly, with all the safety you get from pure assembly...