phobos/std/loader.d
jmdavis 213c064e70 Reverted change from toStringz to toStringZ.
There was no consenus in the newsgroup about what to do about renaming
toStringz to be properly camelcased. It was pretty much divided between
renaming it to toCString and leaving it exactly as-is. No one wanted it
to be toStringZ. So, given the lack of consensus, I'm just going to
leave it as toStringz.
2011-06-18 01:13:21 -07:00

706 lines
16 KiB
D

// Written in the D programming language.
/* /////////////////////////////////////////////////////////////////////////////
* File: loader.d (originally from synsoft.win32.loader)
*
* Purpose: Win32 exception classes
*
* Created 18th October 2003
* Updated: 24th April 2004
*
* Author: Matthew Wilson
*
* Copyright 2004-2005 by Matthew Wilson and Synesis Software
* Written by Matthew Wilson
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, in both source and binary form, subject to the following
* restrictions:
*
* - The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* - Altered source versions must be plainly marked as such, and must not
* be misrepresented as being the original software.
* - This notice may not be removed or altered from any source
* distribution.
*
* ////////////////////////////////////////////////////////////////////////// */
/** \file D/std/loader.d This file contains the \c D standard library
* executable module loader library, and the ExeModule class.
* Source: $(PHOBOSSRC std/_loader.d)
*/
/* ////////////////////////////////////////////////////////////////////////// */
module std.loader;
/* /////////////////////////////////////////////////////////////////////////////
* Imports
*/
private import std.string;
import std.conv;
private import std.c.string;
private import std.c.stdlib;
private import std.c.stdio;
//import synsoft.types;
/+ + These are borrowed from synsoft.types, until such time as something similar is in Phobos ++
+/
public alias int boolean;
/* /////////////////////////////////////////////////////////////////////////////
* External function declarations
*/
version(Windows)
{
private import std.c.windows.windows;
private import std.windows.syserror;
extern(Windows)
{
alias HMODULE HModule_;
}
}
else version(Posix)
{
private import core.sys.posix.dlfcn;
extern(C)
{
alias void* HModule_;
}
}
else
{
const int platform_not_discriminated = 0;
static assert(platform_not_discriminated);
}
/** The platform-independent module handle. Note that this has to be
* separate from the platform-dependent handle because different module names
* can result in the same module being loaded, which cannot be detected in
* some operating systems
*/
typedef void *HXModule;
/* /////////////////////////////////////////////////////////////////////////////
* ExeModule functions
*/
/* These are "forward declared" here because I don't like the way D forces me
* to provide my declaration and implementation together, and mixed in with all
* the other implementation gunk.
*/
/** ExeModule library Initialisation
*
* \retval <0 Initialisation failed. Processing must gracefully terminate,
* without making any use of the ExeModule library
* \retval 0 Initialisation succeeded for the first time. Any necessary resources
* were successfully allocated
* \retval >0 Initialisation has already succeefully completed via a prior call.
*/
public int ExeModule_Init()
{
return ExeModule_Init_();
}
public void ExeModule_Uninit()
{
ExeModule_Uninit_();
}
/**
*
* \note The value of the handle returned may not be a valid handle for your operating
* system, and you <b>must not</b> attempt to use it with any other operating system
* or other APIs. It is only valid for use with the ExeModule library.
*/
public HXModule ExeModule_Load(in string moduleName)
{
return ExeModule_Load_(moduleName);
}
public HXModule ExeModule_AddRef(HXModule hModule)
{
return ExeModule_AddRef_(hModule);
}
/**
*
* \param hModule The module handler. It must not be null.
*/
public void ExeModule_Release(ref HXModule hModule)
{
ExeModule_Release_(hModule);
}
public void *ExeModule_GetSymbol(ref HXModule hModule, in string symbolName)
{
return ExeModule_GetSymbol_(hModule, symbolName);
}
public string ExeModule_Error()
{
return ExeModule_Error_();
}
version(Windows)
{
private __gshared int s_init;
private __gshared int s_lastError; // This is NOT thread-specific
private void record_error_()
{
s_lastError = GetLastError();
}
private int ExeModule_Init_()
{
return ++s_init > 1;
}
private void ExeModule_Uninit_()
{
--s_init;
}
private HXModule ExeModule_Load_(in string moduleName)
in
{
assert(null !is moduleName);
}
body
{
HXModule hmod = cast(HXModule)LoadLibraryA(toStringz(moduleName));
if(null is hmod)
{
record_error_();
}
return hmod;
}
private HXModule ExeModule_AddRef_(HXModule hModule)
in
{
assert(null !is hModule);
}
body
{
return ExeModule_Load_(ExeModule_GetPath_(hModule));
}
private void ExeModule_Release_(ref HXModule hModule)
in
{
assert(null !is hModule);
}
body
{
if(!FreeLibrary(cast(HModule_)hModule))
{
record_error_();
}
hModule = null;
}
private void *ExeModule_GetSymbol_(ref HXModule hModule, in string symbolName)
in
{
assert(null !is hModule);
}
body
{
void *symbol = GetProcAddress(cast(HModule_)hModule, toStringz(symbolName));
if(null is symbol)
{
record_error_();
}
return symbol;
}
private string ExeModule_Error_()
{
return sysErrorString(s_lastError);
}
private string ExeModule_GetPath_(HXModule hModule)
{
char szFileName[260]; // Need to use a constant here
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/getmodulefilename.asp
uint cch = GetModuleFileNameA(cast(HModule_)hModule, szFileName.ptr, szFileName.length);
if (cch == 0)
{
record_error_();
}
return szFileName[0 .. cch].idup;
}
}
else version(Posix)
{
private class ExeModuleInfo
{
public:
int m_cRefs;
HModule_ m_hmod;
string m_name;
this(HModule_ hmod, string name)
{
m_cRefs = 1;
m_hmod = hmod;
m_name = name;
}
};
private __gshared int s_init;
private __gshared ExeModuleInfo [string] s_modules;
private __gshared string s_lastError; // This is NOT thread-specific
private void record_error_()
{
char *err = dlerror();
s_lastError = (null is err) ? "" : err[0 .. std.c.string.strlen(err)].idup;
}
private int ExeModule_Init_()
{
if(1 == ++s_init)
{
return 0;
}
return 1;
}
private void ExeModule_Uninit_()
{
if(0 == --s_init)
{
}
}
private HXModule ExeModule_Load_(in string moduleName)
in
{
assert(null !is moduleName);
}
body
{
ExeModuleInfo* mi_p = moduleName in s_modules;
ExeModuleInfo mi = mi_p is null ? null : *mi_p;
if(null !is mi)
{
return (++mi.m_cRefs, cast(HXModule)mi);
}
else
{
HModule_ hmod = dlopen(toStringz(moduleName), RTLD_NOW);
if(null is hmod)
{
record_error_();
return null;
}
else
{
ExeModuleInfo mi2 = new ExeModuleInfo(hmod, moduleName.idup);
s_modules[moduleName] = mi2;
return cast(HXModule)mi2;
}
}
}
private HXModule ExeModule_AddRef_(HXModule hModule)
in
{
assert(null !is hModule);
ExeModuleInfo mi = cast(ExeModuleInfo)hModule;
assert(0 < mi.m_cRefs);
assert(null !is mi.m_hmod);
assert(null !is mi.m_name);
assert(null !is s_modules[mi.m_name]);
assert(mi is s_modules[mi.m_name]);
}
body
{
ExeModuleInfo mi = cast(ExeModuleInfo)hModule;
if(null !is mi)
{
return (++mi.m_cRefs, hModule);
}
else
{
return null;
}
}
private void ExeModule_Release_(ref HXModule hModule)
in
{
assert(null !is hModule);
ExeModuleInfo mi = cast(ExeModuleInfo)hModule;
assert(0 < mi.m_cRefs);
assert(null !is mi.m_hmod);
assert(null !is mi.m_name);
assert(null !is s_modules[mi.m_name]);
assert(mi is s_modules[mi.m_name]);
}
body
{
ExeModuleInfo mi = cast(ExeModuleInfo)hModule;
if(0 == --mi.m_cRefs)
{
string name = mi.m_name;
if (dlclose(mi.m_hmod))
{
record_error_();
}
s_modules.remove(name);
delete mi;
}
hModule = null;
}
private void *ExeModule_GetSymbol_(ref HXModule hModule, in string symbolName)
in
{
assert(null !is hModule);
ExeModuleInfo mi = cast(ExeModuleInfo)hModule;
assert(0 < mi.m_cRefs);
assert(null !is mi.m_hmod);
assert(null !is mi.m_name);
assert(null !is s_modules[mi.m_name]);
assert(mi is s_modules[mi.m_name]);
}
body
{
ExeModuleInfo mi = cast(ExeModuleInfo)hModule;
void *symbol = dlsym(mi.m_hmod, toStringz(symbolName));
if(null == symbol)
{
record_error_();
}
return symbol;
}
private string ExeModule_Error_()
{
return s_lastError;
}
private string ExeModule_GetPath_(HXModule hModule)
in
{
assert(null !is hModule);
ExeModuleInfo mi = cast(ExeModuleInfo)hModule;
assert(0 < mi.m_cRefs);
assert(null !is mi.m_hmod);
assert(null !is mi.m_name);
assert(null !is s_modules[mi.m_name]);
assert(mi is s_modules[mi.m_name]);
}
body
{
ExeModuleInfo mi = cast(ExeModuleInfo)hModule;
return mi.m_name;
}
}
else
{
const int platform_not_discriminated = 0;
static assert(platform_not_discriminated);
}
/* /////////////////////////////////////////////////////////////////////////////
* Classes
*/
public class ExeModuleException
: Exception
{
public:
this(string message)
{
super(message);
}
this(uint errcode)
{
version (Posix)
{
char[80] buf = void;
super(to!string(strerror_r(errcode, buf.ptr, buf.length)).idup);
}
else
{
super(to!string(strerror(errcode)));
}
}
}
/// This class represents an executable image
public scope class ExeModule
{
/// \name Construction
/// @{
public:
/// Constructs from an existing image handle
this(HXModule hModule, boolean bTakeOwnership)
in
{
assert(null !is hModule);
}
body
{
if(bTakeOwnership)
{
m_hModule = hModule;
}
else
{
version (Windows)
{
string path = Path();
m_hModule = cast(HXModule)LoadLibraryA(toStringz(path));
if (m_hModule == null)
throw new ExeModuleException(GetLastError());
}
else version (Posix)
{
m_hModule = ExeModule_AddRef(hModule);
}
else
static assert(0);
}
}
this(string moduleName)
in
{
assert(null !is moduleName);
}
body
{
version (Windows)
{
m_hModule = cast(HXModule)LoadLibraryA(toStringz(moduleName));
if (null is m_hModule)
throw new ExeModuleException(GetLastError());
}
else version (Posix)
{
m_hModule = ExeModule_Load(moduleName);
if (null is m_hModule)
throw new ExeModuleException(ExeModule_Error());
}
else
{
static assert(0); // unsupported system
}
}
~this()
{
close();
}
/// @}
/// \name Operations
/// @{
public:
/// Closes the library
///
/// \note This is available to close the module at any time. Repeated
/// calls do not result in an error, and are simply ignored.
void close()
{
if(null !is m_hModule)
{
version (Windows)
{
if(!FreeLibrary(cast(HModule_)m_hModule))
throw new ExeModuleException(GetLastError());
}
else version (Posix)
{
ExeModule_Release(m_hModule);
}
else
static assert(0);
}
}
/// @}
/// \name Accessors
/// @{
public:
/** Retrieves the named symbol.
*
* \return A pointer to the symbol. There is no null return - failure to retrieve the symbol
* results in an ExeModuleException exception being thrown.
*/
void *getSymbol(in string symbolName)
{
version (Windows)
{
void *symbol = GetProcAddress(cast(HModule_)m_hModule, toStringz(symbolName));
if(null is symbol)
{
throw new ExeModuleException(GetLastError());
}
}
else version (Posix)
{
void *symbol = ExeModule_GetSymbol(m_hModule, symbolName);
if(null is symbol)
{
throw new ExeModuleException(ExeModule_Error());
}
}
else
{
static assert(0);
}
return symbol;
}
/** Retrieves the named symbol.
*
* \return A pointer to the symbol, or null if it does not exist
*/
void *findSymbol(in string symbolName)
{
return ExeModule_GetSymbol(m_hModule, symbolName);
}
/// @}
/// \name Properties
/// @{
public:
/// The handle of the module
///
/// \note Will be \c null if the module load in the constructor failed
HXModule Handle()
{
return m_hModule;
}
/// The handle of the module
///
/// \note Will be \c null if the module load in the constructor failed
string Path()
{
assert(null != m_hModule);
version (Windows)
{
char szFileName[260]; // Need to use a constant here
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/getmodulefilename.asp
uint cch = GetModuleFileNameA(cast(HModule_)m_hModule, szFileName.ptr, szFileName.length);
if (cch == 0)
throw new ExeModuleException(GetLastError());
return szFileName[0 .. cch].idup;
}
else version (Posix)
{
return ExeModule_GetPath_(m_hModule);
}
else
static assert(0);
}
/// @}
private:
HXModule m_hModule;
};
/* ////////////////////////////////////////////////////////////////////////// */
version(TestMain)
{
int main(string[] args)
{
if(args.length < 3)
{
printf("USAGE: <moduleName> <symbolName>\n");
}
else
{
string moduleName = args[1];
string symbolName = args[2];
try
{
auto ExeModule xmod = new ExeModule(moduleName);
printf("\"%.*s\" is loaded\n", moduleName);
void *symbol = xmod.getSymbol(symbolName);
if(null == symbol)
{
throw new ExeModuleException(ExeModule_Error());
}
else
{
printf("\"%.*s\" is acquired\n", symbolName);
}
}
catch(ExeModuleException x)
{
x.print();
}
}
return 0;
}
}
/* ////////////////////////////////////////////////////////////////////////// */