GDB调试--以汇编语言为例
#rpm -qa |grep? gdb
下載:
安裝
#tar -zxvf
#./configure
#make
使用GDB
以匯編語言調試為例
匯編語言實現CPUID指令
CPUID
 
cpuid是Intel? Pentinum以上級CPU內置的一個指令(486級以下的CPU不支持),他用于識別某一類型的CPU,
它能返回CPU級別,型號,CPU步進以及CPU字串信息,從此命令也可以得到CPU的緩存和TLB信息
 
CPUID返回數據類型是在EAX寄存器里定義的,而指令返回的數值則存儲在EAX,EBX,ECX和EDX寄存器中。
返回的信息分兩部分:基本信息與擴展信息。
在EAX輸入0-3參數時,它返回CPU基本信息;
而在EAX輸入0x8000000至ox800000x,他返回的是CPU擴展信息。擴展信息只包括在Pentinum4及以后的CPU上。
 
CPU級別???????? 基本信息?? 擴展信息
 486及以前的CPU????? 不可用??? 不可用
 Pentium??????? 0x1??? 不可用
 Pentium Pro,Pentium 2??? 0x2??? 不可用
 Pentium 3????? 0x3??? 不可用
 Pentium 4????? 0x2??? 0x80000004
 Xeon(至強)????? 0x2??? 0x80000004
 
 
代碼
cpuid.s
#cpuid.s Sample program to extract the processor Vendor ID                
.section .data                
output:                   .ascii "The processor Vendor ID is 'xxxxxxxxxxxx'\n"
.section .text                
.globl _start                
_start:                   movl $0, %eax                   cpuid                   movl $output, %edi                   movl %ebx, 28(%edi)                   movl %edx, 32(%edi)                   movl %ecx, 36(%edi)                   movl $4, %eax                   movl $1, %ebx                   movl $output, %ecx                   movl $42, %edx                   int $0x80                   movl $1, %eax                   movl $0, %ebx                   int $0x80 
? 匯編
使用GNU匯編器
#as -o cpuid.o cpuid.s
使用GNU鏈接器
 
#ld -o cpuid cpuid.o
運行程序
#./cpuid
輸出
 
調試
為了調試匯編語言程序,必須使用-gstabs參數重新匯編代碼
#as -gstabs -o cpuid.o cpuid.s
#ld -o cpuid cpuid.o
使用-gstabs參數在可執行文件中添加了附加信息,所以新產生的文件會大些
如果沒有必要不要使用調試信息
 
運行
在gdb內運行
#gdb cupid
GNU調試器啟動,把程序加載到內存,使用run命令(簡化r也可以)從gdb內運行程序
 
可見調試器內的運行和從命令行直接運行一樣
 
斷點
匯編語言中指定斷點時,必須指定對于最近的標簽的相對位置,上述代碼中只有一個標簽,所以每個斷點必須依據_start指定。
 
break(簡化的b)命令格式 break * label+offset
break 函數名
break 行號
break 文件名:行號
break 文件名:函數名
break +偏移量
break -偏移量
break *地址
label是被引用的源代碼中的標簽
offset是應該停止的地方距離標簽的行數
break * _start
 
*_start參數指定了斷點,該參數指定_start標簽后的第一條指令碼。
run或者(簡化的r)啟動程序,暫停在第一條指令碼處。
單步
使用next(簡化的n)或者step(簡化的s)命令單步調試
注意:s會步入調用的函數,類似于VS中的F11,而n類似于VS中的F10
每個next或者step命令執行下一行代碼
繼續運行
使用continue(或者c)按正常方式繼續運行,類似于VS中的F5
 
查看數據
info? registers?? 查看所有寄存器的值
print (或者p)?? 顯示特定寄存器或者來自程序的變量值
p $eax??????????? 顯示寄存器的內容
p/格式? 變量
格式
p/t??? 顯示為2進制數
p/o?? 顯示為8進制數
p/d?? 顯示為10進制數
p/u?? 顯示為無符號10進制數
p/x?? 顯示為16進制數
p/a?? 地址
p/c?? 顯示為字符
p/s?? 顯示為字符串
p/f??? 浮點小數
p/i??? 顯示為機器語言(僅在顯示內存的x命令中可用)
程序指針可以寫為$pc或者$eip,因為Intel IA-32架構中的程序指針名為eip
p $pc
p $eip
x???????? 顯示特定的內存位置內容
x/格式 地址
x命令顯示特定內存位置的值。
x命令的格式 x/nyz
n是要顯示的字段數;
y是輸出格式同之前的p
z是要顯示的字段長度:
b??????? 字節
h??????? 半字(2字節)
w?????? 字(4字節)默認值
g??????? 雙字(8字節)
例如:
x $pc
x/i $pc
此處x/i意為顯示匯編指令
也有反匯編命令disassemble(或者disas)
格式:
disas????????????????????????????????????
disas?????? 程序計數器
disas?????? 開始地址? 結束地址
由此可見,在cpuid指令執行之前,EBX,ECX,EDX寄存器都是0,之后他們包含從廠商ID字符串來的值。
 
 
使用x命令顯示位于output變量前42個字節的內存位置的值(&符號表明是一個內存位置):
 
GDB的數據顯示格式:
x 按十六進制格式顯示變量。
 d 按十進制格式顯示變量。
 u 按十六進制格式顯示無符號整型。
 o 按八進制格式顯示變量。
 t 按二進制格式顯示變量。
 a 按十六進制格式顯示變量。
 c 按字符格式顯示變量。
 f 按浮點數格式顯示變量。
 
查看寄存器和內存
1) info args
 打印出當前函數的參數名及其值。
 2)info locals
 打印出當前函數中所有局部變量及其值。
 3)info catch
 打印出當前的函數中的異常處理信息。
 4)源代碼的內存
 你可以使用info line命令來查看源代碼在內存中的地址。info line后面可以跟“行號”,“函數名”,“文件名:行號”,“文件名:函
 數名”,這個命令會打印出所指定的源碼在運行時的內存地址,如:
 (gdb) info line tst.c:func
 Line 5 of "tst.c" starts at address 0x8048456 <func+6> and ends at 0x804845d <func+13>.
 5)info break 
 查看斷點信息。
 6)info threads
 看正在運行程序中的線程信息
 7)info registers
 查看寄存器的情況。(除了浮點寄存器)
 8)info all-registers
 查看所有寄存器的情況。(包括浮點寄存器)
 9)info registers <regname >
 查看所指定的寄存器的情況。
例如:info registers ebp
10)info frame或者i f
 這個命令會打印出更為詳細的當前棧層的信息,只不過,大多數都是運行時的內內
 地址。比如:函數地址,調用函數的地址,被調用函數的地址,目前的函數是由什么
 樣的程序語言寫成的、函數參數地址及值、局部變量的地址等等
11)(gdb) disassemble func
disassemble可以查看源程序的當前執行時的機器碼,這個命令
 會把目前內存中的指令dump出來
12)list
用list命令來打印程序的源代碼
13)x
可以使用examine命令(簡寫是x)來查看內存地址中的值.
x/3uh 0x54320表示,從內存地址0x54320讀取內容,h表示以雙字節為一個單位,3表示三個單位,u表示按十六進制顯示。
x/48xw? $ebp?? 也可以一樣顯示改寄存器或者用上述方法按寄存器的地址
參考:GDB查看棧信息
 
查看進程
info proc
查看棧幀
bt
backtrace
info stack
where
都是一樣的只是別名而已
bt? 顯示所有棧幀
bt N? 顯示開頭N個棧幀
bt -N 顯示最后N個棧幀
bt full 顯示棧幀+局部變量
bt full N? N同前
監視
eatch <表達式>
<表達式>發生變化時暫停運行,此處<表達式>是常量或變量等
awatch <表達式>
<表達式>被訪問,改變時暫停運行
nwatch <表達式>
<表達式>被訪問時暫停運行
delete<編號>
刪除監視點
改變變量的值
set variable <變量><表達式>
例如:
(gdb) p? options
$7=1
(gdb)set variable options=0
(gdb)print options
$8=0
GDB命令
1)單步調試:
n (next),
ni(nexti) 執行下一行(以匯編代碼為單位)
s(step 跟n的區別,s進入到函數內)
si(stepi) 執行下一行(以匯編代碼為單位)
u(until)執行到指定行
2)恢復操作:c(continue或者cont) 直到遇到下個斷點
3)臨時斷點: tbreak 有效期,第一次遇到
4)檢查變量:p (printf)? 顯示表達式
x?? 顯示內存內容
5)監視點:watch 當監視點的值發生變化時停止
6)查看棧:bt(backtrace,where) 顯示整個棧的內容。
f(frame)選擇要顯示的棧幀
do(down) 在當前調用的棧幀中選擇要顯示的棧幀
7)看已經設的斷點: ib(info break)
8)設置斷點:break(b)
break function, break line_number, break filename:line_number, break filename:function
info break 查看斷點信息。
9)刪除斷點: d(delete),delete+數值標識符(從第7點可得到) (不加參數,刪除所有斷點), clear使用跟第8點對應
10)禁用斷點:disable+數值標識符 (重新啟用 enable)
11)在單步時跳出函數:finish
12)在單步時跳出循環:until
13)條件斷點:break break-arg if (condition),例: break main if argc > 1
14)斷點命令列表(到斷點自動執行):
commands breakpoint-number 例子:commands 1
... >printf "i = %d", i
commands >end
...
end
a) 在commands 中加入silent,過濾到其他無用的輸出。
b) 最后一個commands是continue的話,自動continue。
例:comands 1
> silent
> printf "i = %d", i
> continue
> end
15)查看局部變量:info locals 得到當前棧中所有局部變量的值列表
16)設置變量:set x=12
17)GDB線程命令:
a) info threads(給出當前所有的線程信息)
b) thread 3(切換查看線程)
c) break 88 thread 3(當線程3到達源代碼行88時停止執行)
d) break 88 thread 3 if x == y
e) thread apply all bt,查看所有的線程的棧信息。
 
18) 您可以以進程ID作為第二個參數,以調式一個正在運行的進程
gdb 程序名 1234
19)finish運行到函數結束
20)forward-search(或者fo)? 向前搜索
21)generate-core-file(或者gcore)? 生成內核轉儲
22edit 編輯文件或函數
23)directory(或者dir)插入目錄
24)list(或者l)顯示函數或者行
25)info (或者i)顯示信息
26)help (或者h)顯示幫助
注意:
1)重新編譯文件時不要退出gdb,斷點可以保存著。
2)在調試時不要開啟優化代碼的選項,不然經過了優化,設置的斷點的位置跟編譯后的位置相差可能很大。
 
《深入理解計算機系統(原書第2版)》
 
更高級的GDB調試:
GDB attach到進程
總結
以上是生活随笔為你收集整理的GDB调试--以汇编语言为例的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: Java字节码instrument研究
 - 下一篇: 汇编语言系统调用过程