`
sammor
  • 浏览: 413742 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

【优化改进】Linux进程文件瘦身——Linux环境验证

阅读更多

背景:

       最近一直在思考,嵌入式产品的代码中存在着很多的无用的函数和全局变量,这些函数和全局量占用着版本的大小,进而对Flash的要求也更多了。但这些通用人工去排查毕竟不现实,那有什么办法可以做到自动的检测出这些没有用的函数和全局变量?

    这次的总结有一点感受很深刻,那就是“世上并不缺少美,缺少的是发现美的眼睛”。

 

思考:

       针对引言中的思考,我主要从以下两个方向去思考:

      1. 是否可以通过符号进行入手,通过查找符号不存在被调用的来判断函数或全局变量未被使用;

      2. 编译过程中,无用的函数或全局变量是否可以不链接进版本中;

 

方向1——检查没有使用到的符号

       很多人也许会说,在内部使用的函数都加上static不就好了吗?这也是一样办法,但排除不了团队编程过程中,有些人不加static,甚至在头文件中添加声明。

 

       那么,从符号调用角度我主要想到了以前出现段错误经常会用到使用objdump命令进行反汇编来看程序的汇编语言,其中函数的调用关系可以看得出来,那么从汇编语言中是不是可以检查到哪些函数都没有被调用过?下面做一个简单的测试。

 

 

#include <stdio.h>
int lUnusedGrobleValue = 2;
int lUsedGrobleValue = 1;

int unUsedFun()
{
    printf("%d", lUnusedGrobleValue);
    return 0;
}

int usedFun()
{
    printf("It's a usedFun, usedGrobleValue=%d\r\n", lUsedGrobleValue);
}

int main()
{
    printf("hello world\n"); 
    usedFun();
    return 0;
}

 

从上面可以看出,存在函数unUsedFun和lUnusedGrobleValue是我们希望找出来的。

 

1. 下面使用nm命令来看一下正常情况下编译出来的进程带的符号信息:

 

sammor@ubuntu:~/test$ gcc test.c -o test
sammor@ubuntu:~/test$ ls -l test
-rwxr-xr-x 1 sammor sammor 7362 2014-10-22 22:00 test
sammor@ubuntu:~/test$ nm --defined-only test
08049f28 d _DYNAMIC
08049ff4 d _GLOBAL_OFFSET_TABLE_
0804854c R _IO_stdin_used
08049f18 d __CTOR_END__
08049f14 d __CTOR_LIST__
08049f20 D __DTOR_END__
08049f1c d __DTOR_LIST__
08048654 r __FRAME_END__
08049f24 d __JCR_END__
08049f24 d __JCR_LIST__
0804a020 A __bss_start
0804a010 D __data_start
08048500 t __do_global_ctors_aux
08048390 t __do_global_dtors_aux
0804a014 D __dso_handle
080484f2 T __i686.get_pc_thunk.bx
08049f14 d __init_array_end
08049f14 d __init_array_start
080484f0 T __libc_csu_fini
08048480 T __libc_csu_init
0804a020 A _edata
0804a028 A _end
0804852c T _fini
08048548 R _fp_hw
080482d4 T _init
08048360 T _start
0804a020 b completed.7108
0804a010 W data_start
0804a024 b dtor_idx.7110
080483f0 t frame_dummy
0804a018 D lUnusedGrobleValue
0804a01c D lUsedGrobleValue
08048457 T main
08048414 T unUsedFun
08048438 T usedFun

 分析1: 从以上的验证看出正常编译情况下,没有用到的函数和全局变量也都被链接进来了,这说明这些无用的函数和全局变量确实在影响着我们最终输出的进程文件大小

 

 

那如何找出这些无用的函数和全局变量,继续进行反汇编来看下结果。

 

 

sammor@ubuntu:~/test$ vim test.dump

test:     file format elf32-i386

Disassembly of section .init:
080482d4 <_init>:
...

08048310 <printf@plt-0x10>:
...
08048320 <printf@plt>:
...
08048330 <puts@plt>:
...
08048340 <__gmon_start__@plt>:
08048350 <__libc_start_main@plt>:
080483f0 <frame_dummy>:
#以上省略很不相关的汇编信息..

08048414 <unUsedFun>: // unUsedFun函数的反汇编
 8048414:       55                      push   %ebp
 8048415:       89 e5                   mov    %esp,%ebp
 8048417:       83 ec 18                sub    $0x18,%esp
 804841a:       8b 15 18 a0 04 08       mov    0x804a018,%edx —— (0804a018 D lUnusedGrobleValue)
 8048420:       b8 50 85 04 08          mov    $0x8048550,%eax
 8048425:       89 54 24 04             mov    %edx,0x4(%esp)
 8048429:       89 04 24                mov    %eax,(%esp)
 804842c:       e8 ef fe ff ff          call   8048320 <printf@plt>
 8048431:       b8 00 00 00 00          mov    $0x0,%eax
 8048436:       c9                      leave
 8048437:       c3                      ret
08048438 <usedFun>:// usedFun函数的反汇编
 8048438:       55                      push   %ebp
 8048439:       89 e5                   mov    %esp,%ebp
 804843b:       83 ec 18                sub    $0x18,%esp
 804843e:       8b 15 1c a0 04 08       mov    0x804a01c,%edx —— (0804a01c D lUsedGrobleValue)
 8048444:       b8 54 85 04 08          mov    $0x8048554,%eax
 8048449:       89 54 24 04             mov    %edx,0x4(%esp)
 804844d:       89 04 24                mov    %eax,(%esp)
 8048450:       e8 cb fe ff ff          call   8048320 <printf@plt>
 804844d:       89 04 24                mov    %eax,(%esp)
 8048450:       e8 cb fe ff ff          call   8048320 <printf@plt>
 8048455:       c9                      leave
 8048456:       c3                      ret
08048457 <main>: // main函数的反汇编
 8048457:       55                      push   %ebp
 8048458:       89 e5                   mov    %esp,%ebp
 804845a:       83 e4 f0                and    $0xfffffff0,%esp
 804845d:       83 ec 10                sub    $0x10,%esp
 8048460:       c7 04 24 79 85 04 08    movl   $0x8048579,(%esp)
 8048467:       e8 c4 fe ff ff          call   8048330 <puts@plt>
 804846c:       e8 c7 ff ff ff          call   8048438 <usedFun>
 8048471:       b8 00 00 00 00          mov    $0x0,%eax
 8048476:       c9                      leave
 8048477:       c3                      ret
 8048478:       90                      nop
 8048479:       90                      nop
 804847a:       90                      nop
 804847b:       90                      nop
 804847c:       90                      nop
 804847d:       90                      nop
 804847e:       90                      nop
 804847f:       90                      nop
0804852c <_fini>:
......

 

 

分析2:从这个反汇编文件中,我们可以看出如下:

1. 函数被调用时,在汇编语言中是以call命令进行用(在我验证的arm板上是bl命令,说明在不同的架构上还是有差异的)。

2. 全局变量函数中被使用时,是以mov命令进行。

 

从以上两点分析来看,如果要做到从dump文件中检查出无用的函数和全局变量,则需要从符号表中找到所有定义的函数和全局变量,在dump文件中分析。

解析原则:

      待思考,当前这也是只思路,也许有更好的办法可以解决这个问题。

 

方向2——编译过程中,无用的函数或全局变量是否可以不链接进版本中

这个方向一直在思想困惑着,也没有去具体去找资源,突然今天在和一朋友聊到这个想法的时候,这位朋友和我说了他们那边通过编译选项来做到的,毕竟他们也是嵌入式设备,flash的空间非常小。他告诉我使用的了编译选项是编译过程添加-ffunction-sections和-fdata-sections, 链接过程添加选项-Wl,--gc-sections。

使用以上两个编译选项做一下试验:

 

sammor@ubuntu:~/test$ gcc -c -ffunction-sections -fdata-sections  test.c 
sammor@ubuntu:~/test$ gcc -Wl,--gc-sections test.o -o test
sammor@ubuntu:~/test$ nm --defined-only test
08049f28 d _DYNAMIC
08049ff4 d _GLOBAL_OFFSET_TABLE_
08048528 R _IO_stdin_used
08049f18 d __CTOR_END__
08049f14 d __CTOR_LIST__
08049f20 D __DTOR_END__
08049f1c d __DTOR_LIST__
08048630 r __FRAME_END__
08049f24 d __JCR_END__
08049f24 d __JCR_LIST__
0804a014 A __bss_start
080484e0 t __do_global_ctors_aux
08048390 t __do_global_dtors_aux
080484d2 T __i686.get_pc_thunk.bx
08049f14 d __init_array_end
08049f14 d __init_array_start
080484d0 T __libc_csu_fini
08048460 T __libc_csu_init
0804a014 A _edata
0804a01c A _end
0804850c T _fini
080482d4 T _init
08048360 T _start
0804a014 b completed.7108
0804a018 b dtor_idx.7110
080483f0 t frame_dummy
0804a010 D lUsedGrobleValue
08048432 T main
08048413 T usedFun

 分析:从符号表来看,没有用到的函数unUsedFun和全局变量lUnusedGrobleValue已经不在符号表里面了。而且和不加编译选项编译出来的进程相比减小了181字节。

 

-rwxr-xr-x 1 sammor sammor   7181 2014-10-22 23:54 test
-rwxr-xr-x 1 sammor sammor   7362 2014-10-22 22:00 test1

 

 

结论:编译过程添加-ffunction-sections和-fdata-sections, 链接过程添加选项-Wl,--gc-sections,可以使得编译出来的进程去除无用函数和全局变量符号,减少进程大小。

 

 

具体原理:

先看看gcc官方文档对这几个选项的说明。



 


 

从说明可以大概看说意思是:

1. 编译过程中添加-ffunction-sections和-fdata-sections会在输出文件object中给每个函数和全局变量控制在一个section中并以对应的函数名或全局变量名命名,

2. 链接过程中-Wl,--gc-sections,因为链接时查找符号是以section为单元进行引用的,对于没有引用到的符号,对应的section也不会引进来,故排除掉了无用的函数和全局变更,从而减少可执行文件的大小。

 

注意:从文档来看,这些选项对-g和gprof会有些影响,需要关注下,具体影响我还没去深挖。

 

这里用readelf命令查看section来比较下使用与未使用编译选项的结果做一下检查,结果如下:

a.未添加编译选项查看:

 

sammor@ubuntu:~/test$ gcc -c test.c -o test.nooption
sammor@ubuntu:~/test$ readelf -t test.nooption 
There are 11 section headers, starting at offset 0x154:
Section Headers:
  [Nr] Name
       Type            Addr     Off    Size   ES   Lk Inf Al
       Flags
  [ 0] 
       NULL            00000000 000000 000000 00   0   0  0
       [00000000]: 
  [ 1] .text
       PROGBITS        00000000 000034 000064 00   0   0  4
       [00000006]: ALLOC, EXEC
  [ 2] .rel.text
       REL             00000000 00044c 000048 08   9   1  4
       [00000000]: 
  [ 3] .data
       PROGBITS        00000000 000098 000008 00   0   0  4
       [00000003]: WRITE, ALLOC
  [ 4] .bss
       NOBITS          00000000 0000a0 000000 00   0   0  4
       [00000003]: WRITE, ALLOC
  [ 5] .rodata
       PROGBITS        00000000 0000a0 000035 00   0   0  4
       [00000002]: ALLOC
  [ 6] .comment
       PROGBITS        00000000 0000d5 00002b 01   0   0  1
       [00000030]: MERGE, STRINGS
  [ 7] .note.GNU-stack
       PROGBITS        00000000 000100 000000 00   0   0  1
       [00000000]: 
  [ 8] .shstrtab
       STRTAB          00000000 000100 000051 00   0   0  1
       [00000000]: 
  [ 9] .symtab
       SYMTAB          00000000 00030c 0000f0 10  10   8  4
       [00000000]: 
  [10] .strtab
       STRTAB          00000000 0003fc 00004f 00   0   0  1
       [00000000]: 

 b.添加编译选项查看。

 

sammor@ubuntu:~/test$ readelf -t test.o
There are 18 section headers, starting at offset 0x1b4:
Section Headers:
  [Nr] Name
       Type            Addr     Off    Size   ES   Lk Inf Al
       Flags
  [ 0] 
       NULL            00000000 000000 000000 00   0   0  0
       [00000000]: 
  [ 1] .text
       PROGBITS        00000000 000034 000000 00   0   0  4
       [00000006]: ALLOC, EXEC
  [ 2] .data
       PROGBITS        00000000 000034 000000 00   0   0  4
       [00000003]: WRITE, ALLOC
  [ 3] .bss
       NOBITS          00000000 000034 000000 00   0   0  4
       [00000003]: WRITE, ALLOC
  [ 4] .data.lUnusedGrobleValue
       PROGBITS        00000000 000034 000004 00   0   0  4
       [00000003]: WRITE, ALLOC
  [ 5] .data.lUsedGrobleValue
       PROGBITS        00000000 000038 000004 00   0   0  4
       [00000003]: WRITE, ALLOC
  [ 6] .rodata
       PROGBITS        00000000 00003c 000035 00   0   0  4
       [00000002]: ALLOC
  [ 7] .text.unUsedFun
       PROGBITS        00000000 000071 000024 00   0   0  1
       [00000006]: ALLOC, EXEC
  [ 8] .rel.text.unUsedFun
       REL             00000000 000614 000018 08  16   7  4
       [00000000]: 
  [ 9] .text.usedFun
       PROGBITS        00000000 000095 00001f 00   0   0  1
       [00000006]: ALLOC, EXEC
  [10] .rel.text.usedFun
       REL             00000000 00062c 000018 08  16   9  4
       [00000000]: 
  [11] .text.main
       PROGBITS        00000000 0000b4 000021 00   0   0  1
       [00000006]: ALLOC, EXEC
  [12] .rel.text.main
       REL             00000000 000644 000018 08  16  11  4
       [00000000]: 
  [13] .comment
       PROGBITS        00000000 0000d5 00002b 01   0   0  1
       [00000030]: MERGE, STRINGS
  [14] .note.GNU-stack
       PROGBITS        00000000 000100 000000 00   0   0  1
       [00000000]: 
  [15] .shstrtab
       STRTAB          00000000 000100 0000b2 00   0   0  1
       [00000000]: 
  [16] .symtab
       SYMTAB          00000000 000484 000140 10  17  13  4
       [00000000]: 
  [17] .strtab
       STRTAB          00000000 0005c4 00004f 00   0   0  1
       [00000000]: 

通过比较看出,添加编译选项的输出文件中,确实以每个符号进行取名作为一个section,最终链接出来的可执行文件也不包含无用的函数和全局变量了。

 

相关文章:

http://elinux.org/Function_sections

http://blog.sina.com.cn/s/blog_602f87700100t0t5.html

 

 

 

  • 大小: 18.1 KB
  • 大小: 22.2 KB
分享到:
评论

相关推荐

    PSD文件瘦身.zip

    9. **使用Save for Web功能**:Photoshop的"Save for Web"选项允许你优化文件以适应网络环境,可以自定义压缩级别,减少颜色和 alpha 通道的数量,从而减小文件大小。 10. **更新Photoshop版本**:新版本的Photo...

    3dmax插件:文件瘦身脚本

    使用这个插件的过程通常是这样的:首先,你需要安装该脚本,然后在3ds Max环境中运行它,选择要优化的场景文件。脚本会分析场景,执行上述的瘦身步骤,并生成一个新的、经过优化的3ds Max文件。在保存新文件时,建议...

    PSD文件瘦身脚本.rar

    PS文件瘦身脚本是一款为ps用户提供的文件瘦身脚本,这款脚本可以帮你快速缩小psd文件体积,有时候用ps做设计图层素材什么太多会导致psd文件过大,大家可以利用这款PS文件瘦身脚本给psd文件减肥,而且不影响图像质量...

    睡觉也瘦身——最减肥的睡姿大揭秘

    在本文中,我们将探究睡觉瘦身的原理和方法,并拆解常见的瘦身误区,从而帮助大家以更加全面和科学的方式实现自己的健康目标。 首先,我们来了解睡觉瘦身的原理。人在深度睡眠状态下,身体的基础代谢率虽然下降,但...

    PPT文件瘦身工具

    PPT文件瘦身工具是一款专为优化和减小PowerPoint(PPT)文件大小设计的应用程序。在日常工作中,我们经常需要分享或传输PPT文件,尤其是含有大量图片、动画、图表或其他多媒体元素的文件,其体积往往较大,这在网上...

    大型word 文件瘦身

    在实际操作中,用户首先需要下载并安装NXPowerLite软件,然后按照软件的提示选择需要瘦身的Word文件,设置相应的压缩选项,最后点击“压缩”按钮即可完成文件瘦身的过程。需要注意的是,在压缩前最好备份原始文件,...

    3dmax文件瘦身,3dmax文件瘦身,

    3dmax文件瘦身,文件无缘无故变大,可以用这个

    protel ddb文件瘦身器

    因此,在使用前,务必确保已经备份了所有重要的设计文件,并在安全的环境中运行该工具。此外,由于Protel软件已经是较老的版本,使用瘦身器时要考虑软件的兼容性和更新支持的问题。对于更现代的电路设计工具,如...

    Acrobat 36课时——身材肥大!该瘦身了-PDF文件的优化和压缩.mp4

    Acrobat 36课时——身材肥大!该瘦身了-PDF文件的优化和压缩.mp4

    ps文件瘦身脚本.jsx

    ps文件瘦身脚本,减少PS运行内存,减少卡顿。

    APK瘦身优化检测工具-Matrix ApkChecker 使用(csdn)————程序.pdf

    Matrix ApkChecker 是一个强大的Android APK分析和优化工具,它能够帮助开发者检查APK安装包中可能存在的问题,以实现APK的瘦身和性能优化。该工具通过一系列预设的规则进行检测,并生成详细的报告,便于分析和解决...

    网页文件瘦身器 v1.0.1

    网页文件瘦身器 v1.0.1 是一款专注于优化网页文件的工具,旨在解决由某些网页编辑器,如FrontPage,生成的冗余空格和无用代码问题。这款软件能够有效地清理这些“垃圾”字符,从而提升网页的加载速度,改善用户体验...

    QQ .db文件瘦身器

    QQ 的记录文件 (.db格式的文件)会随着使用而变得越来越大,比较明显的是聊天记录文件 msg2.0.db。 这类文件是只增不减的,比如就算你删除了所有旧的信息, msg2.0.db仍然是原来大小。 因此做了这个工具,他的目的是...

    360C盘瘦身 绿色单文件版,不想360全家桶的同学可以使用

    360C盘瘦身工具是360公司推出的一款优化软件,主要功能是对C盘进行清理和优化,通过删除无用的系统垃圾文件、转移可移动数据以及卸载不必要的程序,以释放C盘空间,提升电脑运行效率。对于那些系统盘空间紧张,或者...

    办公室6S瘦身运动——高效学习办公室6Sppt模板.rar

    办公室6S瘦身运动,源于制造业中的6S管理理念,它是一种优化工作环境、提升工作效率、保障工作安全的方法。6S分别代表六个英文单词的首字母:整理(Sort)、整顿(Straighten)、清扫(Sweep)、清洁(Sanitize)、...

    PSD大文件优化脚本

    打开大型PSD文件的时候,在PS里 文件-脚本-浏览 中选择这个脚本,然后保存。即可优化PSD文件打开速度

    PhotoShop图像文件瘦身(自动删除元数据脚本)

    在Photoshop中,元数据是关于图像文件的重要信息,它包含拍摄时的...总之,“Del元数据.jsx”是一个实用的Photoshop工具,它帮助用户智能地处理元数据,实现图像文件的瘦身,是提高工作效率和优化文件管理的得力助手。

Global site tag (gtag.js) - Google Analytics