Bison+Flex segfault no backtrace

2019-06-14 18:05发布

问题:

I'm trying to debug code generated by Bison + Flex (what a joy!). It segfaults so badly that there isn't even stack information available to gdb. Is there any way to make this combination generate code that's more debuggable?

Note that I'm trying to compile a reentrant lexer and parser (which is in itself a huge pain).

Below is the program that tries to use the yyparse:

int main(int argc, char** argv) {
    int res;
    if (argc == 2) {
        yyscan_t yyscanner;
        res = yylex_init(&yyscanner);
        if (res != 0) {
           fprintf(stderr, "Couldn't initialize scanner\n");
           return res;
        }
        FILE* h = fopen(argv[1], "rb");

        if (h == NULL) {
            fprintf(stderr, "Couldn't open: %s\n", argv[1]);
            return errno;
        }
        yyset_in(h, yyscanner);
        fprintf(stderr, "Scanner set\n");
        res = yyparse(&yyscanner);
        fprintf(stderr, "Parsed\n");
        yylex_destroy(&yyscanner);
        return res;
    }
    if (argc > 2) {
        fprintf(stderr, "Wrong number of arguments\n");
    }
    print_usage();
    return 1;
}

Trying to run this gives:

(gdb) r
Starting program: /.../program 
[Inferior 1 (process 3292) exited with code 01]

Note 2: I'm passing -d to flex and -t to bison.


After shuffling the code around I was able to get backtrace. But... it appears that passing -t has zero effect as does %debug directive in *.y file. The only way to get traces is to set yydebug = 1 in your code.

回答1:

You are clobbering the stack by passing the address of yyscanner instead of its value to yyparse. Once the stack has been overwritten in that fashion, even gdb will be unable to provide accurate backtraces.

The -d and %debug directives cause bison to emit the code necessary to perform debugging traces. (This makes the parser code somewhat larger and a tiny bit slower, so it is not enabled by default.) That is necessary for tracing to work, but you still have to request traces by setting yydebug to a non-zero value.

This is mentioned right at the beginning of the Bison manual section on tracing: (emphasis added)

8.4.1 Enabling Traces

There are several means to enable compilation of trace facilities

And slightly later on:

Once you have compiled the program with trace facilities, the way to request a trace is to store a nonzero value in the variable yydebug. You can do this by making the C code do it (in main, perhaps), or you can alter the value with a C debugger.

Unless you are working in an extremely resource-constrained environment, I suggest you always use the -t option, as do the Bison authors:

We suggest that you always enable the trace option so that debugging is always possible.