關鍵詞:Android Framework 動態庫 動態鏈接 Binder 1、事件起因 Android Studio一次更新後發現install App,設備就重啟了,跑了一遍開機動畫但不是從開機第一屏開始重啟,tombstones內容查看發現是surfaceflinger掛在libbinder. ...
關鍵詞:Android Framework 動態庫 動態鏈接 Binder
1、事件起因
Android Studio一次更新後發現install App,設備就重啟了,跑了一遍開機動畫但不是從開機第一屏開始重啟,tombstones內容查看發現是surfaceflinger
掛在libbinder.so
,那install app做了什麼這個不得而知,理論上有問題應該掛的是PackageManagerService。先不管Android Studio的事情,雖然掛在Binder的庫里,還是首先懷疑問題出在surfaceflinger
的Binder使用邏輯。
2、原因分析
(以下分析使用RockPi4B還原現場)
tombstone文件如下
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'rockchip/rk3399_ROCKPI4B_Android11/rk3399_ROCKPI4B_Android11:11/RQ3A.210705.001/eng.kryo.20240128.131540:userdebug/release-keys'
Revision: '0'
ABI: 'arm64'
Timestamp: 2024-01-28 23:42:32+0800
pid: 11263, tid: 11289, name: Binder:11263_2 >>> /system/bin/surfaceflinger <<<
uid: 1000
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x791c0e6cd0
x0 b4000079ce312fd8 x1 000000005f444d50 x2 000000791d20c9c0 x3 000000791d20c940
x4 0000000000000010 x5 0000000000000018 x6 b400007a7e312e50 x7 b4000079ce312fd8
x8 000000791c0e6c50 x9 0000000010000000 x10 0000000000000001 x11 0000000000000002
x12 0000000000000000 x13 0000007baff5e020 x14 0001096dce96e5c4 x15 0000000029aaaaf0
x16 0000007bafd6c420 x17 0000007bafd29e30 x18 000000791c592000 x19 000000791d20c940
x20 0000000000000010 x21 000000791d20c9c0 x22 b4000079ce312fd8 x23 000000005f444d50
x24 000000791d20d000 x25 0000000000000000 x26 ffffffff000003e8 x27 b400007a7e313014
x28 0000000000000000 x29 000000791d20c8d0
lr 0000007bafd167bc sp 000000791d20c8c0 pc 0000007bafd1685c pst 0000000080000000
backtrace:
#00 pc 000000000004985c /system/lib64/libbinder.so (android::BBinder::transact(unsigned int, android::Parcel const&, android::Parcel*, unsigned int)+228) (BuildId: d5e42e998e9031430bee87f595521231)
#01 pc 00000000000524a8 /system/lib64/libbinder.so (android::IPCThreadState::executeCommand(int)+1032) (BuildId: d5e42e998e9031430bee87f595521231)
#02 pc 0000000000051fec /system/lib64/libbinder.so (android::IPCThreadState::getAndExecuteCommand()+156) (BuildId: d5e42e998e9031430bee87f595521231)
#03 pc 000000000005282c /system/lib64/libbinder.so (android::IPCThreadState::joinThreadPool(bool)+60) (BuildId: d5e42e998e9031430bee87f595521231)
#04 pc 0000000000078e10 /system/lib64/libbinder.so (android::PoolThread::threadLoop()+24) (BuildId: d5e42e998e9031430bee87f595521231)
#05 pc 000000000001567c /system/lib64/libutils.so (android::Thread::_threadLoop(void*)+260) (BuildId: c081ab14bd4aef44c9c459d77d8c9b48)
#06 pc 0000000000014f14 /system/lib64/libutils.so (thread_data_t::trampoline(thread_data_t const*)+412) (BuildId: c081ab14bd4aef44c9c459d77d8c9b48)
#07 pc 00000000000b0c08 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+64) (BuildId: 0a481e8df134382e9d3effff2fce8b74)
#08 pc 00000000000505d0 /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64) (BuildId: 0a481e8df134382e9d3effff2fce8b74)
使用addr2line工具查看代碼奔潰處源碼,aosp編譯時會在out目錄下生成symbols目錄,會額外保存一份帶符號的so文件方便回溯問題,把崩潰處的地址4985c
傳入
aarch64-linux-android-addr2line -e out/target/product/rk3399_ROCKPI4B_Android11/symbols/system/lib64/libbinder.so 4985c
#frameworks/native/libs/binder/Binder.cpp:188
定位到Binder.cpp 188行處的源碼:
status_t BBinder::transact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
data.setDataPosition(0);
status_t err = NO_ERROR;
switch (code) {
case PING_TRANSACTION:
err = pingBinder();
break;
case EXTENSION_TRANSACTION:
err = reply->writeStrongBinder(getExtension());
break;
case DEBUG_PID_TRANSACTION:
err = reply->writeInt32(getDebugPid());
break;
default:
err = onTransact(code, data, reply, flags); // 188行處
break;
}
//... ...
}
還是目前很難看出崩潰原因,tombstone給出的是SEGV_MAPERR錯誤,推測與訪存有關,再結合objdump -S 查看libbinder.so的反彙編:
... ...
49840: 1400001a b 498a8 <_ZN7android7BBinder8transactEjRKNS_6ParcelEPS1_j@@Base+0x130>
49844: f94002c8 ldr x8, [x22]
49848: aa1603e0 mov x0, x22
4984c: 2a1703e1 mov w1, w23
49850: aa1503e2 mov x2, x21
49854: aa1303e3 mov x3, x19
49858: 2a1403e4 mov w4, w20
4985c: f9404108 ldr x8, [x8,#128]
49860: d63f0100 blr x8
... ...
可以看到4985c
處的彙編指令是通過x8寄存器偏移+128位元組進行訪存(寄存器相對定址) x8內容0x791c0e6c50加128正好是出錯記憶體地址0x791c0e6cd0,取得的記憶體結果再存回x8
tombstone也會把進程的pmap信息列印,由於地址空間佈局隨機化(ASLR)機制,可能每次運行結果的地址都不一樣:
memory map (836 entries): (fault address prefixed with --->)
... ...
00000079'1afe2000-00000079'1bc05fff --- 0 c24000
00000079'1bc06000-00000079'1bc07fff rw- 0 2000
00000079'1bc08000-00000079'1bfe1fff --- 0 3da000
00000079'1bfe2000-00000079'1bfe2fff --- 0 1000
00000079'1bfe3000-00000079'1c0defff rw- 0 fc000 [anon:stack_and_tls:11290]
00000079'1c0df000-00000079'1c0dffff --- 0 1000
--->Fault address falls at 00000079'1c0e6cd0 between mapped regions
00000079'1c113000-00000079'1c591fff --- 0 47f000
00000079'1c592000-00000079'1c593fff rw- 0 2000
00000079'1c594000-00000079'1d112fff --- 0 b7f000
00000079'1d113000-00000079'1d113fff --- 0 1000
... ...
出現錯誤的地址是個空洞,也就是出現空指針,通過對比彙編代碼和C++代碼可以得知x0x3(w0w3)正好是傳遞了4個參數,ldr x8, [x8,#128]
準備賦值給x8寄存器onTransact
函數類型的函數指針,然後blr x8
跳轉到x8執行子程式,說明沒有找到onTransact這個函數地址。
以上分析也不好判斷是前面這個x8寄存器本身出了問題,在運行過程中被意外修改,還是訪問這塊記憶體有毛病被修改了,但還是不要懷疑是binder庫本身的問題,binder是系統核心功能,有問題早就掛在其他地方了。好消息是這個問題是必現的,我們有足夠的試錯機會。
通過重新搜尋surfaceflinger相關代碼,發現MTK為其定製了一個庫libsurface_ext.so
,擴展了一些功能,並且實現了一個名為SurfaceExtService的binder服務添加道ServiceManager里:
extern "C" void createSurfaceExtService() {
const sp<IServiceManager> sm = defaultServiceManager();
if (sm == nullptr) {
LOGE("Can't get ServiceManager");
} else {
sp<IBinder> binder = sm->checkService(String16(SERVICE_NAME));
if (binder != nullptr) {
LOGW("SurfaceExtService added");
} else {
sp<SurfaceExtService> sfext = new SurfaceExtService();
sm->addService(String16(SERVICE_NAME), sfext, false);
LOGI("SurfaceExtService add");
}
}
}
通過巨集註釋編譯代碼,去掉SurfaceExtService服務,重新編譯push,再次通過Android Studio安裝應用,surfaceflinger不出現奔潰。這下問題能夠縮小到libsurface_ext.so
與binder
相關功能。
3、解決問題
反覆看了巨久的代碼,並不能找到問題,後面突發靈感,還原代碼重新編譯運行surfaceflinger,通過cat /proc/[pid]/maps |grep libsurface_ext
命令查看surfaceflinger的記憶體映射,發現libsurface_ext.so並不在記憶體映射中。
搜索代碼發現其動態載入so的位置:
SurfaceExtServiceHelper.cpp
//
// Created by kryo on 1/28/24.
//
#include <log/log.h>
#include <dlfcn.h>
void createSurfaceExtServiceProxy() {
void* soHandle = dlopen("libsurface_ext.so", RTLD_LAZY);
if (soHandle) {
void (*createSurfaceExtPtr)();
createSurfaceExtPtr = (decltype(createSurfaceExtPtr))(dlsym(soHandle, "createSurfaceExtService"));
if (NULL == createSurfaceExtPtr) {
dlclose(soHandle);
soHandle = nullptr;
ALOGE("createSurfaceExtService not found");
} else {
createSurfaceExtPtr();
dlclose(soHandle); // ?
ALOGD("createSurfaceExtPtr()");
}
} else {
soHandle = nullptr;
ALOGE("lib_surface_ext.so not found");
}
}
代碼邏輯dlopen載入libsurface_ext.so庫,然後使用dlsym搜索符號createSurfaceExtService
拿到函數指針並調用(看起來和Java反射有點類似),日誌createSurfaceExtPtr()也成功列印。
乍一看沒有問題,但是為啥maps裡面並沒有libsurface_ext.so,仔細一看createSurfaceExtPtr()調用後又調用了dlclose()
,去掉這個調用,重新編譯push,問題不再復現,之前繞了一大圈沒找到bug,原來被這塊給坑了。
回溯下之前的onTransact調用出錯的問題,x8寄存器訪問的記憶體所在的區間就是libsurface_ext.so載入的記憶體映射,後面dlclose又把so庫卸載了,回頭再看tombstone文件memory map段也沒有這個so
這個庫使用了binder,有重寫BBinder::onTransact方法:
status_t BBinder::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t /*flags*/);
推測進行Binder調用時通過vtable(虛表指針偏移128位元組)訪問該庫Binder對象找被重寫的onTransact函數指針,再跳轉到函數去執行,結果獲取函數指針時訪問的不存在,導致進程crash掉!
調用dumpsys
、service list
等可以觸發surfaceflinger的binder調用,均能復現該問題,應該也是Android Studio安裝app能復現的原因。
總結
對於代碼動態添加binder功能的so庫時,我們儘量保存打開的so句柄,併在所有業務結束後才能通過dlclose()關閉句柄,以防止因so庫的引用計數為0時被系統從記憶體中卸載,且對於surfaceflinger這種永不退出的進程來說沒有必要調用關閉,這個鍋要甩給MTK開發。
Reference
-
《程式員的自我修養:鏈接、裝載與庫》