如何利用gdb調試程式之細節(info reg指令以及寄存器位址) //經典;
https://blog.csdn.net/u010535088/article/details/12191401
gdb檢視記憶體位址和棧中的值
http://blog.sina.com.cn/s/blog_605f5b4f0101ey1q.html
gdb檢視記憶體位址和棧中的值
(2014-05-30 22:17:42)

轉載▼
标簽: gdb記憶體位址單元x/nfuprint/fspit | 分類: C |
gdb檢視指定位址的記憶體位址的值:examine 簡寫 x-----使用gdb> help x 來檢視使用方式
x/ (n,f,u為可選參數)
n: 需要顯示的記憶體單元個數,也就是從目前位址向後顯示幾個記憶體單元的内容,一個記憶體單元的大小由後面的u定義
f:顯示格式
x(hex) 按十六進制格式顯示變量。
d(decimal) 按十進制格式顯示變量。
u(unsigned decimal) 按十進制格式顯示無符号整型。
o(octal) 按八進制格式顯示變量。
t(binary) 按二進制格式顯示變量。
a(address) 按十六進制格式顯示變量。
c(char) 按字元格式顯示變量。
f(float) 按浮點數格式顯示變量
u:每個單元的大小,按位元組數來計算。預設是4 bytes。GDB會從指定記憶體位址開始讀取指定位元組,并把其當作一個值取出來,并使用格式f來顯示
b:1 byte h:2 bytes w:4 bytes g:8 bytes
比如x/3uh 0x54320表示從記憶體位址0x54320讀取内容,h表示以雙位元組為機關,3表示輸出3個機關,u表示按照十六進制顯示。
from http://www.cnblogs.com/super119/archive/2011/03/26/1996125.html
gdb列印表達式的值:print/f 表達式
f是輸出的格式,x/d/u/o/t/a/c/f
表達式可以是目前程式的const常量,變量,函數等内容,但是GDB不能使用程式中所定義的宏
檢視目前程式棧的内容: x/10x $sp-->列印stack的前10個元素
檢視目前程式棧的資訊: info frame----list general info about the frame
檢視目前程式棧的參數: info args---lists arguments to the function
檢視目前程式棧的局部變量: info locals---list variables stored in the frame
檢視目前寄存器的值:info registers(不包括浮點寄存器) info all-registers(包括浮點寄存器)
檢視目前棧幀中的異常處理器:info catch(exception handlers)
from http://blog.chinaunix.net/uid-29062294-id-4255572.html
調試宏定義
http://blog.sina.com.cn/s/blog_605f5b4f0101ey1p.html
調試宏定義的方法:
1:通過gcc -E 産生預編譯後的源代碼,所有的預編譯動作都已完成,如頭檔案的插入,宏定義的展開
example:
- #include <</span>stdlib.h>
- #include <</span>stdio.h>
- #define MACRO1(x) (++(x))
- #define MACRO2(x) (MACRO1(x)+100)
- #define MACRO3(x) (MACRO2(x)+200)
- int main(void)
- {
- int a = 0;
- int b = 0;
- b = MACRO3(a);
- printf("%d\n", b);
- return 0;
- }
這裡的MACRO3嵌套調用了MACRO2,MACRO1。使用gcc -E test.c > test.e,得到預編譯後的代碼:
- int main(void)
- {
- int a = 0;
- int b = 0;
- b = (((++(a))+100)+200);
- printf("%d\n", b);
- return 0;
- }
這裡可以清晰的看到b = (((++(a))+100)+200);這個就比剛才的宏定義要清楚的多。但是從這個例子也可以看到這個方法的局限性。
1. 由于預編譯處理會執行所有的預處理代碼,包括頭檔案的插入,這導緻最後的代碼行數太多。
2. 得到的了一個新的代碼檔案。這樣的話,在大型工程中,如果需要調試多個檔案中的宏定義,需要我們一個一個的預編譯,太麻煩了。
2:使用gcc的-g3選項。-g是為了調試程式,它将調試資訊加入到最後的二進制可執行檔案中。當不指定級别的時候,level為2,為了調試宏定義,使用level 3,即g3。然後gdb調試的過程中使用macro expand/exp 來展開宏定義。比如:
- (gdb) macro exp MACRO3(3)
- expands to: (((++(3))+100)+200)
from http://blog.chinaunix.net/uid-29062294-id-4255572.html
函數傳回值的存放位址
http://blog.sina.com.cn/s/blog_605f5b4f0101ey3a.html
(2014-05-30 22:21:13)

