В Java существует возможность использования программного кода, реализованного на других языках программирования, так называемый JNI. Можно написать динамически линкуемую библиотеку, затем загрузить ее в Java-коде и использовать функции оттуда, объявив их как native методы загрузившего ее класса. JNI создавался в первую очередь для того, чтобы выполнять машинно-зависимые действия (а также, возможно, улучшить производительность критических по скорости частей приложения) на C/C++, но никто не мешает нам написать библиотеку и на ассемблере.
Для создания сего извращения применялись Windows, Oracle JDK, Yasm (без макросов) и линковщик из Microsoft Visual Studio 2010. При желании, заменить любой из этих компонентов на ваш любимый не составит большого труда, я старался не использовать всякие нестандартные фичи.
Для начала, создадим на Java класс, в котором будет вызываться функция из будущей .dll'ки:
Теперь нужно узнать, какое имя функции рассчитывает найти в нашей библиотеке Java-машина. Воспользуемся для этого программой javah из JDK:
При этом будет сгенерирован C++ header:
Информация, содержащаяся здесь, нужна главным образом для дальнейшего написания библиотеки на C++, нас же интересует только сигнатура функции:
Мы видим, что в dll функция должна иметь имя JNICALL Java_TestJNI_sum и принимать 4 параметра. Для простейших функций первые два из них нам не понадобятся. Всякие странные типы данных вроде JNIEXPORT, как несложно догадаться, объявлены в файле jni.h, который входит в состав JDK.
Напишем теперь библиотеку:
Скомпилируем и слинкуем:
На выходе получим .dll-файл, который и нужно передавать коду на Java.
Положим файлы mydll.dll и TestJNI.class в одну папку и посмотрим, что получилось:
Это победа, мы научились складывать два числа!
Если вдруг кому-то стало интересно, то ему пригодятся следующие ссылки:
Инструментарий
Для создания сего извращения применялись Windows, Oracle JDK, Yasm (без макросов) и линковщик из Microsoft Visual Studio 2010. При желании, заменить любой из этих компонентов на ваш любимый не составит большого труда, я старался не использовать всякие нестандартные фичи.
Использование JNI
Для начала, создадим на Java класс, в котором будет вызываться функция из будущей .dll'ки:
public class TestJNI { native static int sum(int x, int y); // импортируемая функция sum public static void main(String[] args) { System.loadLibrary("mydll"); // загружаем библиотеку mydll.dll System.out.println(sum(2, 3)); // вызываем функцию } }
Name Mangling в Java
Теперь нужно узнать, какое имя функции рассчитывает найти в нашей библиотеке Java-машина. Воспользуемся для этого программой javah из JDK:
javac TestJNI.java
javah TestJNI
При этом будет сгенерирован C++ header:
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class TestJNI */ #ifndef _Included_TestJNI #define _Included_TestJNI #ifdef __cplusplus extern "C" { #endif /* * Class: TestJNI * Method: sum * Signature: (II)I */ JNIEXPORT jint JNICALL Java_TestJNI_sum (JNIEnv *, jclass, jint, jint); #ifdef __cplusplus } #endif #endif
Информация, содержащаяся здесь, нужна главным образом для дальнейшего написания библиотеки на C++, нас же интересует только сигнатура функции:
JNIEXPORT jint JNICALL Java_TestJNI_sum (JNIEnv *, jclass, jint, jint);
Мы видим, что в dll функция должна иметь имя JNICALL Java_TestJNI_sum и принимать 4 параметра. Для простейших функций первые два из них нам не понадобятся. Всякие странные типы данных вроде JNIEXPORT, как несложно догадаться, объявлены в файле jni.h, который входит в состав JDK.
Ассемблер
Напишем теперь библиотеку:
;mydll.asm section .text global Java_TestJNI_sum Java_TestJNI_sum: mov eax, [esp + 12] ; игнорируем первые 2 параметра add eax, [esp + 16] ret 16 end
;mydll.def LIBRARY mydll EXPORTS Java_TestJNI_sum
Скомпилируем и слинкуем:
yasm -f win32 mydll.asm
link /SUBSYSTEM:windows /DLL /NOENTRY /DEF:mydll.def mydll.obj
На выходе получим .dll-файл, который и нужно передавать коду на Java.
Результат
Положим файлы mydll.dll и TestJNI.class в одну папку и посмотрим, что получилось:
>java TestJNI
5
Это победа, мы научились складывать два числа!
Если вдруг кому-то стало интересно, то ему пригодятся следующие ссылки:
- Invoking Assembly Language Programs from Java — очень хорошая и гораздо более подробная статья на английском на эту тему;
- Writing dll in assembler for external calling in Maple — создание библиотеки на ассемблере для Maple;
- Java Native Interface: Programmer's Guide and Specification — толстенная книга по JNI.