這是一個程式,它一個一個地獲取十進制 ASCII 數字并將它們轉換為整數。結果存盤在 EDI 暫存器中:
global _start
%macro kernel 4
mov eax, %1
mov ebx, %2
mov ecx, %3
mov edx, %4
int 80h
%endmacro
section .bss
symbol resb 1
section .text
_start:
mov esi, 10
xor edi, edi
.loop:
kernel 3, 0, symbol, 1 ; load 1 char from STDIN into symbol
test eax, eax ; nothing loaded - EOF
jz .quit
xor ebx, ebx
mov bl, [symbol]
sub bl, '0'
cmp bl, 9
jg .quit ; not a number
mov eax, edi ; previously accumulated number
mul esi ; eax *= 10
lea edi, [eax ebx]
jmp .loop
.quit:
mov eax, 1
mov ebx, edi
int 80h
我編譯它:
$ nasm -g -f elf32 st3-18a.asm
$ ld -g -m elf_i386 st3-18a.o -o st3-18a
$ ./st3-18a
2[Enter]
Ctrl-d
當我在 gdb 中逐步運行這段代碼時,一切都是正確的,最后存盤在 EDI 中的結果是 2。但是當我在沒有除錯器的情況下運行時,并回顯程式回傳值:
$ ./st3-18a
2[Enter]
Ctrl-d
$ echo $?
238
為什么會輸出0xEE?怎么了?
uj5u.com熱心網友回復:
您的范圍檢查有問題,使用帶符號的比較 ( jg) 而不是無符號的 ( ja),因此您只檢測來自 10..127 的非數字字符c - '0',而不是當它環繞時(即變為帶符號的負數),幾乎丟失了一半您應該排除的位元組值。包括 ASCII 范圍低端的控制代碼,如換行符。
- 裝配中的雙重條件檢查
- JA和JG在裝配上的區別
- NASM 程式集將輸入轉換為整數?顯示了一個正確執行檢查的回圈。
那么為什么 GDB 讓它起作用呢?
您的程式僅使用read大小為 1 的程式,在您按回車鍵后留下一個未讀的換行符。那是 ASCII 0xa= '\n',所以你的下一個read(1, buf, 1)得到它。
除非 GDB 先獲取它:GDB 接管終端以在您之后讀取更多命令read(1,buf,1),因此 GDB 獲取剩余的終端輸入并在單步執行到下一個read系統呼叫之前丟棄換行符。或者當 GDB 將終端從 cooked 切換到 raw 時,它可能只是被丟棄,這樣它就可以讀取單個擊鍵,而無需等待它從內核的規范模式行編輯中使用 EOL(換行符)或 EOF(ctrl -d) 控制字符。
那是因為您的程式與 GDB 共享一個終端,而不是將 GDB 附加到已經在另一個終端選項卡/視窗中運行的程式。即在不同的 Unix TTY 上。例如與gdb -p $(pidof st3-18a)。
您也可以使用strace, 或只是strace ./st3-18a因為 strace 沒有互動式輸入。
read在使用“熟”TTY 輸入的玩具程式中,進入一個合適大小的緩沖區并忽略后面的字符是很常見的。如果您重定向來自檔案的輸入以便同時準備多行,那將會中斷,因此如果您想要一些健壯的東西,您可以使用fgetslibc。
不過,只要您意識到 I/O 過于簡單且不夠健壯,那么在使用 asm 時做任何讓您的船漂浮的事情,即使這意味著對線路和 tty 處理做出假設并且用戶按下 enter 而不是 control- d.
嘗試cat在終端中運行,輸入部分行并按下 control-D。你可以strace -p $(pidof cat)從另一個終端看到它的系統呼叫。
另請參閱如何使用 NASM 程式集忽略輸入中的換行符?
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/537478.html
標籤:部件x86数据库终端机
