Skip to content

Commit

Permalink
Codechange: refactor the Windows-only DllLoader in a cross-platform L…
Browse files Browse the repository at this point in the history
…ibraryLoader (OpenTTD#11751)
  • Loading branch information
TrueBrain authored Jan 10, 2024
1 parent f7bd70b commit d3ee045
Show file tree
Hide file tree
Showing 14 changed files with 268 additions and 66 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ link_package(LZO)

if(NOT WIN32 AND NOT EMSCRIPTEN)
link_package(CURL ENCOURAGED)
target_link_libraries(openttd_lib ${CMAKE_DL_LIBS})
endif()

if(NOT EMSCRIPTEN)
Expand Down
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ add_files(
league_gui.h
league_gui.cpp
league_type.h
library_loader.h
livery.h
main_gui.cpp
map.cpp
Expand Down
112 changes: 112 additions & 0 deletions src/library_loader.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* This file is part of OpenTTD.
* OpenTTD 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, version 2.
* OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/

/** @file library_loader.h Functions/types related to loading libraries dynamically. */

#ifndef LIBRARY_LOADER_H
#define LIBRARY_LOADER_H

class LibraryLoader {
public:
/**
* A function loaded from a library.
*
* Will automatically cast to the correct function pointer type on retrieval.
*/
class Function {
public:
explicit Function(void *p) : p(p) {}

template <typename T, typename = std::enable_if_t<std::is_function_v<T>>>
operator T *() const
{
return reinterpret_cast<T *>(this->p);
}

private:
void *p;
};

/**
* Load a library with the given filename.
*/
explicit LibraryLoader(const std::string &filename)
{
this->handle = this->OpenLibrary(filename);
}

/**
* Close the library.
*/
~LibraryLoader()
{
if (this->handle != nullptr) {
this->CloseLibrary();
}
}

/**
* Check whether an error occurred while loading the library or a function.
*
* @return Whether an error occurred.
*/
bool HasError()
{
return this->error.has_value();
}

/**
* Get the last error that occurred while loading the library or a function.
*
* @return The error message.
*/
std::string GetLastError()
{
return this->error.value_or("No error");
}

/**
* Get a function from a loaded library.
*
* @param symbol_name The name of the function to get.
* @return The function. Check HasError() before using.
*/
Function GetFunction(const std::string &symbol_name)
{
if (this->error.has_value()) return Function(nullptr);
return Function(this->GetSymbol(symbol_name));
}

private:
/**
* Open the library with the given filename.
*
* Should set error if any error occurred.
*
* @param filename The filename of the library to open.
*/
void *OpenLibrary(const std::string &filename);

/**
* Close the library.
*/
void CloseLibrary();

/**
* Get a symbol from the library.
*
* Should set error if any error occurred.
*
* @param symbol_name The name of the symbol to get.
*/
void *GetSymbol(const std::string &symbol_name);

std::optional<std::string> error = {}; ///< The last error that occurred, if set.
void *handle = nullptr; ///< Handle to the library.
};

#endif /* LIBRARY_LOADER_H */
1 change: 1 addition & 0 deletions src/os/unix/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ add_files(
)

add_files(
library_loader_unix.cpp
unix.cpp
CONDITION UNIX
)
Expand Down
64 changes: 64 additions & 0 deletions src/os/unix/library_loader_unix.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* This file is part of OpenTTD.
* OpenTTD 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, version 2.
* OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/

/** @file library_loader_unix.cpp Implementation of the LibraryLoader for Linux / MacOS */

#include "../../stdafx.h"

#include <dlfcn.h>

#include "../../library_loader.h"

#include "../../safeguards.h"

/* Emscripten cannot dynamically load other files. */
#if defined(__EMSCRIPTEN__)

void *LibraryLoader::OpenLibrary(const std::string &)
{
this->error = "Dynamic loading is not supported on this platform.";
return nullptr;
}

void LibraryLoader::CloseLibrary()
{
}

void *LibraryLoader::GetSymbol(const std::string &)
{
this->error = "Dynamic loading is not supported on this platform.";
return nullptr;
}

#else

void *LibraryLoader::OpenLibrary(const std::string &filename)
{
void *h = dlopen(filename.c_str(), RTLD_NOW | RTLD_LOCAL);
if (h == nullptr) {
this->error = dlerror();
}

return h;
}

void LibraryLoader::CloseLibrary()
{
dlclose(this->handle);
}

void *LibraryLoader::GetSymbol(const std::string &symbol_name)
{
void *p = dlsym(this->handle, symbol_name.c_str());
if (p == nullptr) {
this->error = dlerror();
}

return p;
}

#endif /* __EMSCRIPTEN__ */
1 change: 1 addition & 0 deletions src/os/windows/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ add_files(
crashlog_win.cpp
font_win32.cpp
font_win32.h
library_loader_win.cpp
string_uniscribe.cpp
string_uniscribe.h
survey_win.cpp
Expand Down
23 changes: 12 additions & 11 deletions src/os/windows/crashlog_win.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "../../gamelog.h"
#include "../../saveload/saveload.h"
#include "../../video/video_driver.hpp"
#include "../../library_loader.h"

#include <windows.h>
#include <mmsystem.h>
Expand Down Expand Up @@ -177,7 +178,7 @@ static const uint MAX_FRAMES = 64;

/* virtual */ void CrashLogWindows::SurveyStacktrace(nlohmann::json &survey) const
{
DllLoader dbghelp(L"dbghelp.dll");
LibraryLoader dbghelp("dbghelp.dll");
struct ProcPtrs {
BOOL (WINAPI * pSymInitialize)(HANDLE, PCSTR, BOOL);
BOOL (WINAPI * pSymSetOptions)(DWORD);
Expand All @@ -189,21 +190,21 @@ static const uint MAX_FRAMES = 64;
BOOL (WINAPI * pSymGetSymFromAddr64)(HANDLE, DWORD64, PDWORD64, PIMAGEHLP_SYMBOL64);
BOOL (WINAPI * pSymGetLineFromAddr64)(HANDLE, DWORD64, PDWORD, PIMAGEHLP_LINE64);
} proc = {
dbghelp.GetProcAddress("SymInitialize"),
dbghelp.GetProcAddress("SymSetOptions"),
dbghelp.GetProcAddress("SymCleanup"),
dbghelp.GetProcAddress("StackWalk64"),
dbghelp.GetProcAddress("SymFunctionTableAccess64"),
dbghelp.GetProcAddress("SymGetModuleBase64"),
dbghelp.GetProcAddress("SymGetModuleInfo64"),
dbghelp.GetProcAddress("SymGetSymFromAddr64"),
dbghelp.GetProcAddress("SymGetLineFromAddr64"),
dbghelp.GetFunction("SymInitialize"),
dbghelp.GetFunction("SymSetOptions"),
dbghelp.GetFunction("SymCleanup"),
dbghelp.GetFunction("StackWalk64"),
dbghelp.GetFunction("SymFunctionTableAccess64"),
dbghelp.GetFunction("SymGetModuleBase64"),
dbghelp.GetFunction("SymGetModuleInfo64"),
dbghelp.GetFunction("SymGetSymFromAddr64"),
dbghelp.GetFunction("SymGetLineFromAddr64"),
};

survey = nlohmann::json::array();

/* Try to load the functions from the DLL, if that fails because of a too old dbghelp.dll, just skip it. */
if (dbghelp.Success()) {
if (!dbghelp.HasError()) {
/* Initialize symbol handler. */
HANDLE hCur = GetCurrentProcess();
proc.pSymInitialize(hCur, nullptr, TRUE);
Expand Down
7 changes: 4 additions & 3 deletions src/os/windows/font_win32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@
#include "../../core/mem_func.hpp"
#include "../../error_func.h"
#include "../../fileio_func.h"
#include "../../fontdetection.h"
#include "../../fontcache.h"
#include "../../fontcache/truetypefontcache.h"
#include "../../fontdetection.h"
#include "../../library_loader.h"
#include "../../string_func.h"
#include "../../strings_func.h"
#include "../../zoom_func.h"
Expand Down Expand Up @@ -361,9 +362,9 @@ void LoadWin32Font(FontSize fs)
if (AddFontResourceEx(fontPath, FR_PRIVATE, 0) != 0) {
/* Try a nice little undocumented function first for getting the internal font name.
* Some documentation is found at: http://www.undocprint.org/winspool/getfontresourceinfo */
static DllLoader _gdi32(L"gdi32.dll");
static LibraryLoader _gdi32("gdi32.dll");
typedef BOOL(WINAPI *PFNGETFONTRESOURCEINFO)(LPCTSTR, LPDWORD, LPVOID, DWORD);
static PFNGETFONTRESOURCEINFO GetFontResourceInfo = _gdi32.GetProcAddress("GetFontResourceInfoW");
static PFNGETFONTRESOURCEINFO GetFontResourceInfo = _gdi32.GetFunction("GetFontResourceInfoW");

if (GetFontResourceInfo != nullptr) {
/* Try to query an array of LOGFONTs that describe the file. */
Expand Down
2 changes: 2 additions & 0 deletions src/os/windows/font_win32.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#include "../../fontcache/truetypefontcache.h"
#include "win32.h"

#include <windows.h>

/** Font cache for fonts that are based on a Win32 font. */
class Win32FontCache : public TrueTypeFontCache {
private:
Expand Down
58 changes: 58 additions & 0 deletions src/os/windows/library_loader_win.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* This file is part of OpenTTD.
* OpenTTD 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, version 2.
* OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/

/** @file library_loader_win.cpp Implementation of the LibraryLoader for Windows */

#include "../../stdafx.h"

#include <windows.h>

#include "../../library_loader.h"

#include "../../safeguards.h"

static std::string GetLoadError()
{
auto error_code = GetLastError();

char buffer[512];
if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, error_code,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, sizeof(buffer), nullptr) == 0) {
return fmt::format("Unknown error {}", error_code);
}

return buffer;
}

void *LibraryLoader::OpenLibrary(const std::string &filename)
{
void *h = ::LoadLibraryW(OTTD2FS(filename).c_str());
if (h == nullptr) {
this->error = GetLoadError();
}

return h;
}

void LibraryLoader::CloseLibrary()
{
HMODULE handle = static_cast<HMODULE>(this->handle);

::FreeLibrary(handle);
}

void *LibraryLoader::GetSymbol(const std::string &symbol_name)
{
HMODULE handle = static_cast<HMODULE>(this->handle);

void *p = reinterpret_cast<void *>(::GetProcAddress(handle, symbol_name.c_str()));
if (p == nullptr) {
this->error = GetLoadError();
}

return p;
}
9 changes: 5 additions & 4 deletions src/os/windows/win32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <sys/stat.h>
#include "../../language.h"
#include "../../thread.h"
#include "../../library_loader.h"

#include "../../safeguards.h"

Expand Down Expand Up @@ -573,8 +574,8 @@ int OTTDStringCompare(std::string_view s1, std::string_view s2)
#endif

if (first_time) {
static DllLoader _kernel32(L"Kernel32.dll");
_CompareStringEx = _kernel32.GetProcAddress("CompareStringEx");
static LibraryLoader _kernel32("Kernel32.dll");
_CompareStringEx = _kernel32.GetFunction("CompareStringEx");
first_time = false;
}

Expand Down Expand Up @@ -617,8 +618,8 @@ int Win32StringContains(const std::string_view str, const std::string_view value
static bool first_time = true;

if (first_time) {
static DllLoader _kernel32(L"Kernel32.dll");
_FindNLSStringEx = _kernel32.GetProcAddress("FindNLSStringEx");
static LibraryLoader _kernel32("Kernel32.dll");
_FindNLSStringEx = _kernel32.GetFunction("FindNLSStringEx");
first_time = false;
}

Expand Down
Loading

0 comments on commit d3ee045

Please sign in to comment.