2

I'm fairly new to C, and today I was introduced to Valgrind. I installed it and ran it on my C calculator/equation parser that I'm working on to figure out why I was having a segmentation fault (core dumped), and I got this:

==20== Process terminating with default action of signal 11 (SIGSEGV): dumping core
==20==  General Protection Fault
==20==    at 0x4008E27: _dl_map_object (dl-load.c:2317)
==20==    by 0x40014DD: map_doit (rtld.c:642)
==20==    by 0x4010193: _dl_catch_error (dl-error.c:187)
==20==    by 0x4002169: do_preload (rtld.c:831)
==20==    by 0x4002169: handle_ld_preload (rtld.c:929)
==20==    by 0x4004DEE: dl_main (rtld.c:1667)
==20==    by 0x40176F4: _dl_sysdep_start (dl-sysdep.c:249)
==20==    by 0x4001BB7: _dl_start_final (rtld.c:347)
==20==    by 0x4001BB7: _dl_start (rtld.c:573)
==20==    by 0x4001267: ??? (in /lib/x86_64-linux-gnu/ld-2.19.so)
==20==    by 0x1: ???
==20==    by 0x1FFF0008AE: ???
==20==    by 0x1FFF0008BB: ???

Of course, I have no idea what it means, and the other things I've found about similar errors haven't made much sense to me. Can someone explain this in a relatively simple way that someone like me can understand?

EDIT: I tried running it through gdb (ass suggested by @pm100), and only got this:

Program received signal SIGSEGV, Segmentation fault. 0x000000000040067b in ?? ()

EDIT: Since my code was asked for, here it is. I'm probably doing a lot wrong.

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

void write(char** dest, char* src, int index) {
    int i = 0;
    for (i = 0; src[i] != '\0'; i++) {
        dest[index][i] = src[i];
    }
    dest[index][i] = '\0';
    return;
}

void crite(char** dest, char src, int index) {
    int i = 0;
    dest[index][0] = src;
    dest[index][1] = '\0';
    return;
}

void evaluate(char* args) {
    int i = 0;
    int j = 0;
    const char* numbers = "1234567890";
    const char* operators = "+-*/";
    int chunk = 0;
    char** chunks = calloc(24, sizeof(char*));
    char* current = calloc(24, sizeof(char));


    for (i = 0; strchr("\0\n", args[i]) == NULL; i++) {
        //printf("Args[i]:%c\n\n", args[i]);
        if (strchr(numbers, args[i]) != NULL) {
            //printf("Number added to current: %c\n\n", args[i]);
            current[j] = args[i];
            //printf("\nCurrent: %s\n", current);
            j++;
        } else if (strchr(operators, args[i]) != NULL) {
            write(chunks, current, chunk);
            chunk++;
            crite(chunks, args[i], chunk);
            chunk++;
            j = 0;
            free(current);
            current = calloc(24, sizeof(char));
            //printf("Terminated with operator and operator added.\n\n");
        } else {
            printf("ERROR: Encountered invalid token.\n\n");
            return;
        }

    }

    for (i = 0; chunks[i] != NULL; i++) 
        //printf("\n-Chunk: %s\n\n", chunks[chunk]);

    return;
}

int main(int argc, char** argv) {
    evaluate(argv[1]);
}

The command I used to compile it was gcc calculator.c -g -o calculator

Sample command: ./calculator 1*2

UPDATE: The issue with Valgrind was caused by the Windows subsystem I was using, so as long as you're running Valgrind on linux it should be fine. I tried it in a VM and it worked.

Also, thanks for helping me fix my Segmentation Fault even though that wasn't what the question was originally about:)

