打开APP
未登录
开通VIP,畅享免费电子书等14项超值服
开通VIP
首页
好书
留言交流
下载APP
联系客服
程序减肥,strip,eu
astrotycoon
>《链接加载》
2013.08.17
关注
我们公司产品里面的可执行程序和动态共享库(DSO)里面的符号表都被移除了,所以每次遇到core dump的时候,都需要将符号表导入到/usr/lib/debug目录下。一直没弄明白为啥是这个目录,能不能是其他目录,今天没啥事儿,研究了下这个主题。
我们要给我们生成的可执行文件和DSO瘦身,因为这样可以节省更多的磁盘空间,所以我们移除了debug信息,移除了符号表信息,同时我们还希望万一出事了,比如coredump了,我们能获取更多的信息,这时候我们又希望有符号表。
我们等不能做到呢。Linux下是怎么解决这个矛盾的呢?先看第一个问题,程序减肥。
1 程序减肥
我写了个简单的代码,main调用了foo,foo调用了bar,其中bar故意访问了非法地址,为了引起core dump。
root@manu
:
~
/
code
/
c
/
self
/
debug_symbol# cat test
.
c
#include
<
stdio
.
h
>
#include
<
stdlib
.
h
>
int
bar
(
)
{
char
*
p
=
NULL
;
fprintf
(
stderr
,
"I am bar,I will core dump\n"
)
;
fprintf
(
stderr
,
"%s"
,
p
)
;
return 0
;
}
int
foo
(
)
{
int
i
;
fprintf
(
stderr
,
"I am foo,I will call bar\n"
)
;
bar
(
)
;
return 0
;
}
int
main
(
)
{
fprintf
(
stderr
,
"I am main, I wll can foo\n"
)
;
foo
(
)
;
return 0
;
}
root@manu
:
~
/
code
/
c
/
self
/
debug_symbol#
先编译出一个debug版本来,然后我们看下可执行程序的大小
root@manu
:
~
/
code/c/self/debug_symbol# ll
总用量 24
drwxr
-
xr
-
x 2 root root 4096 3月 16 15
:
56
.
/
drwxr
-
xr
-
x 31 manu root 4096 3月 16 14
:
07
.
.
/
-
rwxr
-
xr
-
x 1 root root 9703 3月 16 15
:
56
test
*
-
rw
-
r
-
-
r
-
-
1 root root 361 3月 16 15
:
53
test
.
c
root@manu
:
~
/
code/c/self/debug_symbol# readelf
-
S
test
然后我们看下section信息:
root@manu
:
~
/
code/c/self/debug_symbol# readelf
-
S
test
然后,我们用strip命令将debug info 去除,指令如下,
root@manu
:
~
/
code/c/self/debug_symbol# strip
-
-
strip
-
debug
test
root@manu
:
~
/
code/c/self/debug_symbol# ll
总用量 20
drwxr
-
xr
-
x 2 root root 4096 3月 16 16
:
10
.
/
drwxr
-
xr
-
x 31 manu root 4096 3月 16 14
:
07
.
.
/
-
rwxr
-
xr
-
x 1 root root 7205 3月 16 16
:
10
test
*
-
rw
-
r
-
-
r
-
-
1 root root 361 3月 16 15
:
53
test
.
c
root@manu
:
~
/
code/c/self/debug_symbol#
可执行文件的大小从9703减小到了7205.当然了,我们也可以直接用gcc生成一个release版本的test。
去除掉debug info的test 和之前的test有什么区别呢,我们看下section信息:
我们可以看到.debug_info/.debug_line/.debug_str等六个debug相关的section都已经不在了,原来的35个section减少到了29个section。但是我们注意到白色两行,是符号表信息和字符串信息,这两个还在。
root@manu
:
~
/
code/c/self/debug_symbol# nm
test
08049f28 d _DYNAMIC
08049ff4 d _GLOBAL_OFFSET_TABLE_
080485cc R _IO_stdin_used
w _Jv_RegisterClasses
08049f18 d __CTOR_END__
。。。。。。。
此时如果执行这个test可执行程序,会产生coredump文件,我们用gdb调试coredump文件的时候,我们可以打印出堆栈信息,因为符号表还在。
root@manu
:
~
/
code/c/self/debug_symbol# ulimit
-
c unlimited
root@manu
:
~
/
code/c/self/debug_symbol#
.
/
test
I am main
,
I wll can foo
I am foo
,
I will
call
bar
I am bar,I will core dump
段错误
(
核心已转储
)
root@manu
:
~
/
code/c/self/debug_symbol# ll
总用量 224
drwxr
-
xr
-
x 2 root root 4096 3月 16 16
:
23
.
/
drwxr
-
xr
-
x 31 manu root 4096 3月 16 14
:
07
.
.
/
-
rw
-
r
-
-
-
-
-
1 root root 200704 3月 16 16
:
23 core
-
rwxr
-
xr
-
x 1 root root 7205 3月 16 16
:
10
test
*
-
rw
-
r
-
-
r
-
-
1 root root 361 3月 16 15
:
53
test
.
c
此时我们用gdb 调试下:
root@manu
:
~
/
code
/
c
/
self
/
debug_symbol# gdb
-
c core test
这似乎是个比较合理的点,程序已经瘦了身,没有什么debug信息,一旦出了core dump,还有符号表信息。程序员喜欢。可惜大部分的发行版的程序都会将符号表信息删除。OK,我们进一步减肥。
这一步就会要删掉符号表了,可以直接用:strip命令,或者strip --strip-all命令。
root@manu
:
~
/
code/c/self/debug_symbol# strip
-
-
strip
-
all
test
root@manu
:
~
/
code/c/self/debug_symbol# ll
总用量 216
drwxr
-
xr
-
x 2 root root 4096 3月 16 16
:
33
.
/
drwxr
-
xr
-
x 31 manu root 4096 3月 16 14
:
07
.
.
/
-
rw
-
r
-
-
-
-
-
1 root root 200704 3月 16 16
:
23 core
-
rwxr
-
xr
-
x 1 root root 5520 3月 16 16
:
33
test
*
-
rw
-
r
-
-
r
-
-
1 root root 361 3月 16 15
:
53
test
.
c
root@manu
:
~
/
code/c/self/debug_symbol#
此时的可执行程序test已经从7205减小到了5520,文件进一步瘦了身,此时符号表已经不在了,请看下图:
symtab和strtab两个section不见了,section从29个减少到了27个。nm 执行也看不到符号表。
能不能进一步的减肥呢? 答案是肯定的。上面提到的这些section中 .note.ABI-tag .gnu.version .comment 本质上都可以移除:
root@manu
:
~
/
code/c/self/debug_symbol# objcopy
-
R
.
comment
-
R
.
note
.
ABI
-
tag
-
R
.
gnu
.
version
test
root@manu
:
~
/
code/c/self/debug_symbol# ll
总用量 216
drwxr
-
xr
-
x 2 root root 4096 3月 16 16
:
48
.
/
drwxr
-
xr
-
x 31 manu root 4096 3月 16 14
:
07
.
.
/
-
rw
-
r
-
-
-
-
-
1 root root 200704 3月 16 16
:
23 core
-
rwxr
-
xr
-
x 1 root root 5320 3月 16 16
:
48
test
*
-
rw
-
r
-
-
r
-
-
1 root root 361 3月 16 15
:
53
test
.
c
可以看到test可执行程序再次减小了,从5520减小到了5320.到目前位置,程序依然是可执行的:
root@manu
:
~
/
code/c/self/debug_symbol#
.
/
test
I am main
,
I wll can foo
I am foo
,
I will
call
bar
I am bar,I will core dump
段错误
(
核心已转储
)
root@manu
:
~
/
code/c/self/debug_symbol#
当然了,这种操作其实没必要,对于大型程序而言,用strip移除符号表,文件会变小很多,但是用objcopy移除上面三个section,节省不了多少空间。
2 符号表与 可执行程序(及DSO)分离
到目前为止,我们玩的很happy,文件越来越小,节省了大量的空间。可惜给自己挖了个坑。常在河边走,哪能不湿鞋,玩C的人,哪能不处理几个core dump。现在我们把符号表移除了,发生了coredump我们就傻眼了。请看:
root@manu
:
~
/
code/c/self/debug_symbol# rm core
root@manu
:
~
/
code/c/self/debug_symbol# ulimit
-
c unlimited
root@manu
:
~
/
code/c/self/debug_symbol#
.
/
test
I am main
,
I wll can foo
I am foo
,
I will
call
bar
I am bar,I will core dump
段错误
(
核心已转储
)
root@manu
:
~
/
code/c/self/debug_symbol# ll
总用量 216
drwxr
-
xr
-
x 2 root root 4096 3月 16 17
:
03
.
/
drwxr
-
xr
-
x 31 manu root 4096 3月 16 14
:
07
.
.
/
-
rw
-
r
-
-
-
-
-
1 root root 200704 3月 16 17
:
03 core
-
rwxr
-
xr
-
x 1 root root 5320 3月 16 16
:
48
test
*
-
rw
-
r
-
-
r
-
-
1 root root 361 3月 16 15
:
53
test
.
c
root@manu
:
~
/
code/c/self/debug_symbol#
看到堆栈信息里面的这些??,有没有在一种叫天天不应,叫地地不灵的感觉?strip文件的符号表的时候有多爽,现在就有多痛苦。
有没有一种办法,把符号表信息保留,需要用符号表的时候在将符号表的信息导入?答案是肯定的。
方法1 使用eu-strip
eu-strip可以把文件的符号表保存起来,需要用的时候,导入需要的符号表就能调试coredump文件了。
这次我直接生成了release版本的test了,然后用eu-strip将
root@manu
:
~
/
code/c/self/debug_symbol# gcc
-
o
test
test
.
c
root@manu
:
~
/
code/c/self/debug_symbol# ll
总用量 20
drwxr
-
xr
-
x 2 root root 4096 3月 16 17
:
12
.
/
drwxr
-
xr
-
x 31 manu root 4096 3月 16 14
:
07
.
.
/
-
rwxr
-
xr
-
x 1 root root 7271 3月 16 17
:
12
test
*
-
rw
-
r
-
-
r
-
-
1 root root 361 3月 16 15
:
53
test
.
c
root@manu
:
~
/
code/c/self/debug_symbol# eu
-
strip
test
-
f
test
.
sym
root@manu
:
~
/
code/c/self/debug_symbol# ll
总用量 24
drwxr
-
xr
-
x 2 root root 4096 3月 16 17
:
13
.
/
drwxr
-
xr
-
x 31 manu root 4096 3月 16 14
:
07
.
.
/
-
rwxr
-
xr
-
x 1 root root 5592 3月 16 17
:
13
test
*
-
rw
-
r
-
-
r
-
-
1 root root 361 3月 16 15
:
53
test
.
c
-
rwxr
-
xr
-
x 1 root root 3524 3月 16 17
:
13
test
.
sym
*
root@manu
:
~
/
code/c/self/debug_symbol#
我们看到生成了test.sym文件,而原始的test文件也变小了,移除了符号表信息。请看下图:
记性好的同学还记得,用strip之后,是27个section(不算NULL),但是我们用了eu-strip居然多了一个section,定睛一看,原来多一个.gnu_deubg_link.
这是啥东东 :
原来记录的是符号表的位置。OK ,现在我们试一试,调试coredump的时候,打印堆栈信息的时候,有没有符号表。
(
gdb
)
bt
#0 __strlen_ia32
(
)
at
.
.
/
sysdeps/i386/i686/multiarch/
.
.
/
.
.
/
i586/strlen
.
S
:
99
#1 0xb761d12e
in
__GI__IO_fputs
(
str
=
0x0
,
fp
=
0xb775d980
)
at iofputs
.
c
:
37
#2 0x0804847d
in
bar
(
)
#3 0x080484b7
in
foo
(
)
#4 0x080484f4
in
main
(
)
(
gdb
)
Bingo,我们堆栈信息里面有符号表的信息。
我们进一步思考下,是不是因为符号表文件test.sym在当前目录下所以可以找,我们将符号表换个位置,放入/root下面,看下能否调试:
root@manu
:
~
/
code/c/self/debug_symbol# mv
test
.
sym /root/
root@manu
:
~
/
code/c/self/debug_symbol# ll
总用量 216
drwxr
-
xr
-
x 2 root root 4096 3月 16 17
:
39
.
/
drwxr
-
xr
-
x 31 manu root 4096 3月 16 14
:
07
.
.
/
-
rw
-
r
-
-
-
-
-
1 root root 200704 3月 16 17
:
34 core
-
rwxr
-
xr
-
x 1 root root 5592 3月 16 17
:
13
test
*
-
rw
-
r
-
-
r
-
-
1 root root 361 3月 16 15
:
53
test
.
c
(
gdb
)
bt
#0 __strlen_ia32
(
)
at
.
.
/
sysdeps/i386/i686/multiarch/
.
.
/
.
.
/
i586/strlen
.
S
:
99
#1 0xb761d12e
in
__GI__IO_fputs
(
str
=
0x0
,
fp
=
0xb775d980
)
at iofputs
.
c
:
37
#2 0x0804847d
in
?
?
(
)
#3 0x080484b7
in
?
?
(
)
#4 0x080484f4
in
?
?
(
)
#5 0xb75d04d3
in
__libc_start_main
(
main
=
0x80484be
,
argc
=
1
,
ubp_av
=
0xbfb851c4
,
init
=
0x8048500
,
fini
=
0x8048570
,
rtld_fini
=
0xb778d270
<
_dl_fini
>
,
stack_end
=
0xbfb851bc
)
at libc
-
start
.
c
:
226
#6 0x080483a1
in
?
?
(
)
(
gdb
)
我们发现,gdb找不到符号表。尽管我们有符号表在/root目录下。WHY?我们可以用strace 跟踪下gdb 调试core文件的过程,看下gdb是怎么寻找符号表的。
root@manu
:
~
/
code/c/self/debug_symbol# strace gdb
-
c core
test
>
>
strace_search_symbol
.
log 2
>
&
1
在strace_search_symbol.log中我们发现了下面内容:
access
(
"/usr/lib/debug/.build-id/0d/5ded87764286512bfa6f6a2c4f9993c0669021.debug"
,
F_OK
)
=
-
1 ENOENT
(
No such file
or
directory
)
open
(
"/home/manu/code/c/self/debug_symbol/test.sym"
,
O_RDONLY
|
O_LARGEFILE
)
=
-
1 ENOENT
(
No such file
or
directory
)
open
(
"/home/manu/code/c/self/debug_symbol/.debug/test.sym"
,
O_RDONLY
|
O_LARGEFILE
)
=
-
1 ENOENT
(
No such file
or
directory
)
open
(
"/usr/lib/debug//home/manu/code/c/self/debug_symbol/test.sym"
,
O_RDONLY
|
O_LARGEFILE
)
=
-
1 ENOENT
(
No such file
or
directory
)
open
(
"/usr/lib/debug/home/manu/code/c/self/debug_symbol/test.sym"
,
O_RDONLY
|
O_LARGEFILE
)
=
-
1 ENOENT
(
No such file
or
directory
)
第一行我们暂且不管,我们发现了,gdb尝试在以下路径中寻找符号表:
/home/manu/code/c/self/debug_symbol/test.sym
/home/manu/code/c/self/debug_symbol/.debug/test.sym
/usr/lib/debug//home/manu/code/c/self/debug_symbol/test.sym
/usr/lib/debug/home/manu/code/c/self/debug_symbol/test.sym
第一条路径和第二条路径 是因为test的.gnu_debuglink里面记录了符号表为test.sym,所以他去找了当前路径下有无test.sym,我们发现,当前路径下的.debug路径也是gdb搜素的对象。这个结论,可以自行验证。
至于第三四条,则是gdb的默认搜索路径。在公司里面调试的时候,需要将符号表信息放在
/usr/lib/debug
目录下,我百思不得其解,直到今日方才明白,只是gdb的默认搜索路径,
(
gdb
)
show debug
-
file
-
directory
The directory where separate debug symbols are searched
for
is
"/usr/lib/debug"
.
(
gdb
)
以我的这个为例,如果我的可执行程序在
/home/manu/code/c/self/debug_sysbol
/
目录下,gdb会尝试在
/usr/lib/debug
/
home/manu/code/c/self/debug_symbol/
下寻找符号表。我们验证之:
root@manu
:
~
/
code/c/self/debug_symbol# mkdir
-
p /usr/lib/debug/home/manu/code/c/self/debug_symbol/
root@manu
:
~
/
code/c/self/debug_symbol# mv /root/
test
.
sym /usr/lib/debug/home/manu/code/c/self/debug_symbol/
root@manu
:
~
/
code/c/self/debug_symbol# ll /usr/lib/debug/home/manu/code/c/self/debug_symbol/
总用量 12
drwxr
-
xr
-
x 2 root root 4096 3月 16 18
:
27
.
/
drwxr
-
xr
-
x 3 root root 4096 3月 16 15
:
23
.
.
/
-
rwxr
-
xr
-
x 1 root root 3524 3月 16 17
:
13
test
.
sym
*
看下gdb打印堆栈信息的时候可以找到符号表:
(
gdb
)
bt
#0 __strlen_ia32
(
)
at
.
.
/
sysdeps/i386/i686/multiarch/
.
.
/
.
.
/
i586/strlen
.
S
:
99
#1 0xb761d12e
in
__GI__IO_fputs
(
str
=
0x0
,
fp
=
0xb775d980
)
at iofputs
.
c
:
37
#2 0x0804847d
in
bar
(
)
#3 0x080484b7
in
foo
(
)
#4 0x080484f4
in
main
(
)
当然了,如果我们的符号表既不在当前路径下,又不在/usr/lib/debug/+执行路径下,比如我们刚才放到了/root路径下,我们还可以用命令行 symbol-file告诉gdb符号表的位置。
(
gdb
)
bt
#0 __strlen_ia32
(
)
at
.
.
/
sysdeps/i386/i686/multiarch/
.
.
/
.
.
/
i586/strlen
.
S
:
99
#1 0xb767612e
in
__GI__IO_fputs
(
str
=
0x0
,
fp
=
0xb77b6980
)
at iofputs
.
c
:
37
#2 0x0804847d
in
?
?
(
)
#3 0x080484b7
in
?
?
(
)
#4 0x080484f4
in
?
?
(
)
#5 0xb76294d3
in
__libc_start_main
(
main
=
0x80484be
,
argc
=
1
,
ubp_av
=
0xbf83bfb4
,
init
=
0x8048500
,
fini
=
0x8048570
,
rtld_fini
=
0xb77e6270
<
_dl_fini
>
,
stack_end
=
0xbf83bfac
)
at libc
-
start
.
c
:
226
#6 0x080483a1
in
?
?
(
)
(
gdb
)
symbol
-
file /root/
test
.
sym
Load new symbol table from
"/root/test.sym"
?
(
y
or
n
)
y
Reading symbols from /root/
test
.
sym
.
.
.
(
no debugging symbols found
)
.
.
.
done
.
(
gdb
)
bt
#0 __strlen_ia32
(
)
at
.
.
/
sysdeps/i386/i686/multiarch/
.
.
/
.
.
/
i586/strlen
.
S
:
99
#1 0xb767612e
in
__GI__IO_fputs
(
str
=
0x0
,
fp
=
0xb77b6980
)
at iofputs
.
c
:
37
#2 0x0804847d
in
bar
(
)
#3 0x080484b7
in
foo
(
)
#4 0x080484f4
in
main
(
)
第二种方法: objcopy
root@manu
:
~
/
code/c/self/debug_symbol# rm core
test
root@manu
:
~
/
code/c/self/debug_symbol# ll
总用量 256
drwxr
-
xr
-
x 2 root root 4096 3月 16 19
:
14
.
/
drwxr
-
xr
-
x 31 manu root 4096 3月 16 14
:
07
.
.
/
-
rw
-
r
-
-
r
-
-
1 root root 248743 3月 16 18
:
06 strace_search_symbol
.
log
-
rw
-
r
-
-
r
-
-
1 root root 361 3月 16 15
:
53
test
.
c
root@manu
:
~
/
code/c/self/debug_symbol# gcc
-
o
test
test
.
c
root@manu
:
~
/
code/c/self/debug_symbol# objcopy
-
-
only
-
keep
-
debug
test
test
.
debug
root@manu
:
~
/
code/c/self/debug_symbol# strip
test
root@manu
:
~
/
code/c/self/debug_symbol# ll
总用量 268
drwxr
-
xr
-
x 2 root root 4096 3月 16 19
:
15
.
/
drwxr
-
xr
-
x 31 manu root 4096 3月 16 14
:
07
.
.
/
-
rw
-
r
-
-
r
-
-
1 root root 248743 3月 16 18
:
06 strace_search_symbol
.
log
-
rwxr
-
xr
-
x 1 root root 5520 3月 16 19
:
15
test
*
-
rw
-
r
-
-
r
-
-
1 root root 361 3月 16 15
:
53
test
.
c
-
rwxr
-
xr
-
x 1 root root 3579 3月 16 19
:
14
test
.
debug
*
root@manu
:
~
/
code/c/self/debug_symbol# objcopy
-
-
add
-
-
-
add
-
gnu
-
debuglink
-
-
add
-
section
root@manu
:
~
/
code/c/self/debug_symbol# objcopy
-
-
add
-
gnu
-
debuglink
=
test
.
debug
test
root@manu
:
~
/
code/c/self/debug_symbol# ll
总用量 268
drwxr
-
xr
-
x 2 root root 4096 3月 16 19
:
15
.
/
drwxr
-
xr
-
x 31 manu root 4096 3月 16 14
:
07
.
.
/
-
rw
-
r
-
-
r
-
-
1 root root 248743 3月 16 18
:
06 strace_search_symbol
.
log
-
rwxr
-
xr
-
x 1 root root 5592 3月 16 19
:
15
test
*
-
rw
-
r
-
-
r
-
-
1 root root 361 3月 16 15
:
53
test
.
c
-
rwxr
-
xr
-
x 1 root root 3579 3月 16 19
:
14
test
.
debug
*
我们生成了test.debug和用eu-strip生成的test.sym一样,放在当前目录下,或者放在/usr/lib/debug/home/manu/code/c/self/debug_symbol,都可以使用test.debug获取到符号表。
另外说一句,这两种方法,我发现都只能将符号表信息放到 当前路径和默认路径/usr/lib/debug下对应的路径,放到其他路径下会找不到。eu-strip -f选项和objcopy --only-keep-debug制定其他路径都没有用。感兴趣的筒子可以自己验证下。
我制作了pdf格式的文档,需要的可以自行下载:
strip,eu-strip及符号表.pdf
参考文献:
1
Separate debug info
2
Split debugging info -- symbols
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请
点击举报
。
打开APP,阅读全文并永久保存
查看更多类似文章
猜你喜欢
类似文章
【热】
打开小程序,算一算2024你的财运
linux rm 自定义回收站功能
手把手教你appium_ios第一个例子
每天一个linux命令(20):find命令之exec
6.2.1 Linux文件属性 - 51CTO.COM
每天一个linux命令(29):chgrp命令
mv命令
更多类似文章 >>
生活服务
热点新闻
留言交流
回顶部
联系我们
分享
收藏
点击这里,查看已保存的文章
导长图
关注
一键复制
下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!
联系客服
微信登录中...
请勿关闭此页面
先别划走!
送你5元优惠券,购买VIP限时立减!
5
元
优惠券
优惠券还有
10:00
过期
马上使用
×