St.Antario:
私はJVMにロードされる共有ライブラリを書いていると、以下の行動は私が捕まってしまいました。ここに私のJavaクラスは、次のとおりです。
package com.test;
public class UnixUtil {
static {
System.loadLibrary("myfancylibrary");
}
static native int openReadOnlyFd(String path);
static native int closeFd(int fd);
}
public class Main {
public static void main(String[] args){
int fd = UnixUtil.openReadOnlyFd("/tmp/testc");
UnixUtil.closeFd(fd);
}
}
:そしてライブラリーのようなルックスをロードします
test_jni.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_test_UnixUtil */
#ifndef _Included_com_test_UnixUtil
#define _Included_com_test_UnixUtil
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_test_UnixUtil
* Method: openReadOnlyFd
* Signature: (Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_com_test_UnixUtil_openReadOnlyFd
(JNIEnv *, jclass, jstring);
/*
* Class: com_test_UnixUtil
* Method: closeFd
* Signature: (I)I
*/
JNIEXPORT jint JNICALL Java_com_test_UnixUtil_closeFd
(JNIEnv *, jclass, jint);
#ifdef __cplusplus
}
#endif
#endif
test_jni.c
#include "test_jni.h"
#include "fs.h"
JNIEXPORT jint JNICALL Java_com_test_UnixUtil_openReadOnlyFd
(JNIEnv *e, jclass jc, jstring path){
const char *const native_path = ((*e) -> GetStringUTFChars)(e, path, NULL);
int fd = read_only_open(native_path);
((*e) -> ReleaseStringUTFChars)(e, path, native_path);
return fd;
}
JNIEXPORT jint JNICALL Java_com_test_UnixUtil_closeFd
(JNIEnv *e, jclass jc, jint fd){
printf("Closing files descriptord %d... \n", fd);
return close(fd);
}
fs.h
#ifndef FS_H
#define FS_H
int read_only_open(const char *path);
int close(int fd);
#endif //FS_H
fs.c
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <sys/fcntl.h>
#include "fs.h"
int read_only_open(const char *path){
printf("Entering %s.%s:%d\n", __FILE__, __func__, __LINE__);
int fd = open(path, O_RDONLY);
return fd;
}
int close(int fd){ //Java_com_test_UnixUtil_closeFd does not invoke this function
printf("Entering %s.%s:%d\n", __FILE__, __func__, __LINE__);
int close_result = close(fd);
return close_result;
}
このコンパイルして実行するとMain
、クラスをJVMがクラッシュしません。これは単に関数を入力しませんfs.h::close(int)
。代わりに、stdlib
さんはclose
GDBに見られるように呼ばれています。
Thread 2 "java" hit Breakpoint 1, Java_com_test_UnixUtil_closeFd (e=<optimized out>,
jc=<optimized out>, fd=4) at /home/rjlomov/test_jni/src/main/java/com/test/lib/test_jni.c:17
17 return close(fd);
(gdb) step
18 }
(gdb)
17 return close(fd);
(gdb)
__close (fd=4) at ../sysdeps/unix/sysv/linux/close.c:27
27 ../sysdeps/unix/sysv/linux/close.c: No such file or directory.
(gdb)
26 in ../sysdeps/unix/sysv/linux/close.c
実行objdump -dS libmyfancylibrary.so
表示されていること
JNIEXPORT jint JNICALL Java_com_test_UnixUtil_closeFd
(JNIEnv *e, jclass jc, jint fd){
7d0: 53 push %rbx
}
//...
return close(fd);
7e9: e9 62 fe ff ff jmpq 650 <close@plt> // <--- PLT section,
// resolved by linker to stdlib::close?
7ee: 66 90 xchg %ax,%ax
質問:なぜさstdlib::close
に呼び出されるJava_com_test_UnixUtil_closeFd
代わりにfs.c::close(int)
?私は想像することができる唯一のことは、JVMが仕事をして、独自のダイナミックリンカーを持っています...
apangin:
共有ライブラリをコンパイルすると、機能があるのでclose()
はないstatic
、コンパイラは、プロシージャリンクテーブル(PLT)throught間接的な呼び出しを行います。ライブラリーを分解するとき、あなたはおそらく指示が表示されます
call <close@plt>
ときにmyfancylibrary
ロードされ、プロセスがすでに実装の持つclose
ダイナミックlikerはのlibcののバージョンを指すようにPLTを更新して、libcのからをclose()
。