yzrStart

 

软件发布前的进程和动态链接库的优化和裁剪过程(C++程序)

接到此项任务后;

自己先做个大致计划,明确自己要做什么事情,以及做这些事情的大致时间。

当时自己列出的大致计划是:

  1. gcc编译优化:-g -> -O2区别

学习gcc编译:

2、gcc链接库优化:去除没有用到的链接库,

1)如何快速找出程序用到的链接库或者如何快速找出程序没有用到的链接库。

方法一:

可以用readelf -d 可执行文件/动态库/静态库

来找到链接的所有动态库,做好统计

方法二:排除法,删除一个链接的动态库后,编译是否能够通过。(逆向思维)

2)熟悉整个项目架构,了解每个进程用了哪些框架,并做好优化。

3、编译后的程序,用aarch64-linux-gnu-strip命令去掉冗余信息。

1)学习linux strip指令

2)写一个脚本调用strip指令优化所有编译好的程序和文件。

为了验证自己计划是否可行,与是否完善;

决定在第一天的时候在网上搜索相关资料;找到对自己完成这项任务有用的资源并收集起来。(事实证明这件事情很对且非常重要,自己能够汲取前人智慧,并且能够找到完成任务的更好方法)

第一天:2023.2.1

学习项目相关知识点:

1、C++程序到可执行程序的过程:预处理 -> 编译 ->汇编 -> 链接 ->可执行程序;

2、静态库与动态库

库:是一种可执行代码的二进制形式,可以被操作系统载入内存执行。库有两种:静态库(.a、.lib)和动态库(.so、.dll)。

g++ -shared 指定生成动态链接库

-static 指定生成静态链接库

-L 表示要连接的库所在的目录

-l 指定链接时需要的动态库。 编译器查找动态连接库时有隐含的命令规则,即在给出的名字前面加上lib,后面加上.a/.so来确定库的名称。

ldd命令 :可以查看一个可执行程序依赖的共享库,

3、gcc编译学习

参考资料:百度百科 ,Keyword Index (Using the GNU Compiler Collection (GCC))

-g,产生符号调试工具(GNU的gdb)所必要的符号资讯,要想对源代码进行调试,我们就必须加入这个选项。

-O2,比-O更好的优化编译、链接,当然整个编译、链接过程会更慢。

 其他此次编译中用到的可选项:

-fpermissive

Downgrade some diagnostics about nonconformant code from errors to warnings. Thus, using -fpermissive allows some nonconforming code to compile.

 

-ftree-vectorize

Perform vectorization on trees. This flag enables -ftree-loop-vectorize and -ftree-slp-vectorize if not explicitly specified.

 

-ffast-math

Sets the options -fno-math-errno, -funsafe-math-optimizations, -ffinite-math-only, -fno-rounding-math, -fno-signaling-nans, -fcx-limited-range and -fexcess-precision=fast.

This option causes the preprocessor macro __FAST_MATH__ to be defined.

This option is not turned on by any -O option besides -Ofast since it can result in incorrect output for programs that depend on an exact implementation of IEEE or ISO rules/specifications for math functions. It may, however, yield faster code for programs that do not require the guarantees of these specifications.

 

-Wwrite-strings  

When compiling C, give string constants the type const char[length] so that copying the address of one into a non-const char * pointer produces a warning. These warnings help you find at compile time code that can try to write into a string constant, but only if you have been very careful about using const in declarations and prototypes. Otherwise, it is just a nuisance. This is why we did not make -Wall request these warnings.

When compiling C++, warn about the deprecated conversion from string literals to char *. This warning is enabled by default for C++ programs.

 

-s (*****)

 

Remove all symbol table and relocation information from the executable.

 

 

 

-fPIC  

If supported for the target machine, emit position-independent code, suitable for dynamic linking and avoiding any limit on the size of the global offset table. This option makes a difference on AArch64, m68k, PowerPC and SPARC.

Position-independent code requires special support, and therefore works only on certain machines.

When this flag is set, the macros __pic__ and __PIC__ are defined to 2.

 

-fstack-protector

Emit extra code to check for buffer overflows, such as stack smashing attacks. This is done by adding a guard variable to functions with vulnerable objects. This includes functions that call alloca, and functions with buffers larger than or equal to 8 bytes. The guards are initialized when a function is entered and then checked when the function exits. If a guard check fails, an error message is printed and the program exits. Only variables that are actually allocated on the stack are considered, optimized away variables or variables allocated in registers don’t count.

  1. Linux的strip指令学习

