Android Prelink实现的源码分析

Android Prelink实现的源码分析


1. 原理简介

1) Prelink
Prelink即预链接技术是利用事先链接以代替运行时链接的技术,以加快共享库的加载速度,它不仅能加快程序启动时间,还可以减少部分内存开销(它能使KDE的启动时间减少50%)。每次程序执行时,进行的链接动作都是一样的,链接相对来说开销很大,尤其是嵌入式系统。

2) 普通Linux系统的Prelink
Redhat
系统中prelink工具(/etc/cron.dialy/prelink)会修改可执行程序,把它与所需库的链接信息加入可执行程序。在程序运行时,使用glibc(glibc > 2.3.1-r2)中的ld-linux.so来进行链接。用此方式,每次更新动态库后,使用它的程序都需要重新prelink,因为新库中的符号信息,地址等很可能与原来不同了。

3) Android的Prelink
Android
源码中有一组map文件,其中定义了需要预连接的动态库,其Prelink信息以及对应的逻辑地址(4G地址空间中位置),在动态库编译时,预处理程序apriori根据map文件中的定义,生成预链接信息重定向信息,并加入到这些二进制文件lib*.so的末尾。它主要节约了查询函数地址等工作所用的时间,动态库重定位的开销。在运行程序,动态库加载时,加载程序linker判断动态库是否为Prelink的,如果是的话,就在首次使用时将其加载到指定的内存空间,直接使用预编译信息。


2. 源码分析

1)  动态库的编译脚本

a) 源代码
frameworks/base/media/libmedia/Android.mk等库的编译选项文件

b) 配置
可在Android.mk中设置该库是否需要Prelink,默认是使用Prelink的,也可设置成否,方法如下:
LOCAL_PRELINK_MODULE := false

c) 分析
2) 内核

a) 源码
kernel/arch/arm/configs/xxx_defconfig
arch/arm/mach-msm/include/mach/vmalloc.h

b) 配置
CONFIG_VMSPLIT_2G=y
一般默认为3G/1G,此项即设置为2G/2G

c) 分析
xxx_defconfig
为默认的内核配置文件(修改其中的CONFIG_VMSPLIT_*),也可通过make menuconfig配置,与Prelink相关的主要是指定用户空间和内核空间内存如何分配4G的虚拟内存空间(Memory split),一般有三种方式:3G/1G,2G/2G,1G/3G(user/kernel),一般默认的是用户空间3G(0x0-0xBFFFFFFF),内核空间1G(0xC0000000 - 0xFFFFFFFF)

d) 内存分析示例,以3G/1G为例(见build/core/prelink-linux-arm.map)
0xC0000000 - 0xFFFFFFFF Kernel
0xB0100000 - 0xBFFFFFFF Thread 0 static
0xB0000000 - 0xB00FFFFF Linker
0xA0000000 - 0xBFFFFFFF Prelinked System Libraries
0x90000000 - 0x9FFFFFFF Prelinked App Libraries
0x80000000 - 0x8FFFFFFF Non-prelinked Libraries
0x40000000 - 0x7FFFFFFF mmap’s stuff
0x10000000 - 0x3FFFFFFF Thread Stacks
0x00000000 - 0x0FFFFFFF .text / .data / heap

3) map文件

a) 源代码
build/core/prelink-linux-<arch>*.map

b) 配置
编译时有多个map文件可选,根据不同的硬件平台及内存分配(3G/1G, 2G/2G)修改系统配置deviceBoardConfig.mk中设置
TARGET_USES_2G_VM_SPLIT := true
以配置Prelink的地址空间

c) 分析
apriori
中的prelinkmap.c它用根据整个系统设置device/*/BoardConfig.mk的内存分配规则(3G/1G, 2G/2G)来判断map中指定地址是否符合Prelink的地址空间范围,如果正常,则在so的末尾加入prelink信息和标识(文件以PRE结束)
apriori
可以预先为若干共享库确定加载地址,并为有依赖关系的共享库做静态重定位和连接, 该命令加入参数--verbose,即可显示出prelink的细节。

5) linker程序

A) 源代码
bionic/linker/*
(bionic目录中存放一些基础的库,如libc, libstdc++, libthread_db, linker等)

b) 分析
linker是Android的专用动态链接库键接器,Linker和传统Linux使用的linker(ld.so,ld-linux.so.2,ld-linux.so.3)有所不同。库的编译参数-dynamic-linker指定了键接器为/system/bin/linker(也可以手动换成别的),该信息将被存放在ELF文件的.interp节中,内核执行目标映像文件前将通过该信息加载并运行相应的解释器程序linker,并链接相应的共享库,共享库以ELF文件的形式保存在文件系统中。核心的load_elf_binary会首先将其映像文件映射到内存,然后映射并执行其解释器也就是linker的代码。linker的代码段是进程间共享的,但数据段为各进程私有。
所有外部过程引用都在映像执行之前解析, Android中的共享库和可执行映像都默认采用ELF格式的文件. 程序头表包含了加载到内存中的各种段的索引及属性信息,它将告诉加载器如何加载映像,初始化时,动态链接器首先解析出外部过程引用的绝对地址,一次性的修改所有相应的GOT表项。
linker
会在共享库加载时,调用is_prelinked查看该库是否是prelink的,并在alloc_mem_region中检查目的地址是否被占用。如果该库不是prelink的,则库加载的起始地址为零。




在Android中添加自己的库文件,在编译时会提示:library 'XXX.so' not in prelink map 错误, 这就是表明Android中应用了prelink技术

想不做prelink操作,需要将相关变量设置为false

    LOCAL_PRELINK_MODULE := false

    如果不设置LOCAL_PRELINK_MODULE变量,那么默认是需要prlink 的,同时需要在 build/core/prelink-linux-arm.map 中加入类公似 "libhellod.so 0x96000000"的串. 这个map文件是制定动态库的地址的,在前面注释上面有一些地址范围的信息,注意库与库之间的间隔数,如果指定不好的话编译的时候会提示说地址 空间冲突的问题。另外,注意排序,这里要把数大的放到前面去,按照大小降序排序。 




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值