c - JNI : call to a function where memset happens causes jvm-fetch to crash in the second call

标签 c jvm java-native-interface memset

该函数首先执行一些 memset 操作,然后调用基本上创建/获取 JVM 的函数,然后与 JVM 进行通信。

int
acCreateHeader(acInputParms *acCreate, acReturnParms *acRet3){

   int     Debug;
    int     rc;
    acInputParms        acInp;
    kc0InputParms       kc0Inp;
    kc0ReturnParms      kc0Ret3;

    initACReturnParms(acRet3);

    rc = invokeKc000(kc0Inp, &kc0Ret3); // JNI Operations

    return rc;
}

initACReturnParms(acRet3);的扩展如下:

void initACReturnParms (acReturnParms *in) {

    memset(in->msgId,0,sizeof(in->msgId));
    memset(in->msgText,0,sizeof(in->msgText));
    memset(in->returnHdrTrl,0,sizeof(in->returnHdrTrl));
    memset(in->returnPaySrc,0,sizeof(in->returnPaySrc));
    memset(in->returnPayLoc,0,sizeof(in->returnPayLoc));
    return;
}

因此,当我执行整个程序时,第一个 JNI 操作发生得很好,但程序的逻辑是以这样的方式构建的:函数 acCreateHeader 被调用两次,因此该程序在第二次调用中卡住在函数 (**jvm)->GetEnv(*jvm, (void**)&env, JNI_VERSION_1_4); (它基本上尝试获取已创建的 JVM)。

抱歉,由于程序非常大,几乎不可能将我的所有代码粘贴到此处。

但问题就在这里:

当我通过注释 initACReturnParms 的调用复制函数 initACReturnParms 的所有逻辑并将其粘贴到函数 acCreateHeader 时(如下所示) ,即使在第二次 JNI 操作之后,程序也根本不会崩溃,并且整个程序按我的预期工作。

int
acCreateHeader(acInputParms *acCreate, acReturnParms *acRet3){

   int     Debug;
    int     rc;
    acInputParms        acInp;
    kc0InputParms       kc0Inp;
    kc0ReturnParms      kc0Ret3;

    memset(acRet3->msgId,0,sizeof(acRet3->msgId));
    memset(acRet3->msgText,0,sizeof(acRet3->msgText));
    memset(acRet3->returnHdrTrl,0,sizeof(acRet3->returnHdrTrl));
    memset(acRet3->returnPaySrc,0,sizeof(acRet3->returnPaySrc));
    memset(acRet3->returnPayLoc,0,sizeof(acRet3->returnPayLoc));
    //initACReturnParms(acRet3);

    rc = invokeKc000(kc0Inp, &kc0Ret3); // JNI Operations

    return rc;
}

那么,为什么这个函数 initACReturnParms 会在这里调用罪魁祸首呢?请帮助我理解。

如果您希望我提供更多信息/代码,请告诉我。

更新:

acRet3 是函数 acCreateHeader 中的局部变量,并作为参数从以下函数传递:

void writeLSHeader() {

  int rc;

  fillHdrTrl();

  rc=acCreateHeader(&acCreate,&acRet);

  //rest of the codes
}

并且 acRet 在下面所示的函数中初始化:

int fillHdrTrl() {
    //Codes here
    memset(&acRet,0,sizeof(acReturnParms));
    memset(&acRet.returnHdrTrl[0],' ',AC_HDR_TRL_SZ);
    //Codes here
}

acRet 是一个全局变量,内存是在堆栈上分配的:

acReturnParms acRet;

并且 acReturnParms 看起来像这样:

struct acReturnParms {
    char msgId[9];
    char msgText[61];
    char returnHdrTrl[351];
    char returnPaySrc[9];
    char returnPayLoc[3];
}

上面使用的宏出现在头文件中,如下所示:

#define AC_HDR_TRL_SZ            350

更新2

以下是 JNI 操作的代码,这可能是导致这种未定义行为的原因:

#include "jni_funct.h"
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

int invokeKc000(kc0InputParms kc0inp, kc0ReturnParms *kc0ret)
{
    JavaVM *jvm;
    JNIEnv *env;
    kc0InputParms input;
    kc0ReturnParms *kc000out;
    char *kc910Path, *kc910Lib;
    int retcode;
    retcode = 8;

    kc910Path = getenv("KC910_BIN");
    kc910Lib = getenv("KC910_LIB");
    if(kc910Path == NULL || strlen(kc910Path) <= 0){
        return -1;
    }

    if(kc910Lib == NULL || strlen(kc910Lib) <= 0){
        return -1;
    }
    env = createKc000Vm(&jvm, kc910Path, kc910Lib);
    if(env == NULL){
        printf("\n JNIEnv is NULL");
        return -2;
    }
    input = kc0inp;
    kc000out = kc0ret;
    communicateKc000(env, input, &kc000out, &retcode);
    return retcode;
}

