2016-09-06 57 views
2

我正在做一個基本的組件減法函數並將結果打印到控制檯。以下是我認爲應該工作的代碼: (與as output.s編譯,ld a.out -e _start -o output爲什麼x86程序段錯誤沒有.data段?

.bss 
output: 
    .int 

    .text 
    .global _start 

_start: 

movl $9, %eax 
movl %eax, %ebx 
movl $8, %eax 
subl %eax, %ebx 

movl %ebx, (output) 

# ASCII for digits is 0x30 greater than digit value 
addl $0x30, output 

movl $2, %edx  # write 2 bytes (need 1 for null?) 
movl $output, %ecx # output 
movl $1, %ebx  # write to stdin 
movl $4, %eax  # syscall number for write 
int $0x80    # invoke syscall 

# CR 
movl $2, %edx 
movl $13, (output) 
movl $output, %ecx 
movl $1, %ebx 
movl $4, %eax 
int $0x80 

# LF 
movl $2, %edx 
movl $10, (output) 
movl $output, %ecx 
movl $1, %ebx 
movl $4, %eax 
int $0x80 

# exit 
movl $0, %ebx 
movl $1, %eax 
int $0x80 

不過,這一方案段錯誤。 我發現,如果我在最後添加一個簡單的.data段:

.data 
pingle: 
    .int 666 

它工作正常。爲什麼我需要.data段?當我每次寫入2個字節時,是否會溢出其中一個段?或者幾次覆蓋output這樣做?

任何想法非常感謝!

+0

它在哪裏段錯誤?在gdb中運行你的程序來找出答案。可能有一個'.data'部分恰好將可寫內存放在程序錯誤地嘗試讀取或寫入的位置,但這只是一個猜測,直到您顯示哪條指令是段錯誤並且尋址中使用的寄存器和符號的內容模式。 –

+0

終於找到了重複。 http://stackoverflow.com/questions/36821123/asm-x64-scanf-printf-double-gas有完全不同的症狀,但原因是一樣的:認爲'.double'與一個空列表將保留一個空間雙。 –

回答

3

.int與一個空列表保留沒有空間。您的程序沒有BSS。 .int 0應該可以工作,但是使用只保留空間的指令更具慣用性:

在BSS部分使用.space 4保留4個字節。或者使用.comm output 4在BSS中預留4B,而不使用.bss指令。 .int 0也應該工作,但使用只有預留空間的指令更具慣用性。

另請參閱gas manual標記wiki。

IIRC,BSS最終可能與數據段位於同一頁面,並且內存訪問檢查具有頁面粒度。這解釋了爲什麼從/到(output)的加載/存儲正常工作,即使它已經超過了BSS的末端。


一個例子

## nobss.S 
.bss 
.globl output  # put this symbol 
output: .int 

.text 
.globl _start 
_start: 
    mov (output), %eax 

$ gcc -g -nostdlib nobss.S 
$ nm -n ./a.out   # nm -a -n to also include debug syms, but gas doesn't make debug info automatically (unlike NASM/YASM) 
00000000004000d4 T _start 
00000000006000db T __bss_start 
00000000006000db T _edata 
00000000006000db T output 
00000000006000db T end_of_bss  # same address as output, proving that .int reserved no space. 
00000000006000e0 T _end 

$ gdb ./a.out 
(gdb) b _start 
(gdb) r 
# a.out is now running, but stopped before the first instruction 

# Then, in another terminal: 
$ less /proc/$(pidof a.out)/maps 
00400000-00401000 r-xp 00000000 09:7e 9527300       /home/peter/src/SO/a.out 
7ffff7ffb000-7ffff7ffd000 r--p 00000000 00:00 0       [vvar] 
7ffff7ffd000-7ffff7fff000 r-xp 00000000 00:00 0       [vdso] 
7ffffffdd000-7ffffffff000 rwxp 00000000 00:00 0       [stack] 
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0     [vsyscall] 

注不存在任何匿名映射可能是BSS,或a.out的(數據)的任何可寫映射。只有我們的程序文本被映射。 (使用私人映射,但它實際上仍是寫入時複製共享。)請參閱this answer for what the fields mean


readwrite終止0字節不需要

movl $2, %edx  # write 2 bytes (need 1 for null?) 

readwrite系統調用採取明確的長度。您不需要(也不應該)在傳遞給write()的長度中包含終止零字節。例如,

# You want this 
$ strace echo foo > /dev/null 
... 
write(1, "foo\n", 4)     = 4 
... 

# not this: 
$ strace printf 'foo\n\0' > /dev/null 

... 
write(1, "foo\n\0", 5)     = 5 
...