环境:目标系统为armeabi,32bit,linux,gcc4.9,glibc2.20,宿主机器为ubuntu12.04
执行readelf -d xxx.so,发现有些是需要连接ld-linux.so.x的,比如libffi.so:
$ /disk7/shuyin.wsy/tv/prebuilts/gcc/linux-x86/arm/arm-linux-gnueabi-4.9-glibc-2.20/bin/arm-linux-gnueabi-readelf -d libffi.so.6.0.2
Dynamic section at offset 0x5060 contains 28 entries:
Tag Type Name/Value
0x00000001 (NEEDED) Shared library: [libgcc_s.so.1]
0x00000001 (NEEDED) Shared library: [libc.so.6]
0x00000001 (NEEDED) Shared library: [ld-linux.so.3]
0x0000000e (SONAME) Library soname: [libffi.so.6]
里面有条NEEDED ld-linux.so.3。
有些库,比如eglplatform_null.so就没有这一行。
即使我添加了-nodefaultlib -nostdlib -lc -lgcc_s也没办法搞掉NEEDED ld-linux.so.3。
首先,可执行程序由arm-linux-gnueabi-ld去连接的,连接过程中需要找到所有的符号,如果用到了ld-linux.so.3里面的符号,那么就应当去连接它;如果没用到,就可以不连接。
arm-linux-gnueabi-nm --dynamic ld-linux.so.3可以查看到ld-linux.so.3的符号,加dynamic的话,只看动态的符号,不加的话会有很多无关的符号输出来:
00017ae0 W free
00000000 A GLIBC_2.0
00000000 A GLIBC_2.1
00000000 A GLIBC_2.3
00000000 A GLIBC_2.4
00000000 A GLIBC_PRIVATE
0002ff40 D __libc_enable_secure
0001793c W __libc_memalign
0002ff38 D __libc_stack_end
00017a84 W malloc
0002fc8c D __pointer_chk_guard
00030940 B _r_debug
00017b84 W realloc
00030050 D _rtld_global
0002fce8 D _rtld_global_ro
0002fce0 D __stack_chk_guard
00013870 T __tls_get_addr
去掉明显不靠谱的GLIBC_2.0,__libcxxx,malloc(你不会以为malloc是ld-linux.so里面实现的函数吧)之类的,还剩5个:
0002fce0 D __stack_chk_guard
0002fc8c D __pointer_chk_guard
00013870 T __tls_get_addr
00030050 D _rtld_global
0002fce8 D _rtld_global_ro四个数据,一个函数。
使用arm-linux-gnueabi-objdump -R xxx可以找到动态链接的xxx所缺的符号(函数,变量什么的):
find . -name "*.so*" | while read line #搜索连接到ld-linux.so的所有的库文件,保存到need_list.log
do
ret=`readelf -d $line | grep ld-linux`
if [ "$ret" != "" ] ;then
echo $line
fi
done | tee need_list.log
cat need_list.log | while read line #在这些库文件里面,搜索5个符号,如果搜索不到,报错
do
echo "filename: $line"
ret=`/disk7/shuyin.wsy/tv/prebuilts/gcc/linux-x86/arm/arm-linux-gnueabi-4.9-glibc-2.20/bin/arm-linux-gnueabi-objdump -R $line | grep -e __stack_chk -e __pointer_chk -e __tls_get_addr -e _rtld_global`
if [ "$ret" == "" ] ; then
echo "error"
fi
done | tee need_error.log
##################################################################################
find . -name "*.so*" | while read line #搜索不连接ld-linux.so的所有的库文件,保存到not_need_list.log
do
ret=`readelf -d $line | grep ld-linux`
if [ "$ret" == "" ] ;then
echo $line
fi
done | tee not_need_list.log
cat not_need_list.log | while read line #在这些库文件里面,搜索5个符号,如果搜索到,报错
do
echo "filename: $line"
ret=`/disk7/shuyin.wsy/tv/prebuilts/gcc/linux-x86/arm/arm-linux-gnueabi-4.9-glibc-2.20/bin/arm-linux-gnueabi-objdump -R $line | grep -e __stack_chk -e __pointer_chk -e __tls_get_addr -e _rtld_global`
if [ "$ret" != "" ] ; then
echo "error"
fi
done | tee not_need_error.log
注意需要排出glibc自带的一些库的干扰(libssp, libasan)。上述shell脚本的结果验证了我们的猜想,需要使用那5个符号的,就需要连接ld-linux.so。(实际上除了glibc里面的一些库,也没人依赖_rtld_global,_rtld_global_ro)
那么为什么会使用到了这几个符号呢:
0002fce0 D __stack_chk_guard
0002fc8c D __pointer_chk_guard
00013870 T __tls_get_addr
man gcc可以看到
-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 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.
-fstack-protector-all
Like -fstack-protector except that all functions are protected.
-mstack-protector-guard=guard
Generate stack protection code using canary at guard. Supported
locations are global for global canary or tls for per-thread
canary in the TLS block (the default). This option has effect
only when -fstack-protector or -fstack-protector-all is
specified.
如果我们不加-fstack-protector和-fstack-protector-all,再次编译程序,发现不依赖于ld-linux.so.3了。
但是加了-fstack-protector-all,-mstack-protector-guard=guard也不一定会依赖ld-linux.so.3,因为一个极其简单的函数,有必要堆栈保护吗?
注意:needed里面的ld-linux.so.3和.interp里面的/lib/ld-linux.so.3都是同一个文件,程序运行前会先执行.interp里面的/lib/ld-linux.so.3,将依赖的库载入内存,处理好GOT,然后将控制权交给程序的_start函数。即使needed里面没有ld-linux.so.3也不影响程序的执行,因为需要的符号已经存在了,/lib/ld-linux.so.3的符号。而且/lib/ld-linux.so.3里面有不少代码,防止重复载入ld-linux.so。