I created an Android project containing c++ code, I use CMake to compile c++ and use JNI to communicate between c++ and Java. In the cpp file, I use miniLZO to compress a file. Here is the code.
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;
}
It actually worked. No error was thrown. But things changed after I added a CMake argument -DCMAKE_BUILD_TYPE:STRING=Release in build.gradle(app).
externalNativeBuild {
cmake {
arguments "-DCMAKE_BUILD_TYPE:STRING=Release"
cppFlags ""
}
}
After adding this argument, TestMiniLzo.cpp shown above continued to work on x86 Android Emulator, but the app crashed on a real Android device(Nexus 6), which has an arm architecture. Here is the error.
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
Just to give more information, here is my 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)
This is /sdcard/testFile, the file to be compressed. It only contains five lines.
android
android
android
android
android
Why does the app crash after adding that CMake argument? How to fix it?
This line is saying that your code is performing an illegally aligned access of some sort. This is undefined behavior in C/C++. The stack trace tells you where it happened (
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)).Because the argument changed the optimization mode for the compiler, which caused it to emit different code that exposed this error. Typically the reason this error is only sometimes visible is because some ARM instructions are valid for unaligned reads/writes, but others aren't. The aligned reads are faster when they are valid, so the compiler will use those if it can "prove" that they are (from the compiler's point of view, undefined behavior is impossible, so it can "safely" use these instructions if it appears that an access should be aligned).
UBSan can sometimes catch these issues, but unfortunately not always. Add
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined")to your CMakeLists.txt to enable it, then look at logcat when your application is running.If that doesn't help, you need to audit your code for undefined behavior. The most common cause of this kind of issue that I've seen is a buffer that is allocated as one type but accessed as another. i.e.
The code above has undefined behavior because the char buffer and the array it is cast to have different alignment requirements. It's rarely so obvious because generally these mistakes aren't made on two adjacent lines. To fix that case, allocate the buffer using the correct type, i.e.
Foo buf[10];.For more information, see https://developer.apple.com/documentation/code_diagnostics/undefined_behavior_sanitizer/misaligned_pointer