Ziyi
Ziyi

Reputation: 1

Segmentation fault - Trying to read binary file into memory

I can't seem to load a binary file into the memory for better read performance compared to the more expensive reads to the actual file. The file is 124 MB and should be able to fit entirely into memory. This is in C, compiled by GCC 6.3 on a 84_64 GNU/Linux.

Something goes wrong when trying to access blk* from the fread.

The two malloc calls that I tried are:

uint8_t *blk = malloc(sizeof(uint8_t) * fileSize + 1);
uint8_t *blk = (uint8_t *) malloc(sizeof(uint8_t) * fileSize + 1);

And checked to see if malloc returned NULL but it didn't.

        FILE *file = fopen("path", "rb");

        fseek(file, 0, SEEK_END); 
        long fileSize = ftell(file);

        if (ftell(file) != EOF) {
            printf("Ftell not EOF);
        }

        fseek(file, 0, SEEK_SET);

        uint8_t *blk = malloc(sizeof(uint8_t) * fileSize + 1);

        if (file != NULL) {
            printf("File Not NULL and %d\n", fileSize);
        }
        if (blk) {
            printf("Not NULL\n");
        } else {
            printf("NULL\n");
        }

        fread(blk, 1, fileSize, file);
        fclose(file);

        printf("blk: %p | %d\n", *&blk, blk);

The output is:

    Ftell not EOF
    File Not NULL and 134215964
    blk: 0x7fffffffdcc0 | -9024 
    Not NULL
    Segmentation fault

Print format might be wrong but it shouldn't matter for a segmentation error.

Upvotes: 0

Views: 408

Answers (1)

David C. Rankin
David C. Rankin

Reputation: 84642

If you haven't figured it out yet, your segmentation fault is cause by:

 printf("blk: %p | %d\n", *&blk, blk);

Due to your attempt to print blk (a pointer to uint8_t) as an integer. A mismatch between argument types and the printf format specifier invokes Undefined Behavior.

C11 Standard - 7.21.6.1 The fprintf function(p9) " If a conversion specification is invalid, the behavior is undefined. If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined."

Note also, you use of '*&' before blk in that statement is superfluous. It is simply blk. Dereferencing the address of a pointer is simply the pointer itself. You can correct your statement to print the pointer address and first byte in blk by using the proper exact width macros from inttypes.h, e.g.

printf("blk: %p | 0x%02" PRIx8 "\n", (void*)blk, *blk);

In your allocation, there is no need for fileSize + 1, unless you expect to use a hack to affirmatively nul-terminate blk so it can be used as a string. It can be convenient in some cases, but it's not generally recommended. When ftell returns the number of bytes in the file, that's all you need to allocate for, unless you plan on adding something at the end. Further, sizeof(uint8_t) like sizeof(char) is always 1 - it too is superfluous, e,g,

    if (!(blk = malloc (filesize))) {       /* validate allocation */
        perror ("malloc-blk");
        return 1;
    }

Further, WhozCraig was trying to convey to you that each step should be validated. With proper validation, there isn't any question where the code is failing. Adding validations to each step would look something similar to:

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

int main (int argc, char **argv) {

    uint8_t *blk;
    long filesize;
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

    if (fseek (fp, 0, SEEK_END) == -1) {    /* validate seek end */
        perror ("fseek-SEEK_END");
        return 1;
    }
    if ((filesize = ftell (fp)) == -1) {    /* validate ftell */
        perror ("ftell-fp");
        return 1;
    }
    if (fseek (fp, 0, SEEK_SET) == -1) {    /* validate seek set */
        perror ("fseek-SEEK_SET");
        return 1;
    }

    if (!(blk = malloc (filesize))) {       /* validate allocation */
        perror ("malloc-blk");
        return 1;
    }

    if (fread (blk, 1, filesize, fp) != (size_t)filesize) { /* validate read */
        perror ("fread-blk");
        return 1;
    }

    if (fp != stdin) fclose (fp);   /* close file if not stdin */

    /* do what you need with blk here */
    printf("blk: %p | 0x%02" PRIx8 "\n", (void*)blk, *blk);

    free (blk);
}

(note: don't forget to free (blk); when you are done with its use)

Example Use/Output

Running the code as-is against any file will simply output the pointer address for blk and the first byte in the file in 2-digit hex, e.g.

$ ./bin/rdfileintoblk ../dat/captnjack.txt
blk: 0x17e9240 | 0x54

Look things over and let me know if you have any questions.

Upvotes: 2

Related Questions