JNIEnv* createKc000Vm(JavaVM **jvm, char *kc910Path, char *kc910lib)
{
        JNIEnv* env;
        JavaVMInitArgs args;
        JavaVMOption options;
        args.version = JNI_VERSION_1_4;
        args.nOptions = 1;
        char *classPath = "shared/misc/kc000";
        char *javaClassPathOption = "-Djava.class.path";
        char javaOptionString[2000];
        char kc910Lib[1000], kc910Bin[1000];

        memset(kc910Lib, '\0', sizeof(kc910Lib));
        memset(kc910Bin, '\0', sizeof(kc910Bin));
        memset(javaOptionString, '\0', sizeof(javaOptionString));
        strncpy(javaOptionString, javaClassPathOption, strlen(javaClassPathOption));
        strncpy(kc910Lib, kc910lib, strlen(kc910lib));
        strncpy(kc910Bin, kc910Path, strlen(kc910Path));
        strcat(javaOptionString, "=");
        strcat(javaOptionString, kc910Lib);
        strcat(javaOptionString, "/com.ibm.ws.ejb.thinclient_8.5.0.jar:");
        strcat(javaOptionString, kc910Lib);
        strcat(javaOptionString, "/com.ibm.ws.runtime.jar:");
        strcat(javaOptionString, kc910Lib);
        strcat(javaOptionString, "/com.ibm.ws.orb_8.5.0.jar:");
        strcat(javaOptionString, kc910Lib);
        strcat(javaOptionString, "/ojdbc6.jar:");
        strcat(javaOptionString, kc910Lib);
        strcat(javaOptionString, "/providerutil.jar:");
        strcat(javaOptionString, kc910Lib);
        strcat(javaOptionString, "/j2ee.jar:");
        strcat(javaOptionString, kc910Lib);
        strcat(javaOptionString, "/src.jar:");
        strcat(javaOptionString, kc910Lib);
        strcat(javaOptionString, "/junit.jar:");
        strcat(javaOptionString, kc910Lib);
        strcat(javaOptionString, "/ldap.jar:");
        strcat(javaOptionString, classPath);
        strcat(javaOptionString, ":");
        strcat(javaOptionString, kc910Path);
        strcat(javaOptionString, "/C2J2.jar");

        options.optionString = javaOptionString;
        args.options = &options;
        args.ignoreUnrecognized = 0;
        int rv;
        jsize buf, num;
        rv = JNI_CreateJavaVM(jvm, (void**)&env, &args);
        if (rv < 0 || !env){
                printf("\nUnable to launch KC000 JVM %d. Perhaps you are attempting to create JVM. Attempting to fetch existing JVM, if running",rv);
                JNI_GetCreatedJavaVMs(jvm, buf, &num);
                (**jvm)->GetEnv(*jvm, (void**)&env, JNI_VERSION_1_4);
        }
        else
                printf("\nLaunched kc000 JVM!");

        return env;
}

void communicateKc000(JNIEnv* env, kc0InputParms in, kc0ReturnParms **kc000outparms, int *retcode)
{
    jclass init_class;
    jmethodID init_method;
    jstring inString, outString;
    kc0ReturnParms **kc000out;
    char returnCodeString[9];
    int flag = 8, i = 0;
    kc000out = kc000outparms;
    inString = (*env)->NewStringUTF(env, in.inStream);

    init_class = (*env)->FindClass(env, "shared/misc/kc000/Kc000");
    init_method = (*env)->GetStaticMethodID(env, init_class, "init", "(Ljava/lang/String;)Ljava/lang/String;");
    outString = (*env)->CallStaticObjectMethod(env, init_class, init_method, inString);
    const char *rawString = (*env)->GetStringUTFChars(env, outString, 0);
    jclass newExcCls = (*env)->FindClass(env, "java/lang/Throwable");
    if(newExcCls == NULL)
    {
        /*printf("Exception not found\n");*/
    }
    else
    {
        (*env)->ExceptionDescribe(env);
    }   
    strcpy((*kc000out)->inStream, rawString);
    memset(returnCodeString, '\0', sizeof(returnCodeString));
    strncpy(returnCodeString, rawString, 8);
    printf("KC000 return code: [%s]\n", returnCodeString);
    if(strlen(returnCodeString) > 0){
        for(i = 0; i < 8; i++){
            if(returnCodeString[i] == ' '){
                flag = 0;
            }
            else{
                flag = 8;
            }
        }
        *retcode = flag;
    }
    else{
        printf("Nothing returned in the return code of KC000. Setting this as an error.\n");
        *retcode = 8;
    }
}

最佳答案

我自己找到了问题的答案。以下是我为摆脱这种未定义行为所做的事情:

#include "jni_funct.h"
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

JavaVM *jvm;
JNIEnv *env;

int invokeKc000(kc0InputParms kc0inp, kc0ReturnParms *kc0ret)
{
    //JavaVM *jvm;
    //JNIEnv *env;
    kc0InputParms input;
    kc0ReturnParms *kc000out;
    char *kc910Path, *kc910Lib;
    int retcode;
    retcode = 8;

    //Rest of the codes

我将 JNIEnvJavaVM 设置为全局变量而不是本地变量。

我仍然不明白为什么堆栈分配会导致这种未定义的行为,而数据段分配不会导致这样的问题。我将发布有关此更改的另一个问题。

感谢@Martin Zabel 的提示。

关于c - JNI : call to a function where memset happens causes jvm-fetch to crash in the second call,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34514290/

相关文章:

java - 如何在 Java + JNI + C++ 进程中查找内存泄漏

Android - native 全局对象析构函数从未被调用

c - Arduino 不等待每个模式(模式)结束我怎样才能重新启动循环?

java - Java中主线程什么时候停止?

c++ - OS X 使用箭头键移动终端插入符

java - 有哪些 JVM 汇编程序?

java - 变量变为字段、栈变为堆的过程是怎样的?

java - 设置 java.library.path 进行测试

C/确定接收数据包的端口

c - 跟踪过程的寄存器值没有改变