I was looking into the implementation of malloc defined here:
http://www.inf.udec.cl/~leo/Malloc_tutorial.pdf.
The author create a metadata structure that is naturally aligned on a 4 byte boundary and then aligns a request for x bytes to a 4 byte boundary after the metadata structure which effectively acts as a header for the block. The pdf states that since the metadata and the request are now aligned , the resultant data will be completely aligned. The result works out if the first call to sbrk() returns a base address for the heap that is aligned to a 4-byte boundary. Does sbrk() always return a 4 byte (or 8 byte in case of 64-bit systems) aligned address in the initial call ?
The standard for brk
and sbrk
explicitly does not specify whether the returned address is aligned in any way. On Mac OS X (and maybe other BSD systems) the sizes/addresses are page-aligned, but on Linux no such rounding takes place as can easily be tested with this little program:
#include <unistd.h>
#include <stdio.h>
int main() {
void *p;
p = sbrk(0);
printf("Initial brk: %p\n", p);
p = sbrk(1); // Increase the brk (returns OLD brk!)
p = sbrk(0); // Get the new brk
printf("New brk: %p\n", p);
return 0;
}
On one of my systems, the output was:
Initial brk: 0x602000
New brk: 0x602001
But you asked for the initial call. The Linux man-page states:
brk() and sbrk() change the location of the program break, which defines the end of the process's data segment (i.e., the program break is the first location after the end of the uninitialized data segment). Increasing the program break has the effect of allocating memory to the process; decreasing the break deallocates memory.
The unitialized data segment is also known as BSS. The keyword here is segment, so it's very likely the initial value is always page-aligned.
If you want to be on the safe side and check, you can verify the initial address by taking the modulo with the page size (which you can query via getpagesize
).
Update: So I was curious and dug around a bit more. In the man-page, I already read that brk
and sbrk
are implemented atop the kernel's sys_brk
. Its implementation in the kernel source can be found in mm/mmap.c
(or mm/nommu.c
for systems without a Memory Management Unit; we'll ignore this one). In the brk
implementation in mm/mmap.c
, we find this line:
newbrk = PAGE_ALIGN(brk);
("brk" here is the argument, not the function.) So the kernel does do page aligning… sort of: while the calculations are done with the page-aligned values and any necessary memory allocation is page-aligned, the value stored for the brk is actually the pointer value you passed:
mm->brk = brk;
So in the user-space it doesn't look like any page-algning took place even though the kernel did. I looked at versions 3.17.5 and 2.4.37, the behaviour is the same.
Regarding the initial value, in fs/binfmt_elf.c
(which implements ELF linking) we find a function set_brk
which sets the initial "brk" value (mm->start_brk
). This value is explicitly page-aligned. The same is true for fs/binfmt_aout.c
which handles the old a.out format and fs/binfmt_som.c
which handles HP-UX SOM format (never heard of it before). There's also fs/binfmt_flat.c
which sets the initial brk value but doesn't align explicitly; the value is implicitly aligned here. So it looks like the initial value is always page-aligned. At least it's guaranteed to be page-aligned for ELF files, which is what we care about for "normal" systems.
The glibc simply wraps sys_brk
and adds bookkeeping to correctly implement sbrk
. So glibc's brk
behaviour is that of the kernel, the return value sys_brk
is stored in an internal hidden variable __curbrk
so that sbrk
can calculate the new address correctly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With