“.LC0”標記的內存位於應用程序的靜態分配區域,這個區域在程序運行後即被分配,即“hello,world”作為一個C語言字符串常量被安排在靜態分配區域。
還有一個非常重要內存分配區域就是堆棧,,堆棧是特殊的內存區域,用於程序中函數傳遞參數、數據的臨時存取,通常是應用程序內存范圍的結尾位置的內存區域,為了方便堆棧數據的存取,有一個堆棧指針(棧頂指針)指向堆棧中的下個內存位置,這意味著如何僅依靠棧頂指針不采用任意偏移地址機制(偏移地址可以以基地址為中心進行調整,比如說訪問某個變量,該變量的基地址為0x400,偏移地址為0x16,則該變量的最終地址為0x416),則只能按照先進後出的順序來訪問堆棧內部存儲的變量。
在匯編語言中將數據放入堆棧中,使用pushl助記符,而將數據從堆棧中彈出,使用popl助記符,每次對堆棧數據的放入與彈出都會導致棧頂指針的變化,因為棧頂指針永遠指向堆棧中下一個可用的地址。下面這段匯編完成了將10壓入堆棧,然後將10彈出到ebx寄存器中的過程。
pushl $10
popl %ebx
(2)C程序執行
C語言的源代碼被翻譯成若干行匯編代碼,由幾個簡單的指令組成的匯編代碼生成二進制文件,執行這個二進制文件,完成了helloworld的執行。
匯編語言中用的較多助記符是movl、addl、subl
movl完成數據的復制,而addl完成數據的加法,subl完成數據的減法。
這3個助記符的語法格式是:
助記符 源數據 目標數據
比如,對於這段靜態分配變量的匯編代碼:
myvalue:
.long 190
mess:
.ascii “hello”
通過addl與movl可以完成將myvalue指示的long類型變量190加100,然後減20的功能。
movl myvalue,%ebx
addl $100,%ebx
subl $20,%ebx
movl %ebx,myvalue
匯編語言的代碼放在了.text段中,分析上面的helloworld的反匯編代碼中一段:
.text
.p2align 4,,15
.globl main
.type main, @function
globl 命令指定了main函數為入口函數(程序啟動時執行的函數),然後接著在後面定義了main函數的組成:
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ecx
subl $4, %esp
movl $.LC0, (%esp)
call puts
movl $0, %eax
addl $4, %esp
popl %ecx
popl %ebp
leal -4(%ecx), %esp
ret
.size main, .-main
.ident "GCC: (GNU) 4.2.1 20070831 patched [FreeBSD]"
.section .note.GNU-stack,"",@progbits
觀察這些匯編代碼,裡面充斥著pushl、popl、movl、subl與addl等助記符,C程序最終就是通過復制、入棧、出棧、加法、減法等簡單操作來完成執行的。注意觀察這些代碼中的如下幾行:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ecx
subl $4, %esp
movl $.LC0, (%esp)
call puts
C語句的print(“helloworld”)輸出字符串就是通過上述幾行實現的,除開最後一行call puts(call指令完成調用C語言的puts函數輸出字符串的功能,puts函數向終端輸出一個字符串,其唯一的參數是char *str,str表示需要輸出的字符串)外,其它行做的所有工作就是將調用puts函數的唯一參數(指向字符串”helloworld”地址的標示“.LC0”)的放入堆棧中,以供puts函數調用,倒數第二行將.LC0標記的地址復制到當前堆棧的棧頂,前面幾行分配堆棧,調整棧頂指針,將需要保存的寄存器入棧(因為調用puts函數會破壞現有寄存器的值,稱之為保存現場),當puts函數完成後,會將入棧的寄存器值彈回各自的寄存器中(稱之為恢復現場)。