2011-06-20 22:06:51 +02:00
|
|
|
/* Copyright (C) 2011 Wildfire Games.
|
2009-04-18 19:00:33 +02:00
|
|
|
* This file is part of 0 A.D.
|
|
|
|
*
|
|
|
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* 0 A.D. is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2006-12-20 04:09:21 +01:00
|
|
|
#include "precompiled.h"
|
|
|
|
|
|
|
|
#include "DllLoader.h"
|
|
|
|
|
2010-07-14 21:39:47 +02:00
|
|
|
#include "lib/timer.h"
|
2007-01-01 22:25:47 +01:00
|
|
|
#include "lib/posix/posix_dlfcn.h"
|
2007-01-02 19:11:00 +01:00
|
|
|
#include "ps/CStr.h"
|
2007-06-03 17:37:26 +02:00
|
|
|
#include "ps/CLogger.h"
|
2010-07-24 14:51:22 +02:00
|
|
|
#include "ps/GameSetup/Config.h"
|
2006-12-20 04:09:21 +01:00
|
|
|
|
2011-06-20 22:06:51 +02:00
|
|
|
static void* const HANDLE_UNAVAILABLE = (void*)-1;
|
2006-12-20 04:09:21 +01:00
|
|
|
|
2010-07-24 14:51:22 +02:00
|
|
|
// directory to search for libraries (optionally set by --libdir at build-time,
|
|
|
|
// optionally overridden by -libdir at run-time in the test executable);
|
2010-07-22 02:25:13 +02:00
|
|
|
// if we don't have an explicit libdir then the linker will look in DT_RUNPATH
|
2010-03-20 21:55:54 +01:00
|
|
|
// (which we set to $ORIGIN) to find it in the executable's directory
|
2010-07-24 14:51:22 +02:00
|
|
|
#ifdef INSTALLED_LIBDIR
|
|
|
|
static CStr g_Libdir = STRINGIZE(INSTALLED_LIBDIR);
|
|
|
|
#else
|
|
|
|
static CStr g_Libdir = "";
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// note: on Linux, lib is prepended to the SO file name
|
2006-12-20 04:09:21 +01:00
|
|
|
#if OS_UNIX
|
2011-06-20 22:06:51 +02:00
|
|
|
static CStr prefix = "lib";
|
2006-12-20 04:09:21 +01:00
|
|
|
#else
|
2011-06-20 22:06:51 +02:00
|
|
|
static CStr prefix = "";
|
2006-12-20 04:09:21 +01:00
|
|
|
#endif
|
2011-06-20 22:06:51 +02:00
|
|
|
|
|
|
|
// we usually want to use the same debug/release build type as the
|
|
|
|
// main executable (the library should also be efficient in release builds and
|
|
|
|
// allow easy symbol access in debug builds). however, that version of the
|
|
|
|
// library might be missing, so we check for both.
|
|
|
|
// this works because the interface is binary-compatible.
|
2006-12-20 04:09:21 +01:00
|
|
|
#ifndef NDEBUG
|
2011-06-20 22:06:51 +02:00
|
|
|
static CStr suffixes[] = { "_dbg", "" }; // (order matters)
|
2006-12-20 04:09:21 +01:00
|
|
|
#else
|
2011-06-20 22:06:51 +02:00
|
|
|
static CStr suffixes[] = { "", "_dbg" };
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// NB: our Windows dlopen() function changes the extension to .dll
|
|
|
|
static CStr extensions[] = {
|
|
|
|
".so",
|
|
|
|
#if OS_MACOSX
|
|
|
|
".dylib" // supported by OS X dlopen
|
2006-12-20 04:09:21 +01:00
|
|
|
#endif
|
2011-06-20 22:06:51 +02:00
|
|
|
};
|
2006-12-20 04:09:21 +01:00
|
|
|
|
|
|
|
// (This class is currently only used by 'Collada' and 'AtlasUI' which follow
|
|
|
|
// the naming/location convention above - it'll need to be changed if we want
|
|
|
|
// to support other DLLs.)
|
|
|
|
|
2011-06-20 22:06:51 +02:00
|
|
|
static CStr GenerateFilename(const CStr& name, const CStr& suffix, const CStr& extension)
|
2010-07-24 14:51:22 +02:00
|
|
|
{
|
|
|
|
CStr n;
|
|
|
|
if (!g_Libdir.empty())
|
|
|
|
n = g_Libdir + "/";
|
2011-06-20 22:06:51 +02:00
|
|
|
n += prefix + name + suffix + extension;
|
2010-07-24 14:51:22 +02:00
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
2011-06-20 22:06:51 +02:00
|
|
|
|
|
|
|
|
|
|
|
// @param name base name of the library (excluding prefix/suffix/extension)
|
|
|
|
// @param errors receives descriptions of any and all errors encountered
|
|
|
|
// @return valid handle or 0
|
|
|
|
static void* LoadAnyVariant(const CStr& name, std::stringstream& errors)
|
|
|
|
{
|
|
|
|
for(size_t idxSuffix = 0; idxSuffix < ARRAY_SIZE(suffixes); idxSuffix++)
|
|
|
|
{
|
|
|
|
for(size_t idxExtension = 0; idxExtension < ARRAY_SIZE(extensions); idxExtension++)
|
|
|
|
{
|
|
|
|
CStr filename = GenerateFilename(name, suffixes[idxSuffix], extensions[idxExtension]);
|
|
|
|
|
|
|
|
// we don't really care when relocations take place, but one of
|
|
|
|
// {RTLD_NOW, RTLD_LAZY} must be specified. go with the former because
|
|
|
|
// it is safer and matches the Windows load behavior.
|
|
|
|
const int flags = RTLD_LOCAL|RTLD_NOW;
|
|
|
|
void* handle = dlopen(filename.c_str(), flags);
|
|
|
|
if(handle)
|
|
|
|
return handle;
|
|
|
|
else
|
|
|
|
errors << "dlopen(" << filename << ") failed: " << dlerror() << "; ";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0; // none worked
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-12-20 04:09:21 +01:00
|
|
|
DllLoader::DllLoader(const char* name)
|
2011-06-20 22:06:51 +02:00
|
|
|
: m_Name(name), m_Handle(0)
|
2006-12-20 04:09:21 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
DllLoader::~DllLoader()
|
|
|
|
{
|
|
|
|
if (IsLoaded())
|
|
|
|
dlclose(m_Handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DllLoader::IsLoaded() const
|
|
|
|
{
|
|
|
|
return (m_Handle != 0 && m_Handle != HANDLE_UNAVAILABLE);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DllLoader::LoadDLL()
|
|
|
|
{
|
|
|
|
// first time: try to open the shared object
|
|
|
|
// postcondition: m_Handle valid or == HANDLE_UNAVAILABLE.
|
|
|
|
if (m_Handle == 0)
|
|
|
|
{
|
2010-07-14 21:39:47 +02:00
|
|
|
TIMER(L"LoadDLL");
|
|
|
|
|
2011-06-20 22:06:51 +02:00
|
|
|
std::stringstream errors;
|
|
|
|
m_Handle = LoadAnyVariant(m_Name, errors);
|
|
|
|
if(!m_Handle) // (only report errors if nothing worked)
|
2007-06-03 17:37:26 +02:00
|
|
|
{
|
2011-06-20 22:06:51 +02:00
|
|
|
LOGERROR(L"DllLoader: %hs", errors.str().c_str());
|
2006-12-20 04:09:21 +01:00
|
|
|
m_Handle = HANDLE_UNAVAILABLE;
|
2007-06-03 17:37:26 +02:00
|
|
|
}
|
2006-12-20 04:09:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return (m_Handle != HANDLE_UNAVAILABLE);
|
|
|
|
}
|
|
|
|
|
2007-01-26 19:26:45 +01:00
|
|
|
void DllLoader::Unload()
|
|
|
|
{
|
2011-06-20 22:06:51 +02:00
|
|
|
if(!IsLoaded())
|
2007-01-26 19:26:45 +01:00
|
|
|
return;
|
|
|
|
|
|
|
|
dlclose(m_Handle);
|
|
|
|
m_Handle = 0;
|
|
|
|
}
|
|
|
|
|
2006-12-20 04:09:21 +01:00
|
|
|
void DllLoader::LoadSymbolInternal(const char* name, void** fptr) const
|
|
|
|
{
|
2011-06-20 22:06:51 +02:00
|
|
|
if(!IsLoaded())
|
2006-12-20 04:09:21 +01:00
|
|
|
{
|
2009-11-03 22:46:35 +01:00
|
|
|
debug_warn(L"Loading symbol from invalid DLL");
|
2006-12-20 04:09:21 +01:00
|
|
|
*fptr = NULL;
|
|
|
|
throw PSERROR_DllLoader_DllNotLoaded();
|
|
|
|
}
|
|
|
|
|
|
|
|
*fptr = dlsym(m_Handle, name);
|
2011-06-20 22:06:51 +02:00
|
|
|
if(*fptr == NULL)
|
2006-12-20 04:09:21 +01:00
|
|
|
throw PSERROR_DllLoader_SymbolNotFound();
|
|
|
|
}
|
2010-07-24 14:51:22 +02:00
|
|
|
|
2010-07-30 23:42:22 +02:00
|
|
|
void DllLoader::OverrideLibdir(const char* libdir)
|
2010-07-24 14:51:22 +02:00
|
|
|
{
|
|
|
|
g_Libdir = libdir;
|
|
|
|
}
|