麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 學院 > 開發設計 > 正文

gdb基礎

2019-11-17 05:15:54
字體:
來源:轉載
供稿:網友

  就調試本機可執行文件(即不是 java* 或 perl 等)而言,使用 gdb 就對了。gdb 可用于源代碼級調試,以及跟蹤沒有源代碼的程序或檢查某個終止的程序留下的核心文件。遺憾的是,當您從來沒有使用過它,或者已經有一段時間沒有使用它時,使用它來做這些工作可能很困難。圖 1 展示了使用 gdb 來進行調試所需的每個命令。


Command Description
file load PRogram
b set breakpoint
r run
c continue
s step (line)
si step (machine instrUCtion)
n next (step over function call)
finish run until function returns
i r show all registers
i r show specific register
l list source
p display value
set args set command line arguments

圖 1

  要將 gdb 用作源代碼級調試器,請確保在包括調試符號的情況下編譯程序;這就是 gcc 的 -g 選項。對于啟動 gdb,您可以通過輸入 gdb programname(此例中是 gdb simple),或者通過單獨運行 gdb 本身并使用 file 命令加載可執行文件來達到目的。

  要設置基本的斷點,您可以在某個函數名稱或行號上中斷。例如,b 27 將在當前文件的第 27 行上設置了一個斷點。有兩種使用函數名稱的方式:b main 在函數 main 中的第一行可執行代碼上中斷,b *main 在 main 的入口地址上設置一個斷點(假如打算單步調試函數的每條指令,這樣是很有用的)。

  一旦設置了第一個斷點,可使用 run 或 r 來啟動程序并運行到第一個斷點。還可以不帶任何斷點運行程序,假如您不知道程序是在何處崩潰的,這樣將很有幫助。當您命中一個斷點 c 或 continue 時,程序將恢復執行,直至命中下一個斷點。

  step“單步”調試源代碼行。Step instruction (si) 單步調試機器代碼行(當您單步調試優化過的代碼時,si 指令可能非凡有用,這將在后面介紹)。 next 工作起來就像 step,但是它不跟蹤進入函數調用(假如的確錯誤地跟蹤進入了函數調用,可使用 finish 來完成該函數,然后在它返回的地方中斷)。

  單獨的 info register(i r)本身顯示所有寄存器的值(浮點值除外),不過您可以指定一個寄存器名稱。在 31 位系統上,通用寄存器被命名為 gpr0、gpr1、gpr2,等等;在 64 位系統上,它們被命名為 r0、r1、r2,等等。浮點寄存器遵循相同的命名約定:在 31 位系統上是 fpr0、fpr1、fpr2,等等;在 64 位系統上是 f0、f1、f2,等等。

  “l”列出程序當前停止位置四周的源代碼。您還可以指定開始列出代碼的行號或要列出的函數名稱。print 答應您打印程序中任何變量的值。 print 的一個最好的優點在于,它會為您取出一個結構中的所有值,或答應您直接引用該結構的一部分:


Breakpoint 1, main () at simple.c:30
30 boink.boik = &r1;
(gdb) print boink
$3 = {boik = 0x0}
(gdb) print boink.boik
$4 = (int *) 0x0

  最后,set args 為程序設置命令行參數。您也可以在執行 run 時指定命令行參數,但是 set args 將使參數在 run 的多次執行中都有效。


gdb Post Mortem

  當程序意外地終止時,內核會嘗試產生一個核心文件,以圖判定發生了什么錯誤。然而,核心文件通常不是在默認設置值下產生的。這可以使用 ulimit 命令來改變。ulimit -c unlimited 幫助確保您獲得應用程序的完整核心文件。

  雖然核心文件當前僅提供多線程應用程序中的有限的值,不過 2.5 版的開發內核已開始處理這個問題。預計 2.6 版的內核中會提供一些理想的線程改進。


Command Description
file load program
core load core file
BT back trace
where same as back trace
i f frame information
up move up stack
down move down stack
frame jump to frame
disassem display function’s machine code
i locals display local variable values


圖 2

  圖 2 突出顯示了一系列便利的 post mortem 命令。


