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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

gdb tui 安装_GDB 单步调试汇编

發布時間:2025/3/12 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 gdb tui 安装_GDB 单步调试汇编 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

之前在看匯編的時候一直是肉眼看GCC -S的結果,缺點是很不直觀,無法實時的看到寄存器的值,所以研究了下如何用GDB調試匯編。當然,寫這篇文章更重要的一個目的是半年沒有寫博客了,博客要長草了。^_^

調試匯編的需求有幾點:

  • 能夠單步進行匯編調試。
  • 能夠實時看到寄存器值的變化。
  • 能夠看到源代碼和對應匯編的關系。

下面分享下用GDB實現上面的3點需求:

單步進行匯編調試

使用si和ni。與s與n的區別在于:s與n是C語言級別的單步調試,si與ni是匯編級別的單步調試。

能夠實時看到寄存器值的變化。

使用gdb時增加-tui選項,打開gdb后運行layout regs命令。注意最好加上-tui,否則很大可能會出現花屏現象。

能夠看到源代碼和對應匯編的關系

在gdb中運行set disassemble-next-line on,表示自動反匯編后面要執行的代碼。

可以清晰的看出int c=sum(x,y);與下面紅框內的匯編指令成對應關系。

如果大家不想用這么原始的方式,可以給GDB安裝插件或者使用emacs達到上面的目的,推薦兩篇文章:

  • GDB 從裸奔到穿戴整齊
  • GDB實用插件(peda, gef, gdbinit)全解

最后以一個小例子結束:

int sum(int x,int y){return x+y; }int main(){int x=10;int y=20;int c=sum(x,y);return 0; }

gcc版本4.4.7,默認的優化選項。

我們單步調試下這段代碼對應的匯編:

設置斷點

注意如果想要把斷點設置在匯編指令層次函數的開頭,應該使用b *fun而不是b func,這里我們把斷點設置在b *main

分配棧幀

0x0000000000400489 <main+0>: 55 push %rbp 0x000000000040048a <main+1>: 48 89 e5 mov %rsp,%rbp 0x000000000040048d <main+4>: 48 83 ec 10 sub $0x10,%rsp

%rbp和%rsp表示的是當前棧幀的棧底和棧頂。其中%rbp是被調用者需要保存的寄存器。sub $0x10,%rsp表示為main函數分配棧幀空間。
注意這里分配了16字節的棧空間,會有4字節用不上,我個人猜測跟gcc匯編產生的cfi_def_cfa_offset 16有關,這個沒有深究。

int x=10

0x0000000000400491 <main+8>: c7 45 f4 0a 00 00 00 movl $0xa,-0xc(%rbp)

將x的值放到棧中

int y=20

0x0000000000400498 <main+15>: c7 45 f8 14 00 00 00 movl $0x14,-0x8(%rbp)

將y的值放到棧中

sum函數調用

0x000000000040049f <main+22>: 8b 55 f8 mov -0x8(%rbp),%edx0x00000000004004a2 <main+25>: 8b 45 f4 mov -0xc(%rbp),%eax0x00000000004004a5 <main+28>: 89 d6 mov %edx,%esi0x00000000004004a7 <main+30>: 89 c7 mov %eax,%edi0x00000000004004a9 <main+32>: e8 c6 ff ff ff callq 0x400474 <sum>

將x與y分別賦值到%esi和%edi中,其中%edi和%esi被規定用來傳遞函數的第一個和第二個參數。(一個疑問是為什么不能直接mov -0x8(%rbp),%esi呢?)
callq會將下一條指令的地址壓入棧中,并跳到sum函數的第一條指令。

進入sum函數

0x0000000000400474 <sum+0>: 55 push %rbp 0x0000000000400475 <sum+1>: 48 89 e5 mov %rsp,%rbp 0x0000000000400478 <sum+4>: 89 7d fc mov %edi,-0x4(%rbp) 0x000000000040047b <sum+7>: 89 75 f8 mov %esi,-0x8(%rbp)

同main函數一樣,首先將%rbp保存,然后從%edi和%esi中取出函數參數。

求和

0x000000000040047e <sum+10>: 8b 45 f8 mov -0x8(%rbp),%eax 0x0000000000400481 <sum+13>: 8b 55 fc mov -0x4(%rbp),%edx 0x0000000000400484 <sum+16>: 8d 04 02 lea (%rdx,%rax,1),%eax

將x和y相加,這里用到的是lea指令,關于lea指令介紹參考LEA instruction? ,這里不贅述了。
將返回值放到%eax中,%rax寄存器規定存放函數的返回值。像GO語言如果函數可以有多個返回值的話,返回值是放到棧中。

sum函數收尾

0x0000000000400487 <sum+19>: c9 leaveq 0x0000000000400488 <sum+20>: c3 retq

我們先看下現在的棧:


(這里不知道為什么沒有sub xx,$rsp,我猜測是gcc發現這個最后一次函數調用,之后不會有棧的增長只會有棧的回退,所以用%rsp和%rbp的結果是一樣的。簡單驗證了下,應該是這樣)。
在函數結束時首先需要回收當前函數的棧幀、恢復保存過的寄存器、恢復%rip的值,即返回地址。

leaveq指令相當于:

mov %rbp,%rsp pop %rbp

作用是釋放(deallocate)當前函數的棧幀并恢復被保存的寄存器的值。由此我們也可以看出%rbp的作用:記住%rsp應該回退的位置,否則函數結束時%rsp不知道該回退到哪。

retq指令相當于:

pop %rip

將上面保存過的callq的下一條指令地址恢復到%rip中。

接收函數返回值

0x00000000004004ae <main+37>: 89 45 fc mov %eax,-0x4(%rbp)

將%eax的值放入到main函數的棧幀中。

return 0

0x00000000004004b1 <main+40>: b8 00 00 00 00 mov $0x0,%eax

同上面sum函數一樣。

main函數收尾

0x00000000004004b6 <main+45>: c9 leaveq 0x00000000004004b7 <main+46>: c3 retq

如果上面%rsp和%rbp指向同一內存區域看起來不太直觀的話,看下現在main函數即將結束時的棧空間:

同上面sum函數的解釋一樣,不再贅述。

程序運行成功退出。

總結

以上是生活随笔為你收集整理的gdb tui 安装_GDB 单步调试汇编的全部內容,希望文章能夠幫你解決所遇到的問題。

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