简体   繁体   中英

signal SIGSEGV caused by the -O2 flag

What does the flag -O2 means? , because I get the signal SIGSEGV when I compile my program with the flag -O2 , but If I remove that flag at the gcc command, the program works perfectly without any error, I was trying to solve Primitive Calculator problem using recursive functions, it's just a recursion and Memoization problem.

then with this command the program works perfectly...

gcc -pipe -std=c11 -g file.c

But with this another command it doesn't works, gets the signal SIGSEGV... :'(

gcc -pipe -O2 -std=c11 -g file.c

For example If run the program compiled with that flag( -O2 ), with the input 96234 , I get this error.

(gdb) run
The program being debugged has been started already.                           
Start it from the beginning? (y or n) y
Starting program: /home/windowsky/Desktop/coursera/week5/a.out                 
96234

Program received signal SIGSEGV, Segmentation fault.                         
0x00005555555551b2 in options (num=num@entry=21355, ptr=ptr@entry=0x7fffffffe7a8) at eje2.c:26                                                                          
26                                                                             
(gdb) 

It crashes with any reason in that point, as I mention before if you remove the flag -O2 the program works perfectly, I just want to know what the -O2 flag means at the compiler, because this doesn't make any sense for me I'm going to lose my head.

Then this is the code.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdbool.h>

/* this is the type of operations that we can do */
enum opeType {
    SUB_1,
    DIV_2,
    DIV_3
};


/*  options: Recursive options */
int options (int num, int **ptr)
{
    int minOperations, numOperations, i;


    if (num <= 1)
        return 0;

    numOperations = 0;
    minOperations= 2147483647;
    
    for (i = 0; i < 3; ++i) {
        switch (i) {
            case DIV_2:
                if (num % 2 == 0) {
                    if (*(*ptr + (num / 2)) == 0)
                        *(*ptr + (num / 2)) = options(num / 2, ptr) + 1;
                    numOperations = *(*ptr + (num / 2));
                }
                break;
            case DIV_3:
                if (num % 3 == 0) {
                    if (*(*ptr + (num / 3)) == 0)
                        *(*ptr + (num / 3)) = options(num / 3, ptr) + 1;
                    numOperations = *(*ptr + (num / 3));
                }
                break;
            case SUB_1:
                if (*(*ptr + (num -1)) == 0)
                    *(*ptr + (num - 1)) = options(num - 1, ptr) + 1;
                numOperations = *(*ptr + (num - 1));
                break;
        }
        if (numOperations < minOperations && numOperations != 0)
            minOperations = numOperations;
    }

    return minOperations;
}


/* findingPath: This is the function that is going to find the path */
bool findingPath (int **ptr2, int num, int c)
{   
    int i;
    bool flag;

    if (num == 1)
        return true;

    else if (c >= 0) {

        flag = false;
        for (i = 2; i > -1; --i) {
            switch (i) {
                case SUB_1:
                    if (flag = findingPath(ptr2, num - 1, c - 1))
                        *(*ptr2 + c) = num - 1;
                    break;
                case DIV_2:
                    if (num % 2 == 0 && (flag = findingPath(ptr2, num / 2, c - 1)))
                        *(*ptr2 + c) = num / 2;
                    break;
                case DIV_3:
                    if (num % 3 == 0 && (flag = findingPath(ptr2, num / 3, c - 1)))
                        *(*ptr2 + c) = num / 3;
                    break;
            }
            if (flag)
                return true;
        }
    }

    return false;
}





/*  one_test: Just one test */
void one_test ()
{
    int num, *ptr, *ptr2;
    int res, i;

    num = 6;
    ptr = (int *) malloc(sizeof(int) * (num + 1));
    memset(ptr, 0, num);

    res = options(num, &ptr);
    assert(res == 2);

    ptr2 = (int *) malloc(sizeof(int) * (res > 0 ? res : 1));
    memset(ptr2, 0, res > 0 ? (res - 1) : 1);

    findingPath(&ptr2, num, res > 0 ? (res - 1) : 1);

    printf("%i\n", res);
    for (i = 0; i < res; i++)
        printf("%i ", *(ptr2 + i));
    printf("%i ", num);
    
    free(ptr);
    free(ptr2);
    ptr = NULL;
    ptr2 = NULL;

}


/*  manual_test: This is the manual test */
void manual_test ()
{
    int num, *ptr, *ptr2;
    int res, i;

    scanf("%i", &num);
    ptr = (int *) malloc(sizeof(int) * (num + 1));
    memset(ptr, 0, num);

    res = options(num, &ptr);

    ptr2 = (int *) malloc(sizeof(int) * (res > 0 ? res : 1));
    memset(ptr2, 0, res > 0 ? (res - 1) : 1);

    findingPath(&ptr2, num, res > 0 ? (res - 1) : 1);

    printf("%i\n", res);
    for (i = 0; i < res; i++)
        printf("%i ", *(ptr2 + i));
    printf("%i ", num);
    
    free(ptr);
    free(ptr2);
    ptr = NULL;
    ptr2 = NULL;
}

 

void main ()
{
    //one_test();

    manual_test();
}

To answer the specific question posed, the meaning of -O2 , as well as every other command-line option to gcc , is explained in the GCC manual . You will find -O2 under Optimization Options , and following the link leads you to:

-O2 Optimize even more...

So the -O2 option enables optimizations . See also How many GCC optimization levels are there? .

It is very common for C programs to have bugs that cause undefined behavior , but that don't actually misbehave until optimizations are enabled.

See also:

If you want help finding the bug in your particular program, then please reduce it to a minimal reproducible example and ask a new question about it.

Running with valgrind shows fairly quickly your problem(s):

==27297== Memcheck, a memory error detector
==27297== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==27297== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==27297== Command: ./t
==27297== 
==27297== Conditional jump or move depends on uninitialised value(s)
==27297==    at 0x1088A5: options.part.0 (t.c:44)
==27297==    by 0x1096CB: options (t.c:153)
==27297==    by 0x1096CB: manual_test (t.c:137)
==27297==    by 0x4E5FBF6: (below main) (libc-start.c:310)
==27297== 
==27297== Conditional jump or move depends on uninitialised value(s)
==27297==    at 0x1088A5: options.part.0 (t.c:44)
==27297==    by 0x108986: options (t.c:21)
==27297==    by 0x108986: options.part.0 (t.c:45)
==27297==    by 0x1096CB: options (t.c:153)
==27297==    by 0x1096CB: manual_test (t.c:137)
==27297==    by 0x4E5FBF6: (below main) (libc-start.c:310)
==27297== 
==27297== Conditional jump or move depends on uninitialised value(s)
==27297==    at 0x1088A5: options.part.0 (t.c:44)
==27297==    by 0x108986: options (t.c:21)
==27297==    by 0x108986: options.part.0 (t.c:45)
==27297==    by 0x108986: options (t.c:21)
==27297==    by 0x108986: options.part.0 (t.c:45)
==27297==    by 0x1096CB: options (t.c:153)
==27297==    by 0x1096CB: manual_test (t.c:137)
==27297==    by 0x4E5FBF6: (below main) (libc-start.c:310)
==27297== 
==27297== Conditional jump or move depends on uninitialised value(s)
==27297==    at 0x1088A5: options.part.0 (t.c:44)
==27297==    by 0x108986: options (t.c:21)
==27297==    by 0x108986: options.part.0 (t.c:45)
==27297==    by 0x108986: options (t.c:21)
==27297==    by 0x108986: options.part.0 (t.c:45)
==27297==    by 0x108986: options (t.c:21)
==27297==    by 0x108986: options.part.0 (t.c:45)
==27297==    by 0x1096CB: options (t.c:153)
==27297==    by 0x1096CB: manual_test (t.c:137)
==27297==    by 0x4E5FBF6: (below main) (libc-start.c:310)
==27297== 
==27297== Stack overflow in thread #1: can't grow stack to 0x1ffe801000
==27297== 
==27297== Process terminating with default action of signal 11 (SIGSEGV): dumping core

So the direct cause of the SIGSEGV is a stack overflow. This happens whenever the input is too large, and happens sooner with -O2 than -O0 but happens regardless of optimization level

The second problem which may or may not be connected is use of uninitialized memory, due to the fact that you are not clearing your malloced arrays properly. You do:

    ptr = (int *) malloc(sizeof(int) * (num + 1));
    memset(ptr, 0, num);

Which allocated space for num+1 ints, but only clears the first num bytes (which is probably just the first quarter of the array). You want

    memset(ptr, 0, sizeof(int) * (num + 1));

that is, the argument to memset should be the same as the argument to malloc. Or you could just use calloc instead.


Fixing the memset problem fixes the "Conditional jump or move depends on uninitialised value" from valgrind but does not affect the stack overflow.

Reversing the order of the "opeType" enum values delays the stack overflow -- more than doubling how large an input is required to trigger it -- but it still happens.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM