diff --git a/.gitignore b/.gitignore index eda54ed08..744d70fe9 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,6 @@ tags *.pc .*.swp .sw? -*.rc *.gcno *.gcda *.gcov diff --git a/build-aux/meson.build b/build-aux/meson.build new file mode 100644 index 000000000..7f3f2ffda --- /dev/null +++ b/build-aux/meson.build @@ -0,0 +1 @@ +subdir('win32') diff --git a/build-aux/win32/app.c b/build-aux/win32/app.c new file mode 100644 index 000000000..e158c3b4d --- /dev/null +++ b/build-aux/win32/app.c @@ -0,0 +1,162 @@ +/* + * Copyright © 2025 Luca Bacci + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + * Author: Luca Bacci + */ + +#include "config.h" + +#include + +#include +#include +#include +#include + +#ifdef _MSC_VER +#include +#endif + +static void +set_process_wide_settings (void) +{ +#if defined (_M_IX86) || defined (__i386__) + /* https://learn.microsoft.com/en-us/archive/blogs/michael_howard/faq-about-heapsetinformation-in-windows-vista-and-heap-based-buffer-overruns */ + /* https://web.archive.org/web/20080825034220/https://blogs.msdn.com/sdl/archive/2008/06/06/corrupted-heap-termination-redux.aspx */ + HeapSetInformation (NULL, HeapEnableTerminationOnCorruption, NULL, 0); + + /* https://learn.microsoft.com/en-us/archive/blogs/michael_howard/new-nx-apis-added-to-windows-vista-sp1-windows-xp-sp3-and-windows-server-2008 */ + SetProcessDEPPolicy (PROCESS_DEP_ENABLE | PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION); +#endif + + SetErrorMode (GetErrorMode () | SEM_FAILCRITICALERRORS); +} + +static void +set_crt_non_interactive (void) +{ + /* The Debug CRT may show UI dialogs even in console applications. + * Direct to stderr instead. + */ + _CrtSetReportFile (_CRT_ASSERT, _CRTDBG_FILE_STDERR); + _CrtSetReportMode (_CRT_ASSERT, _CRTDBG_MODE_FILE); + + _CrtSetReportFile (_CRT_ERROR, _CRTDBG_FILE_STDERR); + _CrtSetReportMode (_CRT_ERROR, _CRTDBG_MODE_FILE); + + _CrtSetReportFile (_CRT_WARN, _CRTDBG_FILE_STDERR); + _CrtSetReportMode (_CRT_WARN, _CRTDBG_MODE_FILE); +} + +static void +set_stderr_unbuffered_mode (void) +{ + /* MSVCRT.DLL can open stderr in full-buffering mode. That depends on + * the type of output device; for example, it's fully buffered for + * named pipes but not for console devices. + * + * Having a fully buffered stderr is not a good default since we can + * loose important messages before a crash. Moreover, POSIX forbids + * full buffering on stderr. So here we set stderr to unbuffered mode. + * + * Note: line buffering mode would be good enough, but the Windows C + * RunTime library implements it the same as full buffering: + * + * "for some systems, _IOLBF provides line buffering. However, for + * Win32, the behavior is the same as _IOFBF: Full Buffering" + * + * https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/setvbuf#remarks + * + * References: + * + * - https://sourceforge.net/p/mingw/mailman/message/27121137/ + */ +#if !defined (_UCRT) + int ret = setvbuf (stderr, NULL, _IONBF, 0); + assert (ret == 0); +#endif +} + +static void +early_flush_exit_handler (void) +{ + /* There are two ways to flush open streams: calling fflush with NULL + * argument and calling _flushall. The former flushes output streams + * only, the latter flushes both input and output streams. + * We should not do anything with input streams here since flushing + * means * discarding * data. + */ + fflush (NULL); +} + +static void +register_early_flush_at_exit (void) +{ + /* Implement the two-phase flushing at process exit. + * + * The C RunTime library flushes open streams within its DllMain handler. + * This goes against the rules for DllMain, as each stream is protected + * by a lock and locks must not be acquired in DllMain. + * + * So we flush from app code using an atexit handler. The handler runs when + * the application is in a fully working state and thus is completely safe. + * + * This ensures that all important data is flushed. Anything that is written + * after exit will be flushed lately by the C RunTime library (and therefore + * may be skipped). + * + * See comments in "%ProgramFiles(x86)%\Windows Kits\10\Source\\ + * \ucrt\stdio\fflush.cpp" for more informations. + * + * References: + * + * - https://devblogs.microsoft.com/oldnewthing/20070503-00/?p=27003 + * - https://devblogs.microsoft.com/oldnewthing/20100122-00/?p=15193 + * - https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-best-practices + */ + int ret = atexit (early_flush_exit_handler); + assert (ret == 0); +} + +/* Boilerplate for CRT constructor */ + +#ifdef _MSC_VER +static void startup (void); + +__pragma (section (".CRT$XCT", long, read)) + +__declspec (allocate (".CRT$XCT")) +const void (*ptr_startup) (void) = startup; + +#ifdef _M_IX86 +__pragma (comment (linker, "/INCLUDE:" "_ptr_startup")) +#else +__pragma (comment (linker, "/INCLUDE:" "ptr_startup")) +#endif +#else +static void __attribute__((constructor)) startup (void); +#endif + +static void +startup (void) +{ + set_crt_non_interactive (); + set_process_wide_settings (); + set_stderr_unbuffered_mode (); + register_early_flush_at_exit (); +} diff --git a/build-aux/win32/app.manifest.xml b/build-aux/win32/app.manifest.xml new file mode 100644 index 000000000..57ec3d31d --- /dev/null +++ b/build-aux/win32/app.manifest.xml @@ -0,0 +1,40 @@ + + + + + UTF-8 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build-aux/win32/app.rc b/build-aux/win32/app.rc new file mode 100644 index 000000000..ce9785df1 --- /dev/null +++ b/build-aux/win32/app.rc @@ -0,0 +1,6 @@ +#pragma code_page(65001) + +#define WIN32_LEAN_AND_MEAN +#include + +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "app.manifest.xml" diff --git a/build-aux/win32/meson.build b/build-aux/win32/meson.build new file mode 100644 index 000000000..820b6514f --- /dev/null +++ b/build-aux/win32/meson.build @@ -0,0 +1,14 @@ + +if host_system == 'windows' + static_lib = static_library('os-profile-app-static-lib', + sources: ['app.c'], + include_directories: [configinc]) + + resources = windows.compile_resources('app.rc', + depend_files: ['app.manifest.xml']) + + app_profile_dep = declare_dependency(link_whole: [static_lib], + sources: [resources]) +else + app_profile_dep = declare_dependency() +endif