From df28ea8c0f39467a3f40af403f2a866e6479dc0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Romain=20Tarti=C3=A8re?= Date: Mon, 11 Mar 2019 15:06:39 -1000 Subject: [PATCH] Add support for dlvsym(3) --- jni/jffi/Library.c | 23 ++++++++++ libtest/GNUmakefile | 8 ++-- libtest/VersionedSymbols.c | 14 ++++++ libtest/libtest.map | 2 + src/main/java/com/kenai/jffi/Foreign.java | 10 +++++ src/main/java/com/kenai/jffi/Library.java | 17 +++++++ src/test/java/com/kenai/jffi/UnitHelper.java | 7 +++ src/test/java/com/kenai/jffi/VersionTest.java | 44 +++++++++++++++++++ 8 files changed, 121 insertions(+), 4 deletions(-) create mode 100644 libtest/VersionedSymbols.c create mode 100644 libtest/libtest.map create mode 100644 src/test/java/com/kenai/jffi/VersionTest.java diff --git a/jni/jffi/Library.c b/jni/jffi/Library.c index c1dcd86d..ddc66ec4 100644 --- a/jni/jffi/Library.c +++ b/jni/jffi/Library.c @@ -64,6 +64,7 @@ enum { RTLD_LAZY=1, RTLD_NOW, RTLD_GLOBAL, RTLD_LOCAL }; const char *e = dlerror(); snprintf(buf, size, "%s", e ? e : "unknown"); \ } while(0) # define dl_sym(handle, name) dlsym(handle, name) +# define dl_vsym(handle, name, version) dlvsym(handle, name, version) # define dl_close(handle) dlclose(handle) #ifndef RTLD_LOCAL # define RTLD_LOCAL 8 @@ -154,6 +155,28 @@ Java_com_kenai_jffi_Foreign_dlsym(JNIEnv* env, jclass cls, jlong handle, jstring return p2j(addr); } +JNIEXPORT jlong JNICALL +Java_com_kenai_jffi_Foreign_dlvsym(JNIEnv* env, jclass cls, jlong handle, jstring jsymbol, jstring jversion) +{ + char sym[1024]; + char version[1024]; + void* addr; + + getMultibyteString(env, sym, jsymbol, sizeof(sym)); + getMultibyteString(env, version, jversion, sizeof(version)); +#ifndef _WIN32 + dlerror(); // clear any errors +#endif + addr = dl_vsym(j2p(handle), sym, version); + if (addr == NULL) { + char errbuf[1024] = { 0 }; + dl_error(errbuf, sizeof(errbuf) - 1); + throwException(env, UnsatisfiedLink, "%s", errbuf); + } + + return p2j(addr); +} + /* * Class: com_kenai_jffi_Foreign * Method: dlerror diff --git a/libtest/GNUmakefile b/libtest/GNUmakefile index f16ceb70..9f43f6ed 100644 --- a/libtest/GNUmakefile +++ b/libtest/GNUmakefile @@ -47,7 +47,7 @@ JFLAGS = -fno-omit-frame-pointer -fno-strict-aliasing OFLAGS = -O2 $(JFLAGS) WFLAGS = -W -Werror -Wall -Wno-unused -Wno-unused-parameter -Wno-parentheses PICFLAGS = -fPIC -SOFLAGS = -shared -Wl,-O1 +SOFLAGS = -shared -Wl,-O1 -Wl,--version-script,$(SRC_DIR)/libtest.map LDFLAGS += $(SOFLAGS) IFLAGS = -I"$(BUILD_DIR)" @@ -127,17 +127,17 @@ ifeq ($(OS), solaris) CC = gcc CFLAGS += -std=c99 LD = /usr/ccs/bin/ld - SOFLAGS = -shared -static-libgcc + SOFLAGS = -shared -static-libgcc -Wl,--version-script,$(SRC_DIR)/libtest.map endif ifeq ($(OS), aix) LIBEXT = a - SOFLAGS = -shared -static-libgcc + SOFLAGS = -shared -static-libgcc -Wl,--version-script,$(SRC_DIR)/libtest.map PICFLAGS += -pthread endif ifneq ($(findstring bsd, $(OS)),) - SOFLAGS = -shared -static-libgcc + SOFLAGS = -shared -static-libgcc -Wl,--version-script,$(SRC_DIR)/libtest.map CFLAGS += -pthread LDFLAGS += -pthread endif diff --git a/libtest/VersionedSymbols.c b/libtest/VersionedSymbols.c new file mode 100644 index 00000000..57ad9c5c --- /dev/null +++ b/libtest/VersionedSymbols.c @@ -0,0 +1,14 @@ +int +old_answer(void) +{ + return 41; +} + +int +new_answer(void) +{ + return 42; +} + +__asm__(".symver old_answer,answer@VERS_1.0"); +__asm__(".symver new_answer,answer@@VERS_1.1"); diff --git a/libtest/libtest.map b/libtest/libtest.map new file mode 100644 index 00000000..a4d74572 --- /dev/null +++ b/libtest/libtest.map @@ -0,0 +1,2 @@ +VERS_1.0 {}; +VERS_1.1 {}; diff --git a/src/main/java/com/kenai/jffi/Foreign.java b/src/main/java/com/kenai/jffi/Foreign.java index d3606479..07aaab39 100644 --- a/src/main/java/com/kenai/jffi/Foreign.java +++ b/src/main/java/com/kenai/jffi/Foreign.java @@ -290,6 +290,16 @@ static boolean isMemoryProtectionEnabled() { */ static native long dlsym(long handle, String name); + /** + * Locates the memory address of the specified version of a dynamic library symbol. + * + * @param handle A dynamic library handle obtained from {@link #dlopen} + * @param name The name of the symbol. + * @param version The version of the symbol. + * @return The address where the symbol in loaded in memory. + */ + static native long dlvsym(long handle, String name, String version); + /** * Gets the last error raised by {@link #dlopen} or {@link #dlsym} * diff --git a/src/main/java/com/kenai/jffi/Library.java b/src/main/java/com/kenai/jffi/Library.java index 9ce61fd9..34a0ff3f 100644 --- a/src/main/java/com/kenai/jffi/Library.java +++ b/src/main/java/com/kenai/jffi/Library.java @@ -182,6 +182,23 @@ public final long getSymbolAddress(String name) { } } + /** + * Gets the address of a symbol within the Library. + * + * @param name The name of the symbol to locate. + * @param version The version of the symbol to locate. + * @return The address of the symbol within the current address space. + */ + public final long getSymbolAddressWithVersion(String name, String version) { + try { + return foreign.dlvsym(handle, name, version); + + } catch (UnsatisfiedLinkError ex) { + lastError.set(foreign.dlerror()); + return 0; + } + } + /** * Gets the current error string from dlopen/LoadLibrary. * diff --git a/src/test/java/com/kenai/jffi/UnitHelper.java b/src/test/java/com/kenai/jffi/UnitHelper.java index 87460ab1..e8f9a346 100644 --- a/src/test/java/com/kenai/jffi/UnitHelper.java +++ b/src/test/java/com/kenai/jffi/UnitHelper.java @@ -112,6 +112,13 @@ public static Address findSymbol(String name) { } return new Address(address); } + public static Address findSymbolWithVersion(String name, String version) { + final long address = LibraryHolder.libtest.getSymbolAddressWithVersion(name, version); + if (address == 0L) { + throw new UnsatisfiedLinkError("Could not locate symbol '" + name + "' at version '" + version + "'"); + } + return new Address(address); + } private static final class NativeInvocationHandler implements InvocationHandler { private final ConcurrentMap invokers = new ConcurrentHashMap(); diff --git a/src/test/java/com/kenai/jffi/VersionTest.java b/src/test/java/com/kenai/jffi/VersionTest.java new file mode 100644 index 00000000..f9a46679 --- /dev/null +++ b/src/test/java/com/kenai/jffi/VersionTest.java @@ -0,0 +1,44 @@ +package com.kenai.jffi; + +import org.junit.Test; + +import com.kenai.jffi.InvokerTest.HeapInvoker; +import com.kenai.jffi.InvokerTest.NativeInvoker; +import com.kenai.jffi.UnitHelper.Address; + +import static org.junit.Assert.assertEquals; + +public class VersionTest { + + public VersionTest() { + } + + @Test public void old_answer() { + Invoker invoker = new NativeInvoker(); + + Address sym = UnitHelper.findSymbolWithVersion("answer", "VERS_1.0"); + Function function = new Function(sym.address, Type.SINT); + CallContext ctx = new CallContext(Type.SINT); + + long res = invoker.invokeN0(ctx, function.getFunctionAddress()); + + assertEquals(41, res); + } + + @Test public void new_answer() { + Invoker invoker = new HeapInvoker(); + + Address sym = UnitHelper.findSymbolWithVersion("answer", "VERS_1.1"); + Function function = new Function(sym.address, Type.SINT); + CallContext ctx = new CallContext(Type.SINT); + + long res = invoker.invokeN0(ctx, function.getFunctionAddress()); + + assertEquals(42, res); + } + + @Test(expected = UnsatisfiedLinkError.class) + public void future_answer() { + UnitHelper.findSymbolWithVersion("answer", "VERS_1.2"); + } +}