基於android2.3.5系統:Android動態庫鏈接

***************************************************************************************************************************
作者:EasyWave                                                                                 時間:2013.03.24

類別:Android系統源碼分析                                                              聲明:轉載,請保留鏈接

注意:如有錯誤,歡迎指正。這些是我學習的日誌文章......

***************************************************************************************************************************

跟普通的linux基本一樣,不過android是採用了自己的glibc,也就是在bionic這個文件中,在這個文件夾中它有libc、libdl、libm、libstdc++、libthread_db以及linker,如下圖所示:

1):libc

libc是Android下的C的函數庫。
bionic libc是基本的C語言函數庫,包含了C語言最基本的庫函數。這個庫可以根據 頭文件劃分爲 幾個 個部分,包括:字符類型、錯誤碼、 浮點常數、數學常數、標準定義 、 標準 I/O、工具函數、字符串操作 等等。如下圖所示:

2):libdl
這個一個跟動態庫鏈接有關的一個庫,也就是說,所有libxxx.so都是由它來打開。它提供瞭如下的函數供應用開發者動態的打開和加載動態庫,如下:

void *dlopen(const char *filename, int flag) { return 0; }
const char *dlerror(void) { return 0; }
void *dlsym(void *handle, const char *symbol) { return 0; }
int dladdr(void *addr, Dl_info *info) { return 0; }
int dlclose(void *handle) { return 0; }


不過需要注意的是,上面函數的具體實現,卻是在bionic/linker/Dlfcn.c中實現的。如下所示:

3):libm

這個是andriod下的標準數學庫

4):libstdc++

這個是G++相關的庫

5):libthread_db

這是跟線程相關的庫函數。

6):linker

linker 主要用於實現共享庫的加載與鏈接。它支持應用程序對庫函數的隱式和顯式調用。對於隱式調用,應用程序的編譯與靜態庫大致相同,只是在靜態鏈接的時候通過--dynamic-linker /system/bin/linker 指定動態鏈接器,(該信息將被存放在ELF文件的.interp節中,內核執行目標映像文件前將通過該信息加載並運行相應的解釋器程序linker.)並鏈接相應的共享庫。與ld.so不同的是,Linker目前沒有提供Lazy Binding機制,所有外部過程引用都在映像執行之前解析。對於顯式調用,可以同過linker中提供的接口dlopen,dlsym,dlerror和dlclose來動態加載和鏈接共享庫。

在說到linker時,不得不提PreLinker機制,Prelink即預鏈接技術是利用事先鏈接以代替運行時鏈接的技術,以加快共享庫的加載速度,它不僅能加快程序啓動時間,還可以減少部分內存開銷(它能使KDE的啓動時間減少50%)。每次程序執行時,進行的鏈接動作都是一樣的,鏈接相對來說開銷很大,尤其是嵌入式系統。

Android的Prelink的機制:
Android源碼中有一組map文件,其中定義了需要預連接的動態庫,其Prelink信息以及對應的邏輯地址(4G地址空間中位置),在動態庫編譯時,預處理程序apriori根據map文件中的定義,生成預鏈接信息重定向信息,並加入這些二進制文件lib*.so的末尾。它主要節約了查詢函數地址等工作所用的時間,動態庫重定位的開銷。在運行程序,動態庫加載時,加載程序linker判斷動態庫是否爲Prelink的,如果是的話,就在首次使用時將其加載到指定的內存空間,直接使用預編譯信息。如果要打開prelinker的話,需要進行一些配置,在Android.mk中設置該庫是否需要Prelink,默認是使用Prelink的,也可設置成否,方法如下:
LOCAL_PRELINK_MODULE := false
在android中它是以build/core/prelink_linux_arm.map文件爲基礎,來分配動態庫的地址的,如下所示:

也就是說,如果想自己添加第三方動態庫的話,就需要在build/core/prelink-linux-arm.map 中加入形如libxxx.so 0xxx000000。
手工指定某個庫相應的prelink地址範圍,庫應用對齊1M邊界,注意庫與庫之間的間隔數,如果指定不好編譯的時候會提示說地址空間衝突的問題。另外,注意排序,這裏要把數大的放到前面去,按照大小降序排序。
而在andriod中,以apriori中的prelinkmap.c它用根據整個系統設置BoardConfig.mk的內存分配規則(3G/1G, 2G/2G)來判斷map中指定地址是否符合Prelink的地址空間範圍,如果正常,則在so的末尾加入prelink信息和標識(文件以PRE結束),apriori可以預先爲若干共享庫確定加載地址,併爲有依賴關係的共享庫做靜態重定位和連接, 該命令加入參數--verbose,即可顯示出prelink的細節。

以下爲網絡摘取:
       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的,則庫加載的起始地址爲零。

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