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)        AndroidPrelink
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)         分析
此設置只用於動態庫的編譯,編譯時用showcommands參數即可看到具體編譯使用到的命令行,如在某個庫的目錄中運行mm showcommands,即可看到相應的Prelink操作,示例如下:
target Prelink: libxxx
out/host/linux-x86/bin/apriori --prelinkmap build/core/prelink-linux-arm-2G.map --locals-only --quiet xxx.so --output xxx.so
如果該庫設置爲需要prelink,則也需要在map文件中加入相應項,否則編譯不通過

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/1G2G/2G1G/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)修改系統配置device/*/BoardConfig.mk選擇使用不同的map文件.
prelink-linux-arm-2G.map, prelink-linux-arm.map
系統中每加入一個需要prelink的動態庫,需要在map文件添加相應的項

c)         分析
在用戶空間,共定義了三塊與鏈接相關地址間空
系統庫分配的內存:Prelinked System Libraries
應用庫分配的內存:
Prelinked App Libraries
prelink庫分配的內存:Non-prelinked Libraries

d)        map文件規則
需要在 build/core/prelink-linux-arm.map 中加入形如libhellod.so 0x96000000
手工指定某個庫相應的prelink地址範圍,庫應用對齊1M邊界,注意庫與庫之間的間隔數,如果指定不好編譯的時候會提示說地址空間衝突的問題。另外,注意排序,這裏要把數大的放到前面去,按照大小降序排序。

4)        apriori程序

a)         源代碼
build/tools/apriori/*

b)        配置
device/*/BoardConfig.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的,則庫加載的起始地址爲零。

3.    參考文檔:

1)        動態庫優化——Prelink(預連接)技術
http://www.eefocus.com/article/09-04/71629s.html

2)        Android build system note - 一醉千年 - CSDN博客
http://blog.csdn.net/yili_xie/archive/2009/12/01/4906865.aspx

3)        Linux, Android基礎知識總結
http://wenku.baidu.com/view/f68fc029647d27284b735100.html

4)        android Linker淺析
http://blog.csdn.net/dinuliang/archive/2010/04/20/5509009.aspx

發佈了24 篇原創文章 · 獲贊 19 · 訪問量 19萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章