使用命令行參數

在高級語言中,程序在命令行上啟動時常常帶一個或多個參數,在彙編語言中也可以實現這一特性。在實現這一特性之前,我們先了解一下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字節的空值被存放到堆棧中,其用來將參數指針和指向環境變量的指針分隔開來。