在高級語言中,程序在命令行上啟動時常常帶一個或多個參數,在彙編語言中也可以實現這一特性。在實現這一特性之前,我們先了解一下linux如何從命令行執行程序。
每一個應用程序開始執行時,系統都會為該程序分配一塊內存區域,並且每個程序都分配相同的虛擬內存地址。虛擬內存地址由操作系統映射到物理內存地址。在Linux中,程序的虛擬內存地址是從0x80480000開始,到地址0xbfffffff結束。linux操作系統按照專門的格式把程序放在虛擬內存地址中。如下圖:
內存區域第一塊區域包含彙編程序的bss段和data段。第二塊區域是程序堆棧,之前講過,堆棧從內存區域的最後向下增長。程序每次啟動時,堆棧指針並非設置為0xbfffffff,在加載程序之前,linux會把命令行參數等一些內容放在這裡。程序啟動時,linux會把4種信息存放到程序堆棧中:命令行參數的數目、程序的名稱、命令行包含的命令行參數、程序啟動時所有當前linux環境變量。程序啟動時,堆棧的一般佈局如下圖:
既然已經瞭解了命令行參數位於堆棧中什麼位置,現在來編寫簡單的程序訪問它們。下面示例在調試中運行可以查看所有的命令行參數值,所有的命令行參數在堆棧中存儲為字符串值。
# arg.s
.section .text
.globl _start
_start:
nop
movl $1, %eax
movl $0, %ebx
int $0x80
在調試器中運行該程序如下:
(gdb) r 100 101 102 #運行時帶入三個參數
Starting program: /home/allen/as/i_arg/arg 100 101 102
Breakpoint 1, _start () at arg.s:5
5 nop
(gdb) s
6 movl $1, %eax
(gdb) print $esp #打印堆棧棧頂地址
$1 = (void *) 0xbffff3d0
(gdb) x/20x 0xbffff3d0 #查看堆棧向上20個地址的數據,其中第一個數據為命令行參數的個數,後
緊接著 0xbffff566 0xbffff57f 0xbffff583 0xbffff587為命令行參數的參數的地址(gdb) r 100 101 102
0xbffff3d0: 0x00000004 0xbffff566 0xbffff57f 0xbffff583
0xbffff3e0: 0xbffff587 0x00000000 0xbffff58b 0xbffff5ac
0xbffff3f0: 0xbffff5cb 0xbffff5ec 0xbffff5fc 0xbffff607
0xbffff400: 0xbffff615 0xbffff666 0xbffff6a0 0xbffff6b2
0xbffff410: 0xbffff6c8 0xbffff6e6 0xbffff6fd 0xbffff708
(gdb) x/d ($esp)
0xbffff3d0: 4
(gdb) x/s 0xbffff566 # 參數為程序名稱
0xbffff566: "/home/allen/as/i_arg/arg"
(gdb) x/s 0xbffff57f #查看第一個參數內存地址內的內容
0xbffff57f: "100"
(gdb) x/s 0xbffff583 #查看第二個參數內存地址內的內容
0xbffff583: "101"
(gdb) x/s 0xbffff587 #查看第三個參數內存地址內的內容
0xbffff587: "102"
(gdb)
要注意參數永遠都不會為0,因為程序名稱也算一個參數。在命令行參數之後,4字節的空值被存放到堆棧中,其用來將參數指針和指向環境變量的指針分隔開來。