Fix inject problems (#820)

* CoreCLR: resolve and load nethost on demand instead of requiring it on load

* Remove nethost loading from C# side

* Added option to not chain Process.Dispose; see for last error only if result is empty
This commit is contained in:
kizer 2022-04-25 17:13:32 +09:00 committed by GitHub
parent 5b4833a6f7
commit 9a38a9470c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 57 additions and 94 deletions

View file

@ -117,10 +117,6 @@
<ClInclude Include="pch.h" /> <ClInclude Include="pch.h" />
<ClInclude Include="veh.h" /> <ClInclude Include="veh.h" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Library Include="..\lib\CoreCLR\nethost\libnethost.lib" />
<Library Include="..\lib\CoreCLR\nethost\nethost.lib" />
</ItemGroup>
<Target Name="RemoveExtraFiles" AfterTargets="PostBuildEvent"> <Target Name="RemoveExtraFiles" AfterTargets="PostBuildEvent">
<Delete Files="$(OutDir)$(TargetName).lib" /> <Delete Files="$(OutDir)$(TargetName).lib" />
<Delete Files="$(OutDir)$(TargetName).exp" /> <Delete Files="$(OutDir)$(TargetName).exp" />

View file

@ -1,10 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup> <ItemGroup>
<Filter Include="Library Files">
<UniqueIdentifier>{18be40ac-9367-46ff-b848-4c528aa97a8d}</UniqueIdentifier>
<Extensions>lib</Extensions>
</Filter>
<Filter Include="CoreCLR"> <Filter Include="CoreCLR">
<UniqueIdentifier>{dc468303-865e-43bd-908f-a3542c4bb669}</UniqueIdentifier> <UniqueIdentifier>{dc468303-865e-43bd-908f-a3542c4bb669}</UniqueIdentifier>
</Filter> </Filter>
@ -56,12 +52,4 @@
<Filter>Dalamud.Boot DLL</Filter> <Filter>Dalamud.Boot DLL</Filter>
</ClInclude> </ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Library Include="..\lib\CoreCLR\nethost\nethost.lib">
<Filter>Library Files</Filter>
</Library>
<Library Include="..\lib\CoreCLR\nethost\libnethost.lib">
<Filter>Library Files</Filter>
</Library>
</ItemGroup>
</Project> </Project>

View file

@ -68,6 +68,7 @@ DllExport DWORD WINAPI Initialize(LPVOID lpParam)
void* entrypoint_vfn; void* entrypoint_vfn;
int result = InitializeClrAndGetEntryPoint( int result = InitializeClrAndGetEntryPoint(
g_hModule,
runtimeconfig_path, runtimeconfig_path,
module_path, module_path,
L"Dalamud.EntryPoint, Dalamud", L"Dalamud.EntryPoint, Dalamud",

View file

@ -99,10 +99,6 @@
<ClInclude Include="..\lib\CoreCLR\nethost\nethost.h" /> <ClInclude Include="..\lib\CoreCLR\nethost\nethost.h" />
<ClInclude Include="..\lib\CoreCLR\pch.h" /> <ClInclude Include="..\lib\CoreCLR\pch.h" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Library Include="..\lib\CoreCLR\nethost\libnethost.lib" />
<Library Include="..\lib\CoreCLR\nethost\nethost.lib" />
</ItemGroup>
<Target Name="RemoveExtraFiles" AfterTargets="PostBuildEvent"> <Target Name="RemoveExtraFiles" AfterTargets="PostBuildEvent">
<Delete Files="$(OutDir)$(TargetName).lib" /> <Delete Files="$(OutDir)$(TargetName).lib" />
<Delete Files="$(OutDir)$(TargetName).exp" /> <Delete Files="$(OutDir)$(TargetName).exp" />

View file

@ -13,9 +13,6 @@
<UniqueIdentifier>{4faac519-3a73-4b2b-96e7-fb597f02c0be}</UniqueIdentifier> <UniqueIdentifier>{4faac519-3a73-4b2b-96e7-fb597f02c0be}</UniqueIdentifier>
<Extensions>ico;rc</Extensions> <Extensions>ico;rc</Extensions>
</Filter> </Filter>
<Filter Include="Library Files">
<UniqueIdentifier>{6aff1bed-6979-4bc9-94e8-ddafb626e6bf}</UniqueIdentifier>
</Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Image Include="dalamud.ico"> <Image Include="dalamud.ico">
@ -55,12 +52,4 @@
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Library Include="..\lib\CoreCLR\nethost\nethost.lib">
<Filter>Library Files</Filter>
</Library>
<Library Include="..\lib\CoreCLR\nethost\libnethost.lib">
<Filter>Library Files</Filter>
</Library>
</ItemGroup>
</Project> </Project>

View file

@ -21,6 +21,7 @@ int wmain(int argc, wchar_t** argv)
void* entrypoint_vfn; void* entrypoint_vfn;
int result = InitializeClrAndGetEntryPoint( int result = InitializeClrAndGetEntryPoint(
GetModuleHandleW(nullptr),
runtimeconfig_path, runtimeconfig_path,
module_path, module_path,
L"Dalamud.Injector.EntryPoint, Dalamud.Injector", L"Dalamud.Injector.EntryPoint, Dalamud.Injector",

View file

@ -634,17 +634,13 @@ namespace Dalamud.Injector
private static void Inject(Process process, DalamudStartInfo startInfo) private static void Inject(Process process, DalamudStartInfo startInfo)
{ {
var nethostName = "nethost.dll";
var bootName = "Dalamud.Boot.dll"; var bootName = "Dalamud.Boot.dll";
var nethostPath = Path.GetFullPath(nethostName);
var bootPath = Path.GetFullPath(bootName); var bootPath = Path.GetFullPath(bootName);
// ====================================================== // ======================================================
using var injector = new Injector(process); using var injector = new Injector(process, false);
injector.LoadLibrary(nethostPath, out _);
injector.LoadLibrary(bootPath, out var bootModule); injector.LoadLibrary(bootPath, out var bootModule);
// ====================================================== // ======================================================

View file

@ -27,6 +27,7 @@ namespace Dalamud.Injector
internal sealed class Injector : IDisposable internal sealed class Injector : IDisposable
{ {
private readonly Process targetProcess; private readonly Process targetProcess;
private readonly bool disposeTargetProcess;
private readonly ExternalMemory extMemory; private readonly ExternalMemory extMemory;
private readonly CircularBuffer circularBuffer; private readonly CircularBuffer circularBuffer;
private readonly PrivateMemoryBuffer memoryBuffer; private readonly PrivateMemoryBuffer memoryBuffer;
@ -41,9 +42,11 @@ namespace Dalamud.Injector
/// Initializes a new instance of the <see cref="Injector"/> class. /// Initializes a new instance of the <see cref="Injector"/> class.
/// </summary> /// </summary>
/// <param name="targetProcess">Process to inject.</param> /// <param name="targetProcess">Process to inject.</param>
public Injector(Process targetProcess) /// <param name="disposeTargetProcess">Dispose given process on disposing self.</param>
public Injector(Process targetProcess, bool disposeTargetProcess = true)
{ {
this.targetProcess = targetProcess; this.targetProcess = targetProcess;
this.disposeTargetProcess = disposeTargetProcess;
this.extMemory = new ExternalMemory(targetProcess); this.extMemory = new ExternalMemory(targetProcess);
this.circularBuffer = new CircularBuffer(4096, this.extMemory); this.circularBuffer = new CircularBuffer(4096, this.extMemory);
@ -67,6 +70,7 @@ namespace Dalamud.Injector
{ {
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
if (this.disposeTargetProcess)
this.targetProcess?.Dispose(); this.targetProcess?.Dispose();
this.circularBuffer?.Dispose(); this.circularBuffer?.Dispose();
this.memoryBuffer?.Dispose(); this.memoryBuffer?.Dispose();
@ -85,14 +89,9 @@ namespace Dalamud.Injector
throw new Exception("Unable to allocate LoadLibraryW parameter"); throw new Exception("Unable to allocate LoadLibraryW parameter");
this.CallRemoteFunction(this.loadLibraryShellPtr, lpParameter, out var err); this.CallRemoteFunction(this.loadLibraryShellPtr, lpParameter, out var err);
if (err != 0)
throw new Exception($"LoadLibraryW(\"{modulePath}\") failure: {new Win32Exception((int)err).Message} ({err})");
address = this.extMemory.Read<IntPtr>(this.loadLibraryRetPtr); address = this.extMemory.Read<IntPtr>(this.loadLibraryRetPtr);
if (address == IntPtr.Zero) if (address == IntPtr.Zero)
throw new Exception($"LoadLibraryW(\"{modulePath}\") failure: Error code unavailable"); throw new Exception($"LoadLibraryW(\"{modulePath}\") failure: {new Win32Exception((int)err).Message} ({err})");
} }
/// <summary> /// <summary>
@ -110,12 +109,9 @@ namespace Dalamud.Injector
throw new Exception("Unable to allocate GetProcAddress parameter ptr"); throw new Exception("Unable to allocate GetProcAddress parameter ptr");
this.CallRemoteFunction(this.getProcAddressShellPtr, lpParameter, out var err); this.CallRemoteFunction(this.getProcAddressShellPtr, lpParameter, out var err);
if (err != 0) address = this.extMemory.Read<IntPtr>(this.getProcAddressRetPtr);
throw new Exception($"GetProcAddress(0x{module:X}, \"{functionName}\") failure: {new Win32Exception((int)err).Message} ({err})");
this.extMemory.Read(this.getProcAddressRetPtr, out address);
if (address == IntPtr.Zero) if (address == IntPtr.Zero)
throw new Exception($"GetProcAddress(0x{module:X}, \"{functionName}\") failure: Error code unavailable"); throw new Exception($"GetProcAddress(0x{module:X}, \"{functionName}\") failure: {new Win32Exception((int)err).Message} ({err})");
} }
/// <summary> /// <summary>

View file

@ -2,12 +2,14 @@
#include "CoreCLR.h" #include "CoreCLR.h"
#include <Windows.h> #include <Windows.h>
#include <filesystem>
#include <iostream> #include <iostream>
#include "nethost/nethost.h" #include "nethost/nethost.h"
#pragma comment(lib, "nethost/libnethost.lib") CoreCLR::CoreCLR(void* calling_module)
: m_calling_module(calling_module)
CoreCLR::CoreCLR() {} {
}
/* Core public functions */ /* Core public functions */
int CoreCLR::load_hostfxr() int CoreCLR::load_hostfxr()
@ -18,19 +20,43 @@ int CoreCLR::load_hostfxr()
int CoreCLR::load_hostfxr(const struct get_hostfxr_parameters* parameters) int CoreCLR::load_hostfxr(const struct get_hostfxr_parameters* parameters)
{ {
// Get the path to CoreCLR's hostfxr // Get the path to CoreCLR's hostfxr
std::wstring calling_module_path(MAX_PATH, L'\0');
do
{
calling_module_path.resize(GetModuleFileNameW(static_cast<HMODULE>(m_calling_module), &calling_module_path[0], static_cast<DWORD>(calling_module_path.size())));
}
while (!calling_module_path.empty() && GetLastError() == ERROR_INSUFFICIENT_BUFFER);
if (calling_module_path.empty())
return -1;
calling_module_path = (std::filesystem::path(calling_module_path).parent_path() / L"nethost.dll").wstring();
auto lib_nethost = reinterpret_cast<void*>(load_library(calling_module_path.c_str()));
if (!lib_nethost)
return -1;
auto get_hostfxr_path = reinterpret_cast<get_hostfxr_path_type>(
get_export(lib_nethost, "get_hostfxr_path"));
if (!get_hostfxr_path)
return -1;
wchar_t buffer[MAX_PATH]{}; wchar_t buffer[MAX_PATH]{};
size_t buffer_size = sizeof buffer / sizeof(wchar_t); size_t buffer_size = sizeof buffer / sizeof(wchar_t);
if (int rc = get_hostfxr_path(buffer, &buffer_size, parameters); rc != 0) if (int rc = get_hostfxr_path(buffer, &buffer_size, parameters); rc != 0)
return rc; return rc;
// Load hostfxr and get desired exports // Load hostfxr and get desired exports
auto lib = reinterpret_cast<void*>(load_library(buffer)); auto lib_hostfxr = reinterpret_cast<void*>(load_library(buffer));
if (!lib_hostfxr)
return -1;
m_hostfxr_initialize_for_runtime_config_fptr = reinterpret_cast<hostfxr_initialize_for_runtime_config_fn>( m_hostfxr_initialize_for_runtime_config_fptr = reinterpret_cast<hostfxr_initialize_for_runtime_config_fn>(
get_export(lib, "hostfxr_initialize_for_runtime_config")); get_export(lib_hostfxr, "hostfxr_initialize_for_runtime_config"));
m_hostfxr_get_runtime_delegate_fptr = reinterpret_cast<hostfxr_get_runtime_delegate_fn>( m_hostfxr_get_runtime_delegate_fptr = reinterpret_cast<hostfxr_get_runtime_delegate_fn>(
get_export(lib, "hostfxr_get_runtime_delegate")); get_export(lib_hostfxr, "hostfxr_get_runtime_delegate"));
m_hostfxr_close_fptr = reinterpret_cast<hostfxr_close_fn>( m_hostfxr_close_fptr = reinterpret_cast<hostfxr_close_fn>(
get_export(lib, "hostfxr_close")); get_export(lib_hostfxr, "hostfxr_close"));
return m_hostfxr_initialize_for_runtime_config_fptr return m_hostfxr_initialize_for_runtime_config_fptr
&& m_hostfxr_get_runtime_delegate_fptr && m_hostfxr_get_runtime_delegate_fptr

View file

@ -5,8 +5,10 @@
#include "nethost/nethost.h" #include "nethost/nethost.h"
class CoreCLR { class CoreCLR {
public: void* const m_calling_module;
explicit CoreCLR();
public:
explicit CoreCLR(void* calling_module);
~CoreCLR() = default; ~CoreCLR() = default;
int load_hostfxr(); int load_hostfxr();
@ -32,7 +34,7 @@ class CoreCLR {
void* reserved, void* reserved,
void** delegate) const; void** delegate) const;
private: private:
/* HostFXR delegates. */ /* HostFXR delegates. */
hostfxr_initialize_for_runtime_config_fn m_hostfxr_initialize_for_runtime_config_fptr{}; hostfxr_initialize_for_runtime_config_fn m_hostfxr_initialize_for_runtime_config_fptr{};
hostfxr_get_runtime_delegate_fn m_hostfxr_get_runtime_delegate_fptr{}; hostfxr_get_runtime_delegate_fn m_hostfxr_get_runtime_delegate_fptr{};

View file

@ -26,6 +26,7 @@ void ConsoleTeardown()
std::optional<CoreCLR> g_clr; std::optional<CoreCLR> g_clr;
int InitializeClrAndGetEntryPoint( int InitializeClrAndGetEntryPoint(
void* calling_module,
std::wstring runtimeconfig_path, std::wstring runtimeconfig_path,
std::wstring module_path, std::wstring module_path,
std::wstring entrypoint_assembly_name, std::wstring entrypoint_assembly_name,
@ -33,7 +34,7 @@ int InitializeClrAndGetEntryPoint(
std::wstring entrypoint_delegate_type_name, std::wstring entrypoint_delegate_type_name,
void** entrypoint_fn) void** entrypoint_fn)
{ {
g_clr = CoreCLR(); g_clr.emplace(calling_module);
int result; int result;
SetEnvironmentVariable(L"DOTNET_MULTILEVEL_LOOKUP", L"0"); SetEnvironmentVariable(L"DOTNET_MULTILEVEL_LOOKUP", L"0");

View file

@ -2,6 +2,7 @@ void ConsoleSetup(const std::wstring console_name);
void ConsoleTeardown(); void ConsoleTeardown();
int InitializeClrAndGetEntryPoint( int InitializeClrAndGetEntryPoint(
void* calling_module,
std::wstring runtimeconfig_path, std::wstring runtimeconfig_path,
std::wstring module_path, std::wstring module_path,
std::wstring entrypoint_assembly_name, std::wstring entrypoint_assembly_name,

Binary file not shown.

View file

@ -6,36 +6,6 @@
#include <stddef.h> #include <stddef.h>
#ifdef _WIN32
#ifdef NETHOST_EXPORT
#define NETHOST_API __declspec(dllexport)
#else
// Consuming the nethost as a static library
// Shouldn't export attempt to dllimport.
#ifdef NETHOST_USE_AS_STATIC
#define NETHOST_API
#else
#define NETHOST_API __declspec(dllimport)
#endif
#endif
#define NETHOST_CALLTYPE __stdcall
#ifdef _WCHAR_T_DEFINED
typedef wchar_t char_t;
#else
typedef unsigned short char_t;
#endif
#else
#ifdef NETHOST_EXPORT
#define NETHOST_API __attribute__((__visibility__("default")))
#else
#define NETHOST_API
#endif
#define NETHOST_CALLTYPE
typedef char char_t;
#endif
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
@ -87,10 +57,10 @@ struct get_hostfxr_parameters {
// The full search for the hostfxr library is done on every call. To minimize the need // The full search for the hostfxr library is done on every call. To minimize the need
// to call this function multiple times, pass a large buffer (e.g. PATH_MAX). // to call this function multiple times, pass a large buffer (e.g. PATH_MAX).
// //
NETHOST_API int NETHOST_CALLTYPE get_hostfxr_path( using get_hostfxr_path_type = int(__stdcall *)(
char_t * buffer, char_t* buffer,
size_t * buffer_size, size_t* buffer_size,
const struct get_hostfxr_parameters *parameters); const struct get_hostfxr_parameters* parameters);
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"

Binary file not shown.