I've been inspecting the heap memory when executing the following code:
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
struct data {
char name[64];
};
struct fp {
int (*fp)();
};
void winner()
{
printf("level passed\n");
}
void nowinner()
{
printf("level has not been passed\n");
}
int main(int argc, char **argv)
{
struct data *d;
struct fp *f;
d = malloc(sizeof(struct data));
f = malloc(sizeof(struct fp));
f->fp = nowinner;
printf("data is at %p, fp is at %p\n", d, f);
strcpy(d->name, argv[1]);
f->fp();
}
The code is compiled like this:
gcc winner.c -w -g -fno-stack-protector -z norelro -z execstack -o winner
(More code will be added later on so tags like fno-stack-protector
are there)
I executed the project with argument "HELLO" and I set a break point on f->fp()
and inspect the heap memory:
Everything after the first malloc()
makes sense but I'm kinda puzzled about what happened after the second malloc()
. The second chunk should only request 4 bytes of memory to store the function pointer but instead it took 12 bytes, which reflects on what is stored on the address 0x804a04c
(4 bytes of metadata + 12 bytes of requested memory + status bit 1 = 17 => 0x00000011).
And as you can see, the function pointer did only take up four bytes on 0x804a050
with the address of nowinner
(0x080484a1
).
I read up on this SO post and this article but it seems it still can't explain why.
Your initial question can be answered very easily by printing sizeof
of your pointer. You will not see a 12 here.
The answer to your question "Why is function pointer 12 bytes long?" is simply: "It's not!"
But your question describes a different underlying question: "Why does allocating 4 bytes take 12 bytes on the heap?"
You are under the wrong impression that memory allocation only takes exactly what is needed to store the user data.
This is wrong.
Memory management also needs to store some management data for each allocation.
When you call free
the runtime library needs to know the size of the allocated block.
Therefore you can take it as granted that every allocation consumes more memory than the requested amount.
Depending on the implementation of the heap this can be within the heap itself or in a separate area. You can also not rely on taking the same amount of overhead for each allocation. There are weird implementation out there.
Some implementations take the requested amount and add fixed length of management data. Some implementations use a buddy system and follow a sequence of fibonacci numbers to determine smallest suitable block size.
User contributions licensed under CC BY-SA 3.0