I can get the address of the end of the heap with sbrk(0)
, but is there any way to programmatically get the address of the start of the heap, other than by parsing the contents of /proc/self/maps
?
-
It makes me confused...if I get a heap with p = (int *) malloc (sizeof(int)); , then why I can't get the start address of the heap by p? – bazysong Mar 30 '16 at 06:23
2 Answers
I think parsing /proc/self/maps
is the only reliable way on the Linux to find the heap segment. And do not forget that some allocators (including one in my SLES) do use for large blocks mmap()
thus the memory isn't part of the heap anymore and can be at any random location.
Otherwise, normally ld
adds a symbol which marks the end of all segments in elf and the symbol is called _end
. E.g.:
extern void *_end;
printf( "%p\n", &_end );
It matches the end of the .bss
, traditionally the last segment of elf. After the address, with some alignment, normally follows the heap. Stack(s) and mmap()s (including the shared libraries) are at the higher addresses of the address space.
I'm not sure how portable it is, but apparently it works same way on the Solaris 10. On HP-UX 11 the map looks different and heap appears to be merged with data segment, but allocations do happen after the _end
. On AIX, procmap
doesn't show heap/data segment at all, but allocations too get the addresses past the _end
symbol. So it seems to be at the moment quite portable.
Though, all considered, I'm not sure how useful that is.
P.S. The test program:
#include <stdio.h>
#include <stdlib.h>
char *ppp1 = "hello world";
char ppp0[] = "hello world";
extern void *_end; /* any type would do, only its address is important */
int main()
{
void *p = calloc(10000,1);
printf( "end:%p heap:%p rodata:%p data:%p\n", &_end, p, ppp1, ppp0 );
sleep(10000); /* sleep to give chance to look at the process memory map */
return 0;
}

- 16,630
- 5
- 41
- 63
-
1Well, if no way to found the heap starting address, how is malloc implemented, which is trying to split heap into smaller memory chunks? – Jun Dec 04 '13 at 17:34
-
Note that this may fail in a system with randomized offsets. It can be enabled, but by default linux fuzzes where the heap "starts" so it's harder to find it from the outside of the machine (preventing buffer overflow attacks). – jwarner112 Mar 22 '15 at 23:32
-
@Jun, libc calls `sbrk(0)` to find the address of the start of the heap. But that only works for the libc: it is called very early during the application start-up, before the `main()`, and thus the end of the heap is the same as the beginning of the heap. Inside the `main()` that it is not true anymore. – Dummy00001 Mar 23 '15 at 09:18
You may call sbrk(0)
to get the start of the heap, but you have to make sure no memory has been allocated yet.
The best way to do this is to assign the return value at the very beginning of main()
. Note that many functions do allocate memory under the hood, so a call to sbrk(0)
after a printf
, a memory utility like mtrace
or even a call to putenv
will already return an offset value.
Although much of what we can find say that the heap is right next to .bss, I am not sure what is in the difference between end
and the first break. Reading there seems to results in a segmentation fault.
The difference between the first break and the first address returned by malloc
is, among (probably) other thing:
- the head of the memory double-linked-list, including the next free block
- a structure prefixed to the
malloc
ed block incuding:- the length of this block
- the address of the previous free block
- the address of the next free block
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
void print_heap_line();
int main(int argc, char const *argv[])
{
char* startbreak = sbrk(0);
printf("pid: %d\n", getpid()); // printf is allocating memory
char* lastbreak = sbrk(0);
printf("heap: [%p - %p]\n", startbreak, lastbreak);
long pagesize = sysconf(_SC_PAGESIZE);
long diff = lastbreak - startbreak;
printf("diff: %ld (%ld pages of %ld bytes)\n", diff, diff/pagesize, pagesize);
print_heap_line();
printf("\n\npress a key to finish...");
getchar(); // gives you a chance to inspect /proc/pid/maps yourself
return 0;
}
void print_heap_line() {
int mapsfd = open("/proc/self/maps", O_RDONLY);
if(mapsfd == -1) {
fprintf(stderr, "open() failed: %s.\n", strerror(errno));
exit(1);
}
char maps[BUFSIZ] = "";
if(read(mapsfd, maps, BUFSIZ) == -1){
fprintf(stderr, "read() failed: %s.\n", strerror(errno));
exit(1);
}
if(close(mapsfd) == -1){
fprintf(stderr, "close() failed: %s.\n", strerror(errno));
exit(1);
}
char* line = strtok(maps, "\n");
while((line = strtok(NULL, "\n")) != NULL) {
if(strstr(line, "heap") != NULL) {
printf("\n\nfrom /proc/self/maps:\n%s\n", line);
return;
}
}
}
pid: 29825
heap: [0x55fe05739000 - 0x55fe0575a000]
diff: 135168 (33 pages of 4096 bytes)
from /proc/self/maps:
55fe05739000-55fe0575a000 rw-p 00000000 00:00 0 [heap]
press a key to finish...

- 78
- 5