1、概念介紹。
1、Windows下的cmd就是Windows的命令行終端,其中的一些命令和Ubuntu的有些許區別,不過在這個博客的范疇中沒有涉及。
2、Windows的環境變數,大概可以說是如果設置好了在path路徑裡面了,就可以直接在cmd命令行里直接調用程序。
2、配置操作
1、選擇計算機,右鍵,選擇「屬性」
2、「高級系統設置」 –> 「環境變數」
4、後期優化
大家還可以給自己配一個用的順手的編輯器,比如sublime,gvim,(甚至是notepad ,hhh)同理把它放在環境變數里,那麼也可以在cmd中直接打開,這時在win下的編程過程就和Ubuntu的非常像了。。
5、備注
1、此處以win7為例,其他版本的應該大同小異。
2、另附片面的gdb調試方法
3、使用編輯器和命令行編譯的方法對於了解程序的運行過程更加有好處,但是從方便性上的確不如IDE,特別是用熟了vs的同學們就應該更能體會得到,所以各有利弊,大家自己選擇最符合自己口味的編程環境就好:)
B. linux 用g++編譯c++代碼的問題
*
運行 gcc/egcs
*
gcc/egcs 的主要選項
*
gdb
*
gdb 的常用命令
*
gdb 使用範例
*
其他程序/庫工具 (ar, objmp, nm, size, strings, strip, ...)
* 創建和使用靜態庫
* 創建和使用共享庫
* 使用高級共享庫特性
1.7.1 運行 gcc/egcs
Linux 中最重要的軟體開發工具是 GCC。GCC 是 GNU 的 C 和 C++ 編譯器。實際上,GCC 能夠編譯三種語言:C、C++ 和 Object C(C 語言的一種面向對象擴展)。利用 gcc 命令可同時編譯並連接 C 和 C++ 源程序。
#DEMO#: hello.c
如果你有兩個或少數幾個 C 源文件,也可以方便地利用 GCC 編譯、連接並生成可執行文件。例如,假設你有兩個源文件 main.c 和 factorial.c 兩個源文件,現在要編譯生成一個計算階乘的程序。
-----------------------
清單 factorial.c
-----------------------
#include <stdio.h>
#include <stdlib.h>
int factorial (int n)
{
if (n <= 1)
return 1;
else
return factorial (n - 1) * n;
}
-----------------------
-----------------------
清單 main.c
-----------------------
#include <stdio.h>
#include <stdlib.h>
int factorial (int n);
int main (int argc, char **argv)
{
int n;
if (argc < 2) {
printf ("Usage: %s n\n", argv [0]);
return -1;
}
else {
n = atoi (argv[1]);
printf ("Factorial of %d is %d.\n", n, factorial (n));
}
return 0;
}
-----------------------
利用如下的命令可編譯生成可執行文件,並執行程序:
$ gcc -o factorial main.c factorial.c
$ ./factorial 5
Factorial of 5 is 120.
GCC 可同時用來編譯 C 程序和 C++ 程序。一般來說,C 編譯器通過源文件的後綴名來判斷是 C 程序還是 C++ 程序。在 Linux 中,C 源文件的後綴名為 .c,而 C++ 源文件的後綴名為 .C 或 .cpp。
但是,gcc 命令只能編譯 C++ 源文件,而不能自動和 C++ 程序使用的庫連接。因此,通常使用 g++ 命令來完成 C++ 程序的編譯和連接,該程序會自動調用 gcc 實現編譯。假設我們有一個如下的 C++ 源文件(hello.C):
#include <iostream.h>
void main (void)
{
cout << "Hello, world!" << endl;
}
則可以如下調用 g++ 命令編譯、連接並生成可執行文件:
$ g++ -o hello hello.C
$ ./hello
Hello, world!
1.7.2 gcc/egcs 的主要選項
表 1-3 gcc 命令的常用選項
選項 解釋
-ansi 只支持 ANSI 標準的 C 語法。這一選項將禁止 GNU C 的某些特色,
例如 asm 或 typeof 關鍵詞。
-c 只編譯並生成目標文件。
-DMACRO 以字元串「1」定義 MACRO 宏。
-DMACRO=DEFN 以字元串「DEFN」定義 MACRO 宏。
-E 只運行 C 預編譯器。
-g 生成調試信息。GNU 調試器可利用該信息。
-IDIRECTORY 指定額外的頭文件搜索路徑DIRECTORY。
-LDIRECTORY 指定額外的函數庫搜索路徑DIRECTORY。
-lLIBRARY 連接時搜索指定的函數庫LIBRARY。
-m486 針對 486 進行代碼優化。
-o FILE 生成指定的輸出文件。用在生成可執行文件時。
-O0 不進行優化處理。
-O 或 -O1 優化生成代碼。
-O2 進一步優化。
-O3 比 -O2 更進一步優化,包括 inline 函數。
-shared 生成共享目標文件。通常用在建立共享庫時。
-static 禁止使用共享連接。
-UMACRO 取消對 MACRO 宏的定義。
-w 不生成任何警告信息。
-Wall 生成所有警告信息。
#DEMO#
MiniGUI 的編譯選項
1.7.3 gdb
GNU 的調試器稱為 gdb,該程序是一個互動式工具,工作在字元模式。在 X Window 系統中,
有一個 gdb 的前端圖形工具,稱為 xxgdb。gdb 是功能強大的調試程序,可完成如下的調試
任務:
* 設置斷點;
* 監視程序變數的值;
* 程序的單步執行;
* 修改變數的值。
在可以使用 gdb 調試程序之前,必須使用 -g 選項編譯源文件。可在 makefile 中如下定義
CFLAGS 變數:
CFLAGS = -g
運行 gdb 調試程序時通常使用如下的命令:
gdb progname
在 gdb 提示符處鍵入help,將列出命令的分類,主要的分類有:
* aliases:命令別名
* breakpoints:斷點定義;
* data:數據查看;
* files:指定並查看文件;
* internals:維護命令;
* running:程序執行;
* stack:調用棧查看;
* statu:狀態查看;
* tracepoints:跟蹤程序執行。
鍵入 help 後跟命令的分類名,可獲得該類命令的詳細清單。
#DENO#
1.7.4 gdb 的常用命令
表 1-4 常用的 gdb 命令
命令 解釋
break NUM 在指定的行上設置斷點。
bt 顯示所有的調用棧幀。該命令可用來顯示函數的調用順序。
clear 刪除設置在特定源文件、特定行上的斷點。其用法為:clear FILENAME:NUM。
continue 繼續執行正在調試的程序。該命令用在程序由於處理信號或斷點而
導致停止運行時。
display EXPR 每次程序停止後顯示表達式的值。表達式由程序定義的變數組成。
file FILE 裝載指定的可執行文件進行調試。
help NAME 顯示指定命令的幫助信息。
info break 顯示當前斷點清單,包括到達斷點處的次數等。
info files 顯示被調試文件的詳細信息。
info func 顯示所有的函數名稱。
info local 顯示當函數中的局部變數信息。
info prog 顯示被調試程序的執行狀態。
info var 顯示所有的全局和靜態變數名稱。
kill 終止正被調試的程序。
list 顯示源代碼段。
make 在不退出 gdb 的情況下運行 make 工具。
next 在不單步執行進入其他函數的情況下,向前執行一行源代碼。
print EXPR 顯示表達式 EXPR 的值。
1.7.5 gdb 使用範例
-----------------
清單 一個有錯誤的 C 源程序 bugging.c
-----------------
#include <stdio.h>
#include <stdlib.h>
static char buff [256];
static char* string;
int main ()
{
printf ("Please input a string: ");
gets (string);
printf ("\nYour string is: %s\n", string);
}
-----------------
上面這個程序非常簡單,其目的是接受用戶的輸入,然後將用戶的輸入列印出來。該程序使用了
一個未經過初始化的字元串地址 string,因此,編譯並運行之後,將出現 Segment Fault 錯誤:
$ gcc -o test -g test.c
$ ./test
Please input a string: asfd
Segmentation fault (core mped)
為了查找該程序中出現的問題,我們利用 gdb,並按如下的步驟進行:
1.運行 gdb bugging 命令,裝入 bugging 可執行文件;
2.執行裝入的 bugging 命令;
3.使用 where 命令查看程序出錯的地方;
4.利用 list 命令查看調用 gets 函數附近的代碼;
5.唯一能夠導致 gets 函數出錯的因素就是變數 string。用 print 命令查看 string 的值;
6.在 gdb 中,我們可以直接修改變數的值,只要將 string 取一個合法的指針值就可以了,為
此,我們在第 11 行處設置斷點;
7.程序重新運行到第 11 行處停止,這時,我們可以用 set variable 命令修改 string 的取值;
8.然後繼續運行,將看到正確的程序運行結果。
#DEMO#
1.7.6 其他程序/庫工具
strip:
nm:
size:
string:
1.7.7 創建和使用靜態庫
創建一個靜態庫是相當簡單的。通常使用 ar 程序把一些目標文件(.o)組合在一起,成為一個單獨的庫,然後運行 ranlib,以給庫加入一些索引信息。
1.7.8 創建和使用共享庫
特殊的編譯和連接選項
-D_REENTRANT 使得預處理器符號 _REENTRANT 被定義,這個符號激活一些宏特性。
-fPIC 選項產生位置獨立的代碼。由於庫是在運行的時候被調入,因此這個
選項是必需的,因為在編譯的時候,裝入內存的地址還不知道。如果
不使用這個選項,庫文件可能不會正確運行。
-shared 選項告訴編譯器產生共享庫代碼。
-Wl,-soname -Wl 告訴編譯器將後面的參數傳遞到連接器。而 -soname 指定了
共享庫的 soname。
# 可以把庫文件拷貝到 /etc/ld.so.conf 中列舉出的任何目錄中,並以
root 身份運行 ldconfig;或者
# 運行 export LD_LIBRARY_PATH='pwd',它把當前路徑加到庫搜索路徑中去。
1.7.9 使用高級共享庫特性
1. ldd 工具
ldd 用來顯示執行文件需要哪些共享庫, 共享庫裝載管理器在哪裡找到了需要的共享庫.
2. soname
共享庫的一個非常重要的,也是非常難的概念是 soname——簡寫共享目標名(short for shared object name)。這是一個為共享庫(.so)文件而內嵌在控制數據中的名字。如前面提到的,每一個程序都有一個需要使用的庫的清單。這個清單的內容是一系列庫的 soname,如同 ldd 顯示的那樣,共享庫裝載器必須找到這個清單。
soname 的關鍵功能是它提供了兼容性的標准。當要升級系統中的一個庫時,並且新庫的 soname 和老的庫的 soname 一樣,用舊庫連接生成的程序,使用新的庫依然能正常運行。這個特性使得在 Linux 下,升級使用共享庫的程序和定位錯誤變得十分容易。
在 Linux 中,應用程序通過使用 soname,來指定所希望庫的版本。庫作者也可以通過保留或者改變 soname 來聲明,哪些版本是相互兼容的,這使得程序員擺脫了共享庫版本沖突問題的困擾。
查看/usr/local/lib 目錄,分析 MiniGUI 的共享庫文件之間的關系
3. 共享庫裝載器
當程序被調用的時候,Linux 共享庫裝載器(也被稱為動態連接器)也自動被調用。它的作用是保證程序所需要的所有適當版本的庫都被調入內存。共享庫裝載器名字是 ld.so 或者是 ld-linux.so,這取決於 Linux libc 的版本,它必須使用一點外部交互,才能完成自己的工作。然而它接受在環境變數和配置文件中的配置信息。
文件 /etc/ld.so.conf 定義了標准系統庫的路徑。共享庫裝載器把它作為搜索路徑。為了改變這個設置,必須以 root 身份運行 ldconfig 工具。這將更新 /etc/ls.so.cache 文件,這個文件其實是裝載器內部使用的文件之一。
可以使用許多環境變數控制共享庫裝載器的操作(表1-4+)。
表 1-4+ 共享庫裝載器環境變數
變數 含義
LD_AOUT_LIBRARY_PATH 除了不使用 a.out 二進制格式外,與 LD_LIBRARY_PATH 相同。
LD_AOUT_PRELOAD 除了不使用 a.out 二進制格式外,與 LD_PRELOAD 相同。
LD_KEEPDIR 只適用於 a.out 庫;忽略由它們指定的目錄。
LD_LIBRARY_PATH 將其他目錄加入庫搜索路徑。它的內容應該是由冒號
分隔的目錄列表,與可執行文件的 PATH 變數具有相同的格式。
如果調用設置用戶 ID 或者進程 ID 的程序,該變數被忽略。
LD_NOWARN 只適用於 a.out 庫;當改變版本號是,發出警告信息。
LD_PRELOAD 首先裝入用戶定義的庫,使得它們有機會覆蓋或者重新定義標准庫。
使用空格分開多個入口。對於設置用戶 ID 或者進程 ID 的程序,
只有被標記過的庫才被首先裝入。在 /etc/ld.so.perload 中指定
了全局版本號,該文件不遵守這個限制。
4. 使用 dlopen
另外一個強大的庫函數是 dlopen()。該函數將打開一個新庫,並把它裝入內存。該函數主要用來載入庫中的符號,這些符號在編譯的時候是不知道的。比如 Apache Web 伺服器利用這個函數在運行過程中載入模塊,這為它提供了額外的能力。一個配置文件控制了載入模塊的過程。這種機制使得在系統中添加或者刪除一個模塊時,都不需要重新編譯了。
可以在自己的程序中使用 dlopen()。dlopen() 在 dlfcn.h 中定義,並在 dl 庫中實現。它需要兩個參數:一個文件名和一個標志。文件名可以是我們學習過的庫中的 soname。標志指明是否立刻計算庫的依賴性。如果設置為 RTLD_NOW 的話,則立刻計算;如果設置的是 RTLD_LAZY,則在需要的時候才計算。另外,可以指定 RTLD_GLOBAL,它使得那些在以後才載入的庫可以獲得其中的符號。
當庫被裝入後,可以把 dlopen() 返回的句柄作為給 dlsym() 的第一個參數,以獲得符號在庫中的地址。使用這個地址,就可以獲得庫中特定函數的指針,並且調用裝載庫中的相應函數。
C. 如何gdb調試一個運行中的進程
第一步 編譯一個死循環程序。
/* File name malloc.c*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void getmem(void **p, int num){
*p = (void *)malloc(num);
}
void test(void){
char *str = NULL;
getmem((void **)&str, 100);
strcpy(str, "Hello");
printf("%s\n", str);
}
int main(void){
int i = 0;
while(1){
if (i == 1){
test();
return 1;
}
}
return 0;
}
我們可以看出,這個程序就是malloc一段內存空間,用來供strcpy使用,由於只是調試一下,就沒有在test程序中加上一些關於strcpy的正確性判斷語句。
函數的正常退出的情況是i==1,但是程序運行過程中根本無法使i==1成立。i的變數的值將會在使用gdb時用到。
開始編譯
$gcc -g malloc.c
得用gdb,加上-g還是需要的。生成的可執行文件為a.out
第二步 讓gdb連接到正在執行的進程上去
首先運行程序。
$./a.out
明顯的,是一個死循環。
重新開一個shell
$ps -u
我的機器的運行情況如下所示:
Warning: bad ps syntax, perhaps a bogus '-'? See http://procps.sf.net/faq.html
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
wyc 7712 0.0 0.1 6092 3644 pts/8 Ss 10:24 0:00 bash
wyc 7880 0.0 0.1 6092 3608 pts/9 Ss 10:27 0:00 bash
wyc 7929 0.0 0.3 10848 6468 pts/9 S+ 10:28 0:00 gdb
wyc 8347 93.0 0.0 1652 284 pts/8 R+ 10:42 0:13 ./a.out
...
看到沒有? ./a.out的進程號是8347。
現在啟動gdb
$gdb
由於是調試運行的進程,不是可執行文件,後面不需要跟任何參數。在用 gdb調試運行狀態下的程序時,最核心的就是gdb內部的attach命令
用法為
(gdb) attach
這是我的機器上的例子:
$ gdb
GNU gdb (GDB) 7.1.50.20100621
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show ing"
and "show warranty" for details.
This GDB was configured as "i686-pc-linux-gnu".
For bug reporting instructions, please see:
.
(gdb) attach 8347
Attaching to process 8347
Reading symbols from /home/wyc/desktop/my_program/review/a.out...done.
Reading symbols from /lib/tls/i686/cmov/libc.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib/tls/i686/cmov/libc.so.6
Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib/ld-linux.so.2
main () at malloc.c:19
19 if (i == 1){
(gdb) p i
$1 = 0
(gdb) set i=1
Ambiguous set command "i=1": .
(gdb) i=1
Undefined info command: "=1". Try "help info".
(gdb) set i=1
Ambiguous set command "i=1": .
(gdb) set var i=1
(gdb) l
14 }
15
16 int main(void){
17 int i = 0;
18 while(1){
19 if (i == 1){
20 test();
21 return 1;
22 }
23 }
(gdb) n
20 test();
(gdb)
21 return 1;
(gdb)
25 }
(gdb)
0xb7f47775 in __libc_start_main () from /lib/tls/i686/cmov/libc.so.6
(gdb)
Single stepping until exit from function __libc_start_main,
which has no line number information.
Program exited with code 01.
(gdb)
在運行到第20行命令的時候,可以看一下到運行./a.out的那個shell,應該hello字元串在標准輸出上了。當gdb中顯示進程退出時,./a.out的shell應該結束了當前進程了。
在gdb中用set var i=1 來修改變數i的值(用set i=1不能識別命令),使程序能夠正常退出。
在調試時,當前程序調用的所有庫也全部都出來了。這個例子中的
Reading symbols from /home/wyc/desktop/my_program/review/a.out...done.
Reading symbols from /lib/tls/i686/cmov/libc.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib/tls/i686/cmov/libc.so.6
Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib/ld-linux.so.2
是a.out程序所調用的全部庫。可以用這種辦法分析當前運行的程序的庫的調用情況。
千萬不要關掉gdb,以下調試更精彩:
第三步 在gdb中重啟程序
在上面已經知道了程序正常退出了,但是gdb還沒有退出,這時在gdb中運行run效果如何?
(gdb) run
Starting program: /home/wyc/desktop/my_program/review/a.out
下面是死循環了...
接下Ctrl+c,給gdb發個SIGINT的信號。
^C
Program received signal SIGINT, Interrupt.
main () at malloc.c:19
19 if (i == 1){
(gdb) p i
$2 = 0
(gdb) set var i=1
(gdb) n
20 test();
(gdb) n
Hello
21 return 1;
(gdb) n
25 }
(gdb) n
0xb7e7b775 in __libc_start_main () from /lib/tls/i686/cmov/libc.so.6
(gdb) n
Single stepping until exit from function __libc_start_main,
which has no line number information.
Program exited with code 01.
可以看出,用gdb連接進程後,他會找到運行這個進程所需的全部文件,當前進程關閉後,仍然可以在gdb中啟動這個程序。
不得不佩服GDB的調試功能的強大
gdb中的其它命令,就看你分析程序時是否用到了,例如下面的一些簡單的命令:
常用的bt, p , p/x , setp, info registers, break , jump ......
D. 如何使用gdb調試android程序
用gdb調試動態鏈接庫
大家都知道在 Linux 可以用 gdb 來調試應用程序,當然前提是用 gcc 編譯程序時要加上 -g 參數。
我這篇文章里將討論一下用 gdb 來調試動態鏈接庫的問題。
首先,假設我們准備這樣的一個動態鏈接庫:
引用:
庫名稱是: ggg
動態鏈接庫文件名是: libggg.so
頭文件是: get.h
提供這樣兩個函數調用介面:
int get ();
int set (int a);
要生成這樣一個動態鏈接庫,我們首先編寫這樣一個頭文件:
/************關於本文檔********************************************
*filename: get.h
*********************************************************************/
int get ();
int set (int a);
然後准備這樣一個生成動態鏈接庫的源文件:
/************關於本文檔********************************************
*filename: get.cpp
*********************************************************************/
#include
#include "get.h"
static int x=0;
int get ()
{
printf ("get x=%d\n", x);
return x;
}
int set (int a)
{
printf ("set a=%d\n", a);
x = a;
return x;
}
然後我們用 GNU 的 C/C++ 編譯器來生成動態鏈接庫,編譯命令如下:
引用:
g++ get.cpp -shared -g -DDEBUG -o libggg.so
這樣我們就准備好了動態鏈接庫了,下面我們編寫一個應用程序來調用此動態鏈接庫,源代碼如下:
/************關於本文檔********************************************
*filename: pk.cpp
*********************************************************************/
#include
#include "get.h"
int main (int argc, char** argv)
{
int a = 100;
int b = get ();
int c = set (a);
int d = get ();
printf ("a=%d,b=%d,c=%d,d=%d\n",a,b,c,d);
return 0;
}
編譯此程序用下列命令,如果已經把上面生成的 libggg.so 放到了庫文件搜索路徑指定的文件目錄,比如 /lib 或 /usr/lib 之類的,就用下面這條命令:
引用:
g++ pk.cpp -o app -Wall -g -lggg
否則就用下面這條命令:
引用:
g++ pk.cpp -o app -Wall -g -lggg -L`pwd`
下面我們就開始調試上面命令生成的 app 程序吧。如果已經把上面生成的 libggg.so 放到了庫文件搜索路徑指定的文件目錄,比如 /lib 或 /usr/lib 之類的,調試就順利完成,如下:
引用:
#gdb ./app
GNU gdb 6.4-debian
Copyright 2005 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show ing" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i486-linux-gnu"...Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".
(gdb) b main /* 這是在程序的 main 處設置斷點 */
Breakpoint 1 at 0x804853c: file pk.cpp, line 7.
(gdb) b set /* 這是在程序的 set 處設置斷點 */
Function "set" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y /* 這里必須選擇 y 調試程序才會跟蹤到動態鏈接庫內部去 */
Breakpoint 2 (set) pending.
(gdb) run /* 開始運行我們的程序,直到遇見斷點時暫停 */
Starting program: /data/example/c/app
Breakpoint 3 at 0xb7f665f8: file get.cpp, line 11.
Pending breakpoint "set" resolved
Breakpoint 1, main (argc=1, argv=0xbfArrayArray0504) at pk.cpp:7
7 int a = 100;
(gdb) n /* 繼續執行程序的下一行代碼 */
8 int b = get ();
(gdb) n /* 程序執行到了我們斷點所在的動態鏈接庫了 */
get x=0
Array int c = set (a);
(gdb) n
Breakpoint 3, set (a=100) at get.cpp:11
11 printf ("set a=%d\n", a);
(gdb) list /* 查看當前代碼行周圍的代碼,證明我們已經跟蹤到動態鏈接庫的源代碼裡面了 */
6 printf ("get x=%d\n", x);
7 return x;
8 }
Array int set (int a)
10 {
11 printf ("set a=%d\n", a);
12 x = a;
13 return x;
14 }
(gdb) n
set a=100
12 x = a;
(gdb) n
13 return x;
(gdb) n
14 }
(gdb) n
main (argc=1, argv=0xbfArrayArray0504) at pk.cpp:10
10 int d = get ();
(gdb) n
get x=100
11 printf ("a=%d,b=%d,c=%d,d=%d\n",a,b,c,d);
(gdb) n
a=100,b=0,c=100,d=100
12 return 0;
(gdb) c
Continuing.
Program exited normally.
(gdb) quit /* 程序順利執行結束 */
如果我們沒有把動態鏈接庫放到指定目錄,比如/lib裡面,調試就會失敗,過程如下:
引用:
# gdb ./app
GNU gdb 6.4-debian
Copyright 2005 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show ing" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i486-linux-gnu"...Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".
(gdb) b main
Breakpoint 1 at 0x804853c: file pk.cpp, line 7.
(gdb) b set
Function "set" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 2 (set) pending.
(gdb) run /* 雖然調試操作都一樣,但程序執行失敗 */
Starting program: /data/example/c/app
/data/example/c/app: error while loading shared libraries: libggg.so: cannot open shared object file: No such file or directory
Program exited with code 0177.
(gdb) quit
E. linux入門基礎(四)Gdb調試程序
Gdb調試
注意:在Gcc編譯選項中一定要加入
–g
退出GDB:quit
或
Ctrl+d
調試過程:
1.
查看文件
命令:
(gdb)
l
命令:(gdb)
b
行號
註:到第17行停止,並沒有執行17行
3.
查看斷點情況
info
b
4.
運行代碼
r
命令:
(gdb)
r
(也可以指定行開始運行,在r後面加上行號)
5.
查看變數值
p
命令:(gdb)
p
變數名
6.
設置監視點:
watch
z
(變數名)
也可以是復雜的表達式
7.
單步運行
命令:(gdb)
n
(逐過程)
8.逐步,會進入函數
命令:(gdb)
s
9.條件斷點
將正常斷點轉變為條件斷點:condition
如:condition
1
i
==
10
只有當滿足條件i
==
1時,才會在斷點1處暫停
10.
恢復程序運行
c
命令:
(gdb)
c
(程序就會運行,如果沒有斷點,就運行剩下部分,如果有斷點,就會運行到下一個斷點)
11.
去除斷點
clear
line_number
clear
filename:line_number
12.
help
12.1
help找出類別
12.2help從列表中
F. gdb調試命令是什麼
gdb調試命令如下:
1、啟動gdb
$gdb
這樣可以和gdb進行交互了。
2、啟動gdb,並且分屏顯示源代碼
$gdb -tui
這樣,使用了'-tui'選項,啟動可以直接將屏幕分成兩個部分,上面顯示源代碼,比用list方便多了。這時候使用上下方向鍵可以查看源代碼,想要命令行使用上下鍵就用[Ctrl]n和[Ctrl]p。
3、啟動gdb調試指定程序app
$gdb app
這樣就在啟動gdb之後直接載入了app可執行程序,需要注意的是,載入的app程序必須在編譯的時候有gdb調試選項,例如'gcc -g app app.c',注意,如果修改了程序的源代碼,但是沒有編譯,那麼在gdb中顯示的會是改動後的源代碼,但是運行的是改動前的程序,這樣會導致跟蹤錯亂的。
4、啟動程序之後,再用gdb調試
$gdb <program> <PID>
這里,<program>是程序的可執行文件名,<PID>是要調試程序的PID.如果你的程序是一個服務程序,那麼你可以指定這個服務程序運行時的進程ID。gdb會自動attach上去,並調試他。program應該在PATH環境變數中搜索得到。
5、啟動程序之後,再啟動gdb調試
$gdb <PID>
這里,程序是一個服務程序,那麼你可以指定這個服務程序運行時的進程ID,<PID>是要調試程序的PID.這樣gdb就附加到程序上了,但是現在還沒法查看源代碼,用file命令指明可執行文件就可以顯示源代碼了。
G. 如何使用arm-eabi-gdb調試android c/c++程序
1,先下載最新版本的gdb源代碼包,我使用的是gdb-7.6.tar.gz,使用tar命令進行解包(tar -xvzf gdb-7.6.tar.gz),cd進gdb-7.6/gdb目錄,使用vi找到remote.c中的如下代碼:
if(buf_len > 2 * rsa->sizeof_g_packet)
error(_("Remote 'g' packet reply is too long: %s"),rs->buf);
將上面兩行注釋掉,添加如下代碼
if(buf_len > 2 * rsa->sizeof_g_packet)
{
rsa->sizeof_g_packet = buf_len;
for(i = 0; i < gdbarch_num_regs(gdbarch); i++)
{
if(rsa->regs[i].pnum == -1)
continue;
if(rsa->regs[i].offset >= rsa->sizeof_g_packet)
rsa->regs[i].in_g_packet = 0;
else
rsa->regs[i].in_g_packet = 1;
}
}
使用如下命令對代碼進行配置、編譯和安裝
./configure --target=arm-linux --prefix=/usr/local/arm-gdb -v
make
make install
2,gdbserver使用android4.2模擬器中自帶的版本(v7.1)
3,將NDK編譯好的C/C++可執行程序,上傳到模擬器中/data/test目錄下,假設可執行程序的名稱為testHello。
4,使用命令:gdbserver :7000 /data/test/testHello 啟動模擬器端的調試。
5,啟動arm-linux-gdb之前,使用vi打開~/.bash_profile文件,在其中添加:
export PATH=$PATH:/usr/local/arm-gdb/bin,以便在程序的其他目錄可以直接啟動arm-linux-gdb程序
6,cd至ndk編譯好的testHello文件所在目錄
7,使用如下命令進行埠映射:adb forward tcp:7000 tcp:7000,將模擬器的7000埠和本機的7000埠進行映射
8,使用命令:arm-linux-gdb testHello啟動gdb調試
9,使用target remote :7000 鏈接模擬器中gdbserver啟動的服務。
10,自此,我們就可以使用gdb命令進行代碼調試了。
H. 如何編譯可以在Windows下運行的帶有python支持的ARM Linux GDB
做這件事情的目的是為了在QtCreator里調試ARM Linux程序的時候,能看清楚QString、QList這些Qt特有的對象的內容,而不是一個完全看不懂的結構體。
目前(2014年8月)Linaro、CodeSourcery的GCC工具鏈里的GDB都不支持Python。想知道你用的GDB支持不支持,試一試就行,這樣表示不支持:
(gdb) python
>print 'Hello GDB!'
>(按Ctrl+D)Python scripting is not supported in this of GDB.
這樣表示支持:
(gdb) python
>print 'Hello GDB!'
>(按Ctrl+D)Hello GDB!
這件事情乍一看也很簡單,只要把GDB源碼下載下來,然後再配置,打開Python支持就行了。實際上會遇到的問題是,在MinGW下,又要與「\」和「:」這兩個Windows路徑里的刺頭斗爭了。我覺得我之前挺傻,編譯MinGW下Qt的時候,就去硬磕源碼和configure腳本去了。這次GDB的configure是自動生成的,不是給人看的,configure.ac看起來也很費勁,根本磕不下去,於是我換了個思路,在ubuntu下交叉編譯吧,sudo apt-get install mingw32,這是Ubuntu下的MinGW交叉編譯器。
然後是依賴,這樣的GDB要依賴expat和python的開發版本。如果是ubuntu底下直接編譯,apt-cache search一下他們的開發版本,然後sudo apt-get install一下就好了;給MinGW交叉編譯就麻煩了。先說expat,這個好辦,把http://downloads.sourceforge.net/project/expat/expat/2.1.0/expat-2.1.0.tar.gz下載下來,然後:
./configure --prefix=[安裝目錄,如/home/c/mingw-gdb/expat] --host=i586-mingw32msvc
make
make install
會提示一些警告,無視即可。
Python就無語了,目前的GDB貌似最高支持Python 2.7,而2.7版本的Python本身不支持MinGW…… 好在有高手做了Patch,也寫了說明,可以參考這文章:http://mdqinc.com/blog/2011/10/cross-compiling-python-for-windows-with-mingw32/
但是,就算這樣,編譯也充滿挑戰,要修復很多問題,出來的Python還少「nt」模塊。就在我覺得沒辦法的時候,突然發現Windows版Qt提供的MinGW居然內置了Python開發包,位置在Tools/mingw48_32/opt,趕緊把它拷貝到Linux下,比如/home/c/mingw-gdb/python。當然,你也必須確保ubuntu下有可用的python。
然後,給GDB打一個補丁:
--- gdb-7.8/gdb/configure 2014-07-29 20:37:42.000000000 +0800
+++ gdb-7.8-old/gdb/configure 2014-08-30 00:08:27.122042706 +0800
@@ -8263,21 +8263,22 @@
# We have a python program to use, but it may be too old.
# Don't flag an error for --with-python=auto (the default).
have_python_config=yes
- python_includes=`${python_prog} ${srcdir}/python/python-config.py --includes`
+ python_config_tool=`echo ${python_prog} | sed "s#python.exe#python-config#g"`
+ python_includes=`${python_config_tool} --includes`
if test $? != 0; then
have_python_config=failed
if test "${with_python}" != auto; then
as_fn_error "failure running python-config --includes" "$LINENO" 5
fi
fi
- python_libs=`${python_prog} ${srcdir}/python/python-config.py --ldflags`
+ python_libs=`${python_config_tool} --ldflags`
if test $? != 0; then
have_python_config=failed
if test "${with_python}" != auto; then
as_fn_error "failure running python-config --ldflags" "$LINENO" 5
fi
fi
- python_prefix=`${python_prog} ${srcdir}/python/python-config.py --exec-prefix`
+ python_prefix=`${python_config_tool} --exec-prefix`
if test $? != 0; then
have_python_config=failed
if test "${with_python}" != auto; then
@@ -8343,12 +8344,12 @@
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+#if ac_fn_c_try_link "$LINENO"; then :
have_libpython=${version}
found_usable_python=yes
PYTHON_CPPFLAGS=$new_CPPFLAGS
PYTHON_LIBS=$new_LIBS
-fi
+#fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
CPPFLAGS=$save_CPPFLAGS
這個補丁的目的是強制為檢測到python。
然後給拷貝到Linux下的python開發包打一個補丁:
--- python-old/bin/python-config 2013-04-18 02:43:01.000000000 +0800
+++ python/bin/python-config 2014-08-30 00:53:16.630060288 +0800
@@ -1,4 +1,4 @@
-#!/temp/x32-480-posix-dwarf-r2/mingw32/opt/bin/python2.7.exe
+#!/usr/bin/python
import sys
import os
@@ -31,26 +31,23 @@
for opt in opt_flags:
if opt == '--prefix':
- print sysconfig.PREFIX
+ print '../python'
elif opt == '--exec-prefix':
- print sysconfig.EXEC_PREFIX
+ print '../python'
elif opt in ('--includes', '--cflags'):
- flags = ['-I' + sysconfig.get_python_inc(),
- '-I' + sysconfig.get_python_inc(plat_specific=True)]
+ flags = ['-I' + os.path.split(os.path.realpath(__file__))[0] + '/../include/python2.7']
if opt == '--cflags':
- flags.extend(getvar('CFLAGS').split())
+ flags += ['-fno-strict-aliasing -DMS_WIN32 -DMS_WINDOWS -DHAVE_USABLE_WCHAR_T -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes']
print ' '.join(flags)
elif opt in ('--libs', '--ldflags'):
- libs = getvar('LIBS').split() + getvar('SYSLIBS').split()
- libs.append('-lpython'+pyver)
+ libs = ['-lm -lpython2.7 -Wl,--out-implib=libpython2.7.dll.a']
# add the prefix/lib/pythonX.Y/config dir, but only if there is no
# shared library in prefix/lib/.
if opt == '--ldflags':
if not getvar('Py_ENABLE_SHARED'):
- libs.insert(0, '-L' + getvar('LIBPL'))
- libs.extend(getvar('LINKFORSHARED').split())
+ libs.insert(0, '-L' + os.path.split(os.path.realpath(__file__))[0] + '/../lib/python2.7/config')
print ' '.join(libs)
因為Linux下是無法運行開發包中的python.exe的,所以這個補丁借用了ubuntu的python。裡面的cflags和ldflags都是在Windows底下運行原始python-config獲得的。prefix和exec-prefix設成「../python」,可以在編譯完以後,把python開發包拷貝到gdb安裝目錄裡面的python子目錄,這樣運行GDB的時候就不需要設定PYTHONHOME環境變數了。
最後一個事情,確保你的Linux下有arm交叉編譯器,我的是arm-linux-gnueabihf,是啥target就寫啥。
准備工作做完了,開始配置和編譯:
./configure --with-expat --host=i586-mingw32msvc --target=arm-linux-gnueabihf --with-libexpat-prefix=[expat安裝位置] --with-python=[python開發包安裝位置/bin/python.exe]
make
make DESTDIR=[GDB安裝位置] install
然後把GDB安裝位置下面的所有文件拷貝到Windows下,再把python開發包拷貝到同目錄下的python子目錄,大功告成。
如果提示沒找到libpython2.7.dll,那就把GDB安裝目錄的python/bin下的拷貝到bin下。
如果發現生成的exe文件太大了,那就strip一下。
2015年9月12日追加:
在windows下調試時,一般會提示說載入不了共享庫,讓你用"set sysroot"或"set solib-search-path"之類設定路徑的。這個問題可以通過.gdbinit文件,用上面這兩條命令來設定路徑解決,如果想一勞永逸,可以在編譯的時候加上host_configargs環境變數來解決這個問題:
host_configargs=--with-sysroot=E:\MinGW\opt\sysroot-arm ./configure ...
或者
export host_configargs=--with-sysroot=E:\MinGW\opt\sysroot-arm
./configure ...
後面的路徑是放在windows下的sysroot的位置。
I. 如何使用gdb調試C程序
GDB是GNU開源組織發布的一個強大的UNIX下的程序調試工具,基於命令行界面。
在Windows下如果想使用GDB,可以先下載安裝一個MiniGW,這個工具包含GCC編譯器和GDB調試器,再配合一個Notepad++或者Sublime Text(Sublime這款編輯器不是免費的,可以參考我前面寫的一篇文章的破解方法來免費使用《Sublime Text 2在Linux和Windows下的破解》)來寫代碼,這樣就足夠了;
大多數Linux發行版都包含了GCC編輯器和GDB調試器,如果沒有可以自行安裝,這不是本文討論論的重點,配合Vim或者Linux版本的Sublime Text來寫代碼也是不錯的選擇;