Wiooooo
  • 25
  • 8
  • 1
    probably better off to compile your program with -g and run it under gdb. You will get a better set of messages. But that is a thoroughly dead program. How did you build it? – pm100 Jul 13 '17 at 23:13
  • @pm100 Ok, I'll try that. – Wiooooo Jul 13 '17 at 23:13
  • The message means you don't have any signal handler in place to catch SIGSEGV (and you normally shouldn't — that's perfectly OK), and that Valgrind is going to let the process die after it has done some work on the statistics. As already noted, you should compile all your code with dbug enabled (`-g`) — in fact, you should do that whether you turn optimization on or not. Then Valgrind will point to function names, file names and line numbers in your code too. Running Valgrind on non-debug code is pointless if it is reporting any trouble — if it reports no problems, it doesn't really matter. – Jonathan Leffler Jul 13 '17 at 23:17
  • This isn't really a valgrind error. It's the same as if you ran the program normally and it crashed with a segmentation violation. – Barmar Jul 13 '17 at 23:18
  • @Barmar I am aware of that, I was wondering what it meant and if it said what the actual error was in my C program. – Wiooooo Jul 13 '17 at 23:20
  • There was most likely an error message printed earlier that points to the problem. What does that say? – dbush Jul 13 '17 at 23:21
  • @JonathanLeffler I tried running valgrind on it after compiling it again with `-g`. I got the exact same thing except the numbers surrounded by `==` are 66 instead of 20... – Wiooooo Jul 13 '17 at 23:23
  • You missed something in the recompilation. You need the `-g` while creating the object files; you also need it in the linker (linking) command line. – Jonathan Leffler Jul 13 '17 at 23:24
  • @dbush this: `Memcheck, a memory error detector ==66== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==66== Using Valgrind-3.14.0.SVN and LibVEX; rerun with -h for copyright info ==66== Command: ./calculator-g 1*2` was the only thing above that. – Wiooooo Jul 13 '17 at 23:24
  • @JonathanLeffler I used `gcc calculator.c -g -o calculator` to compile it, what else do I need to do? – Wiooooo Jul 13 '17 at 23:26
  • Um @Barmar this has nothing to do with that question. This isn't asking why there's a segmentation fault, it's asking what this Valgrind error means/how to get Valgrind to work correctly if it isn't... Could you please un-duplicate it? – Wiooooo Jul 13 '17 at 23:28
  • Cry? If that doesn't help...make sure you run `calculator-g` and not `calculator`, though you show that in a previous comment. Worry about why your compiler and Valgrind are not compatible? I see you're building your own from Subversion — or someone is building the version from Subversion — rather than a release tarball. That's usually good (it's what I currently do). I've not seen that problem before. Platform? GCC version? – Jonathan Leffler Jul 13 '17 at 23:28
  • 1
    You can enable some warnings by compiling with `-Wall` and `-Wextra`, that will probably point you to some troublesome spots. As already mentioned, runining it under `gdb` will at least tell you where its crashing (doesn't necessarily mean the bug is there, however). – yano Jul 13 '17 at 23:29
  • @JonathanLeffler I'm using the Ubuntu subsystem on Windows 10 (ew, I know, but my laptop that has Debian on it is being weird lately), and I'm using gcc 4.8.4. I used Subversion because that's the only way to get Valgrind to work on the Windows subsystem. – Wiooooo Jul 13 '17 at 23:31
  • @Wiooooo I've reopened it, but I still think it's the same. You got a segmentation fault when you ran the program with `gdb`, so the problem is a bug in your program, just like all the other segmentation faults. `Valgrind` is simply reporting that while it was running your program, it crashed with this fault. – Barmar Jul 13 '17 at 23:31
  • 1
    @Barmar Thank you for reopening it. I see where you're coming from, but I still think that it's a more Valgrind-related question as I'm asking how to get Valgrind to correctly display the error in an understandable way/how to interpret the errors it's currently displaying, not how to fix my segmentation fault. – Wiooooo Jul 13 '17 at 23:33
  • 1
    That's not an environment I've used — nor one I have a way of using (no Windows machine). Normally, I'd expect it to 'just work'. The fact that the debugger is also have problems may be indicative. There's one other possibility — your code is trashing the stack so thoroughly that neither Valgrind nor GDB can work out where the calls were made from. That would take a seriously large buffer overflow of a stack-allocated array. It feels unlikely for a calculator program, but it is something to look for. – Jonathan Leffler Jul 13 '17 at 23:35
  • Try running the code in GDB again, but put a breakpoint in early on and look to see whether you get good stack backtraces before any damage has been done. (`b main` to stop at the beginning, for example.) – Jonathan Leffler Jul 13 '17 at 23:36
  • @JonathanLeffler I'll look through my code again and see if I can find any insanely large errors (though I highly doubt that there are any), then I'll make a VM and try running Valgrind and the GCC compiler in that to see if that works. – Wiooooo Jul 13 '17 at 23:37
  • @Wiooooo Valgrind is generally not used to debug this type of error. It tries to detect memory use problems *before* they corrupt the environment and cause a crash. If it got this far, valgrind failed in its attempt, and it probably can't give any more information. – Barmar Jul 13 '17 at 23:40
  • @Barmar Ok... are there any other debuggers that you'd recommend running this through that could possibly catch this? – Wiooooo Jul 13 '17 at 23:42
  • dont use valgrind. compile with -g and run under gdb. If you really are just getting that numeric output in that case then you are doing something really wierd. Please paste the code and the exact command you used to build it – pm100 Jul 13 '17 at 23:48
  • In my experience it's not all that hard to smash the stack so that gdb is unable to give a helpful backtrace. But usually valgrind will catch something before that happens. Is that really the very first valgrind error reported? – aschepler Jul 13 '17 at 23:49
  • @aschepler As I said, I'm fairly new to C... I don't really know what 'smashing the stack' is. – Wiooooo Jul 13 '17 at 23:50
  • @pm100 the code has been added to the question. – Wiooooo Jul 13 '17 at 23:54
  • and the command used to compile it – pm100 Jul 13 '17 at 23:55
  • and sample input – pm100 Jul 13 '17 at 23:55
  • @pm100 done and done – Wiooooo Jul 13 '17 at 23:57
  • I don't get your results. I see an attempted write to a null pointer, with no damage to the stack trace. One possible cause of "??" in gdb is running on an executable you compiled without "-g"... – aschepler Jul 14 '17 at 00:18
  • `for (...; strchr("\0\n", args[i]); ...)`?? Why not `for (...; args[i] && args[i] != '\n'; ...)`? – David C. Rankin Jul 14 '17 at 00:20
  • BTW `strchr("\0\n",` --> `strchr("\n",` – BLUEPIXY Jul 14 '17 at 00:28
  • @BLUEPIXY is it? Isn't `\n` the newline character and `\0` the null character? – Wiooooo Jul 14 '17 at 01:40
  • A string literal has NUL(`'\0'`) at the end of the string. E.g `"\n"` is `{ '\n', '\0' }`. In the case of `"\0\n"`, `\n` isn't recognized by `strchr`. – BLUEPIXY Jul 14 '17 at 01:47
  • @BLUEPIXY ok, thanks! – Wiooooo Jul 14 '17 at 01:54
  • See [DEMO](http://ideone.com/Uf3sll) – BLUEPIXY Jul 14 '17 at 01:54

3 Answers3

5

Running this code under valgrind resulting in the following:

[dbush@db-centos ~]$ valgrind /tmp/x1 1*2
==1431== Memcheck, a memory error detector
==1431== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==1431== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info
==1431== Command: /tmp/x1 1*2
==1431== 
==1431== Invalid write of size 1
==1431==    at 0x80484B3: write (x1.c:8)
==1431==    by 0x80485E8: evaluate (x1.c:39)
==1431==    by 0x804869D: main (x1.c:61)
==1431==  Address 0x0 is not stack'd, malloc'd or (recently) free'd
==1431== 
==1431== 
==1431== Process terminating with default action of signal 11 (SIGSEGV)
==1431==  Access not within mapped region at address 0x0
==1431==    at 0x80484B3: write (x1.c:8)
==1431==    by 0x80485E8: evaluate (x1.c:39)
==1431==    by 0x804869D: main (x1.c:61)
==1431==  If you believe this happened as a result of a stack
==1431==  overflow in your program's main thread (unlikely but
==1431==  possible), you can try to increase the size of the
==1431==  main thread stack using the --main-stacksize= flag.
==1431==  The main thread stack size used in this run was 10485760.
==1431== 
==1431== HEAP SUMMARY:
==1431==     in use at exit: 120 bytes in 2 blocks
==1431==   total heap usage: 2 allocs, 0 frees, 120 bytes allocated
==1431== 
==1431== LEAK SUMMARY:
==1431==    definitely lost: 0 bytes in 0 blocks
==1431==    indirectly lost: 0 bytes in 0 blocks
==1431==      possibly lost: 0 bytes in 0 blocks
==1431==    still reachable: 120 bytes in 2 blocks
==1431==         suppressed: 0 bytes in 0 blocks
==1431== Rerun with --leak-check=full to see details of leaked memory
==1431== 
==1431== For counts of detected and suppressed errors, rerun with: -v
==1431== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 13 from 8)

