日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

汇编语言系统调用过程

發布時間:2023/11/27 生活经验 58 豆豆
生活随笔 收集整理的這篇文章主要介紹了 汇编语言系统调用过程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

以printf為例,詳細解析一個簡單的printf調用里頭,系統究竟做了什么,各寄存器究竟如何變化。

如何在匯編調用glibc的函數?其實也很簡單,根據c convention call的規則,參數反向壓棧,call,然后結果保存在eax里頭。注意,保存的是地址。

在匯編里頭,一切皆地址。


當我們調用 result = printf( "%d %d", 12, a )的時候,編譯器默認是這樣處理的(除非函數定義聲明了pascal call)。

在棧里頭,先一次push a的地址,還有12這個立即數,再push "%d %d"這個字符串的地址,內存模型如下,x86的esp是往下增長的。

(這里是buttom,往下增長的是top)

&a

12

address of "%d %d"

-------------------------------------------(esp 指著這里 ,我們假設地址是4字節,12這個數也是4字節)

當call printf的時候,首先,push當前的eip入esp,解析esp+4所指的"%d %d",因為%d這樣的特定字符都定義了后面每個參數的大小,所以只要解析“%d %d”,我們就可以知道棧里頭參數的情況,例如esp+4+4就是一個int,esp+4+4+4是另外一個int。

當返回的時候,先pop到eip,也就是把eip還原到call之后馬上要執行的機器碼,這時,esp就指著“%d %d”,esp+4指著12,esp+8指著a的地址。esp里頭的內容怎么處理,看需要吧,你也可以pop出來,也可以不pop。但為了效率著想,如果空間夠用,通常不pop,直接用mov指令把下一次要用的參數move進去。返回指儲存在eax里頭。


這也一定程度上解釋了為什么c convention call是反向壓棧,這樣編譯器處理起來方便,特別對于這些va_list,因為va_list后面不能繼續跟參數,va_list一定出現在函數的末尾,如果是對printf這類的函數使用pascal call,也就是參數正向壓棧,匯編級別處理起來就特別麻煩了。


下面就用匯編語言寫一個調用printf,并用gdb跟蹤寄存器。


代碼test_printf.s

.section .data            format: .asciz "%d\n" 
.section .text    
.global _start    
_start:            pushl $12            pushl $format         call printf         movl $0, (%esp)            call exit

編譯

#as -g test_printf.s -o test_printf.o

鏈接

#ld -lc -I /lib/ld-linux.so.2 test_printf.o -o test_printf

-g是要加入調試信息

ld的-lc是鏈接libc.a,-I是--dynamic-linker,/lib/ld-linux.so.2

運行

#./test_printf

輸出12


調試

用objdump看看test_printf里頭的.text section,注意Disassembly of section .text


使用gdb跟蹤,看看上述是否正確

#gdb test_printf

設置斷點到_start

(gdb) break _start

(gdb) run

執行,遇到斷點,停下,eip指著第6行,也就是第一條要執行的push指令

?

(gdb) info reg

察看寄存器狀況

(gdb) s

執行一步,eip指著下一條指令地址

(gdb) info reg


esp 0xbffff6cc 0xbffff6cc

6cc = 6d0 - 4,對比上一條的esp,小了4,也就是stack增長了4個字節


(gdb) s

(gdb) info reg

esp 0xbffff6c8 0xbffff6c8

6c8 = 6cc - 4,對比上一條的esp,小了4,也就是stack增長了4個字節


(gdb) s

in printf () from /lib/libc.so.6

執行一步,正式進入printf

(gdb) info reg

esp 0xbffff6c4 0xbffff6c4

6c4=6c8-4 新push進去4個字節


(gdb) x /1x $esp
0xbffff6c4: 0x080481c4

esp的棧頂保存的是下一條要執行的代碼的位置,movl的位置,(參考上面objdump的結果)

可以使用bt查看棧幀,下面對比棧變化

(gdb) s

printf出12,已經執行完畢

(gdb) info reg

eax保存著這次printf的返回值,也就是被打印的字符數量,12\n,一共3個字符。

esp恢復到call printf之前的狀態

恢復eip


(gdb) s

執行movl指令,下一條是call exit

(gdb) x /1x $esp

esp并沒有增長,因為printf之前的數據已經沒用了,我沒有把他們pop出來,而是直接用新的數據刷寫esp所指的內存。


(gdb) s
(gdb) s

正常退出


關于EIP、ESP、EBP寄存器

1.EIP寄存器里存儲的是CPU下次要執行的指令的地址

也就是調用完fun函數后,讓CPU知道應該執行main函數中的printf("函數調用結束")語句了。

2.EBP寄存器里存儲的是是棧的棧底指針,通常叫棧基址,這個是一開始進行fun()函數調用之前,由ESP傳遞給EBP的。(在函數調用前你可以這么理解:ESP存儲的是棧頂地址,也是棧底地址。)

3.ESP寄存器里存儲的是在調用函數fun()之后,棧的棧頂。并且始終指向棧頂。

堆棧是一種簡單的數據結構,是一種只允許在其一端進行插入或刪除的線性表。
允許插入或刪除操作的一端稱為棧頂,另一端稱為棧底,對堆棧的插入和刪除操作被稱入棧出棧

有一組CPU指令可以實現對進程的內存實現堆棧訪問。其中,POP指令實現出棧操作,PUSH指令實現入棧操作。
CPU的ESP寄存器存放
當前線程的棧頂指針
EBP寄存器中保存
當前線程的棧底指針
CPU的EIP寄存器存放
下一個CPU指令存放的內存地址,當CPU執行完當前的指令后,從EIP寄存器中讀取下一條指令的內存地址,然后繼續執行。

?

參考:http://blog.csdn.net/feng_zh/article/details/7075986

總結

以上是生活随笔為你收集整理的汇编语言系统调用过程的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。