AT&T彙編函數是如何聲明的?
很簡單用.type命令來創建函數的標籤,如下:
.type fun1, @function
fun1:
保存寄存器等值。
函數體
ret
Ps:函數一開始可以使用finit命令來清空FPU寄存器。
還有pusha和popa很方便的讓你全部的寄存器值一次性都保存在堆棧中和取出
那麼如何訪問彙編函數呢?
直接call+函數標籤 就OK了。
函數調用是如何傳值的?
通過棧和寄存器。注意如果是寄存器的話,那麼類型必須相同,所以我估計在類型不同的時候如果進行進行不同類型傳值,強制類型轉換是在傳值之前就已經解決了的。
至於到低是怎麼傳值的,按照C樣式來的話,從參數從右到左入棧。
這也就解釋了爲什麼
Int a;
f(a++,++a);
第一個參數會是2.應該是通過從右到左邊解析表達式,然後入棧的。
注意不光存儲傳遞的值,還要保存調用函數處的地址。
所以棧中就像下面這樣:
函數參數。。 |
函數參數2 |
函數參數1 |
返回地址 |
舊EBP |
局部變量1 |
局部變量2 |
局部變量。。 |
關於這個EBP值。
因爲在函數中,你的棧頂指針ESP隨時可能變化,所以爲了簡化問題,確保每次尋找地址都是定長的。所以引入了另外一個寄存器EBP,來指向就舊的EBP這個位置。那麼函數參數只用n(%ebp),n表示偏移量,n大於0。如果n小於0,就是指向局部變量(高地址在上)。那麼函數參數和局部變量都可以通過地址來表示了。因此這樣.data基本上可以說就不需要了。之前我也說過,.data可能更多的用於保存常量。
所以C語言中的自動變量的問題也解釋了。因爲當函數調用結束,ret之後EBP值恢復到原先的值。自動的變量的值仍然存儲在棧中,但是你已經找不到了。估計C語言之前只能支持在函數開頭定義變量也是因爲這個吧。
注意前面的call指令只支持將返回地址壓入棧中。所以其餘東西都是靠程序員來手動完成的。在ret之前一定要恢復ESP和EBP的值。
一個linux程序是如何構成的?
1. 一開始,linux爲要執行的程序在內存中創建一個區域。然後linux將程序的虛擬地址通過映射,轉換到物理地址上真正區域運行。
2. Linux分配給程序運行的虛擬內存地址從0x80480000開始到0xbfffffff(7個f 即2G-3G)。
3. 內存區域中的第一塊區域是包含彙編程序的所有指令和數據。這個還包含一系列的運行程序的連接過程所需的指令信息
4. 內存區域中的第二塊區域是程序的棧。
實際上棧中的頂部並不是從0xbfffffff開始的。它還包含一些程序運行的所需路徑和命令行參數等。
結構如下:
環境變量 命令行參數 |
指向環境變量的指針 |
0x00000000 |
指向命令行參數3的指針 |
指向命令行參數2的指針 |
指向命令行參數1的指針 |
程序名稱 |
ESP--->參數數目 |
這幾天剛好在看《UNIX高級環境編程》,這個裏面就說了。如果要增加環境變量必須要重新分配空間來存儲這些命令行參數的指針。(減少就直接減少就行了)
並且C語言中argc和argv兩個參數也可以理解了。
不過有些書上說是3個環境變量,加上了envp。其實就是指向環境變量的指針。
在我參考的彙編書中,堆棧說的就是棧,但是實際上堆和棧是分開的兩個位置。並且在個人實際編寫代碼的時候瞭解到,棧的空間比堆的空間小得多。
注意:
可能有比較豐富C編程經驗的會知道,大的數組只能開在外面。其實這個區域存放全局和靜態變量,但是既不是堆也不是棧,而是靜態數據區(bss段)。我一開始就以爲是在堆中,但其實堆是由運行時使用系統調用分配的。而棧在程序開始運行的時候就已經分配好了。(從APUE書上看,堆似乎也是分配好的,我想如果在運行過程中動態的變化,那這個數據結構一定是能夠支持變化的)
關於linux系統調用:
通常用EAX保存系統調用值。然後int0x80進入中斷。
當然還有其他函數需要傳遞。
EBX ECX EDX ESI EDI中分別存放第2~5個參數。
並且系統調用之後的返回值存儲在EAX寄存器中。
如何跟蹤系統調用?
使用strace。使用就是strace直接跟上可執行文件的路徑。
下面是一些參數
-c |
統計每個系統調用的時間、調用和錯誤 |
-d |
顯示strace的一些調試輸出 |
-e |
指定輸出的過濾表達式 |
-f |
在創建紫禁城的時候跟蹤它們 |
-ff |
如果寫入到輸出文件,則把每個子進程寫入到單獨的文件中 |
-i |
顯示執行系統調用時的指令指針 |
-o |
把輸出寫入到指定文件 |
-p |
附加到由PID指定的現有進程 |
-q |
抑制關於附加和分離的消息 |
-r |
對每個系統調用顯示一個相對的時間戳 |
-t |
把時間添加到每一行 |
-tt |
把時間添加到每一行,包括微妙 |
-ttt |
添加epoch形式的時間(從1970年1月1日開始的秒數),包括微妙 |
-T |
顯示每個系統調用花費的時間 |
-v |
顯示系統調用信息的不經省略的版本 |
-x |
以十六進制格式顯示所有非ASCII字符 |
-xx |
以十六進制格式顯示所有字符串 |
這東西對於測試程序效率,或者學習系統調用的效率之類的真是很有用。
並且這個東西可以用於系統上的任何程序!
並且還可以監視已經運行在系統上的程序的能力。
只用strace+PID就OK了。
關於在線判題系統是如何防止不懷好意的人使用系統調用。可以通過ptrace來判斷。
這是一個做過OJ的人告訴我的。貌似網上也有資料,有興趣的可以去查查看。
再多一句嘴,一般的系統調用速度是比標準庫速度要快的。因爲標準庫也是調用系統調用的。不過在部分標準IO調用方面由於與系統調用的設置不同,標準IO庫有BUFFER。這就得看具體的情況而定了。
最後今天剛在貼吧看到一個題目。http://tieba.baidu.com/p/2367967516
其實這個可以用上面說的函數棧針來做。
void xym5366()
{
char *p=""xym 是大騙子\n";
*(&(*(&p+22)))=p;
}
這裏的+22,我一開始直接用的+4。。結果不對。。估計VC吧所有的寄存器都壓入函數棧中了吧。。比上面所說的壓入棧的多一些。