gcc或g++的编译选项 -shared -fPIC 与 -g -rdynamic 部分转载

动态库

Linux 下动态链接库(shared object file,共享对象文件)的文件后缀为.so,它是一种特殊的目标文件(object file),可以在程序运行时被加载(链接)进来。使用动态链接库的优点是:程序的可执行文件更小,便于程序的模块化以及更新,同时,有效内存的使用效率更高。

创建一个动态链接库,可使用 GCC 的-shared选项。输入文件可以是源文件、汇编文件或者目标文件。另外还得结合-fPIC选项。-fPIC 选项作用于编译阶段,告诉编译器产生与位置无关代码(Position-Independent Code);这样一来,产生的代码中就没有绝对地址了,全部使用相对地址,所以代码可以被加载器加载到内存的任意位置,都可以正确的执行。这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的。

gcc选项-g与-rdynamic的异同

 -g ,是一个调试选项,是一个编译选项,即在源代码编译的过程中起作用,让gcc把更多调试信息(也就包括符号信息)收集起来并将存放到最终的目标文件内。调试信息本地格式(stabs, COFF, XCOFF,或DWARF)(`-gstabs+ ', `-gstabs', `-gxcoff+ ', `-gxcoff', `-gdwarf+ ',或
`-gdwarf' )

-rdynamic 却是一个 连接选项 ,它将指示连接器把所有符号(而不仅仅只是程序已使用到的外部符号)都添加到动态符号表(即.dynsym表)里,以便那些通过 dlopen() 或 backtrace() (这一系列函数使用.dynsym表内符号)这样的函数使用。

添加-rdynamic选项后,.dynsym表就包含了所有的符号,不仅是已使用到的外部动态符号,还包括本程序内定义的符号,比如bar、foo、baz等。 

[root@www c]# gcc -O0 -rdynamic -o t.rd t.c
[root@www c]# readelf -s t.rd 
Symbol table '.dynsym' contains 20 entries:
Symbol table '.symtab' contains 67 entries:
[root@www c]# strip t.rd
[root@www c]# readelf -s t.rd

简单总结一下-g选项与-rdynamic选项的差别: 
1,-g选项新添加的是调试信息(一系列.debug_xxx段),被相关调试工具,比如gdb使用后,可以被strip掉。(可以用strip a.exe

2,-rdynamic选项新添加的是动态连接符号信息,用于动态连接功能,比如dlopen()系列函数、backtrace()系列函数使用,不能被strip掉,即强制strip将导致程序无法执行:

[root@www c]# ./t.rd
test[root@www c]# strip -R .dynsym t.rd
[root@www c]# ./t.rd

3 .symtab表在程序加载时会被加载器 丢弃 ,gdb等调试工具由于可以直接访问到磁盘上的二进制程序文件:

[root@www c]# gdb t.g -q
Reading symbols from /home/work/dladdr/c/t.g...done.
因此可以使用所有的调试信息,这包括.symtab表;

而backtrace()系列函数作为程序执行的逻辑功能,无法去读取磁盘上的二进制程序文件,因此只能使用.dynsym表。 
其它几个工具可以动态指定查看,比如nm、objdump:
[root@www c]# nm t.rd
nm: t.rd: no symbols
[root@www c]# nm -D t.rd
0000000000400848 R _IO_stdin_used
                 w _Jv_RegisterC

[root@www c]# objdump -T t.rd
t.rd:     file format elf64-x86-64
DYNAMIC SYMBOL TABLE:
0000000000000000      DF *UND*	0000000000000000  GLIBC_2.2.5 printf

4,-rdynamic选项不产生任何调试信息,因此在一般情况下,新增的附加信息比-g选项要少得多。除非是完全的静态连接,否则即便是没有加-rdynamic选项,程序使用到的外部动态符号,比如前面示例里的printf,也会被自动加入到.dynsym表。

makefile编写

[fdj@hs-192-168-33-206 tst]$ cat makefile 
INCDIR = -I .
CC = g++
CFLAGS = -c -g
LFLAGS = -rdynamic
#so LFLAGS = -shared -fPIC
TARGET = demo

OBJS=testDst.o US_Time_Until.o

all: cleanobj $(TARGET)
testDst.o:testDst.cpp
        $(CC) $(CFLAGS) testDst.cpp $(INCDIR)
US_Time_Until.o:US_Time_Until.cpp
        $(CC) $(CFLAGS) US_Time_Until.cpp $(INCDIR)

$(TARGET):$(OBJS)
        $(CC) $(LFLAGS) -o $(TARGET) $(OBJS)

cleanobj:
        rm -f *.o
clean:cleanobj
        clean all

gdb -q —————— 启动gdb时不输出版权说明

[root@VM-4-7-centos ~]# gdb -q a.out 
Reading symbols from /root/a.out...done.
(gdb) 

exe 启动gdb的方式

不带参数启动方式  gdb a.exe  或者 gdb + file a.exe

带参数启动方式  gdb --args a.exe pa1 pa2 或 gdb a.exe + run pa1 pa2 或者 gdb a.exe +  set args pa1 pa2 + run

gdb 附加已运行进程 gdb + attach pid 或者 gdb -p pid ; (不影响附加后进程运行,且退出gdb, 使用dettach)

-g编译的程序去掉编译的调试信息,可以用strip a.exe

print /x /d /u /o /t(二进制) /f  /c(字符形式)

ptype 打印变量类型

  whatis 变量或表达式 ------- 显示变量或表达式的数据类型

(gdb) whatis buf
type = char [3]
(gdb) ptype buf
type = char [3]

修改变量的值  p data = 100

p data  打印显示100

   set variable 变量=值 ------ 给变量赋值

(gdb) set buf[0]='a'
(gdb) p buf
$1 = "a\345\377"
(gdb) p buf[1]='b'
$2 = 98 'b'
(gdb) p buf
$3 = "ab\377"
(gdb) 

打印全局变量  p 'xxx.cpp'::st_um

打印内存块内容,如果只用print *pData则只能打印一个pData类型的内存块,如果是char*则是一个字符,如果是char*[10] 类型则是10个字符。下面假设其是char* pData

要打印一段内容需要使用 print *pData@length  eg: p *pData@20

(加餐,gdb默认最多打印200字符,要打印超过200个字符需要先 设置  set print elements 0,然后再调用 p *pData@210 打印内存块中的210个字符)——置为0表示没有限制

examine命令(简写是x)来查看内存地址中的值。x命令的语法如下所示:

    x/<n/f/u> <addr>

  n、f、u是可选的参数。

  n是一个正整数,表示需要显示的内存单元的个数,也就是说从当前地址向后显示几个内存单元的内容,一个内存单元的大小由后面的u定义

  f 表示显示的格式,参见下面。如果地址所指的是字符串,那么格式可以是s,如果是指令地址,那么格式可以是i。

  u 表示从当前地址往后请求的字节数,如果不指定的话,GDB默认是4个bytes。u参数可以用下面的字符来代替,b表示单字节,h表示双字节,w表示四字 节,g表示八字节。当我们指定了字节长度后,GDB会从指内存定的内存地址开始,读写指定字节,并把其当作一个值取出来。

  <addr>表示一个内存地址。

  注意:严格区分n和u的关系,n表示单元个数,u表示每个单元的大小。

  n/f/u三个参数可以一起使用。例如:x/10tb buf

  命令:x/3uh 0x54320 表示,从内存地址0x54320读取内容,h表示以双字节为一个单位,3表示输出三个单位,u表示按无符号十进制显示。

监视点变量或监测变量设置 watch data

rwatch data (data值被读取则中止)和 awatch =(watch + rwatch)读写则中止

删除变量检测

(gdb) info watch
Num     Type           Disp Enb Address            What
2       hw watchpoint  keep y                      buf
        breakpoint already hit 2 times
(gdb) delete 2
(gdb) info watch

No watchpoints.

watch变量的使能和不能设置

(gdb) watch buf
Hardware watchpoint 3: buf
(gdb) info watch
Num     Type           Disp Enb Address            What
3       hw watchpoint  keep y                      buf
(gdb) disable 3
(gdb) info watch
Num     Type           Disp Enb Address            What
3       hw watchpoint  keep n                      buf
(gdb) enable 3
(gdb) i watch
Num     Type           Disp Enb Address            What
3       hw watchpoint  keep y                      buf
(gdb) 

enable once 3使能一次,触发后disable

enable delete 3 使能,触发后删除

观察点设置,观察变量 display data

watch和display设置监測点的差别:
   watch监測仅仅有当监測的变量值发生变化时才显示变量,并且旧值和新值都会显示。
   display监測每一次执行命令都会显示变量的值,仅仅会显示变量的最新值。

查看和删除display

(gdb) info display
Auto-display expressions now in effect:
Num Enb Expression
1:   y  buf
(gdb) undisplay 1

不能和使能display

(gdb) display buf
2: buf = 0x7fffffffe4e0
(gdb) info display
Auto-display expressions now in effect:
Num Enb Expression
2:   y  buf
(gdb) disable display 2
(gdb) info display

Auto-display expressions now in effect:
Num Enb Expression
2:   n  buf
(gdb) enable display 2
(gdb) info display

Auto-display expressions now in effect:
Num Enb Expression
2:   y  buf

设置条件断点  break filenum if condi==2

忽略断点n次 ignore

info break

$1xxxx

$2 xxx

ignore 2 10  //忽略断点命中10次

next  单步执行程序,遇到函数调用不会进入

step  单步执行程序,遇到函数调用会进入

next cnt 一次执行多步

step cnt 一次执行多步,会进入函数

nexti  // nexti/stepi --------------- 单步运行一条机器指令

(gdb) ni
0x00000000004005c9      11         printf("44\n");
(gdb) n
44
12         print();

stepi  指令形式执行

(gdb) si
0x00000000004005a6      4          printf("xxxx");
(gdb) 
0x00000000004005ab      4          printf("xxxx");
(gdb) 
0x0000000000400470 in printf@plt ()

until ====循环体最后一句时,执行完循环后在循环外停止

until line  ====停止在某行

jump location  可以修改代码执行逻辑,执行原本不可能执行到的代码,

(gdb) jump print()
Line 4 is not in `main()'.  Jump anyway? (y or n) y
Continuing at 0x4005f1.

Breakpoint 1, print () at main.cpp:4
4          printf("xxxx");

(gdb) jump 4
Line 4 is not in `main()'.  Jump anyway? (y or n) y
Continuing at 0x4005f1.

Breakpoint 1, print () at main.cpp:4
4          printf("xxxx");

finish 立即执行完函数后返回

return直接返回,不会执行下面的代码,可以指定函数返回值

disassemble

查看栈信息 bt (backtrace)

$1 ssss休息休息

$2  xxxx西溪湿地

切换栈编号  frame 2

$2  xxxx西溪湿地

===========gdbserver =============

server端启动
[root@VM-4-7-centos ~]# gdbserver 127.0.0.1:8818 a.out  
或gdbserver 127.0.0.1:8818 attach 28678

Process a.out created; pid = 28678
Listening on port 8818
Remote debugging from host 127.0.0.1
print %
Detaching from process 28678
 

客户端启动
[root@VM-4-7-centos ~]# gdb 

(gdb) target remote 127.0.0.1:8818
Remote debugging using 127.0.0.1:8818
warning: Could not load vsyscall page because no executable was specified
0x00007ffff7ddc140 in ?? ()
(gdb) l
No symbol table is loaded.  Use the "file" command.
(gdb) file a.out
A program is being debugged already.
Are you sure you want to change the file? (y or n) y
Reading symbols from /root/a.out...done.
(gdb) l
1       #include <stdio.h>
2
3       int main(){
4          int a = 100;  
5        printf("print %%\n");
6          return 0;
7       }
(gdb) b 5
Breakpoint 1 at 0x40056c: file m.cpp, line 5. 
(gdb) c
Continuing.
Breakpoint 1, main () at m.cpp:5
5        printf("print %%\n");
(gdb) n
6          return 0;
(gdb) detach
Detaching from program: /root/a.out, process 28678
Ending remote debugging.
[Inferior 1 (process 28678) detached]
(gdb) quit

============================================

  • 5
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值