• 使用gdb调试Android(aarch 64)可执行二进制文件


    Android官方提供了调试ndk进行

    使用场景

    在pc端编写Android可执行程序(注意不是App进程程序,这里指的是一个可执行文件比如linux的ELF文件、windows exe文件)

    现在市面上基本是都是aarch64位的手机也就是arm64-v8a架构的cpu手机,当然,也可以通过adb shell 'cat /proc/cpuinfo'进行查看

    电脑手机模拟器的架构一般是x86架构,下面的教程要按你的架构去选择对应的文件

    Main

    Android是魔改的linux但是一些shell解释器是通用的,且核心架构还是linux内核,我们可以通过C语言编写Android下的可执行文件,首先如下文件:

    demo.c

    #include
    int main(){
      printf("hello \n");
      return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在windows下可以通过软件一键运行、在Linux下可以gcc demo.c -o demo、Mac clang demo.c -o demo 那么Android正常情况下是没有终端提供的,就需要电脑进行操作,clang+llvm可以实现进行多架构程序,当然这里不讲太深,在PC端下载Android-NDK就可以得到一个NDK工具包一级目录如下:

    21.0.6113669 » ls                     ~/Library/Android/sdk/ndk/21.0.6113669
    CHANGELOG.md      ndk-build         prebuilt          sysroot
    NOTICE            ndk-gdb           python-packages   toolchains
    NOTICE.toolchain  ndk-stack         shader-tools      wrap.sh
    README.md         ndk-which         simpleperf
    build             package.xml       source.properties
    meta              platforms         sources
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    方便起见讲这个目录添加环境变量

    export PATH=$PATH:~/Library/Android/sdk/ndk/21.0.6113669
    
    • 1

    然后在demo.c的同级目录下编写配置文件(也可不同级,但是需要制定路径,这里为了方便),名字不能错Android.mk Application.mk 具体的属性配置可以去官网配合着看

    $  cat Android.mk                                            ~/home/work
    
    # 一个Android.mk file首先必须定义好LOCAL_PATH变量。
    # 它用于在开发树中查找源文件。在这个例子中,宏函数’my-dir’,
    # 由编译系统提供,用于返回当前路径(即包含Android.mk file文件的目录)。
    LOCAL_PATH := $(call my-dir)
    # CLEAR_VARS由编译系统提供,
    # 指定让GNU MAKEFILE为你清除许多LOCAL_XXX变量(例如 LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES, 等等...),除LOCAL_PATH 。这是必要的,
    # 因为所有的编译控制文件都在同一个GNU MAKE执行环境中,所有的变量都是全局的。
    include $(CLEAR_VARS)
    # LOCAL_MODULE变量必须定义,以标识你在Android.mk文件中描述的每个模块。名称必须
    是唯一的,而且不包含任何空格。
    # 注意编译系统会自动产生合适的前缀和后缀,换句话说,一个被命名为'foo'的共享库模块,将会生成'libfoo.so'文件。
    LOCAL_MODULE    := demo
    # LOCAL_SRC_FILES变量必须包含将要编译打包进模块中的C或C++源代码文件。注意,你不用在这里列出头文件和包含文件,
    # 因为编译系统将会自动为你找出依赖型的文件;仅仅列出直接传递给编译器的源代码文
    件就好。
    LOCAL_SRC_FILES := demo.c
    # BUILD_EXECUTABLE 表示以一个可执行程序的方式进行编译
    # BUILD_SHARED_LIBRARY 表示动态链接库的方式进行编译
    include $(BUILD_EXECUTABLE)
    
    $  cat Application.mk                                        ~/home/work
    APP_ABI := armeabi-v7a arm64-v8a  #这里是指定编译后的架构种类,如果全都要可以选择 all
    APP_OPTIM := debug
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    然后就可以通过ndk-build进行编译了它是基于cmake的有兴趣可以了解,非常强大的工具,在使用ndk-build时需要对它指定编译环境参数,这里我封装成脚本:

    $ cat load.sh
    #!/bin/zsh
    
    ndk-build NDK_PROJECT_PATH=. NDK_APPLICATION_MK=Application.mk APP_BUILD_SCRIPT=Android.mk  NDK_DEBUG=1 NDK_HOST_32BIT=1  #32bit
    
    #adb push ./obj/local/arm64-v8a/$1 /data/local/tmp/
    adb push ./obj/local/armeabi-v7a/$1 /data/local/tmp/
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    编译完成后会在当前目录下生成一个libsobj目录如下:

    $  ls                                                        ~/home/work
    Android.mk     demo.c         libs           obj
    Application.mk gdb.sh         load.sh
    
    • 1
    • 2
    • 3

    libs

    libs
    ├── arm64-v8a
    │   └── demo
    └── armeabi-v7a
        └── demo
    
    • 1
    • 2
    • 3
    • 4
    • 5

    生成的就是指定的架构可执行文件, release version

    obj

    obj
    └── local
        ├── arm64-v8a
        │   ├── demo
        │   ├── objs
        │   │   └── demo
        │   │       ├── demo.o
        │   │       ├── demo.o.commands.json
        │   │       └── demo.o.d
        │   └── objs-debug
        │       └── demo
        │           ├── demo.o
        │           ├── demo.o.commands.json
        │           └── demo.o.d
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    对应的debug version具备符号表,可调试

    具体从File Size可知:

    $  du -sh libs/arm64-v8a/demo                                ~/home/work
    8.0K	libs/arm64-v8a/demo
    $  du -sh obj/local/arm64-v8a/demo                           ~/home/work
     12K	obj/local/arm64-v8a/demo
    
    • 1
    • 2
    • 3
    • 4

    参数

    #编译执行程序
    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    LOCAL_MODULE    := demo
    LOCAL_SRC_FILES := demo.c
    include $(BUILD_EXECUTABLE)
    #会在obj/local/arm64-v8a/生成demo程序
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    #编译动态链接库
    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    LOCAL_MODULE    := demo
    LOCAL_SRC_FILES := demo.c
    include $(BUILD_SHARED_LIBRARY)
    #会在obj/local/arm64-v8a/生成libdemo.so库
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    调试

    上面的脚本已经封装好了编译和推送到移动端在移动端则需要gdbserver与PC端进行通信,ndk工具集已经准备好了这个文件如下:

    21.0.6113669 » adb push ./prebuilt/android-arm64/gdbserver/gdbserver /data/local/tmp  
    
    • 1

    这里注意是push到手机的/data/local/tmp路径下,这个是本地临时数据目录所以是低权限的、在这里就可以成功给上gdbserver执行权限,然后通过开启服务端口的形式映射到网络层,命令如下:

    adb shell "su -c '/data/local/tmp/gdbserver :9090 demo'"
    #这里的:9090表示只要是同局域网都可以访问
    #demo表示需要调试的可执行二进制文件
    
    • 1
    • 2
    • 3

    然后同局域网下的PC端就可以进行附加了,运行脚本如下:

    $  cat gdb.sh                                                ~/home/work
    #!/bin/bash
    adb forward tcp:9090 tcp:9090  #转发局域网端口到本地
    gdb -ex "file ./obj/local/arm64-v8a/demo" \      #指定带有符号表的可执行文件
        -ex "set solib-search-path ./obj/local/arm64-v8a" \ #lib的路径,如果可执行文件依赖了lib就需要指定它
        -ex "target remote localhost:9090"  #连接远程9090端口
        #-ex "set architecture aarch64" \
        #-ex "set solib-absolute-prefix ./" \
        #-ex "set architecture aarch64:ilp32" \
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    一键脚本

    $ ls
    gdb.sh                load.sh               start.sh
    heif_decode_to_rgba.c png_to_yuv.c
    
    • 1
    • 2
    • 3

    目录下面应该有这些文件

    • gdb.sh:运行附加gdb远程调试脚本
    • load.sh:编译项目脚本
    • start.sh:一键启动脚本
    • xxx.c:源文件
    $ cat load.sh
    #!/bin/bash
    
    ndk-build NDK_PROJECT_PATH=. NDK_APPLICATION_MK=Application.mk APP_BUILD_SCRIPT=Android.mk  NDK_DEBUG=1 NDK_HOST_32BIT=1  #32bit
    
    #adb push ./obj/local/arm64-v8a/$1 /data/local/tmp/
    adb push ./obj/local/armeabi-v7a/$1 /data/local/tmp/
    
    #----------------------------------------------------------------------------------------------
    $ cat start.sh
    #!/bin/bash
    
    file="$1"  #Input Compile FileName
    
    rm -rf Android.mk Application.mk libs obj
    
    if [ ! -d "./Android.mk" ];then
        echo -e "LOCAL_PATH := \$(call my-dir)\ninclude \$(CLEAR_VARS)\nLOCAL_MODULE    := $file\nLOCAL_SRC_FILES := $file.c\ninclude \$(BUILD_EXECUTABLE)" > Android.mk
    fi
    if [ ! -d "./Application.mk" ];then
        echo -e "APP_ABI := armeabi-v7a arm64-v8a\nAPP_OPTIM := debug" > Application.mk
    fi
    
    ./load.sh $file
    
    #adb shell "su -c '/data/local/tmp/gdbserver :9090 /data/local/tmp/$file'"&
    
    #sleep 1
    #./gdb.sh $file
    
    #----------------------------------------------------------------------------------------------
    $ cat gdb.sh
    #!/bin/bash
    
    adb forward tcp:9090 tcp:9090
    gdb -ex "file ./obj/local/armeabi-v7a/$1" \
        -ex "set solib-search-path ./obj/local/armeabi-v7a" \  #我用的32bit!
        -ex "set architecture armv7e-m" \
        -ex "target remote localhost:9090" \
        -ex "b main" \
        -ex "c"
        #-ex "set architecture aarch64" \
        #-ex "set solib-absolute-prefix ./" \
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    大家按照注释取消开启对应需求

    使用:

    $ ./start.sh png_to_yuv  #注意这里只需要写源代码的名字不用加.c
    
    • 1

    然后Android开启等待调试后,PC端进行附加

    ./gdb.sh
    
    • 1
  • 相关阅读:
    【算法刷题 | 贪心算法03】4.25(最大子数组和、买卖股票的最佳时机|| )
    猿创征文|【Linux Debug】有了core-dump,Bug一举拿下!
    流程控制-if else的多分支控制
    php hyperf框架接入链路追踪skywalking
    tensorflow的tensor
    22下半年软考集成广东卷(中项)真题在线估分
    2. 单主机 Elasticsearch 双节点或多节点集群环境部署
    C++函数在重载时如何判断函数签名(function signature)是否相同
    python烟花代码
    MySQL——单表与多表查询练习
  • 原文地址:https://blog.csdn.net/csdn546229768/article/details/125902149