So at this line in write:

dest[index][i] = src[i];

You're dereferencing a NULL pointer and attempting to write there. The NULL pointer in question is dest[index], which is NULL since you used calloc to allocate the memory. Here's what you did do:

char** chunks = calloc(24, sizeof(char*));
char* current = calloc(24, sizeof(char));

You creating chunks as an array of pointers, but you didn't assign anything to those pointers. You need to loop through each array element and allocate space for each one:

char** chunks = calloc(24, sizeof(char*));
for (i=0; i<24; i++) {
    chunks[i] = calloc(24, sizeof(char));
}

Alternately, you can allocate this memory in write and crite before copying:

void write(char** dest, char* src, int index) {
    int i = 0;
    dest[index] = calloc(24, sizeof(char));
    for (i = 0; src[i] != '\0'; i++) {
        dest[index][i] = src[i];
    }
    dest[index][i] = '\0';
    return;
}

void crite(char** dest, char src, int index) {
    int i = 0;
    dest[index] = calloc(2, sizeof(char));
    dest[index][0] = src;
    dest[index][1] = '\0';
    return;
}
dbush
  • 205,898
  • 23
  • 218
  • 273
  • What? You mean I can't just write to the address contained in an uninitialized pointer and make it work ? `:)` – David C. Rankin Jul 14 '17 at 00:23
  • They're not uninitialized pointers - they have been filled with `'\0'` bytes (which very likely makes them null pointer values). – aschepler Jul 14 '17 at 00:29
1

This is a stack trace from deep within the Linux loader. Valgrind creates a sandbox execution environment and loads your program into that, where its various tools can insert their own instrumentation code into your instruction stream.

This is exotic stuff: very dependent a good valgrind build and care in building your program under test correctly. This tiny stack trace alone can't possibly explain what's going wrong. The loader could be dying while trying to load valgrind. Or perhaps its dying within the sandbox as its trying to load you program. The problem could be in the valgrind binary or the binary for your program, causing the Linux loader to fail. It's even possible (I don't know) that valgrind includes its own copy of the loader, and that copy built incorrectly and so is dying.

The bigger picture is that valgrind isn't a good tool do debug a simple seg fault in your (I expect) small program. Two things much more likely to bear fruit are to build and run the program correctly so that gdb produces a correct symbolic stack trace and to simply insert fprintfs.

This ought to produce a stack trace with gdb:

$ gcc calculator.c -g -o calculator
$ gdb ./calculator
(gdb) run

If you need command line args to trigger the bug, you can give them with set args, e.g.

(gdb) set args 1*2
(gdb) run

If you're not seeing a stack trace, your build environment is almost certainly broken: there's something wrong with your compiler or gdb.

The other technique, which isn't very elegant but nonetheless can be effective is to add fprintf(stderr, ...)s starting with the first line in main() and periodically after. The last output tells you how far the execution got. When you're a beginning programmer, it can be more efficient to avoid learning new tools until you're familiar with the language, compiler, and write-compile-debug-revise cycle.

Gene
  • 46,253
  • 4
  • 58
  • 96
0

When I run your program I get a complaint from gdb about line 8 (my cut and paste wont work)

It seems like there is somehting serioulsy wrong with your toolchain

Can you even write and run a hello world program?

#include <stdio.h>
int main()
{
    printf("hello world\n");
}
Wiooooo
  • 25
  • 8
pm100
  • 48,078
  • 23
  • 82
  • 145
  • Uhhh... yes? Also, you made a mistake. The `\n` needs to be in the string. I'll fix that for you. – Wiooooo Jul 14 '17 at 01:45