在Android与iOS中使用LLDB调试Rust程序

Posted 唯鹿

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在Android与iOS中使用LLDB调试Rust程序相关的知识,希望对你有一定的参考价值。

在Rust中通过println!()打印的日志信息在Xcode中可以显示,但是android Studio里不显示。所以Android可以使用android_logger实现日志输出。但是开发中仅使用打印日志的方式进行调试还是不够的,我们还需要debug调式。所以有了本篇的内容。

LLDB(Low Level Debugger)是新一代轻量级高性能调试器, 是XcodeAndroid Studio中的默认调试器。相较于Android开发同学,ios同学更熟悉它。Rust提供了基于LLDB的调试工具链,所以我们就可以借此用来调试Rust程序。

准备工作

首先建议你先阅读Rust库交叉编译以及在Android与iOS中使用这篇内容,了解如何将Rust程序集成进Android或iOS。本篇的例子也是使用此篇的demo。

不论是Android还是iOS,我们本篇的内容都使用CodeLLDB插件进行调试,所以你必须有安装 VS Code和此插件。

Android

具体操作

注意Android项目中我们需要将调试的so文件配置为不压缩优化,同时注意使用debug生成的so文件。

android 
	...

	packagingOptions
    	doNotStrip "**/libxxx.so"
	


1.push lldb-server到手机

adb push lldb-server /data/local/tmp/

如果之前你的设备有调式过JNI代码,那么此文件就会存在/data/local/tmp/目录下,可以忽略这一步。同理,如果你没有这个文件,可以新建一个Native C++项目调式一下。

或者去ndk中寻找lldb-server文件:

find . -name lldb-server


比如我的手机64位的, 所以选择aarch64下的。然后pushlldb-server到手机。

2.启动App,然后获取pid

# 启动App
adb shell am start -a android.intent.action.MAIN -n <package-name>/.<activity-name>
# 获取pid
adb shell pidof <package-name>

3.启动lldb-server

adb shell /data/local/tmp/lldb-server p --server --listen "*:9876"

4.rust项目debug配置

launch.json配置如下,作用是attch到目标进程。


    "version": "0.2.0",
    "configurations": [
        
            "type": "lldb",
            "request": "attach",
            "name": "Android",
            "pid": "xxx",
            "initCommands": [
                "platform select remote-android",
                "platform connect connect://localhost:9876",
                "file target/aarch64-linux-android/debug/libxxx.so",
            ]
        
    ]

  • pid 按照前面步骤获取的填入。
  • target/aarch64-linux-android/debug/librust_demo.so为调试二进制文件位置,保持与Android项目中的文件一致。

其他部分可以不用修改。然后Debug运行就可以打断点进行调式了。

问题

以上其实只适合root设备,否则执行attach xxx时会提示无权限。所以一开始我是使用的模拟器,然后获取root权限进行调试的。

后面看到了Android基础开发实践:如何分析Native Crash这篇gdb调试部分,找到了解决思路。

我们用Android Studio的lldb调试器进行native调试时有如下的输出:

从上面可以看出,Android Studio通过cat输出lldb-server并run-as以应用的权限执行cat进行接收,然后将lldb-server写入到app的私有数据目录,紧接着chmod 700增加可执行权限。然后使用同样的方式将一个shell脚本start_lldb_server.sh发送到app数据目录。最后以app的权限运行脚本启动lldb。

其实这个问题仔细想想,为啥as可以不用root就能调试,我们不行。所以就是抄as的作业:

adb shell "cat /data/local/tmp/lldb-server 
| run-as <package-name> sh -c 'cat > /data/data/<package-name>/lldb/bin/lldb-server 
&& chmod 700 /data/data/<package-name>/lldb/bin/lldb-server'"

# 启动lldb-server
adb shell run-as <package-name> ./lldb/bin/lldb-server p --server --listen "*:9876"

如果启动时,报Operation not permitted错误。可以使用unix-abstract方式,start_lldb_server.sh中就是此种方法实现。

adb shell run-as <package-name> ./lldb/bin/lldb-server p --server --listen unix-abstract:///<package-name>/debug.sock

相应的launch.json中的platform connect connect://localhost:9876替换为
platform connect unix-abstract-connect:///<package-name>/debug.sock

当然<package-name>/debug.sock名字随你怎么定义,这里只是给了一个建议格式,避免冲突。

优化

在第四步中,我们需要每次手动替换pid,这还是比较麻烦的。我们可以优化一下这里,动态获取pid。

创建tasks.json文件,输入以下内容:


    "version": "2.0.0",
    "tasks": [
        
            "label": "get pid",
            "type": "shell",
            "command": "adb shell pidof <package-name> | tr -d '\\n' > $workspaceRoot/pid.txt"
        
    ]

作用是获取pid,然后写入到pid.txt文件中。

launch.json修改如下:


    "version": "0.2.0",
    "configurations": [
        
            "type": "lldb",
            "request": "attach",
            "name": "Attach",
            "pid": "$input:pid",
            "preLaunchTask": "get pid",
            "initCommands": [
                "platform select remote-android",
                "platform connect connect://localhost:9876",
                "file target/aarch64-linux-android/debug/libxxx.so",
            ]
        
    ],
    "inputs": [
        
            "id": "pid",
            "type": "command",
            "command": "extension.commandvariable.file.content",
            "args": 
                "fileName": "$workspaceFolder/pid.txt"
            
        
    ]

运行时,读取pid.txt文件内容作为pid。

  • 注意这里使用extension.commandvariable.file.content需要vs code安装Command Variable插件。
  • 目前这种方式的问题是会先读取文件中的pid,早于get pid的执行。所以需要运行第二次才可以拿到正确的pid。但由于pid只要app不杀死就不会变动,所以问题不大。

Android虽然可以通过上述方法实现调试Rust代码,但毕竟没有as直接支持来的方便。目前来说,聊胜于无。

iOS

iOS整体比Android简单方便很多,首先和Android一样需要用debug静态库包(cargo lipo命令)。然后在 Frameworks,Libraries,and Embedded ContentLibrary Search Paths 配置libxxx.a

我用的模拟器,所以这里选择x86_64-apple-ios文件夹下的。

launch.json配置如下:


    "version": "0.2.0",
    "configurations": [
        
            "type": "lldb",
            "request": "attach",
            "name": "iOS",
            "program": "RustDemo",
        ,
    ]

这里是根据App名称连接的,RustDemo就是这个的demo的名称。

启动App,然后debug就好了。

是不是非常简单。

参考

以上是关于在Android与iOS中使用LLDB调试Rust程序的主要内容,如果未能解决你的问题,请参考以下文章

iOS调试奇巧淫技之LLDB

iOS逆向工程之使用LLDB的USB连接调试第三方App

ubuntu18.04上使用LLDB调试Chromium_Android

ios逆向过程中lldb调试技巧-po篇

windows下用vscode调试rust

IOS调试lldb命令常用po