Reputation:
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
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
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