Skip to content

Crash at `foreign export ccall function` that return `Ptr CString` for C on Arm64 (android)

Hello dev:

I export a haskell function for c like below:

foreign export ccall "namesMatching" _namesMatchingC :: CString -> IO (Ptr CString)

_namesMatchingC filePath = do
    filePath' <- peekCString filePath
    pathNames <- namesMatching filePath'
    pathNames' <- forM pathNames mapFilePath
    withArray pathNames' $ \cstrArr -> return cstrArr
  where
    mapFilePath = (`withCString` (\cstr -> do return cstr))

This function return IO (Ptr CString) that interface with char** in c++.

My android jni cpp code that calling namesMatching exported from haskell:

#include <jni.h>

#include <unistd.h>
#include <sstream>
#include <string>

#include "ghcversion.h"
#include "HsFFI.h"
#include "Rts.h"

#include "my_log.h"
#include "Lib_stub.h"
#include "FileSystem_stub.h"

extern "C" {

// ........

JNIEXPORT jobjectArray
JNICALL
Java_com_zw3rk_helloworld_MainActivity_namesMatchingJNI(
        JNIEnv *env,
        jobject thiz,
        jstring path) {

    LOG_ASSERT(NULL != env, "JNIEnv cannot be NULL.");
    LOG_ASSERT(NULL != thiz, "jobject cannot be NULL.");
    LOG_ASSERT(NULL != path, "jstring cannot be NULL.");

    const char *c_value = env->GetStringUTFChars(path, NULL);
    const char **result = static_cast<const char **>(namesMatching(
            const_cast<char *>(c_value)));
    jsize len = 0;
    for (; result[len] != NULL; len++);

    env->ReleaseStringUTFChars(path, c_value);
    jobjectArray strs = env->NewObjectArray(len, env->FindClass("java/lang/String"), env->NewStringUTF(""));
    for (int i = 0; i < len; i++) {
        jstring str = env->NewStringUTF(result[i]);  // <-----------  crash here
        env->SetObjectArrayElement(strs, i, str);
    }
    // freeCStringArray frees the newArray pointer created in haskell module
    // freeCStringArray(len, result);
    return strs;
}

}

My android kotlin code that calling Java_com_zw3rk_helloworld_MainActivity_namesMatchingJNI:


private fun doSthAfterAllPermissionGranted() {
        // Example of a call to a native method
        val tv = findViewById(R.id.sample_text) as TextView

        Handler(Looper.getMainLooper()).postDelayed(
                {
                    tv.text = stringFromJNI()
                    Log.e("test1", "--------- begin --------namesMatchingJNI")
                    Log.w("test2", "${namesMatchingJNI("/sdcard/*.txt").joinToString()}}")
                    Log.e("test3", "---------- end --------namesMatchingJNI")

                    doSthAfterAllPermissionGranted()
                }
                , 2000)
    }

   external fun namesMatchingJNI(path: String): Array<String>

EveryThing is ok, but sometimes crash at jstring str = env->NewStringUTF(result[i]);

result[i] is obtained from haskell.

The Crash dump is here:

********** Crash dump: **********
Build fingerprint: 'HONOR/COR-AL00/HWCOR:8.1.0/HUAWEICOR-AL00/151(C00GT):user/release-keys'
pid: 19991, tid: 19991, name: w3rk.helloworld  >>> com.zw3rk.helloworld <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x9
Stack frame #00 pc 000000000001cea0  /system/lib64/libc.so (strlen+16)
Stack frame #01 pc 00000000003bfbe0  /system/lib64/libart.so (art::mirror::String::AllocFromModifiedUtf8(art::Thread*, char const*)+28)
Stack frame #02 pc 000000000037be84  /system/lib64/libart.so (art::JNI::NewStringUTF(_JNIEnv*, char const*)+572)
Stack frame #03 pc 0000000000b55b90  /data/app/com.zw3rk.helloworld-VAuV-C-ri9T0r3qoc_cgzQ==/lib/arm64/libnative-lib.so (_JNIEnv::NewStringUTF(char const*)+48)
Stack frame #04 pc 0000000000b562d8  /data/app/com.zw3rk.helloworld-VAuV-C-ri9T0r3qoc_cgzQ==/lib/arm64/libnative-lib.so (Java_com_zw3rk_helloworld_MainActivity_namesMatchingJNI+652)
Stack frame #05 pc 0000000000012558  /data/app/com.zw3rk.helloworld-VAuV-C-ri9T0r3qoc_cgzQ==/oat/arm64/base.odex (offset 0x12000)

I think the AllocFromModifiedUtf8 in error log means that cstring return from haskell has been changed? Or Sth Else cause it?

Edited by Ben Gamari
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information