Skip to content

Add support for dlvsym(3) #66

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions jni/jffi/Library.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions libtest/GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -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)"
Expand Down Expand Up @@ -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
Expand Down
14 changes: 14 additions & 0 deletions libtest/VersionedSymbols.c
Original file line number Diff line number Diff line change
@@ -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");
2 changes: 2 additions & 0 deletions libtest/libtest.map
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
VERS_1.0 {};
VERS_1.1 {};
10 changes: 10 additions & 0 deletions src/main/java/com/kenai/jffi/Foreign.java
Original file line number Diff line number Diff line change
Expand Up @@ -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}
*
Expand Down
17 changes: 17 additions & 0 deletions src/main/java/com/kenai/jffi/Library.java
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,23 @@ public final long getSymbolAddress(String name) {
}
}

/**
* Gets the address of a symbol within the <tt>Library</tt>.
*
* @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.
*
Expand Down
7 changes: 7 additions & 0 deletions src/test/java/com/kenai/jffi/UnitHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<Method, MethodInvoker> invokers
= new ConcurrentHashMap<Method, MethodInvoker>();
Expand Down
44 changes: 44 additions & 0 deletions src/test/java/com/kenai/jffi/VersionTest.java
Original file line number Diff line number Diff line change
@@ -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");
}
}