Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 初級開發 >> 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)          分析
此設置只用於動態庫的編譯,編譯時用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/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的,則庫加載的起始地址為零。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved