我试图理解printfC中的一个简单案例是如何工作的.我写了以下程序:
#include "stdio.h"
int main(int argc, char const *argv[])
{
printf("Test %s\n", argv[1]);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
运行objdump在二元我注意到Test %s\n寓于.rodata
objdump -sj .rodata bin
bin: file format elf64-x86-64
Contents of section .rodata:
08e0 01000200 54657374 2025730a 00 ....Test %s..
Run Code Online (Sandbox Code Playgroud)
如此格式化的打印似乎执行从rodata其他地方的其他模式复制.
在编译并运行它之后,stare ./bin rr我注意到brk在实际写入之前有一个系统调用.所以运行它
gdb catch syscall brk
gdb catch syscall write
Run Code Online (Sandbox Code Playgroud)
显示在我的情况下,当前中断等于0x555555756000,但然后设置为0x555555777000.当write发生格式化的字符串时
x/s $rsi
0x555555756260: "Test rr\n"
Run Code Online (Sandbox Code Playgroud)
位于"旧"和"新"休息之间.写入发生后,程序退出.
问题:为什么我们分配了这么多页面,为什么在写入系统调用后,中断没有返回到前一个页面?是否有任何理由使用brk而不是mmap这种格式?
brk()(及其同伴sbrk())是某种mmap()专门用于操纵堆大小的工具。由于历史原因,libc也可以mmap()直接使用mremap()。
当分配额外的内存时,堆会扩展,例如malloc(),这发生在 libc 内部,例如有足够的空间来从格式字符串和参数或许多其他内部事物(即,当将缓冲 io 与 f* 函数系列一起使用)。
如果堆的某些部分不再使用,通常不会自动释放,原因有两个:堆可能碎片化,和/或未使用的堆没有低于证明操作合理的某个阈值,因为它可能是很快再次需要。
附带说明:格式字符串本身肯定不会从 ro 部分复制到堆,这将完全无用。但结果字符串(通常)构建在堆上。