blob: 28909dd4d98d58478ec4a9715dfc746f026d384c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author Pavel Rebriy, Ilya Berezhniuk
*/
#include <string.h>
#include <vector>
#include <apr_strings.h>
#define LOG_DOMAIN "natives"
#include "cxxlog.h"
#include "port_malloc.h"
#include "port_dso.h"
#include "exceptions.h"
#include "natives_support.h"
#include "environment.h"
#include "lock_manager.h"
#include "jni_direct.h" // FIXME ???? Can we use it here ????
#include "open/vm_util.h"
#include "port_filepath.h"
#include "jni_types.h"
#include "jni_utils.h"
typedef jint (JNICALL *f_JNI_OnLoad) (JavaVM *vm, void *reserved);
typedef void (JNICALL *f_JNI_OnUnload) (JavaVM *vm, void *reserved);
struct JNILibInfo
{
JNILibInfo(): ppool(NULL) {}
public:
NativeLibInfo* lib_info_list;
apr_pool_t* ppool;
Lock_Manager lock;
};
static JNILibInfo jni_libs;
// Initializes natives_support module. Caller must provide thread safety.
jint natives_init()
{
apr_status_t apr_status = apr_pool_create(&jni_libs.ppool, NULL);
if (APR_SUCCESS != apr_status)
{
assert(jni_libs.ppool == NULL);
return JNI_ENOMEM;
}
return JNI_OK;
}
// Searches for given name trough the list of loaded libraries
// Lock must be acquired by the caller
static NativeLibInfo* search_library_list(const char* library_name)
{
Global_Env *ge = VM_Global_State::loader_env;
String* name = ge->string_pool.lookup(library_name);
for(NativeLibraryList lib = jni_libs.lib_info_list;
lib; lib = lib->next)
{
if (name == lib->name)
return lib;
}
return NULL;
}
// Searches for JNI_OnLoad through given library and calls it
static jint find_call_JNI_OnLoad(NativeLibraryHandle lib_handle)
{
static const char* name_list[] =
{ "JNI_OnLoad"
#ifdef PLATFORM_NT
, "_JNI_OnLoad@8"
#endif
};
assert(lib_handle);
for (unsigned i = 0; i < sizeof(name_list)/sizeof(name_list[0]); i++)
{
f_JNI_OnLoad onload_func; // = (f_JNI_OnLoad)handle;
//apr_dso_handle_sym_t handle = NULL;
apr_status_t apr_status =
apr_dso_sym((apr_dso_handle_sym_t*)&onload_func, lib_handle, name_list[i]);
if (APR_SUCCESS != apr_status)
continue;
assert(onload_func);
JavaVM* vm = jni_get_java_vm(p_TLS_vmthread->jni_env); // FIXME ???? Can we use it here ????
assert(hythread_is_suspend_enabled());
jint res = onload_func(vm, NULL);
return res;
}
return 0;
}
// Searches for JNI_OnUnload through given library and calls it
// Lock must be acquired by the caller
// FIXME unused static
void find_call_JNI_OnUnload(NativeLibraryHandle lib_handle)
{
static const char* name_list[] =
{ "JNI_OnUnload"
#ifdef PLATFORM_NT
, "_JNI_OnUnload@8"
#endif
};
assert(lib_handle);
for (unsigned i = 0; i < sizeof(name_list)/sizeof(name_list[0]); i++)
{
f_JNI_OnUnload onunload_func;// = (f_JNI_OnUnload)handle;
apr_status_t apr_status =
apr_dso_sym((apr_dso_handle_sym_t*)&onunload_func, lib_handle, name_list[i]);
if (APR_SUCCESS != apr_status)
continue;
assert(onunload_func);
JavaVM* vm = jni_get_java_vm(p_TLS_vmthread->jni_env); // FIXME ???? Can we use it here ????
assert(hythread_is_suspend_enabled());
onunload_func(vm, NULL);
return;
}
}
static inline bool is_name_lowercase(const char* name)
{
for(; *name; name++)
{
if (isalpha(*name) && !islower(*name))
return false;
}
return true;
}
/**
* Creates a copy of the argument with all characters
* lowercase
*
* returns a new copy of the string with all chars lowercase
*/
static inline char *convert_to_lowercase(const char* name)
{
if (name == NULL) {
return NULL;
}
char *newCopy = strdup(name);
char *temp = newCopy;
for(; *temp; temp++)
{
if (isalpha(*temp) && !islower(*temp)) {
*temp = tolower(*temp);
}
}
return newCopy;
}
void ncai_library_load_callback(const char* name);
void ncai_library_unload_callback(const char* name);
// Function loads native library with a given name.
NativeLibraryHandle
natives_load_library(const char* library_name, bool* just_loaded,
NativeLoadStatus* pstatus)
{
assert(NULL != library_name);
assert(NULL != pstatus);
char *localLibName = (char *) library_name;
NativeLibraryHandle returnCode = NULL;
NativeLibInfo* pfound;
NativeLibInfo* pinfo;
NativeLibraryHandle handle;
apr_status_t apr_status;
Global_Env *ge;
jint UNREF res;
#ifdef PLATFORM_NT
TRACE2("init", "### lib name = " << library_name);
/*
* always convert on NT (and any other non-case-sensitive
* platform - it's as fast as checking to see if
* lowercase, and keeps the free() logic simpler
*/
localLibName = convert_to_lowercase(localLibName);
TRACE2("init", "### lib name = " << localLibName);
assert(is_name_lowercase(localLibName));
#endif
jni_libs.lock._lock();
pfound = search_library_list(localLibName);
if (pfound)
{
jni_libs.lock._unlock();
*just_loaded = false;
*pstatus = APR_SUCCESS;
returnCode = pfound->handle;
goto NATIVES_LOAD_LIBRARY_EXIT;
}
*just_loaded = true;
// library was not loaded previously, try to load it
apr_status = port_dso_load_ex(&handle, localLibName,
PORT_DSO_BIND_DEFER, jni_libs.ppool);
if (APR_SUCCESS != apr_status)
{
char buf[1024];
apr_dso_error( handle, buf, 1024 );
TRACE( "Natives: could not load library " << localLibName << "; " << buf );
jni_libs.lock._unlock();
*pstatus = apr_status;
returnCode = NULL;
goto NATIVES_LOAD_LIBRARY_EXIT;
}
pinfo = (NativeLibInfo*)apr_palloc(jni_libs.ppool, sizeof(NativeLibInfo));
if (NULL == pinfo)
{
apr_dso_unload(handle);
jni_libs.lock._unlock();
LDIE(29,"{0} apr_palloc failed" << "natives_load_library: ");
returnCode = NULL;
goto NATIVES_LOAD_LIBRARY_EXIT;
}
pinfo->handle = handle;
ge = VM_Global_State::loader_env;
pinfo->name = ge->string_pool.lookup(localLibName);
pinfo->next = jni_libs.lib_info_list;
jni_libs.lib_info_list = pinfo;
ncai_library_load_callback(localLibName);
jni_libs.lock._unlock();
res = find_call_JNI_OnLoad(pinfo->handle); // What to do with result???
*pstatus = APR_SUCCESS;
returnCode = pinfo->handle;
NATIVES_LOAD_LIBRARY_EXIT :
#ifdef PLATFORM_NT
free(localLibName);
#endif
return returnCode;
} // natives_load_library
// Function unloads native library.
static void
natives_unload_library_internal(NativeLibInfo* pfound)
{
assert(pfound);
// FIXME find_call_JNI_OnUnload(pfound->handle);
apr_dso_unload(pfound->handle);
} // natives_unload_library
// Function unloads native library.
void
natives_unload_library(NativeLibraryHandle library_handle)
{
assert(NULL != library_handle);
NativeLibInfo* prev = NULL;
for(NativeLibInfo* lib = jni_libs.lib_info_list;
lib; lib = lib->next)
{
if(lib->handle == library_handle)
{
jni_libs.lock._lock();
if(jni_libs.lib_info_list == lib) {
jni_libs.lib_info_list = lib->next;
} else {
prev->next = lib->next;
}
ncai_library_unload_callback(lib->name->bytes);
natives_unload_library_internal(lib);
jni_libs.lock._unlock();
return;
}
prev = lib;
}
} // natives_unload_library
// Function looks for loaded native library with a given name.
bool
natives_is_library_loaded(const char* library_name)
{
return (search_library_list(library_name) != NULL);
} // natives_is_library_loaded
// Cleanups natives_support module. Cleans all remaining libraries.
// Is tread safety provided?????
void
natives_cleanup()
{
for(NativeLibraryList lib = jni_libs.lib_info_list;
lib; lib = lib->next)
{
natives_unload_library_internal(lib);
}
apr_pool_destroy(jni_libs.ppool);
jni_libs.ppool = NULL;
} // natives_cleanup
// Function calculates mangled string length.
static inline int
natives_get_mangled_string_length( const char *string )
{
int added = 0;
assert(string);
// calculate mangled string length
int len = (int)strlen(string);
for( int index = 0; index < len; index++ ) {
switch( string[index] ) {
case '_':
case ';':
case '[':
added++;
break;
case '$':
added += 6 - 1;
break;
case '(':
added--;
continue;
case ')':
added -= len - index;
index = len;
break;
default:
break;
}
}
return added + len;
} // natives_get_mangled_string_length
// Function mangles string.
static inline char *
natives_get_mangled_string( char *buf,
const char *string)
{
assert(buf);
assert(string);
// mangle string
int len = (int)strlen(string);
for( int index = 0; index < len; index++ ) {
switch( string[index] ) {
case '_':
*buf = '_';
*(buf + 1) = '1';
buf += 2;
break;
case ';':
*buf = '_';
*(buf + 1) = '2';
buf += 2;
break;
case '[':
*buf = '_';
*(buf + 1) = '3';
buf += 2;
break;
case '/':
*buf = '_';
buf++;
break;
case '$':
{
int len = sprintf(buf, "_0%04x", '$' );
assert(len == 6);
buf += len;
}
break;
case '(':
continue;
case ')':
index = len;
break;
default:
*buf = string[index];
buf++;
break;
}
}
return buf;
} // natives_get_mangled_string
// Function returns mangled symbol name length based on class and method names.
static inline int
natives_get_mangled_symbol_name_length( const char* klass,
const char* name)
{
// 1. prefix 'Java_'
int len = 5;
// 2. class name
len += natives_get_mangled_string_length( klass );
// 3. an underscore ('_') separator
len++;
// 4. method name
len += natives_get_mangled_string_length( name );
return len;
} // natives_get_mangled_symbol_name_length
// Function extends mangled symbol name length to mandled overloaded symbol
// name length which is based on class name method name and descriptor of method.
static inline int
natives_get_overloaded_length( int mandled_len,
const char* desc)
{
// 5. for overloaded an underscore ('__') separator
mandled_len += 2;
// 6. method descriptor
mandled_len += natives_get_mangled_string_length( desc );
return mandled_len;
} // natives_get_overloaded_length
// Function returns mangled symbol name based on class and method names.
static void
natives_get_mangled_symbol_name( char *buf,
const char* klass,
const char* name)
{
// 1. prefix 'Java_'
memcpy( buf, "Java_", 5 );
buf += 5;
// 2. class name
buf = natives_get_mangled_string( buf, klass );
// 3. an underscore ('_') separator
*buf = '_';
buf++;
// 4. method name
buf = natives_get_mangled_string( buf, name );
// 5. end of string
*buf = '\0';
return;
} // natives_get_mangled_symbol_name
// Function extends mangled symbol name to mangled overloaded symbol name
// based on class name, method name and descriptor of method.
static void
natives_get_overloaded_name( char *buf,
const char* desc)
{
// 5. for overloaded an underscore ('__') separator
buf[0] = '_';
buf[1] = '_';
buf += 2;
// 6. method descriptor
buf = natives_get_mangled_string( buf, desc );
// 7. end of string
*buf = '\0';
return;
} // natives_get_overloaded_name
#ifdef PLATFORM_NT
// Function calculates function arguments length in bytes
// and writes string to the buffer.
static inline unsigned
natives_get_args_len(const char* desc)
{
// calculate argument length
unsigned value = 8;
int len = (int)strlen(desc);
bool is_array = false;
for( int index = 0; index < len; index++ ) {
switch( desc[index] ) {
case 'L':
// skip class name
do {
index++;
} while( desc[index] != ';' );
case 'B':
case 'C':
case 'F':
case 'I':
case 'S':
case 'Z':
value += 4;
is_array = false;
break;
case 'D':
case 'J':
if( is_array ) {
value += 4;
} else {
value += 8;
}
is_array = false;
break;
case '[':
is_array = true;
break;
case '(':
break;
case ')':
index = len;
break;
default:
break;
}
}
return value;
} // natives_append_args_size
// Function extends mangled symbol name length to mangled name length for Windows.
static inline int
natives_get_win_length( int mandled_len,
const char* desc)
{
// 1. prefix '_' before 'Java_'
mandled_len++;
// 2. postfix '@' and bytes amount of arguments
mandled_len += 1 + 10;
return mandled_len;
} // natives_get_win_length
// Function extends mangled symbol name to mangled name for Windows.
static inline void
natives_get_win_name(char* buf,
const char* symbol,
unsigned args_len)
{
// 1. prefix '_' before 'Java_'
*buf = '_';
buf++;
// 2. copy symbol name
int len = (int)strlen(symbol);
memcpy( buf, symbol, len);
buf += len;
// 3. postfix '@'
*buf = '@';
buf++;
// 4. size of arguments in bytes
sprintf(buf, "%d", args_len);
return;
} // natives_get_win_name
#endif // PLATFORM_NT
static GenericFunctionPointer
natives_lookup_symbol(NativeLibraryList libraries, const char* symbol)
{
for(NativeLibInfo* lib = libraries; lib; lib = lib->next) {
apr_dso_handle_sym_t func;
apr_status_t res = apr_dso_sym(&func, lib->handle, symbol);
if(res == APR_SUCCESS) {
return (GenericFunctionPointer)func;
}
}
return NULL;
}
// Function looks for method with a given name and descriptor in a given native library.
GenericFunctionPointer
natives_lookup_method( NativeLibraryList libraries,
const char* class_name,
const char* method_name,
const char* method_desc)
{
TRACE("Looking for native: "
<< class_name << "."
<< method_name << method_desc);
// get mangled symbol name length and mangled overloaded symbol length
int len = natives_get_mangled_symbol_name_length(class_name, method_name);
int ov_len = natives_get_overloaded_length(len, method_desc);
#ifdef PLATFORM_NT
// get lengths for Windows
int wlen = natives_get_win_length( len, method_desc );
int wov_len = ov_len + wlen - len;
int wargs_len = natives_get_args_len(method_desc);
#endif // PLATFORM_NT
// alloc memory for mangled overloaded symbol
char *name = (char*)STD_ALLOCA( ov_len + 1 );
// get mangled symbol name
natives_get_mangled_symbol_name(name, class_name, method_name);
TRACE("\ttrying: " << name);
// find function in library
GenericFunctionPointer func;
if((func = natives_lookup_symbol(libraries, name)) != NULL)
return func;
#ifdef PLATFORM_NT
// alloc memory for mangled overloaded symbol for Windows
char *wname = (char*)STD_ALLOCA( wov_len + 1 );
// get mangled symbol name for Windows
natives_get_win_name(wname, name, wargs_len);
TRACE("\ttrying: " << wname);
// find function in library
if((func = natives_lookup_symbol(libraries, wname)) != NULL)
return func;
#endif // PLATFORM_NT
// get mangled overloaded symbol name
natives_get_overloaded_name(name + len, method_desc);
TRACE("\ttrying: " << name);
// find function in library
if((func = natives_lookup_symbol(libraries, name)) != NULL)
return func;
#ifdef PLATFORM_NT
// get mangled overloaded symbol name
natives_get_win_name(wname, name, wargs_len);
TRACE("\ttrying: " << wname);
// find function in library
if((func = natives_lookup_symbol(libraries, wname)) != NULL)
return func;
#endif // PLATFORM_NT
return NULL;
} // natives_lookup_method
void
natives_describe_error(NativeLoadStatus error, char* buf, size_t buflen)
{
apr_strerror(error, buf, buflen);
} // natives_describe_error
#ifdef PLATFORM_NT
// Converts to lower case within given buffer
void lowercase_buf(char* name)
{
assert(name != NULL);
for(; *name; name++)
{
if (isalpha(*name) && !islower(*name)) {
*name = tolower(*name);
}
}
}
#endif // #ifdef PLATFORM_NT
// Fills provided buffer with short file name
// Returns pointer to buffer or NULL if file name is too long
char* short_name(const char* name, char* buf)
{
assert(name != NULL && buf != NULL);
const char* filepointer = port_filepath_basename(name);
if (!filepointer)
return NULL;
if (strlen(filepointer) > PORT_PATH_MAX)
return NULL;
strcpy(buf, filepointer);
#ifdef PLATFORM_NT
lowercase_buf(buf);
#endif
return buf;
}
// Checks if given library was loaded already
// Method is stupid and slow but it works for full and partial names
bool natives_is_library_loaded_slow(const char* libname)
{
char src_buf[PORT_PATH_MAX + 1], cmp_buf[PORT_PATH_MAX + 1];
if (short_name(libname, src_buf) == NULL)
return false; // Error case
bool result = false;
jni_libs.lock._lock();
for(NativeLibraryList lib = jni_libs.lib_info_list;
lib; lib = lib->next)
{
if (short_name(lib->name->bytes, cmp_buf) == NULL)
break; // Error case
if (0 == strcmp(src_buf, cmp_buf))
{
result = true;
break;
}
}
jni_libs.lock._unlock();
return result;
}