当我使用brk系统调用时,我分配的内存实际上是从哪里开始的



我试图在NASM/x86汇编中使用sys_brk分配一些内存。Sys_break返回break的新地址,这是数据段的结束,对吗?那么我新分配的内存在哪里呢?我假设它在旧的断点值和新的断点值之间。因此,如果我用sys_brk分配64字节的内存,我可以使用从调用sys_brk之前存储的旧break值开始的下一个64字节。我说的对吗?

我的汇编代码将分配内存看起来像这样https://gist.github.com/nikAizuddin/f4132721126257ec4345

另一个侧面问题是;

我应该在汇编中编写一个函数,返回指向动态分配内存的指针,该函数将从C程序中调用。我怎样才能从我的程序的C端释放这个内存块?仅仅调用free()就足够了吗?

brk(2)手册页(部分:C库/内核ABI差异)描述了如何在Linux的系统调用上实现glibc包装器,它在成功时返回新brk,或在失败时返回旧brk。

据我所知,当前断点之外的内存是未映射的。当前断点下面的地址是数据段的一部分(在data+bss+heap的意义上)。文档没有明确说明是否必须对齐。(也就是说,你能sbrk(64)还是只能sbrk(4096)?)如果ASLR被启用,初始中断将是BSS结束后的一段随机距离。

参见:brk()系统调用是做什么的?关于这个问题的答案有一个使用sbrk代替malloc的示例。所以是的,旧的break是要返回的地址。显然,您可以sbrk任何您想要的增量,而不仅仅是页面。


是你在写内存分配器。sbrk只是让你从操作系统中获得更多,就像mmap(MAP_ANONYMOUS)一样,但不那么灵活。它不能帮助您跟踪空闲块,因此您可以将它们用于将来的分配,而不是总是从操作系统获得更多。

sbrk返回内存的方法是用一个负参数调用sbrk。显然,这需要后进先出的使用模式,这就是为什么glibc的malloc只在小的分配中使用sbrk(当被释放时可以放在free-list上,为将来的malloc分发)。大的分配最好立即返回给操作系统,而不是保持映射,所以glibc的malloc使用mmap

永远不要在你没有从malloc(3)获得的内存上调用free(3)(或者一个相关的函数,比如strdup(3),在文档中说你可以并且应该free(3)内存)。IDK如果在程序断点下面的内存页上调用munmap会发生什么。也许它只是工作,但你会在你的数据段有一个洞,可能会导致问题,如果中断曾经减少到那里。


在汇编中,Linux brk系统调用接受您想要设置中断的地址。如手册页所述,它要么返回成功,要么在失败时返回旧的break,而不是像-ENOMEM那样的-errno代码。

参见Assembly x86 brk() call use获取x86-64示例。

在POSIX API中,你可以使用正负整数偏移量,你可以通过总是调用两次来实现,或者像glibc一样跟踪全局变量中的当前断点。要初始化该变量,使用brk一次,请求地址为0,这将失败,如下面的strace输出所示。

这与您使用POSIX API所做的类似,用increment = 0调用sbrk

这是glibc的malloc(3)内部做的:

$ strace -e brk ls 2>&1 | m
brk(0)                                  = 0x650000
brk(0)                                  = 0x650000
brk(0x671000)                           = 0x671000

brk手册页提到end(3)。显然,全局变量位于文本、数据和bss段的末尾。然而,&end只是"接近"。程序中断,这就是为什么malloc仍然需要进行系统调用来获得初始中断。我不知道为什么有一个冗余的brk(0)。这些是原始的系统调用,而不是库函数调用,因此sbrk(0)可能无法解释它。

相关内容

  • 没有找到相关文章

最新更新