Вызов собственного кода из приложения Java, скомпилированного AOT, с использованием CNI

Компилятор GNU для Java предоставляет два метода для вызова собственного кода из приложения Java.

  1. Во-первых, существует спецификация JNI , разработанная Sun Microsystems , и GCJ придерживается этой спецификации.
  2. Во-вторых, есть CNI, скомпилированный собственный интерфейс , реализованный только GCJ, который почти не документирован, но позволяет реализовать собственные методы на C++, предоставляя API более высокого уровня для взаимодействия между собственным кодом и JVM.

Теперь, поскольку GCJ также допускает предварительную компиляцию, это дает нам следующие возможности вызывать собственный код из кода Java:

  1. Запустите обычную JVM с помощьюgijзагрузите свой основной класс, который загрузит собственную библиотеку, используяи вызовите собственный метод. Это обычный способ, к которому мы привыкли при работе с JVM Sun/Oracle.
  2. То же, что и выше, но AOT-компилирует ваш байт-код в собственный код, используяgcjи динамически связать полученный исполняемый файл с внешней собственной библиотекой с помощью-lвыключатель. Вызов можно сохранить для совместимости, но на самом деле он не нужен, поскольку библиотека уже будет загружена динамическим интерпретатором задолго до создания JVM. На самом деле есть три подварианта:
    • Загрузите библиотеку с помощью JVM через , аналогичноdlopen().
    • Связать библиотеку с помощьюld, с помощьюgcj -l. Это удобнее, чемloadLibrary()подход, поскольку вы можете пройти-rpathпереключитесь на компоновщик и избегайте необходимости вручную устанавливатьLD_LIBRARY_PATHперед запуском исполняемого файла.
    • Ничего из вышеперечисленного, но библиотека предварительно загружена черезLD_PRELOAD.
  3. Статически свяжите объект Java, скомпилированный с помощью AOT, с объектом, созданным из части собственного кода. Это полностью избавляет нас от необходимости звонитьSystem.loadLibrary()(поскольку библиотеки нет), что делает код собственного метода сразу доступным в результирующем исполняемом файле.

Поскольку у нас есть два протокола доступа к собственному коду (JNI и CNI), это удваивает количество доступных опций (с 3 до 6).

Хотя подход CNI со статическим связыванием работает как прелесть, я хочу также изучить, как динамическое связывание работает вместе с CNI и компиляцией AOT, и это не удается во время компоновки.

Предположим, у меня есть простой основной класс:

      public final class Main {
        static {
                System.loadLibrary("foo");
        }

        private Main() {
                assert false;
        }

        public static native void foo();

        public static void main(final String args[]) {
                foo();
        }
}

The Main.hСозданный из него заголовок CNI выглядит следующим образом:

      #ifndef __Main__
#define __Main__

#pragma interface

#include <java/lang/Object.h>
#include <gcj/array.h>

extern "Java"
{
    class Main;
}

class Main : public ::java::lang::Object
{

  Main();
public:
  static void foo();
  static void main(JArray< ::java::lang::String * > *);
public: // actually package-private
  static jboolean $assertionsDisabled;
public:
  static ::java::lang::Class class$;
};

#endif // __Main__

... и вот довольно стандартная реализация общей библиотеки по умолчанию:

      #include <iostream>

#include <gcj/cni.h>

#include "Main.h"

void Main::foo() {
        std::cout << "Hello, World!" << std::endl;
}

При связывании объекта, созданного из основного класса Java, с внешней библиотекой:

      gcj -pie -fPIE -save-temps --main=Main -o cni-dynamic-native -L/opt/gcc64/6.5/lib64 -Wl,-rpath=/opt/gcc64/6.5/lib64 -L. -Wl,-rpath='$ORIGIN' -lstdc++ -lgcj -lfoo Main.class

Я получаю следующую ошибку:

      /usr/bin/ld: Main.o: in function `void Main::main(JArray<java::lang::String*>*)':
Main.java:13: undefined reference to `hidden alias for void Main::foo()'
/usr/bin/ld: Main.o:(.data.rel+0xc0): undefined reference to `hidden alias for void Main::foo()'
collect2: error: ld returned 1 exit status

Действительно, если я исследуюMain.oобъект сnm, есть неопределенный символ:

                       U hidden alias for void Main::foo()

Символ присутствует в общей библиотеке (libfoo.so) Я ссылаюсь против. Это глобально (T) в объектном файле, но статический (t) в общей библиотеке.

      0000000000001180 t hidden alias for void Main::foo()

Та же проблема описана в этом отчете об ошибке , но решения, к сожалению, нет. Как я уже сказал, всё работает отлично при статическом связывании двух объектов с помощью CNI или динамическом с помощью JNI (пример кода ).

Итак, как мне загрузить собственную библиотеку в исполняемый файл, скомпилированный с помощью AOT, с помощью интерфейса CNI? Был ли CNI когда-либо предназначен для динамического связывания?

0 ответов

Другие вопросы по тегам