轉載▼
标簽: 傳回值eaxraxstruct棧it | 分類: C |
from http://nxlhero.blog.51cto.com/962631/703953
在作業系統中(以linux為例),每個程式都需要有一個傳回值,傳回給作業系統.
在shell中,可以利用echo $?檢視程式的傳回值
比如:
$ ls not_exist
ls: not_exist: No such file or directory
$ echo $?
2
$ ls main.c
main.c
$ echo $?
可以看到,not_exist不存在,傳回2,main.c存在,傳回0,一般傳回0表示成功,而傳回非0表示失敗或者其他意義。
其實這個傳回值是存放在eax中的,c規範要求main必須傳回int,而int和eax長度是一緻的(32位系統)。
$ cat ret_test.s
.globl main
main:
pushq %rbp
movl $4,�x
popq %rbp
ret
$ make ret_test
cc ret_test.s -o ret_test
$ ./ret_test
$ echo $
4
這個彙程式設計式隻有一條指令,将4存到eax,檢測傳回值發現是4。
如果你的程式用void main(),有的編譯器會報錯,有的會警告,如果編譯過了,運作時一般沒問題。
$ cat test.c
int f()
{
return 100;
}
void main()
{
f();
}
$ make test
cc test.c -o test
$ ./test
$ echo $?
100
函數f把傳回值放到eax了,main函數什麼都沒做,是以傳回值還是100。
但是我們來看另外一個例子cat haha.c
struct xxx{
int a[50];
};
struct xxx main()
{
struct xxx haha;
return haha;
}
$make haha
cc haha.c -o haha
$ ./haha
Segmentation fault (core dumped)
$ echo $?
139
為什麼會出現段錯誤?我們後面會研究它。
我們先把傳回值進行分類:
首先是基本類型,void,char,short,long,long long,float,double,指針
然後是結構類型struct。
對于void類型,沒有傳回值,不做讨論。
char隻有1個位元組,eax有4個位元組,怎麼存?隻用低8位al就可以了。下面是示例
- //示例1:傳回值為char
-
char f()
{
char a = 'a';
return a;
}
int main()
{
char b = f();
return 0;
}
- .file "char.c"
- .text
- .globl f
- f:
- pushl �p
- movl %esp, �p
- subl $16, %esp
- movb $97, -1(�p) //我的顯示的為 movb $0x61,-0x1(%rbp) with objdump or the same with gcc -S
- movsbl -1(�p),�x //符号擴充,我自個的顯示為movzbl -0x1(%rbp),�x
- leave
- ret
- .globl main
- main:
- leal 4(%esp), �x
- andl $-16, %esp
- pushl -4(�x)
- pushl �p
- movl %esp, �p
- pushl �x
- subl $16, %esp // sub $0x10,%rsp or subq $16,%rsp
- call f
- movb %al, -5(�p) //movb %al, -1(%rbp)
- movl $0, �x
- addl $16, %esp
- popl �x
- popl �p
- leal -4(�x), %esp
- ret
從彙編代碼中可以看出,調用完f後,main函數從al中找傳回值。
同樣,對于short,int,分别把傳回值存放到ax,eax,假如在64位系統裡,那麼long long 傳回值是存到rax的,它的長度為64位,在32位系統裡是怎麼存的呢?
在32位系統裡傳回64位數,是通過edx和eax聯合實作的,edx存高32位,eax存低32位。
- long long f()
- {
- long long a = 5;
- return a;
- }
- int main()
- {
- long long b;
- b=f();
- return 0;
- }
- .file "longint.c"
- .text
- .globl f
- f:
- pushl �p
- movl %esp, �p
- subl $16, %esp
- movl $5, -8(�p)
- movl $0, -4(�p)
- movl -8(�p), �x
- movl -4(�p), �x
- leave
- ret
- .globl main
- main:
- leal 4(%esp), �x
- andl $-16, %esp
- pushl -4(�x)
- pushl �p
- movl %esp, �p
- pushl �x
- subl $20, %esp
- call f
- movl �x, -16(�p)
- movl �x, -12(�p)
- movl $0, �x
- addl $20, %esp
- popl �x
- popl �p
- leal -4(�x), %esp
- ret
對于64位的機器來說:
f函數中 movl -8(�p),�x movl -4(�p),�x 變成了mov -0x8(%rbp),%rax
而main函數取傳回值的 movl �x,-16(�p) movl �x,-12(�p) 變成了mov %rax -0x8(%rbp)
對于浮點類型,雖然運算過程中會存放在eax等普通寄存器中,但是作為傳回值時,不會用eax,edx等,即使運算結果已經存到了eax中,也要再壓到浮點數寄存器堆棧中,在主調函數中,會認為傳回結果存到浮點數寄存器了,當然,如果你要手動優化彙編代碼也是沒問題的。
下面是示例。
- float f()
- {
- return 0.1;
- }
- int main()
- {
- float a = f();
- return 0;
- }
- .file "float.c"
- .text
- .globl f
- f:
- pushl �p
- movl %esp, �p
- subl $4, %esp
- movl $0x3dcccccd, �x
- movl �x, -4(�p)
- flds -4(�p) //把結果壓到浮點寄存器棧頂 //我的使用的是movss -0x4(%rbp),%xmm0
- leave
- ret
- .globl main
- main:
- leal 4(%esp), �x
- andl $-16, %esp
- pushl -4(�x)
- pushl �p
- movl %esp, �p
- pushl �x
- subl $16, %esp
- call f
- fstps -8(�p) //從浮點寄存器棧頂取數 //我的使用的是movss %xmm0,-0x4(%rbp)
- movl $0, �x
- addl $16, %esp
- popl �x
- popl �p
- leal -4(�x), %esp
- ret
關于浮點寄存器及浮點運算指令,可參考:http://www.diybl.com/course/3_program/hb/hbjs/2007124/89946.html
如果傳回值為指針?那肯定是用eax(32bit)或者rax(64bit)了。不管是什麼類型的指針,都一樣,我們來看一個奇怪的程式。
- int f()
- {
- return 5;
- }
- int (*whatisthis()) () //這個函數的傳回類型是函數指針
- {
- return f;
- }
- int main()
- {
- int (*a) ();
- int b;
- a = whatisthis();
- b = a();
- printf("%d\n",b);
- return 0;
- }
- .file "ret_fun.c"
- .text
- .globl f
- f:
- pushl �p
- movl %esp, �p
- movl $5, �x
- popl �p
- ret
- .globl whatisthis
- whatisthis:
- pushl �p
- movl %esp, �p
- movl $f, �x
- popl �p
- ret
- .LC0:
- .string "%d\n"
- .text
- .globl main
- main:
- leal 4(%esp), �x
- andl $-16, %esp
- pushl -4(�x)
- pushl �p
- movl %esp, �p
- pushl �x
- subl $36, %esp
- call whatisthis
- movl �x, -12(�p) //我的顯示為movq %rax,-16(%rbp)
- movl -12(�p), �x //我的顯示movq -16(%rbp),%rdx
- call *�x //我的顯示 call *�x
- movl �x, -8(�p) //把傳回結果5 放入�x,進而main函數從�x讀取傳回值5到-8(%rbp)
- movl -8(�p), �x
- movl �x, 4(%esp)
- movl $.LC0, (%esp)
- call printf
- movl $0, �x
- addl $36, %esp
- popl �x
- popl �p
- leal -4(�x), %esp
- ret
一個函數的傳回值可以是函數指針,定義一個這樣的函數如下:
函數1 int f(int,char)
函數2 傳回值為上面函數的類型的指針,假如函數名為g,參數為float
那麼g的定義為 int (* g(float x) ) (int,char)
基本類型讨論完了,那麼struct類型呢?struct可大可小,怎麼存到寄存器裡呢?
答案是:主調函數會把被指派對象的位址傳給被調用函數。你可能會說這不是傳引用嗎,其實傳引用傳值什麼的都是浮雲。
還有一個問題就是,對于struct xxx { char a; };這樣的結構也要傳位址嗎?答案是肯定的,gcc是這樣做的,其它編譯器可能不這樣,當然也可以手動修改彙編代碼。
- struct xxx{
- char a;
- };
- struct xxx f()
- {
- struct xxx x;
- x.a = '9';
- return x;
- }
- int main()
- {
- struct xxx y = f();
- return 0;
- }
- .file "struct_char.c"
- .text
- .globl f
- f:
- pushl �p
- movl %esp, �p
- subl $16, %esp
- movl 8(�p), �x //取出位址,放入edx
- movb $57, -1(�p) // movb $0x39, -0x1(%rbp)
- movzbl -1(�p), �x //'9'放到 al
- movb %al, (�x) //将al内容寫到edx指向的位址 我的無此步驟
- movl �x, �x
- leave
- ret $4
- .globl main
- main:
- leal 4(%esp), �x
- andl $-16, %esp
- pushl -4(�x)
- pushl �p
- movl %esp, �p
- pushl �x
- subl $24, %esp
- leal -21(�p), �x //位址放到eax
- movl �x, (%esp) //位址壓入棧中
- call f
- subl $4, %esp //沒有取傳回值的指令了
- movzbl -21(�p), �x//因為已經寫到目的位址了 我的main中使用mov %al, -0x1(%rbp)來取得位址
- movb %al, -5(�p)
- movl $0, �x
- movl -4(�p), �x
- leave
- leal -4(�x), %esp
- ret
我們再來看個複雜點的例子
- struct xxx {
- char a[10];
- };
- struct xxx f(int a)
- {
- struct xxx t;
- t.a[9] = 1;
- return t;
- }
- int main()
- {
- struct xxx m=f(1);
- return 0;
- }
- .file "struct.c"
- .text
- .globl f
- f:
- pushl �p
- movl %esp, �p
- subl $16, %esp
- movl 8(�p), �x //取位址 我的顯示的是是movl �i, -36(%rbp) 讀取傳入參數,并複制到棧内
- movb $1, -1(�p)
- movl -10(�p), �x
- movl �x, (�x)
- movl -6(�p), �x
- movl �x, 4(�x)
- movzwl -2(�p), �x
- movw %ax, 8(�x)
- movl �x, �x
- leave
- ret $4
- .globl main
- main:
- leal 4(%esp), �x
- andl $-16, %esp
- pushl -4(�x)
- pushl �p
- movl %esp, �p
- pushl �x
- subl $24, %esp
- leal -14(�p), �x
- movl $1, 4(%esp) //先壓入參數 我的顯示 movl $1,�i
- movl �x, (%esp) //再壓入傳回值位址 我的顯示沒有此項
- call f
- subl $4, %esp
- movl $0, �x
- movl -4(�p), �x
- leave
- leal -4(�x), %esp
- ret
進入被調用函數後的堆棧情況
參數2
參數1
傳回值位址
舊EIP
舊EBP
它會到假定8(�p)處存放着傳回值的位址。這也是為什麼main的傳回值為struct時會引起段錯誤,main函數認為這個地方存着傳回值的位址,實際上這個地方是作業系統寫入的特定值,把這個當作傳回值的位址亂寫,肯定會引起段錯誤。
下面這個程式
假如對于struct,有傳回值的函數卻不指派怎麼辦?
比如
- struct xxx {
- char a[10];
- };
- struct xxx f(int a)
- {
- struct xxx t;
- t.a[9] = 1;
- return t;
- }
- int main()
- {
- f(1);
- return 0;
- }
對于上述程式,主調用函數需要開辟垃圾空間作為傳回值空間,感興趣的可以驗證下看看。
如下貼出的是我機器上上述代碼的彙編部分
f:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl �i, -36(%rbp)
movb $1, -23(%rbp)
movq -32(%rbp), %rax
movq %rax, -16(%rbp)
movzwl -24(%rbp), �x
movw %ax, -8(%rbp)
movq -16(%rbp), %rax //f函數依舊傳回位址給%rax
movzwl -8(%rbp), �x
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size f, .-f
.globl main
.type main, @function
main:
.LFB1:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $1, �i
call f
movl $0, �x //main 函數對于抛棄的傳回值不做處理而直接将�x清零。
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1:
.size main, .-main
.ident "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
.section .note.GNU-stack,"",@progbits
補充:
gcc支援代碼塊有傳回值
比如a = { int b = 2; int c = 3; c-b;} 最終a = 1;
根據我的測試:代碼塊裡必須有除了變量聲明的其他語句,否則不對,不能有return;
另外,隻能對基本類型指派,struct類型不能指派。
最後的結果是:代碼塊執行結束後,取出eax的值,檢查要指派的變量類型,如果是char,取al,如果是int,取eax,如果是long long,符号擴充,如果是float或者double,将eax強制轉換成浮點數。
下面代碼可正常運作:
- int main()
- {
- int a;
- long long a1;
- double a2;
- a = {int b = 5; printf("xxx\n");;};
- a1 = {int b = 5;int c = 2; 3-4;b-c;};
- a2 = {int b = 5;int c = 2; 10-8;};
- printf("%d\n",a);
- printf("%ld\n",a1);
- printf("%lf\n",a2);
- return 0;
- }
這個有點商榷,目前我在本機上無法編譯成功。三行的block指派語句都報錯:
retblock.c:9:4: error: expected expression before ‘{’ token
retblock.c:10:5: error: expected expression before ‘{’ token
retblock.c:11:5: error: expected expression before ‘{’ token