Android NDK交叉编译c++源码

为什么写这篇博客:android开发中,部分功能需要依赖c++代码实现,需要将一些c++的源码编译为动态链接库,并在android app上调用实现c++代码功能。在交叉编译开发过程中有遇到很多麻烦,此文总结经验和教训,并希望为其他朋友提供些帮助,避些困扰。本文后半部分以实例形式分析并编译了一个完整的项目,可参考学习。

内容关键词:NDK编译链简介,NDK编译链的使用,多层依赖的动态链接库交叉编译

遇到了哪些问题?

  1. C++原生编译链,无法编译兼容android各平台的库文件。
  2. C++库文件的多层级依赖。
  3. 动态链接库在android项目中的调用。

NDK编译工具链:

https://developer.android.com/ndk/guideshttps://developer.android.com/ndk/guides/other_build_systems

  • Native Development Kit:一句话总结,用来在android项目里调用c/c++的代码来完成app的某些功能的工具包。
  • 避坑:NDK版本中,以NDK19为界做了调整,编译项目和依赖的库文件时请用同一个版本的NDK及编译工具链,本文用的是clang。(19之前有GCC,之后GCC被clang替代)

 笔者算定版本为r21e,其他版本均可,建议19以上,下载地址如下。https://github.com/android/ndk/wiki/Unsupported-Downloads


项目分析:

项目地址:secure_channel_client: 独立编译安装的安全通道客户端SDK - Gitee.com

接下来以此开源项目为例,逐步解释如何将C++项目编译并集成至android app 并调用其功能。

此项目的编译和依赖关系如下图:

我们最终要实现的是client.c的功能(android app中),而其实现依赖三个库文件,teeverifier, pthread, csecure_channel,都需要NDK独立编译。(见子目录下的cmakelist)。

其中libteeverify.so 需要用NDK独立编译,其依赖三个库文件,core.a, libcrypro.so, libcjson.so.

libcsecure_channel.so,依赖源码(项目的thirtypart文件包含),及两个openssl的动态链接库,libssl.so,licrypto.so。


结合依赖关系分析,得到以下编译顺序

  • 首先需要编译mircal子项目中core.a;然后是openssl的两个库libcrypro.so,libssl.so; 以及cjson的libcjson.so。
  • 有了以上基础库文件,接下来编译libteeverify.so,以及libcsecure_channel.so.
  • 最后将库文件放在android的文件目录中,client.c的cmakelist调用以上库文件,完成端侧安全通道的功能。

编译的坑:

了解整个依赖关系和编译逻辑之后,就是实操部分。这部分遇到问题最多,花费时间最长。我会一一列出遇到的问题及解决办法。

  1. core.a静态链接库编译:路径:\kunpengsecl\attestation\tee\tverlib\miracl>。 文件目录下有一个makefile文件.这一步中,要将原本的gcc替换为NDK目录下的aarch64-linux-android21-clang来编译,CC和CFLAGS分别为两个参数输入到python脚本中(提醒:安装python3)。NDK19版本之前可用GCC,之后版本用Clang,选定这个编译器之后,接下来编译库都用同一个,来避免不兼容问题。
    .PHONY: all build test install clean
    all build: 
    	# CC=gcc CFLAGS=-fPIC python3 config64.py -o 33 > ./build.log
    
    	CC=/自己的路径/ndk_openssl/android-ndk-r21e/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android21-clang CFLAGS=-fPIC python3 config64.py -o 33 > ./build.log
    	
    
    test:
    	@echo "to be implemented!"
    
    clean:
    	@rm -rf *.o *.so *.a build.log
  2. libcjson库编译:按照正常的编译,编译器为同一个clang,替换为本地自己的路径。命令为:
    /自己的路径/ndk_openssl/android-ndk-r21e/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android21-clang -wl,soname libcjson.so -o shared libcjson.so cJSON.c
  3. 编译好的库文件,libsjson.so 要复制到所有编译器:” /自己的路径/ndk_openssl/android-ndk-r21e/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android21-clang “ 对应的 系统默认搜索目录下目下,我这里对应的是”/自己路径/ndk_openssl/android-ndk-r21e/toolchains/llvm/prebuilt/linux-x86_64/aarch64-linux-android/lib64”。下面编译完openssl,libteeverify.so 也执行相同的操作。 
  4. openssl编译,这个库编译的时候坑比较多。可借鉴以下文章和官方文档,此处要注意NDK版本选择。此处编译的libssl.so和libcrypto.so, 如果和下面编译cseccure_channel.so不兼容,可以尝试删除android-ndk-r21e/toolchains/llvm/prebuilt/linux-x86_64/bin目录
    下除aarch64-linux-android21-clang(21为我选定的版本,可以为其他),clang外其他文件。执行第3步。https://github.com/openssl/openssl/blob/master/NOTES-ANDROID.mdhttp://t.csdnimg.cn/IblIGhttps://github.com/openssl/openssl/blob/master/NOTES-ANDROID.md
  5. 编译libteeverify.so: 路径/kunpengsecl/attestation/tee/tverlib/verifier
    icbc@ubuntu:~/Desktop/kunpengsecl/attestation/tee/tverlib/verifier修改makefile中的编译器的位置,动态链接库的位置。编译成功后,执行第3步骤,参考代码如下:
    LIBTAR = /usr/lib64
    INCLUDETAR = /usr/include
    
    .PHONY: all build test clean install
    all build: teeverifier.c teeverifier.h common.h
    	# gcc -fPIC -shared -o libteeverifier.so teeverifier.c ../miracl/core.a -I /usr/local/include -I ../miracl -L /usr/local/lib -lcrypto -lcjson
    
    	/自己路径/android-ndk-r21e/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android21-clang -fPIC -shared -Wl,-soname,libteeverifier.so -o libteeverifier.so teeverifier.c ../miracl/core.a -I /usr/local/include -I ../miracl -L /自己路径/openssl-1.1.1i/output -lcrypto  -L /自己路径/secure_channel_client/thirdparty/cjson -lcjson
    
    install: all build
    	mkdir -p $(DESTDIR)$(LIBTAR)
    	mkdir -p $(DESTDIR)$(INCLUDETAR)
    	install -m 755 libteeverifier.so $(DESTDIR)$(LIBTAR)
    	install -m 644 teeverifier.h $(DESTDIR)$(INCLUDETAR)
    
    test: teeverify_test.c teeverifier.c
    	gcc teeverify_test.c ../miracl/core.a -o test.out -I /usr/local/include -I ../miracl -L /usr/local/lib -lcrypto -lcjson
    
    clean:
    	@rm -rf *.o *.so *.out
  6.  编译libcsecure_channel.so, 在secure_channel_client目录下有一个cmakelist,src/下有一个cmakalist,需要对两个cmakelist做些改造。项目根目录下,需要添加CMAKE_C_ COMPILER ,代码如下:
    # Copyright (c) Huawei Technologies Co., Ltd. 2020. All rights reserved.
    # secGear is licensed under the Mulan PSL v2.
    # You can use this software according to the terms and conditions of the Mulan PSL v2.
    # You may obtain a copy of Mulan PSL v2 at:
    #     http://license.coscl.org.cn/MulanPSL2
    # THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
    # IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
    # PURPOSE.
    # See the Mulan PSL v2 for more details.
    
    cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
    set(CMAKE_C_STANDARD 99)
    
    #--------------------------------for ndk-------------------------------------------#
    # Set the path to the Android NDK
    set(CMAKE_ANDROID_NDK /自己目录/android-ndk-r21e)
    
    # # Specify the toolchain
    set(CMAKE_C_COMPILER ${CMAKE_ANDROID_NDK}/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android21-clang)
    # set(CMAKE_CXX_COMPILER ${CMAKE_ANDROID_NDK}/toolchains/llvm/prebuilt/linux-x86_64/bin/clang++)
    #--------------------------------for ndk-------------------------------------------#
    
    
    project(secure_channel_client C)
    
    set(LOCAL_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
    
    add_subdirectory(src)
    
    file(GLOB SEC_CHL_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h)
    
    install(FILES ${SEC_CHL_HEADERS}
            DESTINATION /usr/include/secGear
            PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
    

    src/cmakelist:add_library添加crypto,ssl的位置, include_directories添加openssl的include位置.  然后执行mkdir build && cd  build , cmake .. && make . 

    set(PREFIX secure_channel)
    set(SOURCE_FILE secure_channel_client.c secure_channel_common.c)
    
    aux_source_directory(${LOCAL_ROOT_PATH}/thirdparty/cjson/ CJSON_SRC)
    aux_source_directory(${LOCAL_ROOT_PATH}/src/ra_verify/ RA_SRC)
    FILE (GLOB_RECURSE BASE64_SRC "${LOCAL_ROOT_PATH}/thirdparty/base64url/*.c")
    
    set(CMAKE_C_FLAGS "-g -fstack-protector-all -W -Wall -Werror -Wextra -Werror=array-bounds -D_FORTIFY_SOURCE=2 -O2 -ftrapv -fPIC")
    set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS} -s")
    
    set(CMAKE_EXE_LINKER_FLAGS    "-Wl,-z,relro -Wl,-z,now -Wl,-z,noexecstack")
    
    if(${CMAKE_VERSION} VERSION_LESS "3.13.0")
        link_directories(${CMAKE_LIBRARY_OUTPUT_DIRECTORY})
    endif()
    
    include_directories( 
        /usr/local/include #openssl 添加openssl的include位置
        ${CMAKE_CURRENT_BINARY_DIR}
        ${CMAKE_CURRENT_SOURCE_DIR}
        ${LOCAL_ROOT_PATH}/include
        ${LOCAL_ROOT_PATH}/src/ra_verify
        ${LOCAL_ROOT_PATH}/thirdparty/cjson
        ${LOCAL_ROOT_PATH}/thirdparty/base64url
        ${LOCAL_ROOT_PATH}/thirdparty/kunpengsecl/verifier
    )
    add_library(c${PREFIX} SHARED ${SOURCE_FILE} ${CJSON_SRC} ${BASE64_SRC} ${RA_SRC})
    
    if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.13.0")
        target_link_directories(c${PREFIX} PRIVATE ${CMAKE_LIBRARY_OUTPUT_DIRECTORY})
    endif()
    
    #添加crypto,ssl的位置
    add_library( crypto SHARED IMPORTED)
    set_target_properties(crypto PROPERTIES IMPORTED_LOCATION /usr/lib64/libcrypto.so)
    add_library( ssl SHARED IMPORTED)
    set_target_properties(ssl PROPERTIES IMPORTED_LOCATION /usr/lib64/libssl.so)
    
    target_link_libraries(c${PREFIX}  ssl crypto)
    
    install(TARGETS c${PREFIX} 
            LIBRARY
            DESTINATION /usr/lib64
            PERMISSIONS  OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_READ  GROUP_EXECUTE WORLD_READ  WORLD_EXECUTE)
    

    编译完成后在usr/lib64目录下可找到libcsecure_channel.so,找不到的话,在secure_channel_client/build/src下面可以找到对应文件。

  7. 至此此项目所依赖的所有动态链接库均编译完毕,将client.c的代码通过JNI接口改写至Android的cpp模块内,即可在android app中实现app相同功能。


其他潜在问题:

  1. 编译过后使用库的时候可能存在,cannot find /某路径/下的/xxxx.so (其他库,入yyyy.so连接此库)的问题。这个问题的原因是编译xxxx.so库时,库名有问题,需要在编译的命令中加入 -wl,-soname,libxxxx.so -o libxxxx.so 来编译这个库。参考:Linux动态库soname的使用(转载)-CSDN博客
  2. 找不到库的问题:比如编译libcsecurechannel.so,需要libssl,libcrypto,libverifier等库。使用NDK的交叉编译链可能存在找不到库的情况,这个情况是因为,在编译链的默认搜索目录中,没有对应的库文件。壳参照上文第三步找到相应目录。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值