交叉编译android native程序

Background

前几天比赛用到要编译c代码到android上面跑,当时用了最直接gnu套件交叉编译过去,后来感觉有必要学习一下android ndk的用法,这篇文章就总结一下。

正文

gnu套件

最土制的方法,直接装个编译器,这些应该都可以,ubuntu直接apt装上之后编译就行了。

ndk + cmake

先到官网下个对应版本的ndk

https://developer.android.com/ndk/downloads?hl=zh-cn

cmake 3.21之后好像原生支持了使用ndk交叉编译,只要在cmake开始指定CMAKE_SYSTEM_NAME,再把ndk路径丢给它就行了。

cmake代码:

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
cmake_minimum_required(VERSION 3.21)

# --------------------------------

# set NDK path
set(ANDROID_NDK "/path/to/your/android-ndk")

# 设定CMAKE_SYSTEM_NAME要放在project前面,不然不会生效
# set CMake Cross-Compile Target
set(CMAKE_SYSTEM_NAME Android)

set(CMAKE_ANDROID_NDK ${ANDROID_NDK})
set(CMAKE_ANDROID_API 24)
set(CMAKE_ANDROID_ARCH_ABI armeabi-v7a)
# set(CMAKE_ANDROID_STL_TYPE c++_static)

# --------------------------------

# optional: for clangd
set(CMAKE_EXPORT_COMPILE_COMMANDS 1)

project(main)

set(CMAKE_BUILD_TYPE Release)

add_executable(${PROJECT_NAME} test.cpp)

# --------------------------------

# ${CMAKE_STRIP}是ndk toolchain下的strip工具
# 各种工具都放在${CMAKE_ANDROID_NDK_TOOLCHAIN_UNIFIED}/bin里面
# strip symbols
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_STRIP} -s ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}
)

老版本cmake也可以指定-D参数传过去

1
2
3
4
cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
-DANDROID_ABI="armeabi-v7a" \
-DANDROID_NDK=$ANDROID_NDK \
-DANDROID_PLATFORM="android-22" \

具体cmake的参数和ndk的参数参见

cmake参数:https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling-for-android

工具链参数:https://developer.android.com/ndk/guides/cmake?hl=zh-cn#variables

ndk build

使用ndk自带的ndk build脚本构建,需要写一个Android.mk,是个类似cmake的东西,

1
2
3
4
5
6
7
8
9
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := cpufeatures
LOCAL_SRC_FILES := cpu-features.c
LOCAL_CFLAGS := -Wall -Wextra -Werror
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
LOCAL_EXPORT_LDLIBS := -ldl
include $(BUILD_STATIC_LIBRARY)

样例在ndk/sources/下面有,具体参数参见下面的链接。

https://developer.android.com/ndk/guides/android_mk?hl=zh-cn

别的一些问题

之前编译的时候老是找不到process_vm_readv和writev,后来发现是android编译api版本太老了,有的函数可能不支持。

还有编译出来的东西直接跑会报个unsupported flags DT_FLAGS_1=0x8000001,好像是.dynamic段的什么东西炸了,但是好像不影响运行。github上有个脚本好像可以修复这个东西,叫termux-elf-cleaner