-strip指令可以针对可执行文件、目标文件、动态库

-strip命令是GNU Binutils中的一员,用于剥掉文件中一些符号信息和调试信息,使文件变小。

-gcc -s 是删除执行程序的符号表和重定位信息,与strip作用类似。

- strip学习手册:Linux Strip Command Help and Examples (computerhope.com)

- strip裁剪静态库时,要小心,可能会导致程序无法运行,所以在没有全面掌握程序的组成和strip命令时,不要裁剪静态库。

  1. readelf命令学习

-readelf 用来显示一个或者多个elf格式单独目标文件信息,可以通过它的选项来控制显示哪些信息

-elf文件类型:三种

  1. 可重定位文件:用户和其他目标文件一起创建可执行文件或者共享目标文件,例如lib*.a文件。
  2. 可执行文件:用于生成进程映像,载入内存执行,例如编译好的可执行文件a.out.
  3. 共享目标文件:用于和其他共享目标文件或者可重定位文件一起生成elf目标文件或者和执行文件一起创建进程映像,例如lib*.so文件。

-本次自己使用的时readelf -d 文件名

-具体学习参考:

Linux命令学习手册-readelf - 知乎 (zhihu.com)

(83条消息) readelf命令使用说明_木虫下的博客-CSDN博客_readelf

第二天:2023.2.2

工作内容:

  1. 将gcc -g 编程 gcc -O2
  2. 加入 gcc -s
  3. 找到每个进程的动态链接库:

如何找到的呢;

1)用readelf -d 命令查看每个进程的动态链接库,并信息记录在“进程所用到的动态链接库.txt”文件中;

2) 然后dependencies目录下,按顺序依次浏览每个库的lib中文件,提取出文件名,在“进程所用到的动态链接库.txt”中搜索是否有相同内容,有则保留此文件,无则删除此文件,但静态库不删除。(这个过程非常需要耐心)

3)然后验证自己找的动态链接库是否齐全,然后把库导入到HDMIBOX中看是否能正常运行;发现运行不成功;查找运行不成功原因,单独运行程序,发现还是有动态链接库缺少;于是思考为什么,然后readelf查看grpc中的.so文件,发现这些库还动态链接着其他文件,于是便知道找到的库不完整(当时想,是不是自己方法错了,然后自己对剩下的每个.so文件readelf看一下,统计的工作量不大,于是便继续做下去了)。

4)查找每个.so所动态链接的库,这个过程也很痛苦,很费眼睛,幸好有Everything神器。

5)然后继续查找动态链接链接的库是否有链接库的,这个过程也很难受。

6)检查找到库是否完整,于是便把库继续用HDMIBOX项目测试,大概测试了一遍,没有啥大问题。

4、手动使用:linux strip命令 对一些进程和所有的动态链接库strip一下,裁剪不必要的信息。

最后把248 M -> 46.3 M 非常有成就感。

总结:

在进行进程链接库查找与记录时想到了一句话:路虽远,行则将至;事虽难,做则必成。

只要方向没错,那么久耐心的做下去。

第三天:2023.2.3

1、在生产脚本上修改,来用脚本自动裁剪进程和动态库

1)修改build_app_deploy.sh文件

a.只将必要的第三方库放到dist目录下;

-使用Beyond Compare4对比工具快速找到不同。 11:17完成

b.在dist目录下,使用shell编程,strip所用进程和动态库,也就是.so文件。 14:00完成

-找到该目录下的所有进程

-对进程执行strip操作

-找到所有.so文件 (find命令)

-对.so文件执行strip操作

关于找到所有.so文件想法:一开始是打算用递归遍历目录的方法找到,后来发现这样可能太复杂和不可靠,于是想想看有没有更简单的方法,于是想到了find命令,一试果然和方便。(悟:如果你做一件事情很复杂,可能是方法错了,跳出来看看是否有更好的方法来完成这件事情。)

大总结:这个任务的快速完成原因有:

  1. 向导师寻求了方法;
  2. 自己做了计划;
  3. 明确了需求;
  4. 网上资源的利用;

今后自己在做任务时:

  1. 明确需求;
  2. 导师求助;
  3. 自己做好计划;
  4. 网上资源;
  5. 做好总结。

总的来说,自己是按照计划在走的,所以说做计划的必要性,今后要养成做计划的习惯。

posted on 2023-02-03 16:13  yzrStart  阅读(180)  评论(0编辑  收藏  举报

导航