我正在尝试使用汇编语言并编写了一个程序,它将2个硬编码字节打印到stdout中。这是:
section .text
global _start
_start:
mov eax, 0x0A31
mov [val], eax
mov eax, 4
mov ebx, 1
mov ecx, val
mov edx, 2
int 0x80
mov eax, 1
int 0x80
segment .bss
val resb 1; <------ Here
请注意,我在bss段中只保留了1个字节,但实际上将2个字节(1
和newline
符号的字符代码)放入内存位置。该计划运作良好。它打印了1
字符,然后是newline
。
但我预计分段错误。为什么不发生。我们只保留了1个字节,但是放了2个。
答案 0 :(得分:4)
val
的4字节存储将不会发生故障,除非链接器恰好将其放在页面的最后3个字节中,并且下一页未映射。
实际发生的事情是你只是覆盖val
之后的任何内容。在这种情况下,它只是页面末尾的未使用空间。如果您在BSS中有其他静态存储位置,那么您将踩到它们的值。 (如果需要,可以将它们称为“变量”,但“变量”的高级概念不仅仅意味着存储位置,变量可以存在于寄存器中,而且永远不需要有地址。)
除了上面链接的维基百科文章,另见:
但实际上将2个字节(charcode为1和换行符号)放入内存位置。
mov [val], eax
是一个4字节的存储区。操作数大小由寄存器决定。如果您想要执行2字节存储,请使用mov [val], ax
。
mov [val], 0x0A31
,那将是一个错误。这两个操作数都不是一个大小,因此您需要mov dword [val], 0x0A31
(或word
或byte
)。
val
以获取段错误由于某种原因,BSS不是以32位二进制文件的页面开头开头,而是靠近页面的开头。您没有链接任何会占用BSS中大部分页面的内容。 nm bss-no-segfault
显示它位于0x080490a8
,4k页面为0x1000
字节,因此BSS映射中的最后一个字节将为0x08049fff
。
当我向.text
部分添加指令时,似乎BSS起始地址发生了变化,因此这里的链接器选择可能与将事物打包成ELF可执行文件有关。它没有多大意义,因为BSS没有存储在文件中,它只是一个基址+长度。我不会去那个兔子洞;我确信有一个原因是在页面开头的BSS中使.text
略大一些,但IDK是什么。
无论如何,如果我们构建BSS以使val
在页面结尾之前就可以了,我们就会出错:
... same .text
section .bss
dummy: resb 4096 - 0xa8 - 2
val: resb 1
;; could have done this instead of making up constants
;; ALIGN 4096
;; dummy2: resb 4094
;; val2: resb
然后构建并运行:
$ asm-link -m32 bss-no-segfault.asm
+ yasm -felf32 -Worphan-labels -gdwarf2 bss-no-segfault.asm
+ ld -melf_i386 -o bss-no-segfault bss-no-segfault.o
peter@volta:~/src/SO$ nm bss-no-segfault
080490a7 B __bss_start
080490a8 b dummy
080490a7 B _edata
0804a000 B _end <--------- End of the BSS
08048080 T _start
08049ffe b val <--------- Address of val
gdb ./bss-no-segfault
(gdb) b _start
(gdb) r
(gdb) set disassembly-flavor intel
(gdb) layout reg
(gdb) p &val
$2 = (<data variable, no debug info> *) 0x8049ffe
(gdb) si # and press return to repeat a couple times
mov [var], eax
段错误,因为它跨越了未映射的页面。 mov [var], ax
会起作用(因为我在页面结尾之前放了var
2个字节。)
此时,/proc/<PID>/smaps
显示:
... the r-x private mapping for .text
08049000-0804a000 rwxp 00000000 00:15 2885598 /home/peter/src/SO/bss-no-segfault
Size: 4 kB
Rss: 4 kB
Pss: 4 kB
Shared_Clean: 0 kB
Shared_Dirty: 0 kB
Private_Clean: 0 kB
Private_Dirty: 4 kB
Referenced: 4 kB
Anonymous: 4 kB
...
[vvar] and [vdso] pages exported by the kernel for fast gettimeofday / getpid
关键词:rwxp
表示读/写/执行,私有。甚至在第一条指令之前停止,不知何故它已经“脏”(即写入)。文本段也是如此,但是gdb会将指令更改为int3
。
08049000-0804a000(以及映射的4 kB
大小)向我们显示BSS只有1页映射。没有数据段,只有文本和BSS。