您正在查看从以下位置开始的 4 个字节result
, 包括input
作为第二个或第三个字节。 (这就是为什么该值会增加 256 或 65536 的倍数,如果您将低字节保留为 1print (char)result
)。如果你使用的话,这会更明显p /x
以十六进制打印。
GDB 的默认行为print result
当没有调试信息时假设int
。现在,由于这样的用户错误,gdb
Arch Linux 上的 8.1,print result
says 'result' has unknown type; cast it to its declared type
GAS + ld
意外地(无论如何对我来说)将 BSS 和数据段合并到一页中,因此即使您将变量放在不同的部分中(您希望得到不同的处理),它们也是相邻的。 (BSS 由匿名归零页支持,数据由文件的私有读写映射支持)。
构建后gcc -nostdlib -no-pie test.S
, I get:
(gdb) p &result
$1 = (<data variable, no debug info> *) 0x600126
(gdb) p &input
$2 = (<data variable, no debug info> *) 0x600128 <input>
与使用不同.section .bss
并手动预留空间,.lcomm
如果需要可以随意垫。大概是为了对齐,也许在这里 BSS 从 8 字节边界开始。当我用clang
, I got input
在之后的字节中result
(在不同的地址)。
我通过添加一个大数组进行调查.lcomm arr, 888332
。一旦我意识到它没有在可执行文件中存储 BSS 的文字零,我就使用了readelf -a a.out
进一步检查:
(有关的:ELF文件格式中的节和段有什么区别)
...
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x0000000000000126 0x0000000000000126 R E 0x200000
LOAD 0x0000000000000126 0x0000000000600126 0x0000000000600126
0x0000000000000001 0x00000000000d8e1a RW 0x200000
NOTE 0x00000000000000e8 0x00000000004000e8 0x00000000004000e8
0x0000000000000024 0x0000000000000024 R 0x4
Section to Segment mapping:
Segment Sections...
00 .note.gnu.build-id .text
01 .data .bss
02 .note.gnu.build-id
...
所以是的,.data
and .bss
部分最终位于相同的 ELF 段中。
我认为这里发生的事情是 ELF 元数据说要映射 MemSize0xd8e1a
从 virt addr 开始的字节(归零页)0x600126
. and 从偏移量加载 1 个字节0x126
在文件中到虚拟地址0x600126
.
因此,ELF 程序加载器必须将文件中的数据复制到支持 BSS 的清零页中,而不仅仅是 mmap.data
部分。
这可能是一个更大的.data
链接器决定使用单独的段所需的部分。