簡體   English   中英

如何構建從另一個 DLL 調用函數的 JNI DLL? - JNI,搖籃

[英]How to build JNI DLL that calls function from another DLL? - JNI, Gradle

我已經在這個repo 中實現了 IDEA 網站關於 JNI 的指南中給出的示例(你可以在那里看到 fork),現在它甚至可以在我的 aarch64 Mac 上運行現代 JDK 和 JUnit 測試(希望它可以在其他任何地方運行)。 但我真的不明白在hello Gradle 子項目文件夾中的build.gradle中發生了什么。 我可以在那里識別編譯器參數,但包含選項是在單獨的方法中設置的,這讓我感到困惑。

我的目標是只包裝 C 文件來執行dlopen (或其 Windows 的替代方案)並運行動態庫中的一些函數。

例如

<ProjectRoot>/hello/src/main/c/ wrapper-with-JNI-call-convensions.c 
                                libsomelib.dylib
                                libsomelib.so
                                somelib.dll

Calls looks like this: Java -[JNI]-> wrapper.dll -> somelib.dll
                                     ^    ^    ^
                                     |    |    |
                         building wrapper.c using Gradle to this

因此,如果我只使用兩個 C 源文件(使用 JNI 和我的庫的 C 源代碼的包裝器)運行構建,我已經成功地使 JNI 工作(同時不更改示例中的build.gradle )。 然后我使用 macOS 內置的clang編譯器構建了dylib並編寫了正確的(我猜) dlopendlsym代碼。 但是當我構建項目時,我從dlopen調用返回 NULL。

我試圖向用於編譯使用動態庫的二進制文件的build.gradle添加一些參數,但它沒有幫助。

我的測試運行在這里退出:

// my wrapper
void* dlHandle = dlopen("libsomelib.dylib", RTLD_LAZY);
    if (dlHandle == NULL) {
        printf("DLL NOT OPENED !!!\n");
        exit(0);
    }

我知道我必須在我的包裝器中編寫適當的平台相關調用,這對我來說沒什么大不了的。

那么你能告訴我如何使用 Gradle 正確地讓它工作嗎? 請記住,在我的例子中,目標是只有 JNI-ready *.c包裝器和動態鏈接庫*.so & *.dylib & *.dll

更新:我決定不使用 Gradle,我使用 VSCode tasks.json ,它的直觀性提高了 20 倍。 我會寫我的方法作為自我回答。

要讓它運行,您可以嘗試以下操作:

> cc -g -shared -fpic wrapper.c -L. -lsome -o libwrapper.dylib

我假設您在當前目錄中有libsome.dylib 然后你可以在JNI加載libwrapper.dylib並引用另一個庫: libsome.dylib

更新

假設您有這兩個文件:

#include <stdio.h>

void anotherFunction () {
  // we are printing message from another C file
  printf ("Hello from another function!\n");
}

以及對應的頭文件

#ifndef another_h__
#define another_h__

void anotherFunction (void)
#endif                          // another_h__

您可以通過調用以下內容來構建一個庫:

cc -shared -fpic recipeNo023_AnotherFunction.c -o libanother.dylib

這將相當於你的: somelib

然后,我們可以有這樣的事情:

#include "recipeNo023_redux_HelloWorld.h"

void anotherFunction ();

JNIEXPORT void JNICALL Java_recipeNo023_redux_HelloWorld_displayMessage
  (JNIEnv * env, jclass obj) {

  printf ("Hello world!\n");
  /* We are calling function from another source */

  anotherFunction ();
}

我們聲明函數並使用它。 名稱將在稍后在鏈接階段解析。

現在,您可以編譯wrapper庫。 在這個示例中,它是HelloWorld

cc -shared -fpic recipeNo023_redux_HelloWorld.c -L. -lanother

現在,在JNI ,您要做的就是:

System.loadLibrary("HelloWorld");

由於HelloWorld與共享庫鏈接, another它可以訪問其符號。 所以,你不需要使用dlopen

嘗試示例(甚至還有基於 Docker 的文件,因此您不必為設置環境而煩惱)。

正如@Oo.oO 提到的, 這個食譜正是我所需要的。 它是動態加載方法,適用於 Mac 和 Linux。 此外,正如他所提到的, 這里和他的回答中展示靜態鏈接方法。

我還會在其中添加一些編譯器選項:

-O3                     \   # for optimisation of speed
-x c                    \   # to explicitly set language to C, in my case
-fvisibility=default    \   # to export only that I need (read below)
-arch x86_64            \   # option ONLY for macOS built-in clang in case you want build Intel binary on Silicon Mac

在 lib 的源代碼中,您還應該定義

#define EXPORT __attribute__((visibility("default")))

然后將它寫在每個函數聲明之前的頭文件中,或者在每個要從外部或庫中使用的函數定義之前的源文件中。

例如

// somelib.h
EXPORT void somefunc(int a, int b);

// somelib.c
#include "somelib.h" // somelib.c and somelib.h in same directory
void somefunc(int a, int b) { ... }

或者

// in case you don't want to use header (I recommend to use)
// somelib.c
EXPORT void somefunc(int a, int b) { ... }

稍后將在 Windows 案例中推進此答案。

概括

macOS+ clang & Linux+ gcc

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM