blob: 7de9d8e3a20dac816ce7e67fd516c687287881ab [file] [log] [blame]
/*
* Copyright (c) 2009 Tomasz Grabiec
*
* This file is released under the GPL version 2 with the following
* clarification and special exception:
*
* Linking this library statically or dynamically with other modules is
* making a combined work based on this library. Thus, the terms and
* conditions of the GNU General Public License cover the whole
* combination.
*
* As a special exception, the copyright holders of this library give you
* permission to link this library with independent modules to produce an
* executable, regardless of the license terms of these independent
* modules, and to copy and distribute the resulting executable under terms
* of your choice, provided that you also meet, for each linked independent
* module, the terms and conditions of the license of that module. An
* independent module is a module which is not derived from or based on
* this library. If you modify this library, you may extend this exception
* to your version of the library, but you are not obligated to do so. If
* you do not wish to do so, delete this exception statement from your
* version.
*
* Please refer to the file LICENSE for details.
*/
#include <dlfcn.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "jit/disassemble.h"
#include "vm/die.h"
#include "vm/jni.h"
#include "vm/stack-trace.h"
#include "vm/gc.h"
#include "lib/hash-map.h"
#include "lib/string.h"
struct jni_object {
void *handle; /* returned by dlopen() */
struct vm_object *classloader;
};
struct hash_map *jni_objects;
static char *vm_jni_get_mangled_name(const char *name)
{
struct string *str;
char *result;
int err;
int i;
str = alloc_str();
if (!str)
return NULL;
result = NULL;
for (i = 0; name[i]; i++) {
if (name[i] == '_')
err = str_append(str, "_1");
else if (name[i] == ';')
err = str_append(str, "_2");
else if (name[i] == '[')
err = str_append(str, "_3");
else if (name[i] == '/')
err = str_append(str, "_");
else if (name[i] & 0x80) {
/*
* FIXME: Unicode characters should be transformed to "_0xxxx"
*/
die("unicode characters are not supported");
} else
err = str_append(str, "%c", (char)name[i]);
if (err)
goto out;
}
result = strdup(str->value);
out:
free_str(str);
return result;
}
static int vm_jni_add_object(void *handle, struct vm_object *classloader)
{
struct jni_object *object;
if (hash_map_contains(jni_objects, handle))
return 0;
object = malloc(sizeof(*object));
if (!object)
return -ENOMEM;
object->handle = handle;
object->classloader = classloader;
if (hash_map_put(jni_objects, handle, object)) {
free(object);
return -1;
}
return 0;
}
void vm_jni_init(void)
{
jni_objects = alloc_hash_map(&pointer_key);
if (!jni_objects)
error("failed to create jni_objects hash map");
}
typedef jint onload_fn(JavaVM *, void *);
int vm_jni_load_object(const char *name, struct vm_object *classloader)
{
void *handle;
handle = dlopen(name, RTLD_NOW);
if (!handle)
return -1;
if (vm_jni_add_object(handle, classloader)) {
dlclose(handle);
return -ENOMEM;
}
onload_fn *onload = dlsym(handle, "JNI_OnLoad");
if (!onload)
return 0;
vm_enter_jni(__builtin_frame_address(0), NULL,
(unsigned long) &&call_site);
call_site:
onload(vm_jni_get_current_java_vm(), NULL);
vm_leave_jni();
return 0;
}
static void *vm_jni_lookup_symbol(const char *symbol_name)
{
struct hash_map_entry *this;
hash_map_for_each_entry(this, jni_objects) {
struct jni_object *object;
void *addr;
object = this->value;
addr = dlsym(object->handle, symbol_name);
if (addr)
return addr;
}
return NULL;
}
static char *get_method_args(const char *type)
{
char *result, *end;
if (type[0] != '(')
return NULL;
result = strdup(type + 1);
end = index(result, ')');
if (!end) {
free(result);
return NULL;
}
*end = 0;
return result;
}
void *vm_jni_lookup_method(const char *class_name, const char *method_name,
const char *method_type)
{
char *mangled_class_name;
char *mangled_method_name;
char *mangled_method_type;
char *symbol_name;
void *sym_addr;
char *method_args;
method_args = get_method_args(method_type);
mangled_class_name = vm_jni_get_mangled_name(class_name);
mangled_method_name = vm_jni_get_mangled_name(method_name);
mangled_method_type = vm_jni_get_mangled_name(method_args);
symbol_name = NULL;
if (asprintf(&symbol_name, "Java_%s_%s__%s", mangled_class_name,
mangled_method_name, mangled_method_type) < 0)
die("asprintf");
sym_addr = vm_jni_lookup_symbol(symbol_name);
if (sym_addr)
goto out;
if (asprintf(&symbol_name, "Java_%s_%s", mangled_class_name,
mangled_method_name) < 0)
die("asprintf");
sym_addr = vm_jni_lookup_symbol(symbol_name);
out:
free(method_args);
free(mangled_method_name);
free(mangled_class_name);
free(mangled_method_type);
free(symbol_name);
return sym_addr;
}