user3844996
user3844996

Reputation:

Segfault on specifying string in struct field to use in other functions

I have declared the following struct. In the second function, I am declaring a struct of that type and then try and fill in the string name field. However, if I try to access this field in other functions, it is giving me a seg-fault. Is everything declared correctly, or does it require a malloc? Also, other fields in this struct appear to be stored properly, except the string name.

typedef struct {
unsigned int   name;                        // offset into string table for symbol name
uintptr_t      addr;                    // symbol address
unsigned int   size;                        // symbol size in bytes 
} Symbol;

typedef struct {
    char *name;
    unsigned int address;
    int size;
    char binding;
} PrintedSymbol;

One of the functions creates a struct like this:

PrintedSymbol* create_symbols(char *string_data, Symbol *symbols) {

    Symbol symbol;
    int count = 0;

    int size = 22;
    PrintedSymbol *arr = malloc(sizeof(PrintedSymbol) * size);

    for (int i = 0; i < size; i++) {

        PrintedSymbol unique;
        symbol = symbols[i];

        char *str_location = (char *) string_data + symbol.name;
        char *str_name = strdup(str_location);
        unique.name = str_name;

        arr[count] = unique;
        count++;

    }

    Array printthis;
    printthis.arr = arr;
    printthis.count = count;

    return printthis;
}

EDIT: I have also edited the function above - note, I still have not malloced as I am storing it in a struct.

I am trying to print the name by doing the following:

I have stored the array along with its count in another struct like this: typedef struct { PrintedSymbol *arr; int count } Array

This is now what the function PrintedSymbol is returning to plug into this function:

   void output_symbols(Array printthis, char *hex, char *string_data, Symbol *symbols) {

    // set-up variables from struct
    PrintedSymbol *arr = printthis.arr;
    int count = printthis.count;

    if (hex == NULL) {
            print_symbol_table(arr, count); <-- THIS FUNCTION IS PRINTING OUT THE ARRAY PROPERLY

    } else {

            PrintedSymbol key;
            qsort(arr, count, sizeof(key), lex_sort);
            key.address = strtol(hex, NULL, 16);
            key.size = 1;
            size_t symbol_size = sizeof(key);

            void *matching = lfind(&key, arr, &symbol_size, count, compare_address);
            unsigned int hex_int = strtol(hex, NULL, 16);

            if (matching != NULL) {

                    PrintedSymbol matched = *(PrintedSymbol *) matching; 
                    printf("%d and %s\n", hex_int, matched.name, matched.size); <-- printing the SIZE is fine, the NAME gives a segfault!

            } 
    }

When I print with the malloc in, it gives me this:

 00000029 and (Ps
 0000000d and �Oh
 00000014 and �Oh
 00000012 and main
 00000014 and P_
 00000015 and P)

The messed up characters are where the strings should be, everything else is fine. Lfind is the standard C lfind function, I have not defined it.

Printing the size of the symbol being returned by lfind is fine, but not the name, which is giving a seg-fault. Why?

Upvotes: 0

Views: 123

Answers (2)

Patrick Collins
Patrick Collins

Reputation: 10604

As Michael Walz said in a comment: you allocate the array on the stack. After you return, it passes out of scope and the memory associated with it gets overwritten. You need to change this:

PrintedSymbol arr[size];

to:

PrintedSymbol* arr = malloc(sizeof(PrintedSymbol) * size);

Apart from that, you're not doing any bounds checking here:

char *str_location = (char *) string_data + symbol.name;

and something will go wrong if symbol.name puts you outside of string_data.

EDIT: You've made a mistake with your types. Symbol has a member named name, but its an int, not a char*. So when you try to print it, it interprets the bytes it finds as a char* and gives you garbage.

You probably meant:

PrintedSymbol matched = *(PrintedSymbol*) matching; 

EDIT EDIT: lfind has the following signature:

 void*  lfind ( const void * key, const void * base, size_t num, size_t width, int (*fncomparison)(const void *, const void * ) ); 

When you call:

 lfind(&key, arr, &symbol_size, count, compare_address);

and pass &symbol_size as a parameter, it gets a very large value for num. It also looks like you've reversed num and width. This is probably causing unexpected behavior. You probably meant:

 lfind(&key, arr, count, symbol_size, compare_address);

You should turn on -Wall, because gcc will warn you if you try to pass in a size_t* where the function is expecting a size_t.

Upvotes: 2

barak manos
barak manos

Reputation: 30146

The address of a local variable in a function depends on the state of the stack (the value of the SP register) at the point in execution when the function is called.

In other words, this address is not the same every time the function is called.

Therefore, returning the address of a local variable leads to undefined behavior.

With return arr, you are essentially returning the address of arr in memory.

Note that with arrays (unlike other types of variables) there is no such thing as "value".

For example:

int  var;    // mostly var != &var
int* ptr;    // mostly ptr != &ptr
int  arr[4]; // always arr == &arr

So while you could safely return var or ptr after initializing them properly, you may not do so with arr.

Upvotes: 0

Related Questions