android 調試 native 程序的方法


一、背景

 

首先說需求,這個需求非常常見,就是android上需要的一個功能,linux已經有開源代碼而且非常穩定,希望能直接porting過去使用,這個程序是pure c 的代碼,也就是說,跟android framework, java 沒關系,也跟jni沒有關系,我們希望的就是能編譯成一個可執行工具,push到android后能直接跑起來使用。

 

既然是native 代碼,主要影響到的就是bionic libc, 因為android的這個基礎庫跟 linux 上常見的 gnu libc 有區別,主要是被精簡了,至於更具體理解什么叫被精簡,建議找個linux 開源工具代碼在 android ndk 環境里編一下就知道了,其實,這里邊還隱藏另外兩層區別,一個是 android api level, 就是所謂的android 版本,比如 android 4.4.4 對應 android API 19, android 5 對應 android API 21 , 不同版本的 bionic libc 本身也是有區別的。 另一個是 abi , 就是體系架構的區別,比如 x86, x86_64, armeabi -v7a 等等,不同 abi 設置下 bionic libc 用到的接口也不一樣。上面這兩個影響會導致,你的代碼可能在 API 21 編譯通過了,但是在 API 19 失敗,或者,在 armeabi-v7a 成功了,但是在 arm64-v8a 失敗了。這兩點的處理放在另外的文章說明,這篇文章不考慮。可以認為,我們暫時不考慮編譯出來的工具的通用性,只在特定的目標,如 abi = armeabi-v7a, api = 21 的android devices 上運行。

 

好了,我們已經編成功了這個工具,push 到目標設備后(或模擬器)發現運行出現段錯誤,這是非常常見的,因為在移植過程中,我們把某些 glibc 的調用替換成了 bionic 的調用,接下去應該怎么調試呢?首先說這里的調試,當然是源碼級別的單步調試,就是我們在 linux 上開發並調試程序一樣。這個要求看似簡單,但我搜索了一天的時間,嘗試了多種方式,最后才找到一種簡單可用且匹配需求的方式。

 

二、方法

 

首先,嘗試搜索 gdb-for-arm 的工具,就是看有沒有一個 gdb 可執行程序,是可以直接在 android arm devices 上運行的,如果有的話,就跟直接在 linux 環境里調試一樣了,可惜,這么簡單的需求,都搜索不到

 

下面都是嘗試用遠程調試的方式

 

第一種,客戶端在win7. 使用 ida pro 6.6 , 它提供了一個  android_server 可以用來push到 android 系統里運行,然后在 ida 的界面里 run remote 可執行程序,這種方式可以跑起來,可以單步執行、可以下斷點,但是無法加載正確的符號表。  

第二種,客戶端在win7,找了一個 http://gnutoolchains.com/android/  為android 編譯好的 gnu tool chains 網站,下載 gdbserver 和 gdb.exe , gdbserver push到目標機器跑起來,本地用 gdb.exe 啟動,使用gdb 遠程調試那套步驟進行設置,發現也是能夠跑起來,還是無法正確加載符號表。 

第三種,客戶在在win7,使用 Cygwin 的方式跑gdb客戶端,啟動之后 cygwin 報了一個長度太長的問題,沒有深入去解決

 

從這里開始,突然對在win7 下啟動 gdb 客戶端有些失望,隨即改為在  ubuntu14.04 里啟動 gdb 客戶端, android 模擬器也在 ubuntu 里啟動。這里的 ubuntu 跑在 win7 上的 vmware 里。具體如果在 ubutnu 里下載並配置 android sdk 開發環境,並創建 avd , 啟動 avd, 可以從網上搜索,最終就是在 ubuntu 里正常啟動一個 android emulator. 

 

然后,就使用下面的方式,就是可以源代碼單步調試了:

 

1. 在設備上部署gdbserver
所謂部署其實就是把android ndk下的gdbserver拷貝到設備上,可以通過如下命令
adb push $ANDROID_NDK_ROOT/prebuilt/android-arm/gdbserver/gdbserver /data
 
2. 在設備上部署你的Native程序
需要把你編譯出的程序和相關so庫部署到設備上,注意so庫要放在/system/lib下,/system路徑默認是只讀的,可以通過adb remount來重置。
adb push ./myapp /data/data
adb push ./libmylib.so /system/lib
 
3. 把設備上的相關調試環境拷貝到本地
因為遠程調試需要一些目標機的庫,把如下文件拷貝到本地文件夾
adb pull /system/lib ./debugging/lib
adb pull /system/bin/linker ./debugging/lib
 
4. 在設備上通過gdbserver運行你的程序
adb shell gdbserver :12345 /data/data/myapp
 
5. 在本地把本地TCP端口forward到設備的TCP端口
adb forward tcp:12345 tcp:12345
 
6. 在本地運行Android ndk路徑下的gdb程序
$ANDROID_NDK/toolchains/arm-linux-androideabi-4.4.3/prebuild/darwin-x86/bin/arm-linux-androideabi-gdb
這里注意如果你在Ubuntu下用的是Linux的NDK包,那路徑會有點不同,darwin-x86的地方應該是linux-x86.最保險的還是自己在NDK下搜索。注意,必須是啟動arm gdb, 如果是啟動linux系統默認的 gdb (x86,x64 架構), 可能執行 “target remote :12345” 會報錯 “Remote 'g' packet reply is too long:”
 
7. 啟動gdb后在gdb下設置solib搜索路徑
就是讓gdb運行時能夠找到調試相關的那些lib,也就是那些第三步中從設備上拉下來的文件。
(gdb) set solib-search-path ./debugging/lib
 
8. 在gdb下設置你希望調試的Native程序
(gdb) file ./myapp
 
9. 連接到設備的gdbserver
(gdb) target remote :12345
上面的6~9步也可以通過如下命令一步執行完
$ANDROID_NDK/toolchains/arm-linux-androideabi-4.4.3/prebuilt/darwin-x86/bin/arm-linux-androideabi-gdb --eval-command="set solib-search-path ./debugging/lib"  --eval-command="file  ./myapp" --eval-command="target remote :12345"
 
10. 開始調試
通過continue或c運行程序。注意不是用run,因為程序在目標機上其實已經啟動了,只是break在程序入口。
 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM