.

在《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