我正在尝试理解在C语言中printf
的工作原理,针对一个简单的例子。我编写了以下程序:
#include "stdio.h"
int main(int argc, char const *argv[])
{
printf("Test %s\n", argv[1]);
return 0;
}
在二进制文件上运行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..
因此,格式化打印似乎会从rodata
执行额外的模式复制到其他地方。
编译并使用stare ./bin rr
运行后,我注意到在实际写入之前有一个brk
系统调用。因此,可以通过以下方式运行:
gdb catch syscall brk
gdb catch syscall write
显示在我的情况下,当前断点等于0x555555756000
,但它随后设置为0x555555777000
。当进行write
操作时,格式化字符串为
x/s $rsi
0x555555756260: "Test rr\n"
这段代码在“旧”和“新”断点之间。写入发生后,程序退出。
问题:为什么我们要分配这么多页面?为什么在写入系统调用之后,断点没有返回到之前的位置?使用brk
而不是mmap
格式化有什么原因吗?
printf
逐字节地复制到目标位置,直到遇到第一个%
字符,然后使用该处的格式说明符格式化下一个参数并继续。在任何语言中,通过插入数据来就地更改字符串几乎是不可能的。 - vgru