我有以下代码
.data
result: .byte 1
.lcomm input 1
.lcomm cha 2
.text
(some other code, syscalls)
起初一切都很好。当调用系统调用(例如,读取)时,标签'结果'改为随机垃圾值。
任何人都知道什么是错的?
P.S。环境 Debian x86_64最新 在virtualbox中运行 使用as -g ld emacs制作最新的
- - - - - - - - 编辑
(continue)
.global _start
_start:
mov $3,%rax
mov $0,%rbx
mov $input,%rcx
mov $1,%rdx
int $0x80
(sys_exit)
'输入'的价值改变得很好,但是'结果'
后也改为随机值int $0x80
答案 0 :(得分:2)
您正在查看从result
开始的4个字节,其中包括input
作为第2个或第3个字节。 (这就是为什么值上升256或65536的倍数,如果你print (char)result
则保留低字节= 1)。如果您使用p /x
打印为十六进制,这将更加明显。
在没有调试信息的情况下,print result
的GDB默认行为是假设int
。现在,由于这样的用户错误,在Arch Linux上使用gdb
8.1,print result
说'result' has unknown type; cast it to its declared type
GAS + ld
意外地(对我来说)将BSS和数据段合并为一个页面,因此即使您将它们放在不同的部分,您希望以不同的方式处理它们,您的变量也是相邻的。 (BSS由匿名归零页面支持,数据由文件的私有读写映射支持。)
使用gcc -nostdlib -no-pie test.S
构建后,我得到:
(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
构建时,我在input
之后的字节中获得了result
(在不同的地址)。
我通过添加.lcomm arr, 888332
的大数组来调查。一旦我意识到它不能在可执行文件中为BSS存储文字零,我使用readelf -a a.out
进一步检查:
(相关:What's the difference of section and segment in ELF file format)
...
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
和.bss
部分最终出现在同一个ELF细分中。
我认为这里发生的是ELF元数据表示从virt addr 0xd8e1a
开始映射0x600126
字节(归零页面)的MemSize。和从文件中的偏移0x126
加载1个字节到虚拟地址0x600126
。
因此,ELF程序加载器不仅仅是一个mmap,而是必须将文件中的数据复制到一个支持BSS和.data
部分的其他页面中。
链接器决定使用单独的段可能需要更大的.data
部分。