/*
* %CopyrightBegin%
*
* Copyright Ericsson AB 2006-2013. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
* compliance with the License. You should have received a copy of the
* Erlang Public License along with this software. If not, it can be
* retrieved online at http://www.erlang.org/.
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* %CopyrightEnd%
*/
/*
* Interface functions to the dynamic linker using dl* functions.
* (As far as I know it works on SunOS 4, 5, Linux and FreeBSD. /Seb)
*/
#include <windows.h>
#define GET_ERTS_ALC_TEST
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "sys.h"
#include "global.h"
#include "erl_alloc.h"
#include "erl_driver.h"
#include "erl_win_dyn_driver.h"
#include "erl_nif.h"
#define EXT_LEN 4
#define FILE_EXT_WCHAR L".dll"
static DWORD tls_index = 0;
static TWinDynDriverCallbacks wddc;
static TWinDynNifCallbacks nif_callbacks;
void erl_sys_ddll_init(void) {
tls_index = TlsAlloc();
ERL_INIT_CALLBACK_STRUCTURE(wddc);
#define ERL_NIF_API_FUNC_DECL(RET,NAME,ARGS) nif_callbacks.NAME = NAME
#include "erl_nif_api_funcs.h"
#undef ERL_NIF_API_FUNC_DECL
return;
}
/*
* Open a shared object
* Expecting 'full_name' as an UTF-8 string.
*/
int erts_sys_ddll_open(const char *full_name, void **handle, ErtsSysDdllError* err)
{
HINSTANCE hinstance;
int len;
wchar_t* wcp;
Sint used;
int code;
if ((len = sys_strlen(full_name)) >= MAXPATHLEN - EXT_LEN) {
if (err != NULL) {
err->str = "Library name too long";
}
return ERL_DE_LOAD_ERROR_NAME_TO_LONG;
}
wcp = (wchar_t*)erts_convert_filename_to_wchar((byte*)full_name, len,
NULL, 0,
ERTS_ALC_T_TMP, &used, EXT_LEN);
wcscpy(&wcp[used/2 - 1], FILE_EXT_WCHAR);
if ((hinstance = LoadLibraryW(wcp)) == NULL) {
code = ERL_DE_DYNAMIC_ERROR_OFFSET - GetLastError();
if (err != NULL) {
err->str = erts_sys_ddll_error(code);
}
}
else {
code = ERL_DE_NO_ERROR;
*handle = (void *) hinstance;
}
erts_free(ERTS_ALC_T_TMP, wcp);
return code;
}
int erts_sys_ddll_open_noext(char *dlname, void **handle, ErtsSysDdllError* err)
{
HINSTANCE hinstance;
if ((hinstance = LoadLibrary(dlname)) == NULL) {
int code = ERL_DE_DYNAMIC_ERROR_OFFSET - GetLastError();
if (err != NULL) {
err->str = erts_sys_ddll_error(code);
}
return code;
} else {
*handle = (void *) hinstance;
return ERL_DE_NO_ERROR;
}
}
/*
* Find a symbol in the shared object
*/
int erts_sys_ddll_sym2(void *handle, const char *func_name, void **function,
ErtsSysDdllError* err)
{
FARPROC proc;
if ((proc = GetProcAddress( (HINSTANCE) handle, func_name)) == NULL) {
int code = ERL_DE_DYNAMIC_ERROR_OFFSET - GetLastError();
if (err != NULL) {
err->str = erts_sys_ddll_error(code);
}
return code;
}
*function = (void *) proc;
return ERL_DE_NO_ERROR;
}
/* XXX:PaN These two will be changed with new driver interface! */
/*
* Load the driver init function, might appear under different names depending on object arch...
*/
int erts_sys_ddll_load_driver_init(void *handle, void **function)
{
void *fn;
int res;
if ((res = erts_sys_ddll_sym(handle, "driver_init", &fn)) != ERL_DE_NO_ERROR) {
return res;
}
*function = fn;
return res;
}
int erts_sys_ddll_load_nif_init(void *handle, void **function, ErtsSysDdllError* err)
{
void *fn;
int res;
if ((res = erts_sys_ddll_sym2(handle, "nif_init", &fn, err)) != ERL_DE_NO_ERROR) {
return res;
}
*function = fn;
return res;
}
/*
* Call the driver_init function, whatever it's really called, simple on unix...
*/
void *erts_sys_ddll_call_init(void *function) {
void *(*initfn)(TWinDynDriverCallbacks *) = function;
return (*initfn)(&wddc);
}
void *erts_sys_ddll_call_nif_init(void *function) {
void *(*initfn)(TWinDynNifCallbacks *) = function;
return (*initfn)(&nif_callbacks);
}
/*
* Close a chared object
*/
int erts_sys_ddll_close2(void *handle, ErtsSysDdllError* err)
{
if (!FreeLibrary((HINSTANCE) handle)) {
int code = ERL_DE_DYNAMIC_ERROR_OFFSET - GetLastError();
if (err != NULL) {
err->str = erts_sys_ddll_error(code);
}
return code;
}
return ERL_DE_NO_ERROR;
}
/*
* Return string that describes the (current) error
*/
#define MAX_ERROR 255
char *erts_sys_ddll_error(int code)
{
int actual_code;
char *local_ptr;
if (code > ERL_DE_DYNAMIC_ERROR_OFFSET) {
return "Unspecified error";
}
actual_code = -1*(code - ERL_DE_DYNAMIC_ERROR_OFFSET);
local_ptr = TlsGetValue(tls_index);
if (local_ptr == NULL) {
local_ptr = erts_alloc(ERTS_ALC_T_DDLL_ERRCODES, MAX_ERROR);
TlsSetValue(tls_index,local_ptr);
}
if (!FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
(DWORD) actual_code,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
local_ptr,
MAX_ERROR, NULL )) {
return "Unspecified error";
} else {
char *ptr = local_ptr + strlen(local_ptr) - 1;
while (ptr >= local_ptr && (*ptr == '\r' || *ptr == '\n')) {
*ptr-- = '\0';
}
}
return local_ptr;
}
void erts_sys_ddll_free_error(ErtsSysDdllError* err)
{
/* err->str may be either a static string or reused as thread local data,
* so wo don't bother free it.
*/
}