fix(relinker): preserve GUI startup failure status without stderr
This commit is contained in:
parent
cea1ef13c3
commit
2ed81aebe4
|
|
@ -162,33 +162,32 @@ WindowsEntryStub WindowsEntryStubBuilder::Build(const std::uint32_t dataRva, con
|
|||
code.Emit({0x0f, 0x0b});
|
||||
};
|
||||
|
||||
const auto requireDiagnosticSuccess = [&] {
|
||||
code.Emit({0x48, 0x85, 0xc0});
|
||||
const auto success = code.Branch({0x0f, 0x85});
|
||||
raise(0xc0000001u);
|
||||
code.PatchBranch(success, code.GetRva());
|
||||
};
|
||||
|
||||
const auto writeString = [&](const std::uint32_t stringRva, const bool isError = false) {
|
||||
code.Rip({0x48, 0x8d, 0x0d}, stringRva);
|
||||
call("lstrlenA");
|
||||
code.Emit({0x48, 0x85, 0xc0});
|
||||
const auto empty = code.Branch({0x0f, 0x84});
|
||||
code.Emit({0x89, 0x44, 0x24, 0x3c, 0xb9});
|
||||
code.U32(isError ? 0xfffffff4u : 0xfffffff5u);
|
||||
call("GetStdHandle");
|
||||
requireDiagnosticSuccess();
|
||||
code.Emit({0x48, 0x85, 0xc0});
|
||||
const auto noHandle = code.Branch({0x0f, 0x84});
|
||||
code.Emit({0x48, 0x83, 0xf8, 0xff});
|
||||
const auto validHandle = code.Branch({0x0f, 0x85});
|
||||
raise(0xc0000001u);
|
||||
code.PatchBranch(validHandle, code.GetRva());
|
||||
const auto invalidHandle = code.Branch({0x0f, 0x84});
|
||||
code.Emit({0x48, 0x89, 0xc1});
|
||||
code.Rip({0x48, 0x8d, 0x15}, stringRva);
|
||||
code.Emit({0x44, 0x8b, 0x44, 0x24, 0x3c, 0x4c, 0x8d, 0x4c, 0x24, 0x38, 0x48, 0xc7, 0x44, 0x24, 0x20, 0, 0, 0, 0});
|
||||
call("WriteFile");
|
||||
requireDiagnosticSuccess();
|
||||
code.Emit({0x48, 0x85, 0xc0});
|
||||
const auto failedWrite = code.Branch({0x0f, 0x84});
|
||||
code.Emit({0x8b, 0x44, 0x24, 0x38, 0x3b, 0x44, 0x24, 0x3c});
|
||||
const auto complete = code.Branch({0x0f, 0x84});
|
||||
raise(0xc0000001u);
|
||||
code.PatchBranch(complete, code.GetRva());
|
||||
const auto incompleteWrite = code.Branch({0x0f, 0x85});
|
||||
const auto done = code.GetRva();
|
||||
code.PatchBranch(empty, done);
|
||||
code.PatchBranch(noHandle, done);
|
||||
code.PatchBranch(invalidHandle, done);
|
||||
code.PatchBranch(failedWrite, done);
|
||||
code.PatchBranch(incompleteWrite, done);
|
||||
};
|
||||
|
||||
const auto writeLastError = [&] {
|
||||
|
|
@ -214,8 +213,10 @@ WindowsEntryStub WindowsEntryStubBuilder::Build(const std::uint32_t dataRva, con
|
|||
code.U32(ErrorMessageCapacity);
|
||||
code.Emit({0x48, 0xc7, 0x44, 0x24, 0x30, 0, 0, 0, 0});
|
||||
call("FormatMessageA");
|
||||
requireDiagnosticSuccess();
|
||||
code.Emit({0x48, 0x85, 0xc0});
|
||||
const auto noMessage = code.Branch({0x0f, 0x84});
|
||||
writeString(errorMessage, true);
|
||||
code.PatchBranch(noMessage, code.GetRva());
|
||||
};
|
||||
|
||||
const auto captureLastError = [&] {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
from pathlib import Path
|
||||
import ctypes
|
||||
import os
|
||||
import struct
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from ctypes import wintypes
|
||||
from pathlib import Path
|
||||
|
||||
from test_guest_intel_trampolines import main_fixture
|
||||
from test_guest_module_directories import module_with_symbol
|
||||
|
|
@ -73,12 +75,72 @@ def executable(owner, symbol='shared#A#B', module_name=None, extra_dependencies=
|
|||
return image
|
||||
|
||||
|
||||
def shell_execute_exit_code(executable_path):
|
||||
see_mask_nocloseprocess = 0x00000040
|
||||
wait_object_0 = 0
|
||||
wait_timeout = 0x00000102
|
||||
|
||||
class ShellExecuteInfo(ctypes.Structure):
|
||||
_fields_ = [
|
||||
('cbSize', wintypes.DWORD),
|
||||
('fMask', wintypes.DWORD),
|
||||
('hwnd', wintypes.HWND),
|
||||
('lpVerb', wintypes.LPCWSTR),
|
||||
('lpFile', wintypes.LPCWSTR),
|
||||
('lpParameters', wintypes.LPCWSTR),
|
||||
('lpDirectory', wintypes.LPCWSTR),
|
||||
('nShow', ctypes.c_int),
|
||||
('hInstApp', wintypes.HANDLE),
|
||||
('lpIDList', wintypes.LPVOID),
|
||||
('lpClass', wintypes.LPCWSTR),
|
||||
('hkeyClass', wintypes.HANDLE),
|
||||
('dwHotKey', wintypes.DWORD),
|
||||
('hIconOrMonitor', wintypes.HANDLE),
|
||||
('hProcess', wintypes.HANDLE),
|
||||
]
|
||||
|
||||
shell32 = ctypes.WinDLL('shell32', use_last_error=True)
|
||||
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
|
||||
shell32.ShellExecuteExW.argtypes = [ctypes.POINTER(ShellExecuteInfo)]
|
||||
shell32.ShellExecuteExW.restype = wintypes.BOOL
|
||||
kernel32.WaitForSingleObject.argtypes = [wintypes.HANDLE, wintypes.DWORD]
|
||||
kernel32.WaitForSingleObject.restype = wintypes.DWORD
|
||||
kernel32.GetExitCodeProcess.argtypes = [wintypes.HANDLE, ctypes.POINTER(wintypes.DWORD)]
|
||||
kernel32.GetExitCodeProcess.restype = wintypes.BOOL
|
||||
kernel32.TerminateProcess.argtypes = [wintypes.HANDLE, wintypes.UINT]
|
||||
kernel32.TerminateProcess.restype = wintypes.BOOL
|
||||
kernel32.CloseHandle.argtypes = [wintypes.HANDLE]
|
||||
kernel32.CloseHandle.restype = wintypes.BOOL
|
||||
info = ShellExecuteInfo()
|
||||
info.cbSize = ctypes.sizeof(info)
|
||||
info.fMask = see_mask_nocloseprocess
|
||||
info.lpVerb = 'open'
|
||||
info.lpFile = str(executable_path)
|
||||
info.nShow = 1
|
||||
if not shell32.ShellExecuteExW(ctypes.byref(info)):
|
||||
raise OSError(ctypes.get_last_error(), 'ShellExecuteExW failed')
|
||||
try:
|
||||
result = kernel32.WaitForSingleObject(info.hProcess, 30_000)
|
||||
if result == wait_timeout:
|
||||
kernel32.TerminateProcess(info.hProcess, 0xffffffff)
|
||||
kernel32.WaitForSingleObject(info.hProcess, 0xffffffff)
|
||||
raise TimeoutError('ShellExecuteExW child did not exit')
|
||||
if result != wait_object_0:
|
||||
raise OSError(f'WaitForSingleObject returned {result}')
|
||||
exit_code = wintypes.DWORD()
|
||||
if not kernel32.GetExitCodeProcess(info.hProcess, ctypes.byref(exit_code)):
|
||||
raise OSError(ctypes.get_last_error(), 'GetExitCodeProcess failed')
|
||||
return exit_code.value
|
||||
finally:
|
||||
kernel32.CloseHandle(info.hProcess)
|
||||
|
||||
|
||||
def main():
|
||||
relinker = Path(sys.argv[1]).resolve()
|
||||
with tempfile.TemporaryDirectory(prefix='anyps5-import-modules-') as directory:
|
||||
work = Path(directory)
|
||||
|
||||
def convert(name, owner, symbol='shared#A#B', guest_owner=None, module_name=None, provider_name='b.prx', extra_dependencies=()):
|
||||
def convert(name, owner, symbol='shared#A#B', guest_owner=None, module_name=None, provider_name='b.prx', extra_dependencies=(), windows_gui=False):
|
||||
case = work / name
|
||||
modules = case / 'prx'
|
||||
modules.mkdir(parents=True)
|
||||
|
|
@ -94,7 +156,11 @@ def main():
|
|||
source = case / 'input.elf'
|
||||
source.write_bytes(executable(owner, symbol, module_name, extra_dependencies))
|
||||
output = case / 'output.exe'
|
||||
result = subprocess.run([str(relinker), '--windows', str(source), str(output)], capture_output=True, text=True, timeout=30)
|
||||
arguments = [str(relinker), '--windows']
|
||||
if windows_gui:
|
||||
arguments.append('--windows-gui')
|
||||
arguments.extend([str(source), str(output)])
|
||||
result = subprocess.run(arguments, capture_output=True, text=True, timeout=30)
|
||||
return result, output
|
||||
|
||||
for owner in ('a.prx', 'b.prx', 'alias.prx'):
|
||||
|
|
@ -143,6 +209,15 @@ def main():
|
|||
assert result.returncode == 2 and 'Ambiguous guest import' in result.stderr, result.stderr
|
||||
assert not output.exists()
|
||||
|
||||
if os.name == 'nt':
|
||||
for name, owner, symbol, expected in [
|
||||
('missing-symbol-gui', 'a.prx', 'absent#A#B', 0xc0000139),
|
||||
('missing-dependency-gui', 'missing.prx', 'shared#A#B', 0xc0000135)]:
|
||||
result, output = convert(name, owner, symbol, windows_gui=True)
|
||||
assert result.returncode == 0, result.stderr
|
||||
run_status = shell_execute_exit_code(output)
|
||||
assert run_status == expected, (name, hex(run_status), hex(expected))
|
||||
|
||||
result, output = convert('missing-symbol', 'a.prx', 'absent#A#B')
|
||||
assert result.returncode == 0, result.stderr
|
||||
if os.name == 'nt':
|
||||
|
|
@ -158,6 +233,7 @@ def main():
|
|||
if os.name == 'nt':
|
||||
run = subprocess.run([str(output)], capture_output=True, text=True, timeout=30)
|
||||
assert run.returncode != 0 and 'Failed to load module:' in run.stderr and 'missing.prx' in run.stderr, (run.returncode, run.stderr)
|
||||
|
||||
print('Windows module-scoped import tests passed')
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue