在基于 arm 的 Android 设备上使用带有参数 -DCMAKE_BUILD_TYPE:STRING=Release 的 lzo 时应用程序崩溃

Posted

技术标签:

【中文标题】在基于 arm 的 Android 设备上使用带有参数 -DCMAKE_BUILD_TYPE:STRING=Release 的 lzo 时应用程序崩溃【英文标题】:App crashes while using lzo with the argument -DCMAKE_BUILD_TYPE:STRING=Release on arm-based Android devices 【发布时间】:2019-06-23 14:10:19 【问题描述】:

我创建了一个包含 c++ 代码的 android 项目,我使用 CMake 编译 c++ 并使用 JNI 在 c++ 和 Java 之间进行通信。在cpp 文件中,我使用miniLZO 来压缩文件。这是代码。

TestMiniLzo.cpp

#include <jni.h>
#include <string>
#include <iostream>
#include <fstream>
#include <vector>
#include <sstream>
#include <iterator>
#include "minilzo.h"

std::vector<unsigned char> readFile(const std::string& file);

/* Work-memory needed for compression. Allocate memory in units
 * of 'lzo_align_t' (instead of 'char') to make sure it is properly aligned.
 */


#define HEAP_ALLOC(var,size) \
    lzo_align_t __LZO_MMODEL var [ ((size) + (sizeof(lzo_align_t) - 1)) / sizeof(lzo_align_t) ]

static HEAP_ALLOC(wrkmem, LZO1X_1_MEM_COMPRESS);

std::string testMiniLzo() 
    if (lzo_init() != LZO_E_OK) 
        return "init failed";
    
    std::vector<unsigned char> fileContents = readFile("/sdcard/testFile");

    // compress with LZO1X-1
    std::vector<unsigned char> compressOutput(fileContents.size() + fileContents.size() / 16 + 64 + 3);
    lzo_uint outSize;
    int r = lzo1x_1_compress(fileContents.data(), fileContents.size(), compressOutput.data(), &outSize, wrkmem);
    if (r != LZO_E_OK) 
        return "fail";
    
    return "success";


std::vector<unsigned char> readFile(const std::string& file) 
    std::ifstream input(file, std::ios::binary);

    // copies all data into buffer
    std::vector<unsigned char> buffer(std::istreambuf_iterator<char>(input), );
    return buffer;

它确实有效。没有抛出错误。但是在我在build.gradle(app) 中添加了一个CMake 参数-DCMAKE_BUILD_TYPE:STRING=Release 之后,情况发生了变化。

externalNativeBuild 
    cmake 
        arguments "-DCMAKE_BUILD_TYPE:STRING=Release"
        cppFlags ""
   

添加此参数后,上面显示的TestMiniLzo.cpp 继续在 x86 Android Emulator 上运行,但该应用程序在具有 arm 架构的真实 Android 设备(Nexus 6)上崩溃。这是错误。

06-23 10:03:40.962 11229 11229 F libc    : Fatal signal 7 (SIGBUS), code 1, fault addr 0xaad38b85 in tid 11229 (y.myapplication)        
06-23 10:03:40.963   261   261 W         : debuggerd: handling request: pid=11229 uid=10109 gid=10109 tid=11229                         
06-23 10:03:41.033 11276 11276 F DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***                              
06-23 10:03:41.033 11276 11276 F DEBUG   : Build fingerprint: 'google/shamu/shamu:7.1.1/NGI77B/4345728:user/release-keys'               
06-23 10:03:41.033 11276 11276 F DEBUG   : Revision: '0'                                                                                
06-23 10:03:41.033 11276 11276 F DEBUG   : ABI: 'arm'                                                                                   
06-23 10:03:41.034 11276 11276 F DEBUG   : pid: 11229, tid: 11229, name: y.myapplication  >>> com.body.myapplication <<<                
06-23 10:03:41.034 11276 11276 F DEBUG   : signal 7 (SIGBUS), code 1 (BUS_ADRALN), fault addr 0xaad38b85                                
06-23 10:03:41.034 11276 11276 F DEBUG   :     r0 0a64696f  r1 00000004  r2 a9abb2c0  r3 aad38b85                                       
06-23 10:03:41.034 11276 11276 F DEBUG   :     r4 0000000c  r5 aad38b7d  r6 aad38b85  r7 beedd800                                       
06-23 10:03:41.034 11276 11276 F DEBUG   :     r8 a9abb2a0  r9 a9abb2c0  sl 00000000  fp a9abb2c0                                       
06-23 10:03:41.034 11276 11276 F DEBUG   :     ip 0000001c  sp beedd7c8  lr aad38b70  pc ac71eab0  cpsr 60052430                        
06-23 10:03:41.037 11276 11276 F DEBUG   :                                                                                              
06-23 10:03:41.037 11276 11276 F DEBUG   : backtrace:                                                                                   
06-23 10:03:41.037 11276 11276 F DEBUG   :     #00 pc 00032ab0  /data/app/com.body.myapplication-1/lib/arm/libnative-lib.so (lzo1x_1_com
press+261)                                                                                                                              
06-23 10:03:41.037 11276 11276 F DEBUG   :     #01 pc 00031435  /data/app/com.body.myapplication-1/lib/arm/libnative-lib.so (_Z11testMin
iLzov+128)                                                                                                                              
06-23 10:03:41.038 11276 11276 F DEBUG   :     #02 pc 000325f7  /data/app/com.body.myapplication-1/lib/arm/libnative-lib.so (Java_com_bo
dy_myapplication_MainActivity_compressAndDecompressUsingLzo+26)                                                                         
06-23 10:03:41.038 11276 11276 F DEBUG   :     #03 pc 00276b71  /data/app/com.body.myapplication-1/oat/arm/base.odex (offset 0x249000)
06-23 10:03:41.210 11253 11258 I art     : Do partial code cache collection, code=28KB, data=30KB
06-23 10:03:41.210 11253 11258 I art     : After code cache collection, code=28KB, data=30KB
06-23 10:03:41.210 11253 11258 I art     : Increasing code cache capacity to 128KB

只是为了提供更多信息,这是我的CMakeLists.txt

CMakeLists.txt

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.4.1)

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

add_subdirectory(minilzo-2.10 "$CMAKE_CURRENT_BINARY_DIR/minilzo-build")
include_directories(minilzo-2.10)
include_directories(include)

add_library( # Sets the name of the library.
        native-lib

        # Sets the library as a shared library.
        SHARED

        TestMiniLzo.cpp
        # Provides a relative path to your source file(s).
        native-lib.cpp)

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

find_library( # Sets the name of the path variable.
        log-lib

        # Specifies the name of the NDK library that
        # you want CMake to locate.
        log)

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.

target_link_libraries( # Specifies the target library.
        native-lib

        # Links the target library to the log library
        # included in the NDK.
        $log-lib
        minilzo)

这是/sdcard/testFile,要压缩的文件。它只包含五行。

android
android
android
android
android

为什么在添加 CMake 参数后应用会崩溃?如何解决?

【问题讨论】:

【参考方案1】:

06-23 10:03:41.034 11276 11276 F 调试:信号 7 (SIGBUS),代码 1 (BUS_ADRALN),故障地址 0xaad38b85

这一行表明您的代码正在执行某种非法对齐的访问。这是 C/C++ 中未定义的行为。堆栈跟踪告诉你它发生在哪里 (06-23 10:03:41.037 11276 11276 F DEBUG : #00 pc 00032ab0 /data/app/com.body.myapplication-1/lib/arm/libnative-lib.so (lzo1x_1_com press+261))。

为什么添加 CMake 参数后应用会崩溃?

因为参数改变了编译器的优化模式,导致它发出不同的代码,暴露了这个错误。通常,此错误仅有时可见的原因是因为某些 ARM 指令对未对齐的读/写有效,而其他指令则无效。对齐的读取在有效时会更快,因此如果编译器可以“证明”它们是有效的,则编译器将使用它们(从编译器的角度来看,未定义的行为是不可能的,因此如果出现,它可以“安全地”使用这些指令访问应该对齐)。

如何解决?

UBSan 有时会发现这些问题,但不幸的是并非总是如此。将set(CMAKE_C_FLAGS "$CMAKE_C_FLAGS -fsanitize=undefined") 添加到您的 CMakeLists.txt 以启用它,然后在您的应用程序运行时查看 logcat。

如果这没有帮助,您需要针对未定义的行为审核您的代码。我见过的此类问题的最常见原因是缓冲区被分配为一种类型但作为另一种类型访问。即

char buf[sizeof(Foo) * 10]; // Allocate space for 10 Foo structs.
Foo* foo_array = reinterpret_cast<Foo>(buf);

上面的代码有未定义的行为,因为 char 缓冲区和它被强制转换为具有不同对齐要求的数组。它很少那么明显,因为通常这些错误不会发生在两条相邻的线上。要解决这种情况,请使用正确的类型分配缓冲区,即Foo buf[10];

欲了解更多信息,请参阅https://developer.apple.com/documentation/code_diagnostics/undefined_behavior_sanitizer/misaligned_pointer

【讨论】:

以上是关于在基于 arm 的 Android 设备上使用带有参数 -DCMAKE_BUILD_TYPE:STRING=Release 的 lzo 时应用程序崩溃的主要内容,如果未能解决你的问题,请参考以下文章

基于 ARM 的设备上 Prefetch Abort 错误的常见原因是啥?

《android深入探索》第五章心得

在 Windows 上使用自定义交叉编译器为 arm 编译 v8

20155338课程设计个人报告——基于ARM实验箱的Android交友软件的设计与实现

如何在带有 ARM CPU 的 WinRT 设备中部署 SQLite?

Android深度探索与驱动开发