-
-
[原创]在GDK8下观察glibc堆
-
发表于: 2021-11-16 16:24 10714
-
GDK8产品主页:Nano Code
glibc堆布局介绍
在程序运行过程中,堆可以提供动态分配的内存,允许程序申请大小未知的内存。堆其实就是程序虚拟地址空间的一块连续的线性区域,它由低地址向高地址方向增长。我们一般称管理堆的那部分程序为堆管理器。
堆在系统中 的布局可以分成两大类,分别是main_arena主场地和non_main_aren辅场地。
main_arena主场地的布局如下图所示:
在main_arena中,当程序第一次动态分配内存的时候,堆会被分成两块,一块是分配区,另一块是Top Chunk,Top Chunk的起始地址亦是top指针所指向的位置;在之后动态分配内存的过程中,只要Top Chunk的位置还够用,分配区就会从Top Chunk中划分空间供自己使用,但是假如Top Chunk中的空间不够用了,那么就会通过brk机制扩展空间。
在main_arena中的top指针记录了分配区的结束位置的地址,top指针的上方为top chunk;而分配区的开始位置的地址,如下图所示。non_main_aren辅场地布局如下图所示:
non_main_arena中如果分配内存数量不够大的话,那么分配区的开始位置的地址和结束位置的地址同main_arena中的情况是一样的;但是如果分配内存数量足够的大,那么在non_main_arena中的分配区会划分成1个又1个的子堆;不管non_main_arena是否划分了子堆,在主子堆中,分配区的开始位置永远是[bins指针+mchunk_size(去标志位;bins指针对应malloc_chunk中的mchunk_size)],而其他子堆分配区的起始位置的地址为[heap info的地址+0x20]。
假如想要获取全部的子堆,就要遍历一下链表,不过这个链表并不是循环的,而且需要依靠perv指针找上一个),直到找到第1个为止。
注意:主子堆的heap info内的prev指针永远是空(主子堆一般排在链表的第一个),此链表并不是循环的chunk的布局如下图所示:
无论是空闲chunk还是被分配的chunk,它们都是由malloc_chunk结构体进行描述,关于空闲的chunk并不会介绍太多,主要介绍一下被分配的chunk;在被分配的chunk中,假如上一个chunk尚未被释放,那么一般情况下prev size中不会记录任何东西(当然也有记录的时候,见下方),但是如果上一个chunk已经被释放了,那么perv size的大小就是上一个块的大小;而size中则会记录当前块的大小及当前块的属性,标志位的具体介绍见下方。
- 需要注意的是当前块的大小必是 2*SIZE_SZ 的整数倍;如果没有清除标志位,那么size的大小可能就不是2*SIZE_SZ的整数倍了。
- 比特0:记录当前块是否已经被分配;0表示不属于,1表示属于。
- 比特1:记录当前块是否是由mmap分配的;0表示不属于,1表示属于。