Contenu connexe Similaire à Android C Library: Bionic 成長計畫 (20) Android C Library: Bionic 成長計畫1. Android C Library:
Bionic 成長計畫
Kito Cheng ( 程皇嘉 )
Developer, 0xlab
kito@0xlab.org
April 14, 2012 / OSDC
2. Rights to copy
© Copyright 2012 0xlab
http://0xlab.org/
contact@0xlab.org
Attribution – ShareAlike 3.0
Corrections, suggestions, contributions
You are free and translations are welcome!
to copy, distribute, display, and perform the work
to make derivative works Latest update: Mar 21, 2012
to make commercial use of the work
Under 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
7. 可解讀 GNU-
style
成長計畫 !
hash 的帥氣墨鏡
具備 S/L 大法的
時間魔法陣
針對 JNI 量測效
能 強化版 8-bit
的機械錶 prelink map
11. 程式啟動流程
執行檔讀入記憶體
libc/libm/
libdvm... 在更新符號的時候需要大量的符號查找操
載入相關動態函式庫
更新符號位址
(Relocation)
printf, sin, cos, ...
開始執行 !
13. 程式載入範例
strtoumax foo
statfs bar
wcsncmp ogc
dlmallopt orz
printf ...
... ox1ab
strtoul
getutent
libc 及 libfoo 各自提供這堆符號 ( 函數 /
libc libfoo
prog 使用到 libc 跟 libfoo 的東西
prog
14. 程式載入範例
strtoumax foo
statfs bar
wcsncmp ogc
dlmallopt orz
printf ...
... ox1ab
strtoul
getutent
libc 及 libfoo 各自提供這堆符號 ( 函數 /
libc libfoo
prog 使用到 libc 跟 libfoo 的東西
prog
假設 prog 只有使用到 printf, foo, bar 這 3 個函數
15. 程式載入範例
strtoumax foo
statfs bar
wcsncmp ogc
dlmallopt orz
printf ...
... ox1ab
strtoul
getutent
libc libfoo
只用到 3 個符號 ,
但在這個例子需要查找
5 次符號
prog
假設 prog 只有使用到 printf, foo, bar 這 3 個函數
16. 程式載入範例
strtoumax foo
statfs bar
wcsncmp ogc
dlmallopt orz
printf ...
... ox1ab
strtoul 查找符號次數 : O(n*m)
getutent n : 相依的函數庫數目
m : 符號數
libc libfoo
只用到 3 個符號 ,
但在這個例子需要查找
5 次符號
prog
假設 prog 只有使用到 printf, foo, bar 這 3 個函數
18. GNU-Style Hash
GNU-Style Hash
則是可以加速這個查詢過程的方法之一 !
原本 ELF 中就有採用 Hash
來加速查找速度 , 但不盡理想
19. GNU-Style Hash
elf 預設的 hash 方法
name 2sym collision # 3sym collision # 3+sym collision #
sysv 1749 5
libiberty 42
dcache 37 gnu-style hash 的方法
djb2 29
sdbm 39
djb3 31
rot 337 39 61
sax 34
fnv 40
oat 30
原本 ELF 中就有採用 Hash
來加速查找速度 , 但不盡理想
Experiment by Jakub Jelinek
http://cygwin.com/ml/libc-alpha/2006-06/msg00098.html
23. GNU-Style Hash
所以在 Android 使用則有些地方要注意 ...
某些傢伙自帶 Dynamic Linker 所以不支援
ex: Unity 3D, B2G...
因此必須要採用向後相容方案 , 新舊的 hash table
都要放 ,-Wl,--hash-style=both
但 gold 有 bug, 所以最好用 bfd 的 linker...
http://sourceware.org/bugzilla/show_bug.cgi?
id=13597
26. No prelink in ICS
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.
官方說法
Git log from
https://github.com/android/platform_build/commit/b375e71d306f2fd356b9b356b636e568c4581fa1
27. No prelink in ICS
硬體進步跟 Zygote 機制 , Prelink 加速跟沒有一樣
只省一丁點空間 , 現在記憶體大又便宜
Prelink 後 Address-Space-Layout
Randomization 效果會變弱
不是 GNU toolchain 的東西 ,
要維護很麻煩
官方說法 ( 白話版 )
Git log from
https://github.com/android/platform_build/commit/b375e71d306f2fd356b9b356b636e568c4581fa1
29. No prelink in ICS
但 prelink 對於開機時間還是有影響阿阿
事實上 Prelink 在 ICS 可以撿回來用
30. 程式啟動流程
執行檔讀入記憶體
libc/libm/
libdvm... 在更新符號的時候需要大量的符號查找操
載入相關動態函式庫
更新符號位址
(Relocation)
printf, sin, cos, ...
開始執行 !
31. 程式啟動流程
Prelink 就是把這邊要找的東西統統都先作掉
一勞永逸
執行檔讀入記憶體
libc/libm/
libdvm... 在更新符號的時候需要大量的符號查找操
載入相關動態函式庫
更新符號位址
(Relocation)
printf, sin, cos, ...
開始執行 !
32. int bar() {
return 100;
} 做個實驗
int foo () {
printf(“foo”);
return bar();
}
libc
libfoo
本實驗中假設已經有一個建好的且 prelink 過後的 libc.so
33. int bar() {
return 100;
} 做個實驗
int foo () {
# arm-eabi-objdump libfoo.so -R
printf(“foo”);
return bar(); libfoo.so: file format elf32-
} littlearm
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
000010e8 R_ARM_JUMP_SLOT printf
libc 000010ec R_ARM_JUMP_SLOT __cxa_finalize
00000424 R_ARM_RELATIVE *ABS*
0000100c R_ARM_RELATIVE *ABS*
libfoo
objdump -R 可檢視 Relocation Record, libfoo.so 共有四個 R
34. int bar() {
return 100;
} 做個實驗
int foo () {
# arm-eabi-objdump libfoo.so -R
printf(“foo”);
return bar(); libfoo.so: file format elf32-
} littlearm
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
000010e8 R_ARM_JUMP_SLOT printf
libc 000010ec R_ARM_JUMP_SLOT __cxa_finalize
00000424 R_ARM_RELATIVE *ABS*
0000100c R_ARM_RELATIVE *ABS*
libfoo 一個 *ABS* 是 bar 的位址
另一個是系統用的
35. int bar() {
return 100;
} 做個實驗
int foo () {
# apriori --locals-only
printf(“foo”); --prelinkmap prelink-linux-arm.map
return bar(); libfoo.so --output prelinked/libfoo.so
}
libc
接著對 libfoo.so 進行 prelink
libfoo apriori 在 out/host/linux-x86/bin/
prelink map 在 build/core
--locals-only(-l) 指定進行 local prelink
36. int bar() {
return 100;
} 做個實驗
int foo () {
# apriori --locals-only
printf(“foo”); --prelinkmap prelink-linux-arm.map
return bar(); libfoo.so --output prelinked/libfoo.so
}
build/tools/apriori/prelinkmap.c(183):
libc library 'libfoo.so' not in prelink map
出現錯誤 , 抱怨在 prelink map 找不到 libfoo..
libfoo
37. Prelink Map
# core system libraries
libdl.so 0xAFF00000 # [<64K]
libc.so 0xAFD00000 # [~2M]
libstdc++.so 0xAFC00000 # [<64K]
libm.so 0xAFB00000 # [~1M]
liblog.so 0xAFA00000 # [<64K]
libcutils.so 預先分配每個動態函式庫的基底位址 (Base
0xAF900000 # [~1M]
libthread_db.so 0xAF800000 # [<64K]
libz.so 0xAF700000 # [~1M] 的函式庫都要加進來
要 Prelink
libevent.so 0xAF600000 # [???]
libssl.so 0xAF400000 # [~2M]
libcrypto.so 0xAF000000 # [~4M]
libsysutils.so 0xAEF00000 # [~1M]
# bluetooth
liba2dp.so 0xAEE00000 # [~1M]
audio.so 0xAED00000 # [~1M]
input.so 0xAEC00000 # [~1M]
…
liblept.so 0x9CA00000 # [~5M]
38. Prelink Map
# core system libraries
libdl.so 0xAFF00000 # [<64K]
libc.so 0xAFD00000 # [~2M]
libstdc++.so 0xAFC00000 # [<64K]
libm.so 0xAFB00000 # [~1M]
liblog.so 0xAFA00000 # [<64K]
libcutils.so 預先分配每個動態函式庫的基底位址 (Base
0xAF900000 # [~1M]
libthread_db.so 0xAF800000 # [<64K]
libz.so 0xAF700000 # [~1M] 的函式庫都要加進來
要 Prelink
libevent.so 0xAF600000 # [???]
libssl.so 0xAF400000 # [~2M]
libcrypto.so 0xAF000000 # [~4M]
libsysutils.so 0xAEF00000 # [~1M]
# bluetooth 把 libfoo 加進來
liba2dp.so 0xAEE00000 # [~1M]
audio.so 0xAED00000 # [~1M]
input.so 0xAEC00000 # [~1M]
…
liblept.so 0x9CA00000 # [~5M]
libfoo 0x9C900000
39. int bar() {
return 100;
} 做個實驗
int foo () {
# apriori --locals-only
printf(“foo”); --prelinkmap prelink-linux-arm.map
return bar(); libfoo.so --output prelinked/libfoo.so
}
libc
加入 Prelink Map 後就可以順利進行 Prelink 了
libfoo
40. int bar() {
return 100;
} 做個實驗
int foo () {
# arm-eabi-objdump prelinked/libfoo.so -R
printf(“foo”);
return bar(); libfoo.so: file format elf32-littlearm
}
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
000010e8 R_ARM_JUMP_SLOT printf
000010ec R_ARM_JUMP_SLOT __cxa_finalize
libc
可以發現只剩下兩個 Relocation 了 !
libfoo
41. int bar() {
return 100;
} 做個實驗
int foo () {
# arm-eabi-objdump prelinked/libfoo.so -R
printf(“foo”);
return bar(); libfoo.so: file format elf32-littlearm
}
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
000010e8 R_ARM_JUMP_SLOT printf
000010ec R_ARM_JUMP_SLOT __cxa_finalize
libc
oo.so 的 Base Address 在 prelink map 中固定 , 而事實上處理 Relocatio
libfoo
42. int bar() {
return 100;
} 做個實驗
int foo () {
# apriori --locals-only
printf(“foo”); --prelinkmap prelink-linux-arm.map
return bar(); libfoo.so --output prelinked/libfoo.so
}
libc
回顧剛才的參數有個— locals-only,
通常有 local 就有相對的 global
libfoo
43. int bar() {
return 100;
} 做個實驗
int foo () {
# apriori
printf(“foo”); --prelinkmap prelink-linux-arm.map
return bar(); libfoo.so --output prelinked/libfoo.so
}
build/tools/apriori/main.c(195): There
are command-line-option errors.
libc
但直接拔掉該參數會跟你抱怨參數錯誤 ...
libfoo
44. int bar() {
return 100;
} 做個實驗
int foo () {
# apriori
printf(“foo”); --prelinkmap prelink-linux-arm.map
return bar(); libfoo.so --output prelinked/libfoo.so
}
build/tools/apriori/main.c(195): There
are command-line-option errors.
libc
不明原因在 apriori 的 global prelink 的部份程式沒有完成 , 但
libfoo
45. int bar() {
return 100;
} 做個實驗
int foo () {
# apriori
printf(“foo”); --prelinkmap prelink-linux-arm.map
return bar(); libfoo.so --output prelinked/libfoo.so
}
libc
所以接下來的實驗請記得先去 0xdroid 撿 patch
散落在下面三個地方 :
libfoo build/
external/elfutils/
external/elfcopy/
46. int bar() {
return 100;
} 做個實驗
int foo () {
# apriori --inc-global-prelink
printf(“foo”); --prelinkmap prelink-linux-arm.map
return bar(); -L /system/lib
} libfoo.so --output prelinked/libfoo.so
libc
patch 打上後把參數加上
--inc-global-prelink
-L < 其他函數庫的路徑 >
libfoo 就會開始進行 Incremental Global Prelink!
47. int bar() {
return 100;
} 做個實驗
int foo () {
# apriori --inc-global-prelink
printf(“foo”); --prelinkmap prelink-linux-arm.map
return bar(); -L /system/lib
} libfoo.so --output prelinked/libfoo.so
# arm-eabi-objdump prelinked/libfoo.so -R
libc libfoo.so: file format elf32-littlearm
DYNAMIC RELOCATION RECORDS (none)
Incremental
libfoo Global Prelink 完後就沒有任何 Relocation 要處
Local Prelink 再去處理 Global Prelink 的漸進方式 , 故命名之 , 因此輸
48. int bar() {
return 100;
} 做個實驗
int foo () {
# apriori --inc-global-prelink
printf(“foo”); --prelinkmap prelink-linux-arm.map
return bar(); -L /system/lib
} libfoo.so --output prelinked/libfoo.so
# arm-eabi-objdump prelinked/libfoo.so -R
libc libfoo.so: file format elf32-littlearm
DYNAMIC RELOCATION RECORDS (none)
nk 的過程中 , 會試著搜索外部的符號 , 如果找的到且該函式庫也 Prelink 過 ,
libfoo
49. int bar() {
return 100;
} 做個實驗
int foo () {
# apriori --inc-global-prelink
printf(“foo”); --prelinkmap prelink-linux-arm.map
return bar(); -L /system/lib
} libfoo.so --output prelinked/libfoo.so
# arm-eabi-objdump prelinked/libfoo.so -R
libc libfoo.so: file format elf32-littlearm
DYNAMIC RELOCATION RECORDS (none)
nk 的過程中 , 會試著搜索外部的符號 , 如果找的到且該函式庫也 Prelink 過 ,
libfoo
以發現 , 不在 Prelink Map 的函式庫事實上也是可以處理掉部份的 Relocation!
50. int bar() {
return 100;
} 做個實驗
int foo () {
printf(“foo”);
return bar();
}
libc
libfoo
現在為了實驗目的將 libfoo 從 prelink map 中驅逐出境
並且加上 LOCAL_PRELINK_MODULE := false
來建置
51. int bar() {
return 100;
} 做個實驗
int foo () {
# apriori --partial-global-prelink
printf(“foo”); -L /system/lib
return bar(); libfoo.so --output prelinked/libfoo.so
}
libc
接著加上新的參數 --partial-global-prelink 來進行 Partial Glo
libfoo
52. int bar() {
return 100;
} 做個實驗
int foo () {
# apriori --partial-global-prelink
printf(“foo”); -L /system/lib
return bar(); libfoo.so --output prelinked/libfoo.so
}
# arm-eabi-objdump prelinked/libfoo.so -R
libc libfoo.so: file format elf32-littlearm
DYNAMIC RELOCATION RECORDS
00000424 R_ARM_RELATIVE *ABS*
0000100c R_ARM_RELATIVE *ABS*
libfoo
來自 libc 符號的 Relocation 都 Prelink 掉了 !
53. int bar() {
return 100;
} 做個實驗
int foo () {
# apriori --partial-global-prelink
printf(“foo”); -L /system/lib
return bar(); libfoo.so --output prelinked/libfoo.so
}
# arm-eabi-objdump prelinked/libfoo.so -R
libc libfoo.so: file format elf32-littlearm
DYNAMIC RELOCATION RECORDS
00000424 R_ARM_RELATIVE *ABS*
0000100c R_ARM_RELATIVE *ABS*
libfoo
進行 Prelink 後符號的位址都已經固定且已知 , 因此即使 libfoo 沒有分配 Base
54. int bar() {
return 100;
} 做個實驗
int foo () {
# apriori --partial-global-prelink
printf(“foo”); -L /system/lib
return bar(); libfoo.so --output prelinked/libfoo.so
}
# arm-eabi-objdump prelinked/libfoo.so -R
libc libfoo.so: file format elf32-littlearm
DYNAMIC RELOCATION RECORDS
00000424 R_ARM_RELATIVE *ABS*
0000100c R_ARM_RELATIVE *ABS*
libfoo
進行 Prelink 後符號的位址都已經固定且已知 , 因此即使 libfoo 沒有分配 Base
這個機制命名為 Partial Global Prelink
58. Aprof : Android Profiler
針對 JNI 部份客製化
類似於常見的 gprof 工具
Sample Based
Instrumentation Based
60. Aprof : Android Profiler
Sample Based
定期查看程式跑到哪
Instrumentation Based
每次函數被呼叫時會紀錄
61. 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 : foo
Cumulative time : 2170 ms
Self 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 : foo
Cumulative time : 2170 ms
Self time : 30 ms
write 0 20
memcpy 0 10
67. 現存的 Checkpoint 工具
CryoPID
http://cryopid.berlios.de/
BLCR
https://ftg.lbl.gov/projects/Checkpoint
Restart/
DMTCP
http://dmtcp.sourceforge.net/
68. DMTCP
支援大部分 OS 功能
檔案 , 網路 , Shared Memory, SysV IPC...
純 user-space checkpoint 實作
架構良好易於擴充且開發活耀 !
C++-based