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?