Arm-GCC 代码瘦身教程
最近项目中用到一个Flash非常小的MCU,在这款芯片上开发程序就一定要注意程序最终二进制代码的大小,于是各种代码缩减的技能都要搞明白。
代码的瘦身,第一步想到的就是要从最终二进制中出去一些不用的函数和变量,这个说起来应该比较简单,似乎应该是一个编译器默认开启的功能,然而实际编译器并不会进行这种代码缩减,而是会将所有出现过的函数统统放到最终二进制文件中去,这样如果我们代码中有用不到的legacy的代码,这些代码就会增加二进制文件的大小。
直接祭出神器
废话不多说,直接祭出神器吧,虽然也是找了好久才找到的,其实实现的方法比较简单,就是在编译和链接的命令上加一个-flto
的参数,lto
的含义是Link Time Optimize
也就是链接时优化,所以加上这个参数之后,编译器和连接器会自动去掉一些没有调用的函数和没有用到的变量。
神器也不是万能的
加上神器之后代码直接减少了一半,这个真的是挺给力的,了呵呵的下进去一看,程序直接跑飞,我擦怎么回事,二进制代码直接打开看看,不会是下载的问题吧。
这是编译出来和下载后的二进制文件,下载没问题,代码有问题。
00000000: 014b 1b78 7047 c046 04ed 00e0 c046 c046 .K.xpG.F.....F.F
00000010: 754b 764c 9a68 9a60 2022 1968 0a43 8021 uKvL.h.` ".h.C.!
00000020: 1a60 734a 5a60 1a68 8a43 1a60 7149 724a .`sJZ`.h.C.`qIrJ
00000030: 724b d150 0022 7249 724b 5b1a 9b10 9342 rK.P."rIrK[....B
00000040: 00d0 c5e0 0020 7049 704b cb1a 9b10 013b ..... pIpK.....;
00000050: 00d3 c2e0 8022 6e4b d205 996a 8020 1143 ....."nK...j. .C
00000060: 9962 d96a 4004 1143 d962 196b 1143 1963 [email protected]
00000070: 596b 1143 5963 996b 0a43 9a63 2422 654b Yk.CYc.k.C.c$"eK
00000080: 6549 da60 1d3a 5a60 1a00 0b00 5468 0442 eI.`.:Z`....Th.B
00000090: fcd0 a022 d200 8858 604c 2040 8850 a120 ..."...X`L @.P.
000000a0: 0124 c000 0c50 5e48 5e4c 0c50 8c58 5e48 .$...P^H^L.P.X^H
000000b0: 2043 8850 8021 4904 9858 0842 fcd0 8020 C.P.!I..X.B...
000000c0: 8021 5a4a 5a4c 5a61 5a4a 8000 9a61 5a69 .!ZJZLZaZJ...aZi
000000d0: 594a 8904 2061 1160 4022 584d e127 6e68 YJ.. a.`@"XM.'nh
000000e0: 7f02 3243 6a60 8122 a062 9200 9c58 544d ..2Cj`.".b...XTM
000000f0: 544e 2c40 9c50 c024 9d58 a400 2c43 9c50 TN,@.P.$.X..,C.P
00000100: d423 514a 5b00 d458 504d 2c40 d450 d458 .#QJ[..XPM,@.P.X
00000110: 0525 2143 d150 8021 d458 c905 0c43 d450 .%!C.P.!.X...C.P
正常二进制文件头:
00000000: 0018 0020 2104 0000 1104 0000 1104 0000 ... !...........
00000010: 1104 0000 1104 0000 1104 0000 1104 0000 ................
00000020: 1104 0000 1104 0000 1104 0000 1104 0000 ................
00000030: 1104 0000 1104 0000 1104 0000 1104 0000 ................
00000040: 1104 0000 1104 0000 1104 0000 1104 0000 ................
00000050: 1104 0000 1104 0000 1104 0000 1104 0000 ................
00000060: 1104 0000 1104 0000 1104 0000 1104 0000 ................
00000070: 1104 0000 1104 0000 1104 0000 1104 0000 ................
00000080: 1104 0000 1104 0000 1104 0000 1104 0000 ................
00000090: 1104 0000 1104 0000 1104 0000 1104 0000 ................
000000a0: 1104 0000 1104 0000 1104 0000 1104 0000 ................
可以看到正常二进制开头是比较整齐的中断向量表,那么看起来是中断向量表被优化掉了,哎,优化有风险。我们看看生成的map
00000000 g .isr_vector 00000000 __vector_table
0000097c w F .text 00000002 .hidden __aeabi_ldiv0
00000694 g F .text 00000000 .hidden __aeabi_uidiv
1ffff800 g .heap 00000000 __end__
000007a8 g F .text 000001cc .hidden __divsi3
1ffffc00 g .heap 00000000 __heap_end
20001800 g .heap 00000000 __StackTop
1ffff800 g .heap 00000000 __heap_start
000007a0 g F .text 00000008 .hidden __aeabi_uidivmod
20001400 g *ABS* 00000000 __StackLimit
00000400 g *ABS* 00000000 Heap_Size
0000097c w F .text 00000002 .hidden __aeabi_idiv0
00000974 g F .text 00000008 .hidden __aeabi_idivmod
00000400 g *ABS* 00000000 Stack_Size
可以看到中断向量表大小为0,真的是优化了。
搞定神器
没办法,网上找来找去,发现可以通过加__attribute__ ((used))
来避免编译器优化,但是我加上了没有用,中断向量表依然被优化,就在快要放弃的时候,偶然发现这个只能用于static类型的变量,于是将中断向量表的数组改为static
再次编译搞定,这个问题比较隐晦,查了好多资料才找到原因。
const
和 static
的区别
实际上一直出问题的主要原因是之前isr
数组一直是用const
修饰的,而__attribute__ ((used))
的作用范围只针对static
,好麻烦哈,所以之前各种尝试都没能搞定,最后才发现要改成static
,这里也大概研究一下这两个修饰符之间的差异。
const
: 它的含义是告诉编译器,这个变量是不可变的,只是说内容不可变,但是变量的空间是可以回收的,也就是一旦编译器认定这个变量不能访问就可以回收其空间,而一旦牵扯到回收空间,那么他的位置可能是SRAM
,不过变量的初始值还是应该在Flash,当然也可能直接放到flash
空间,在这里const
没有起作用的关键还是编译器认定中断向量表用不到,可以直接删掉。。
static
: 静态变量,它的含义就比较单纯,就是告诉编译器这个变量会一直占用空间,并且内容不会变化,所以这种变量一般都是直接放到flash
中去的。
最后更新于 2018-10-27 23:09:18 并被添加「」标签,已有 2926 位童鞋阅读过。
本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。