admin 管理员组

文章数量: 887007

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<stdio.h>
int main(){
  printf("hello \n");
  return 0;
}

在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

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

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

然后在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

然后就可以通过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/

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

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

libs

libs
├── arm64-v8a
│   └── demo
└── armeabi-v7a
    └── demo

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

obj

obj
└── local
    ├── arm64-v8a
    │   ├── demo
    │   ├── objs
    │   │   └── demo
    │   │       ├── demo.o
    │   │       ├── demo.omands.json
    │   │       └── demo.o.d
    │   └── objs-debug
    │       └── demo
    │           ├── demo.o
    │           ├── demo.omands.json
    │           └── demo.o.d

对应的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

参数

#编译执行程序
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE    := demo
LOCAL_SRC_FILES := demo.c
include $(BUILD_EXECUTABLE)
#会在obj/local/arm64-v8a/生成demo程序
#编译动态链接库
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库

调试

补补补,之前都是用Mac OS在调试的,没发现什么问题。今天用Ubuntu来调试一直报错:Starting program: /home/hi/tmp/obj/local/arm64-v8a/demo
Failed to execute process ‘/home/hi/tmp/obj/local/arm64-v8a/demo’. Reason:
exec: unknown error (errno was 8)
The file ‘/home/hi/tmp/obj/local/arm64-v8a/demo’ is marked as an executable but could not be run by the operating system.
During startup program exited with code 126.
给我恶习到了,最后发现我少安装了一个gdb-multiarch,将gdb使用gdb-multiarch来代替就可以调试上去了。。。。。

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

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

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

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

然后同局域网下的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" \

一键脚本

$ ls
gdb.sh                load.sh               start.sh
heif_decode_to_rgba.c png_to_yuv.c

目录下面应该有这些文件

  • 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 ./" \

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

使用:

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

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

./gdb.sh

本文标签: 可执行 二进制文件 Android gdb aarch