【问题标题】:Why does strlen cause SIGSEGV when called from JNI code (64bit)?为什么从 JNI 代码(64 位)调用时 strlen 会导致 SIGSEGV?
【发布时间】:2015-07-13 06:47:38
【问题描述】:

我在尝试使用 C 和 C++ 结合 JNI 和 Java 实现一些逻辑时遇到的一个错误让我有点困惑。

我把它提炼成一个简单的例子,每次在 Linux 上编译为 64 位时都会导致崩溃。使用 32 位时,它工作正常。 :S

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>

char * strlenTest()
{
    int size = 100;
    char * string = (char *) malloc(size * sizeof(char));
    int i;
    for ( i = 0; i < 50; i++)
    {
        string[i] = 'a';
    }
    string[50] = '\0';
    printf("string = '%s'\n", string);
    int length = strlen(string);
    printf("string length = %d\n", length);
    return string;
}

以及崩溃的调用方法:

JNIEXPORT jstring JNICALL Java_foo(JNIEnv *env,
        jobject thiz, jstring dexPath) {
    printf("calling test()\n");
    char * test = strlenTest();
    printf("calling strlen in jni\n");
    int testLength = strlen(test);
    printf("length:%d\n", testLength);
    }

输出: 64位:

calling test()
string = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
string length = 50
calling strlen in jni
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x00007f63cbefb6fa, pid=9563, tid=140066607630080
#
# JRE version: OpenJDK Runtime Environment (7.0_79-b14) (build 1.7.0_79-b14)
# Java VM: OpenJDK 64-Bit Server VM (24.79-b02 mixed mode linux-amd64 compressed oops)
# Derivative: IcedTea 2.5.5
# Distribution: Ubuntu Vivid Vervet (development branch), package 7u79-2.5.5-0ubuntu1
# Problematic frame:
# C  [libc.so.6+0x8b6fa]  strlen+0x2a

32 位:

calling test()
string = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
string length = 50
calling strlen in jni
length:50

生成文件:

MARCH=-m64

CXX      = g++
CXXFLAGS =  -ansi -g -O0 $(MARCH) -fPIC  -I../.. -I/usr/lib/jvm/java-7-openjdk-amd64/include -I/usr/include -Dunix -Wformat=0
CC       = gcc
CCFLAGS  = -g  -O0 -fPIC $(MARCH) -I../.. -I/usr/lib/jvm/java-7-openjdk-amd64/include -I/usr/include -Dunix -Wformat=0
OBJS     = bugtest.o dexFileParser.o miniunz.o unzip.o ioapi.o 

libname.so : $(OBJS)
    $(CXX) -o $@ $(OBJS) -lz  $(MARCH) -shared

%.o : %.cpp
    $(CXX) -c $(CXXFLAGS) $< 

%.o : %.c
    $(CC) -c $(CCFLAGS) $<

clean: 
    rm *.o

奇怪的是,strlen 在方法本身中起作用,但是当 char * 返回到 JNI C 函数时,它以某种方式崩溃,并且仅在 64 位中。

我真的不明白发生了什么。有人能指出错误吗?

【问题讨论】:

  • 请您提供您的构建选项 (gcc) - 您是否添加了 -fPIC。我似乎记得这可能会修复库调用 - 如果它仍在您使用的最新版本中。
  • @Neil 我添加了相关的 Makefile。
  • @Peterik,我赌了一把并发布了一个答案,但是我现在注意到你已经包含了 -fPIC(在看到 makefile 之后)。请至少查看来自 ubuntun 论坛的帖子,希望这可能会有所帮助!
  • 您需要使用调试器。您可以在调用本机代码之前在 java 调试器中在 java 中设置断点,然后将 gdb 附加到您的进程并在本机代码中设置断点,然后查看调试器中发生了什么。

标签: c java-native-interface 32bit-64bit


【解决方案1】:

原来问题是缺少声明函数的.h 文件。 编译器确实已经显示了一个我忽略的警告:

initialization makes pointer from integer without a cast

因此,当我添加了一个正确声明 char * strlenTest(); 函数的 .h 文件时,它起作用了!

【讨论】:

    【解决方案2】:

    我要在这里赌一把,并提出以下建议……

    来自http://ubuntuforums.org/showthread.php?t=1942105(谷歌很棒!)

    使用以下命令行发现问题-

    gcc -c -I/usr/lib/jvm/java-6-openjdk/include -I/usr/lib/jvm/java-6-openjdk/include/linux HelloWorld.c -o libHelloWorld.so
    

    问题是在尝试 64 位时,32 位工作正常 -

    报告为有效的答案是..

    gcc -c -fPIC -I/usr/lib/jvm/java-6-openjdk/include -I/usr/lib/jvm/java-6-openjdk/include/linux HelloWorld.c
    
    gcc -shared -o libHelloWorld.so HelloWorld.o
    

    运行示例 -

    LD_LIBRARY_PATH=./:${LD_LIBRARY_PATH} java HelloWorld
    

    请查看完整的帖子,我希望这可以帮助您解决问题。

    【讨论】:

    • 我没有包含 /linux 目录,不幸的是没有解决它。我已经实施的所有其他选项。太糟糕了,但感谢您的尝试!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-15
    • 2010-12-06
    • 1970-01-01
    • 1970-01-01
    • 2012-07-25
    • 1970-01-01
    相关资源
    最近更新 更多