32位組合語言學習筆記(41)--fgets等函式的使用
阿新 • • 發佈:2019-02-14
在《32位組合語言學習筆記(13)--函式的呼叫》曾分析過c函式的呼叫過程,對於c函式的預設呼叫約定cdecl,要求函式引數的壓棧順序是從右向左,由呼叫方來清理棧。下面示例程式會使用libc庫的幾個函式:
char *fgets( char *string, int n, FILE *stream );
int printf( const char *format [, argument]... );
int scanf( const char *format [,argument]... );
示例程式如下:
[SECTION .data] ; Section containing initialised data SPrompt db 'Enter string data, followed by Enter: ',0 IPrompt db 'Enter an integer value, followed by Enter: ',0 IFormat db '%d',0 SShow db 'The string you entered was: %s',10,0 IShow db 'The integer value you entered was: %5d',10,0 [SECTION .bss] ; Section containing uninitialized data IntVal resd 1 ; Reserve an uninitialized double word InString resb 128 ; Reserve 128 bytes for string entry buffer [SECTION .text] ; Section containing code extern stdin ; Standard file variable for input extern fgets extern printf extern scanf global main ; Required so linker can find entry point main: push ebp ; Set up stack frame for debugger mov ebp,esp push ebx ; Program must preserve ebp, ebx, esi, & edi push esi push edi ;;; Everything before this is boilerplate; use it for all ordinary apps! ; First, an example of safely limited string input using fgets: push SPrompt ; Push address of the prompt string call printf ; Display it add esp,4 ; Stack cleanup for 1 parm push dword [stdin] ; Push file handle for standard input push 72 ; Accept no more than 72 chars from keybd push InString ; Push address of buffer for entered chars call fgets ; Call fgets add esp,12 ; Stack cleanup: 3 parms X 4 bytes = 12 push InString ; Push address of entered string data buffer push SShow ; Push address of the string display prompt call printf ; Display it add esp,8 ; Stack cleanup: 2 parms X 4 bytes = 8 ; Next, use scanf() to enter numeric data: push IPrompt ; Push address of the integer input prompt call printf ; Display it add esp,4 ; Stack cleanup for 1 parm push IntVal ; Push the address of the integer buffer push IFormat ; Push the address of the integer format string call scanf ; Call scanf to enter numeric data add esp,8 ; Stack cleanup: 2 parms X 4 bytes = 8 push dword [IntVal] ; Push integer value to display push IShow ; Push base string call printf ; Call printf to convert & display the integer add esp,8 ; Stack cleanup: 2 parms X 4 bytes = 8 ;;; Everything after this is boilerplate; use it for all ordinary apps! pop edi ; Restore saved registers pop esi pop ebx mov esp,ebp ; Destroy stack frame before returning pop ebp ret ; Return control to Linux
程式分析:
這裡只分析fgets函式的呼叫,其他類似。
push dword [stdin] //stdin是libc庫中的符號,表示標準輸入,實際是一個地址,fgets呼叫需要的是一個控制代碼,而不是地址值,因此要通過間接定址獲取控制代碼值。(對應的入參是stream)
push 72 //從標準輸入讀取的最大字串長度是72。
push InString //快取地址,InString分配的空間是128個位元組,所以最大長度設為72是沒有問題的。
call fgets //呼叫fgets
add esp,12 //清理堆疊,壓入了3個引數,使用了12個位元組。
makefile檔案內容:
charsin: charsin.o gcc charsin.o -o charsin charsin.o: charsin.asm nasm -f elf -g -F stabs charsin.asm
測試:
[[email protected] charsin]# make nasm -f elf -g -F stabs charsin.asm gcc charsin.o -o charsin [[email protected] charsin]# ./charsin Enter string data, followed by Enter: hello world The string you entered was: hello world Enter an integer value, followed by Enter: 123456 The integer value you entered was: 123456