diff --git a/libtest/GNUmakefile b/libtest/GNUmakefile index 6aba23f1e..b916388dd 100644 --- a/libtest/GNUmakefile +++ b/libtest/GNUmakefile @@ -40,7 +40,7 @@ JFLAGS = -fno-omit-frame-pointer -fno-strict-aliasing OFLAGS = -O2 $(JFLAGS) WFLAGS = -W -Wall -Wno-unused -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)" diff --git a/libtest/VersionedSymbols.c b/libtest/VersionedSymbols.c new file mode 100644 index 000000000..57ad9c5cf --- /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 000000000..a4d74572e --- /dev/null +++ b/libtest/libtest.map @@ -0,0 +1,2 @@ +VERS_1.0 {}; +VERS_1.1 {}; diff --git a/src/main/java/jnr/ffi/annotations/Function.java b/src/main/java/jnr/ffi/annotations/Function.java new file mode 100644 index 000000000..c74a8375f --- /dev/null +++ b/src/main/java/jnr/ffi/annotations/Function.java @@ -0,0 +1,12 @@ +package jnr.ffi.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface Function { + String value(); +} diff --git a/src/main/java/jnr/ffi/annotations/Version.java b/src/main/java/jnr/ffi/annotations/Version.java new file mode 100644 index 000000000..8e127c074 --- /dev/null +++ b/src/main/java/jnr/ffi/annotations/Version.java @@ -0,0 +1,12 @@ +package jnr.ffi.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface Version { + String value(); +} diff --git a/src/main/java/jnr/ffi/provider/jffi/AsmLibraryLoader.java b/src/main/java/jnr/ffi/provider/jffi/AsmLibraryLoader.java index a6c9ea679..0276b995a 100644 --- a/src/main/java/jnr/ffi/provider/jffi/AsmLibraryLoader.java +++ b/src/main/java/jnr/ffi/provider/jffi/AsmLibraryLoader.java @@ -152,10 +152,24 @@ private T generateInterfaceImpl(final NativeLibrary library, Class interf continue; } - String functionName = functionMapper.mapFunctionName(function.name(), new NativeFunctionMapperContext(library, function.annotations())); + String searchName = function.name(); + String searchVersion = null; + + for (java.lang.annotation.Annotation a : function.annotations()) { + if (a.annotationType() == jnr.ffi.annotations.Function.class) { + jnr.ffi.annotations.Function ann = (jnr.ffi.annotations.Function) a; + searchName = ann.value(); + } + if (a.annotationType() == jnr.ffi.annotations.Version.class) { + jnr.ffi.annotations.Version ann = (jnr.ffi.annotations.Version) a; + searchVersion = ann.value(); + } + } + + String functionName = functionMapper.mapFunctionName(searchName, new NativeFunctionMapperContext(library, function.annotations())); try { - long functionAddress = library.findSymbolAddress(functionName); + long functionAddress = library.findSymbolAddress(functionName, searchVersion); FromNativeContext resultContext = new MethodResultContext(runtime, function.getMethod()); SignatureType signatureType = DefaultSignatureType.create(function.getMethod().getReturnType(), resultContext); diff --git a/src/main/java/jnr/ffi/provider/jffi/NativeLibrary.java b/src/main/java/jnr/ffi/provider/jffi/NativeLibrary.java index d447d96e8..6de166ff7 100644 --- a/src/main/java/jnr/ffi/provider/jffi/NativeLibrary.java +++ b/src/main/java/jnr/ffi/provider/jffi/NativeLibrary.java @@ -46,8 +46,17 @@ private String locateLibrary(String libraryName) { } long getSymbolAddress(String name) { + return getSymbolAddress(name, null); + } + + long getSymbolAddress(String name, String version) { for (com.kenai.jffi.Library l : getNativeLibraries()) { - long address = l.getSymbolAddress(name); + long address; + if (version != null) + address = l.getSymbolAddressWithVersion(name, version); + else + address = l.getSymbolAddress(name); + if (address != 0) { return address; } @@ -56,7 +65,11 @@ long getSymbolAddress(String name) { } long findSymbolAddress(String name) { - long address = getSymbolAddress(name); + return findSymbolAddress(name, null); + } + + long findSymbolAddress(String name, String version) { + long address = getSymbolAddress(name, version); if (address == 0) { throw new SymbolNotFoundError(com.kenai.jffi.Library.getLastError()); } diff --git a/src/test/java/jnr/ffi/VersionTest.java b/src/test/java/jnr/ffi/VersionTest.java new file mode 100644 index 000000000..5a073f9c4 --- /dev/null +++ b/src/test/java/jnr/ffi/VersionTest.java @@ -0,0 +1,83 @@ +package jnr.ffi; + +import jnr.ffi.annotations.Function; +import jnr.ffi.annotations.Version; + +import jnr.ffi.types.*; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import static org.junit.Assert.*; + + +public class VersionTest { + public VersionTest() { + } + + public static interface TestLib { + public int answer(); + public int old_answer(); + public int new_answer(); + + @Function("answer") + @Version("VERS_1.0") + public int answer_1_0(); + + @Function("answer") + @Version("VERS_1.1") + public int answer_1_1(); + } + static TestLib testlib; + + public static interface OldTestLib { + @Version("VERS_1.0") + public int answer(); + } + static OldTestLib oldtestlib; + + @BeforeClass + public static void setUpClass() throws Exception { + testlib = TstUtil.loadTestLib(TestLib.class); + oldtestlib = TstUtil.loadTestLib(OldTestLib.class); + } + + @AfterClass + public static void tearDownClass() throws Exception { + } + + @Test + public void testAnswer() { + int res = testlib.answer(); + assertEquals(42, res); + } + + @Test + public void testOldAnswer() { + int res = testlib.old_answer(); + assertEquals(41, res); + } + + @Test + public void testNewAnswer() { + int res = testlib.new_answer(); + assertEquals(42, res); + } + + @Test + public void testAnswer_1_0() { + int res = testlib.answer_1_0(); + assertEquals(41, res); + } + + @Test + public void testAnswer_1_1() { + int res = testlib.answer_1_1(); + assertEquals(42, res); + } + + @Test + public void oldTestAnswer() { + int res = oldtestlib.answer(); + assertEquals(41, res); + } +}