如何证明一个静态库没有使用 malloc
今天我还做了另外一个有趣的事情:
有一个客户要求我们提供的一个 libxxx.a 文件不能使用 malloc 动态分配内存。研发排查了下代码都没有用到 malloc。
但是客户说他们还是可以看到 malloc 符号,但是他们把malloc的实现hook成空的也能跑。所以他们相信我们应该没有调用malloc,但是需要我们保证我们的程序没有使用malloc。
产品找到我老大,我老大让我帮忙给分析下怎么证明。我知道这个事情有经验的应该是朋友TT 。咨询了下 TT 说可以看导入符号表,但是Linux的没搞过。
我问了GPT,有 nm -D 和 objdump -D 两个命令可以用。于是我就去看了下我们的 libxxx.a 用法是 nm -D libxxx.a |grep malloc 或者 objdump -D libxxx.a | grep malloc
但是 nm 好像看不出来,nm 只能打印头文件里有直接用的,例如 xxx.h 和 xxx.cpp
nm 只能看到导出符号好像。在 xxx.cpp 的实现函数内调用的符号,通过 objdump 可以看到。
但是我想要进一步分析下到底是哪里引入了 malloc。我写了一个很简单的 test.cpp 把 xxx.h 引入,链接 libxxx.a 然后执行
gcc -E -P test.cpp > test.full.cpp
在 test.full.cpp 里就可以直接看到 malloc 函数的实现代码,这是什么原理呢?原理是 gcc -E 只做预处理,相当于把所有 include 都展开了,于是 malloc 无论是在哪里定义的,都会被展开出来。
那么下面就好办了,通过2分注释方式,很快排查到 #include <cmath>
居然导致了 malloc 符号的引入,cmath 和malloc 居然有关系!
怎么解决呢?我们只用了一个函数 powf,可以直接用 GCC 内置的 __builtin_powf 函数,实际上GCC的 cmath内部也是层层判断最后调用 GCC的某个 __builtin_powf实现。这在都用GCC的时候是安全的。
同时没有 #include <cmath>
会导致 int32_t 之类的符号为定义,这个简单,使用 #include <stdint.h>
这个问题解决了后。我又进一步提供了一个证明库如果不做任何改动,没有使用 malloc 的证明思路:
a) 在 xxx.h 里添加一个 test函数
b) 在 xxx.cpp 里实现 test,在里面调用 malloc,同时添加一个 print 使用malloc的结果,这是因为如果不使用可能编译就被优化掉了。
c)在 test.cpp 里使用 test方法
d)编译后通过 objdump -D test | grep malloc 可以看到
e)在 test.cpp 里注释掉对 test 的调用。
f)再次编译后再通过 objdump -D test | grep malloc 查看,这下看不到了。
g)这就证明了,即使静态库实现代码有 malloc ,只要最终的可执行程序没有调用,通过静态分析就可以判断是否 malloc 被调用了。
h)这样就可以通过对 libxxx.a 在使用前后 objdump -D 结果是否有 malloc 来证明 libxxx.a 确实没有使用 malloc.
--end--