android c library: bionic 成長計畫
DESCRIPTION
本議程主要探討 Android 的 C Library : Bionic , bionic 與一般 Linux 所使用的 C Library 有所不同,為達到輕薄短小, 因此相較下缺少部份功能的實做, 本議程將探討 bionic 何處尚可改善以及發表我們對於 bionic 所改善的部份, 其中改善包含 bionic 中的 Dynamic Linker 及對於 Profiling 的支援.TRANSCRIPT
Android Native Runtime成長計畫
Kito Cheng ( 程皇嘉 ) <[email protected]>
Developer, 0xlabApril 14, 2012 / OSDC.tw
Rights to copy
Attribution – ShareAlike 3.0You are free✔ to copy, distribute, display, and perform the work✔ to make derivative works✔ to make commercial use of the workUnder the following conditions
Attribution. You must give the original author credit.Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under a license identical to this one.
● For any reuse or distribution, you must make clear to others the license terms of this work.
● Any of these conditions can be waived if you get permission from the copyright holder.Your fair use and other rights are in no way affected by the above.License text: http://creativecommons.org/licenses/by-sa/3.0/legalcode
© Copyright 2012 0xlabhttp://0xlab.org/
Corrections, suggestions, contributions and translations are welcome!
Latest update: Apr 17, 2012
Android Native Runtime 成長計畫:探討 0xlab 若干低階 Android 優化的開源項目
(4) 具備 S/L 大法的時間魔法陣
(1) 可解讀 GNU-stylehash 的帥氣墨鏡
(3) 針對 JNI 量測效能的機械錶 (2) 強化版 8-bit
prelink map
Android Native Runetime 成長計畫
可解讀 GNU-stylehash 的帥氣墨鏡
GNU-style Hash
• 在 ELF 程式載入時期,加速 ELF 符號查詢的機制
載入相關動態函式庫
執行檔讀入記憶體
更新符號位址(Relocation)
開始執行 !
在更新符號時,需要大量的符號查詢操作
printf, sin, cos, ...
libc / libm / libdvm...
ELF 程式載入範例
prog
libc libfoo
prog參照到 libc 與 libfoo的符號
ELF 程式載入範例
prog
libc libfoo
strtoumaxstatfswcsncmpdlmalloptprintf...strtoulgetutent
foobar...ox1ab
libc 及 libfoo 各自提供這些符號 (函數 /變數 )
prog參照到 libc 與 libfoo的符號
ELF 程式載入範例
prog
libc libfoo
strtoumaxstatfswcsncmpdlmalloptprintf...strtoulgetutent
foobar...ox1ab
libc 及 libfoo 各自提供這些符號 (函數 /變數 )
prog參照到 libc 與 libfoo的符號
假設 prog 只使用到 printf, foo, bar 等 3 個函數
ELF 程式載入範例
prog
libc libfoo
strtoumaxstatfswcsncmpdlmalloptprintf...strtoulgetutent
foobar...ox1ab
假設 prog 只使用到 printf, foo, bar 等 3 個函數
雖只用到 3 個符號 ,但在這個例子需要查詢
5 次符號
查詢符號次數複雜度 : O(n*m)n : 相依的函數庫數目m : 符號數
GNU-style Hash
• 原本 ELF已採用 hash 來加速查詢,但不盡理想
name 2sym collision # 3sym collision # 3+sym collision #sysv 1749 5libiberty 42dcache 37djb2 29sdbm 39djb3 31rot 337 39 61sax 34fnv 40oat 30
ELF 預設的 hash 方法
GNU-style hash 的方法
Experiment by Jakub Jelinek http://cygwin.com/ml/libc-alpha/2006-06/msg00098.html
GNU-style Hash
• 並使用 bloom filter 進一步加速整體查詢
Bit array
H = {x, y, z} = hash functions
v
號稱 (Jakub Jelinek ) 可加速 50% 的載入時間
GNU-style Hash 在 Android 的相容問題
• 某些 Android 程式自帶(修改自原本 bionic linker的) dynamic linker ,所以不支援– 如: Unity 3D, Mozilla/Android (B2G)
• 因此要採用向後相容方案, 新舊的 hash table 都要在 linker 指定 ,-Wl,--hash-style=both– 但 gold linker 有 bug, 所以最好用 bfd 的 linkerhttp://sourceware.org/bugzilla/show_bug.cgi?id=13597
強化版 8-bitprelink map
Android太貪吃,所以 prelink map拿掉了
No prelink in Android 4.0
• The speedup that it afforded in the early days of Android is now nullified by the speed of hardware, as well as by the presence of Zygote
• The space savings that come with prelinking are no longer important either.
• Prelinking reduces the effectiveness of Address-Space-Layout Randomization.
• Since it is not part of the gcc suite, the prelinker needs to be maintained separately.
No prelink in Android 4.0 :官方說法
Android Git loghttps://github.com/android/platform_build/commit/b375e71d306f2fd356b9b356b636e568c4581fa1
• 硬體進步跟 Zygote 機制的導入, prelink 加速效果不顯著
• 只省一丁點空間, 現在記憶體大又便宜• prelink 之後, Address-Space-Layout
Randomization 效果會變弱
• 並非 GNU toolchain 相關專案, Android 要維護很麻煩
No prelink in Android 4.0 :官方說法
白話版本
Prelink in Android 4.0
• 在較低等級的硬體環境中, prelink 對開機時間仍有影響
• 事實上,在 Android 4.0 已移除的 prelink 機制可撿回來用
程式啟動流程與 prelink
載入相關動態函式庫
執行檔讀入記憶體
更新符號位址(Relocation)
開始執行 !
在更新符號時,需要大量的符號查詢操作
printf, sin, cos, ...
libc / libm / libdvm...
prelink就是將此階段要查詢解析的符號,預先處理過
prelink 實驗
libfoo
libc
int bar() { return 100;}
int foo () { printf(“foo”); return bar();}
假設已存有一個建好的且經 prelink處理過後的 libc.so
prelink 實驗(Relocation)
libfoo
libc
int bar() { return 100;}
int foo () { printf(“foo”); return bar();}
# arm-eabi-objdump libfoo.so -R
libfoo.so: file format elf32-littlearm
DYNAMIC RELOCATION RECORDSOFFSET TYPE VALUE 000010e8 R_ARM_JUMP_SLOT printf000010ec R_ARM_JUMP_SLOT __cxa_finalize00000424 R_ARM_RELATIVE *ABS*0000100c R_ARM_RELATIVE *ABS*
objdump -R可檢視 Relocation Recordlibfoo.so 共有四個 Relocation 要做
prelink 實驗(Relocation)
libfoo
libc
int bar() { return 100;}
int foo () { printf(“foo”); return bar();}
# arm-eabi-objdump libfoo.so -R
libfoo.so: file format elf32-littlearm
DYNAMIC RELOCATION RECORDSOFFSET TYPE VALUE 000010e8 R_ARM_JUMP_SLOT printf000010ec R_ARM_JUMP_SLOT __cxa_finalize00000424 R_ARM_RELATIVE *ABS*0000100c R_ARM_RELATIVE *ABS*
一個 *ABS* 是 bar 的位址,另一個是系統用的
prelink 實驗
libfoo
libc
int bar() { return 100;}
int foo () { printf(“foo”); return bar();}
# apriori --locals-only \ --prelinkmap prelink-linux-arm.map \ libfoo.so --output prelinked/libfoo.so
接著對 libfoo.so 進行 prelink
apriori 位於 out/host/linux-x86/bin/prelink map 位於 build/core--locals-only(-l) 指定進行 local prelink
prelink 實驗
libfoo
libc
int bar() { return 100;}
int foo () { printf(“foo”); return bar();}
# apriori --locals-only \ --prelinkmap prelink-linux-arm.map \ libfoo.so --output prelinked/libfoo.so
build/tools/apriori/prelinkmap.c(183): library 'libfoo.so' not in prelink map
出現錯誤 ,抱怨在 prelink map 找不到 libfoo
Prelink Map
# core system librarieslibdl.so 0xAFF00000 # [<64K]libc.so 0xAFD00000 # [~2M]libstdc++.so 0xAFC00000 # [<64K]libm.so 0xAFB00000 # [~1M]liblog.so 0xAFA00000 # [<64K]libcutils.so 0xAF900000 # [~1M]libthread_db.so 0xAF800000 # [<64K]libz.so 0xAF700000 # [~1M]libevent.so 0xAF600000 # [???]libssl.so 0xAF400000 # [~2M]libcrypto.so 0xAF000000 # [~4M]libsysutils.so 0xAEF00000 # [~1M]
# bluetoothliba2dp.so 0xAEE00000 # [~1M]audio.so 0xAED00000 # [~1M]input.so 0xAEC00000 # [~1M]…liblept.so 0x9CA00000 # [~5M]
預先分配每個動態函式庫的基底位址 (Base Address)
要 prelink 的函式庫都要加入
Prelink Map
# core system librarieslibdl.so 0xAFF00000 # [<64K]libc.so 0xAFD00000 # [~2M]libstdc++.so 0xAFC00000 # [<64K]libm.so 0xAFB00000 # [~1M]liblog.so 0xAFA00000 # [<64K]libcutils.so 0xAF900000 # [~1M]libthread_db.so 0xAF800000 # [<64K]libz.so 0xAF700000 # [~1M]libevent.so 0xAF600000 # [???]libssl.so 0xAF400000 # [~2M]libcrypto.so 0xAF000000 # [~4M]libsysutils.so 0xAEF00000 # [~1M]
# bluetoothliba2dp.so 0xAEE00000 # [~1M]audio.so 0xAED00000 # [~1M]input.so 0xAEC00000 # [~1M]…liblept.so 0x9CA00000 # [~5M]libfoo 0x9C900000
預先分配每個動態函式庫的基底位址 (Base Address)
要 prelink 的函式庫都要加入
把 libfoo 加進來
prelink 實驗(Relocation)
libfoo
libc
int bar() { return 100;}
int foo () { printf(“foo”); return bar();}
# apriori --locals-only \ --prelinkmap prelink-linux-arm.map \ libfoo.so --output prelinked/libfoo.so
加入 prelink map 後,就可順利進行 prelink
# arm-eabi-objdump prelinked/libfoo.so -R
libfoo.so: file format elf32-littlearm
DYNAMIC RELOCATION RECORDSOFFSET TYPE VALUE 000010e8 R_ARM_JUMP_SLOT printf000010ec R_ARM_JUMP_SLOT __cxa_finalize
發現只剩兩個 Relocation!
prelink 實驗
libfoo
libc
int bar() { return 100;}
int foo () { printf(“foo”); return bar();}
# arm-eabi-objdump prelinked/libfoo.so -R
libfoo.so: file format elf32-littlearm
DYNAMIC RELOCATION RECORDSOFFSET TYPE VALUE 000010e8 R_ARM_JUMP_SLOT printf000010ec R_ARM_JUMP_SLOT __cxa_finalize
發現只剩兩個 Relocation!
其中的祕密在於, libfoo.so 的 base address 在prelink map 中固定 , 而事實上處理 Relocation 僅需base address 加上 Offset即可
prelink 實驗
libfoo
libc
int bar() { return 100;}
int foo () { printf(“foo”); return bar();}
# apriori --locals-only \ --prelinkmap prelink-linux-arm.map \ libfoo.so --output prelinked/libfoo.so
回顧剛才的參數有個 --locals-only,通常有local ,就有相對的 global
# apriori \ --prelinkmap prelink-linux-arm.map \ libfoo.so --output prelinked/libfoo.so
但直接移除該參數時, apriori 會抱怨參數錯誤
build/tools/apriori/main.c(195): There are command-line-option errors.
prelink 實驗
libfoo
libc
int bar() { return 100;}
int foo () { printf(“foo”); return bar();}
# apriori \ --prelinkmap prelink-linux-arm.map \ libfoo.so --output prelinked/libfoo.so
build/tools/apriori/main.c(195): There are command-line-option errors.
* 不明原因 *在 apriori的 global prelink的部份程式未完成,但有雛型
進行接下來的實驗前,得先從 0xdroid取用 0xlab的功能增補系列 patch,散落於以下三處:build/external/elfutils/external/elfcopy/
0xdroid git repository:http://gitorious.org/0xdroid
prelink 實驗(incremental global prelink)
libfoo
libc
int bar() { return 100;}
int foo () { printf(“foo”); return bar();}
# apriori --inc-global-prelink \ --prelinkmap prelink-linux-arm.map \ -L /system/lib \ libfoo.so \ –output prelinked/libfoo.so
參數加上 --inc-global-prelink-L <其他函數庫的路徑 >就會開始進行 Incremental Global Prelink
prelink 實驗(incremental global prelink)
libfoo
libc
int bar() { return 100;}
int foo () { printf(“foo”); return bar();}
# apriori --inc-global-prelink \ --prelinkmap prelink-linux-arm.map \ -L /system/lib \ libfoo.so –output prelinked/libfoo.so
# arm-eabi-objdump prelinked/libfoo.so -R
libfoo.so: file format elf32-littlearm
DYNAMIC RELOCATION RECORDS (none)
Incremental Global Prelink 處理後,就沒有任何Relocation 需要處理了 !
名字有 incremental 的原因是,該實作事實上會先進行 local prelink再去處理 global prelink 的漸進方式,故命名之,而輸入的函式庫也可為經 local prelink 處理過者
prelink 實驗(incremental global prelink)
libfoo
libc
int bar() { return 100;}
int foo () { printf(“foo”); return bar();}
# apriori --inc-global-prelink \ --prelinkmap prelink-linux-arm.map \ -L /system/lib \ libfoo.so –output prelinked/libfoo.so
# arm-eabi-objdump prelinked/libfoo.so -R
libfoo.so: file format elf32-littlearm
DYNAMIC RELOCATION RECORDS (none)
在 global prelink 的過程中,會試著搜索外部的符號 , 若找得到且該函式庫也經 prelink 過,則其符號的位址也是已知 , 因此可以 prelink
透過一些觀察可發現,不在 prelink map 的函式庫,其實也可處理掉部份的 Relocation
prelink 實驗
libfoo
libc
int bar() { return 100;}
int foo () { printf(“foo”); return bar();}
(1)為了實驗目的,將 libfoo從 prelink map 中移除並加上 LOCAL_PRELINK_MODULE := false
# apriori --partial-global-prelink \ -L /system/lib \ libfoo.so --output prelinked/libfoo.so
(2) 接著加上新的參數--partial-global-prelink來進行 partial global prelink
prelink 實驗
libfoo
libc
int bar() { return 100;}
int foo () { printf(“foo”); return bar();}
# apriori --partial-global-prelink \ -L /system/lib \ libfoo.so --output prelinked/libfoo.so
# arm-eabi-objdump prelinked/libfoo.so -R
libfoo.so: file format elf32-littlearm
DYNAMIC RELOCATION RECORDS00000424 R_ARM_RELATIVE *ABS*0000100c R_ARM_RELATIVE *ABS*
來自 libc 符號的 Relocation 都被 prelink 處理掉
原因是 libc 進行 prelink 後,符號的位址都已固定且已知,因此即使 libfoo沒有分配 base address ,也可處理掉部份的 prelink
此機制命名為 partial global prelink
prelink家族效能評比
針對 JNI 量測效能的機械錶
Aprof : Android Profiler
• 針對 JNI部份客製化
• 類似常見的 gprof工具– Sample based– Instrumentation based
Aprof : Android Profiler
Aprof : Android Profiler
• Sample based– 定期查看程式跑到哪
• Instrumentation based– 每次函數被呼叫時會紀錄
Aprof : Android Profiler % cumulative self self total time time time calls ms/call ms/call name 99.52 2170 2140 2178309 0 0 fib 0.00 2170 0 1 0 217 main 0.32 0 20 0 0 0 write 0.16 0 10 0 0 0 memcpy
Call graph (explanation follows)
-------------------------------------------------------------Image : fooCumulative time : 2170 msSelf time : 2140 ms Function % time cumulative self Count Call by fib 2170 2140 100.00 2170 0 1 main 100.00 2170 2140 2178308 fib main 2170 0 100.00 2170 0 1 __libc_init-------------------------------------------------------------Image : fooCumulative time : 2170 msSelf time : 30 ms write 0 20 memcpy 0 10
Aprof
具備 S/L 大法的時間魔法陣
Checkpoint
Checkpoint
保存使用狀態 !
Checkpoint
之後直接從上次保存的狀態回覆
現有 Checkpoint 機制
• CryoPID– http://cryopid.berlios.de/
• BLCR– https://ftg.lbl.gov/projects/CheckpointRestart/
• DMTCP– http://dmtcp.sourceforge.net/
DMTCP
• 支援大部分 OS功能– 檔案、網路、 shared memory, SysV IPC...
• 純 user-space checkpoint 實作
• 架構良好易於擴充且開發活耀 !– based on C++
DMTCP for Android
• 預計可有效減少開機時間• 但 Android 上有許多特殊的機制
– Logger, binder, ashmem, wakelock...
• 目前尚在進行中
(4) checkpoint
(1) GNU-stylehash in linker
(3) Aprof(2) partial global prelink
Android Native Runetime 成長計畫
http://0xlab.org