(gdb) file simple
Reading symbols from simple...done.
(gdb) core core
Core was generated by `./simple’.
Program terminated with signal 11, Segmentation fault.
Reading symbols from /lib/libc.so.6...done.
Loaded symbols for /lib/libc.so.6
Reading symbols from /lib/ld.so.1...done.
Loaded symbols for /lib/ld.so.1
#0 0x400ab738 in memcpy () from /lib/libc.so.6
(gdb) where
#0 0x400ab738 in memcpy () from /lib/libc.so.6
#1 0x40066e in main () at simple.c:34
#2 0x40041eb8 in __libc_start_main () from /lib/libc.so.6
#3 0x4004ac in _start ()
(gdb) i f
Stack level 0, frame at 0x7ffff7a0:
pswa = 0x400ab738 in memcpy; saved pswa 0x0
(FRAMELESS), called by frame at 0x7ffff7a0
Arglist at 0x7ffff7a0, args:
Locals at 0x7ffff7a0, Previous frame’s sp is 0x0
(gdb) up
#1 0x40066e in main () at simple.c:34
34 memcpy (doink.boik, boink.boik, sizeof(boink.boik));
(gdb) i locals
doink = {boik = 0x4019a0}
boink = {boik = 0x0}
(gdb) ptype boink.boik
type = int *
(gdb) print *boink.boik
Cannot access memory at address 0x0
(gdb) print *doink.boik
$1 = 4

圖 3

  圖 3 簡要顯示了一個核心程序的完整運行過程。同樣,我們使用了 simple 程序。 但不是手動加載程序和核心文件,而是從命令行調入:


gdb simple core

  在加載符號之后,gdb 將指出程序在何處終止。注重當前幀 #0 包含前一節中計算的地址。gdb 將在 31 位系統上截去高位,僅顯示指令地址。 還要注重幀 #1 包含 gpr14 中的返回地址。

  接著往下看,i f 提供了關于當前堆棧幀的信息。在堆棧幀中往上移到 main,這就是我們離開該幀的地方(即調用 memcpy 的地方)。簡單的 i locals 提供了傳遞給 memcpy 的變量的值,其中一個變量 boink.boik 的值為 0x0。使用 ptype 來檢查變量類型,這樣將確認它是一個整型指針,并且假如目的是為了拷貝內容到其中,它就不應該是 0x0。最后一個選項是使用 print,通過一個星號(*)來解除指針引用,以便接收值。

處理優化過的代碼

  先前,我曾提到當您在源代碼級調試優化過的代碼時,gdb 可能變得有點棘手。編譯器優化一些代碼的執行順序以最大化性能。圖 4 顯示了這樣一個例子。您可以看到行號如何從 32 切換到 30 然后又切換回 32。


(gdb) break main
Breakpoint 1 at 0x800007a8: file simple.c, line 32.
(gdb) r
Starting program: /home/grundym/foo/simple
Breakpoint 1, main () at simple.c:32
32 do_one_thing(&doink);
(gdb) s
30 doink.boik = &r1;
(gdb)
32 do_one_thing(&doink);
(gdb)
do_one_thing (pnum_times=0x1fffffff690) at simple.c:47
47 for (i = 0; i < 4; i++) {

圖 4

  如何處理這種情況呢?使用 si 和 ni(next instruction;它類似 si,但是會跳過子例程調用)將非常有幫助。 在這個層次上,很好理解 zArchitecture 是有所幫助的。


(gdb) break *main
Breakpoint 1 at 0x80000794: file simple.c, line 27.
(gdb) display /i $pswa
(gdb) r
Starting program: /home/grundym/foo/simple

Breakpoint 1, main () at simple.c:27
27 {
1: x/i $pswa 0x80000794 : EB AF F0 50 00 24 stmg %r10,%r15,80(%r15)

(gdb) si
0x8000079a 27 {
1: x/i $pswa 0x8000079a : B9 04 00 1F lgr %r1,%r15
(gdb)
0x8000079e 27 {
1: x/i $pswa 0x8000079e : A7 FB FF 58 aghi %r15,-168
(gdb)
0x800007a2 in main () at simple.c:27
27 {
1: x/i $pswa 0x800007a2 : E3 10 F0 00 00 24 stg %r1,0(%r15)
(gdb)
32 do_one_thing(&doink);
1: x/i $pswa 0x800007a8 : 41 C0 F0 A0 la %r12,160(%r15)
(gdb)
30 doink.boik = &r1;
1: x/i $pswa 0x800007ac : C0 10 00 00 08 C2 larl %r1,0x80001930
(gdb)
0x800007b2 30 doink.boik = &r1;
1: x/i $pswa 0x800007b2 : E3 10 F0 A0 00 24 stg %r1,160(%r15)
(gdb)
32 do_one_thing(&doink);
1: x/i $pswa 0x800007b8 : B9 04 00 2C lgr %r2,%r12
(gdb)
0x800007bc 32 do_one_thing(&doink);
1: x/i $pswa 0x800007bc : C0 E5 FF FF FF 68 brasl %r14,0x8000068c
(gdb)
do_one_thing (pnum_times=0x1fffffff7f8) at simple.c:44
44 {
1: x/i $pswa 0x8000068c : EB BF F0 58 00 24 stmg %r11,%r15,88(%r15)
(gdb)

圖 5

  圖 5 顯示了為調試而對程序進行的設置。首先在 main()的地址處設置一個斷點,然后設置一個 display。display 是一個表達式,它在每次代碼停止執行時打印有關信息。在此例中,display 被設置為顯示當前指令地址處的指令。/i 是打印為反匯編代碼的格式,而當前指令指針在值/寄存器(value/register)$pswa 中。

  單步調試代碼,可以明顯看出每條機器指令都與一行 c 代碼相關聯。 前四行與第 27 行(即函數 main 的開頭)相關聯。前四行是典型的函數引入操作,它們保存寄存器、堆棧指針并調整堆棧。當關聯的行號變為 32 時,我們就設置好了對 do_one_thing() 的函數調用。

  當 display 在工作時,它顯示 x /i 作為實際數據顯示之前的命令。x 是檢查內存的命令。/i 是以指令格式來格式化;/x 將以 16 進制格式來格式化;而 /a 將以 16 進制來格式化。然而,您應該在盡可能的地方把該值看作是地址,并解析符號名稱。


display /i $pswa
display /x $pswm
display /a $r0
display /a $r1
display /a $r2
display /a $r3
display /a $r4
display /a $r5
display /a $r6
display /a $r7
display /a $r8
display /a $r9
display /a $r10
display /a $r11
display /a $r12
display /a $r13
display /a $r12
display /a $r14
display /a $r15
display /10i $pswa

圖 6

  當在指令級工作時,設置一些顯示可能是有所幫助的。您可以將所有 display 命令放在一個文件中,并在命令行上使用 -x 選項來指定它。圖 6 包含了工作在匯編程序級時通常使用的 display 命令。


Breakpoint 1, main () at simple.c:27
27 {
20: x/10i $pswa
0x80000794 : EB AF F0 50 00 24 stmg %r10,%r15,80(%r15)
0x8000079a : B9 04 00 1F lgr %r1,%r15
0x8000079e : A7 FB FF 58 aghi %r15,-168
0x800007a2 : E3 10 F0 00 00 24 stg %r1,0(%r15)
0x800007a8 : 41 C0 F0 A0 la %r12,160(%r15)
0x800007ac : C0 10 00 00 08 C2 larl %r1,0x80001930
0x800007b2 : E3 10 F0 A0 00 24 stg %r1,160(%r15)
0x800007b8 : B9 04 00 2C lgr %r2,%r12

0x800007bc : C0 E5 FF FF FF 68 brasl %r14,0x8000068c
0x800007c2 : E3 10 F0 A0 00 04 lg %r1,160(%r15)
19: /a $r15 = 0x1fffffff698
18: /a $r14 = 0x10000057b04 <__libc_start_main+260>
17: /a $r12 = 0x10000019108 <__curbrk+280>
16: /a $r13 = 0x8006c9be
15: /a $r12 = 0x10000019108 <__curbrk+280>
14: /a $r11 = 0x1fffffff7f8
13: /a $r10 = 0x80000418 <_init>
12: /a $r9 = 0x100000198f8 <_dl_debug_mask>
11: /a $r8 = 0x1000017bee0
10: /a $r7 = 0x1
9: /a $r6 = 0x2
8: /a $r5 = 0x100001803d8
7: /a $r4 = 0x1fffffff808
6: /a $r3 = 0x1fffffff7f8
5: /a $r2 = 0x1
4: /a $r1 = 0x80000794
3: /a $r0 = 0x1ff00000000
2: /x $pswm = 0x705c00180000000
1: x/i $pswa 0x80000794 : EB AF F0 50 00 24 stmg %r10,%r15,80(%r15)
(gdb)

圖 7

  這個命令打印全部 PSW 值、所有通用寄存器和從當前指令地址開始的下 10 行機器代碼。圖 7 顯示了當我們在 main() 處中斷時的結果。可以看到,在其中一些寄存器所指向的地方,/a 格式解析是如何使得理解正在發生的事情更加輕易的。

結束語

  對于一些可用于 linux 應用程序調試的基本工具以及調試過程本身,本文中的信息應該為您提供了有用的入門信息。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 九九热国产在线 | 国产午夜免费福利 | 精品久久久久久久久亚洲 | 91精选视频在线观看 | 精精国产xxxx视频在线播放7 | 欧美视频首页 | 毛片视频网址 | 国产成人精品午夜视频' | 亚洲日色 | 精品亚洲二区 | 欧美黄色大片免费观看 | 成人免费自拍视频 | 九九热在线观看视频 | 国产chinesehd精品91 | 一级做a爱片毛片免费 | 国产激情精品一区二区三区 | 国产精品亚洲三区 | 亚洲人成中文字幕在线观看 | 日本视频在线免费观看 | 青青草成人免费视频在线 | 成人免费福利视频 | 精品国产一区二区三区四区阿崩 | 国产1区2区在线 | 国产亚洲精彩视频 | 国产精品久久久久久影视 | 香蕉久久久精品 | 欧美视频一区二区三区在线观看 | 久久久久久久一区 | 精品国产乱码久久久久久久久 | 久久撸视频 | 欧美精品免费一区二区三区 | 深夜毛片免费看 | www中文在线 | www.17c亚洲蜜桃 | 国产精品呻吟 | 羞羞视频.www在线观看 | 日本高清在线免费 | av成人免费 | 国产精品久久国产精麻豆96堂 | 亚洲二区三区在线 | 精品一区二区久久久久久按摩 |