crt: Add _(v)scwprintf emulation for msvcrt.dll

__ms_vsnwprintf() function now calls _vscwprintf() funcion. _vscwprintf()
is not available in older msvcrt.dll version, so provide emulation of
_scwprintf() and _vscwprintf() functions.

This emulation is similar to existing _scprintf and _vscprintf emulation.

Signed-off-by: Martin Storsjö <martin@martin.st>
This commit is contained in:
Pali Rohár 2024-11-15 17:59:54 +01:00 committed by Martin Storsjö
parent c50df03570
commit 3d539d06a0
4 changed files with 161 additions and 3 deletions

View File

@ -489,7 +489,9 @@ src_msvcrt32=\
misc/wctob.c \
stdio/_fstat64i32.c \
stdio/_scprintf.c \
stdio/_scwprintf.c \
stdio/_vscprintf.c \
stdio/_vscwprintf.c \
string/wcstok.c
# Files included in libmsvcrt-os.a (for msvcrt.dll) on x86_64
@ -732,7 +734,9 @@ src_pre_msvcr70=\
misc/wcstoimax.c \
misc/wcstoumax.c \
stdio/_scprintf.c \
stdio/_vscprintf.c
stdio/_scwprintf.c \
stdio/_vscprintf.c \
stdio/_vscwprintf.c
src_pre_msvcr71=\
misc/_set_purecall_handler.c

View File

@ -1214,7 +1214,7 @@ _getwche
_putwch
_resetstkoflw
F_NON_I386(_scprintf) ; i386 _scprintf replaced by emu
_scwprintf
F_NON_I386(_scwprintf) ; i386 _scwprintf replaced by emu
F_I386(_set_SSE2_enable)
_snscanf
_snwscanf
@ -1222,7 +1222,7 @@ F_NON_I386(_strtoi64) ; i386 _strtoi64 replaced by emu
F_NON_I386(_strtoui64) ; i386 _strtoui64 replaced by emu
_ungetwch
F_NON_I386(_vscprintf) ; i386 _vscprintf replaced by emu
_vscwprintf
F_NON_I386(_vscwprintf) ; i386 _vscwprintf replaced by emu
_wcserror
F_NON_I386(_wcstoi64) ; i386 _wcstoi64 replaced by emu
F_NON_I386(_wcstoui64) ; i386 _wcstoui64 replaced by emu

View File

@ -0,0 +1,67 @@
/**
* This file has no copyright assigned and is placed in the Public Domain.
* This file is part of the mingw-w64 runtime package.
* No warranty is given; refer to the file DISCLAIMER.PD within this package.
*/
#include <windows.h>
#include <stdarg.h>
#include <stdio.h>
#include <wchar.h>
/* mingw-w64 always provides _vscwprintf() implementation, so use it */
static int __cdecl emu_scwprintf(const wchar_t * __restrict__ format, ...)
{
va_list arglist;
int ret;
va_start(arglist, format);
ret = _vscwprintf(format, arglist);
va_end(arglist);
return ret;
}
#ifndef __LIBMSVCRT_OS__
int __attribute__ ((alias ("emu_scwprintf"))) __cdecl _scwprintf(const wchar_t * __restrict__, ...);
int (__cdecl *__MINGW_IMP_SYMBOL(_scwprintf))(const wchar_t * __restrict__, ...) = _scwprintf;
#else
#include <msvcrt.h>
static int __cdecl init_scwprintf(const wchar_t * __restrict__ format, ...);
int (__cdecl *__MINGW_IMP_SYMBOL(_scwprintf))(const wchar_t * __restrict__, ...) = init_scwprintf;
__attribute__((used))
static void resolve_scwprintf(void)
{
HMODULE msvcrt = __mingw_get_msvcrt_handle();
int (__cdecl *func)(const wchar_t * __restrict__, ...) = NULL;
if (msvcrt)
func = (int (__cdecl *)(const wchar_t * __restrict__, ...))GetProcAddress(msvcrt, "_scwprintf");
if (!func)
func = emu_scwprintf;
__MINGW_IMP_SYMBOL(_scwprintf) = func;
}
/* gcc does not provide an easy way to call another variadic function with reusing current arguments
* this source file is used only on i386, so do this function redirect via inline i386 assembly */
#define ASM_SYM(sym) __MINGW64_STRINGIFY(__MINGW_USYMBOL(sym))
asm (
".def\t" ASM_SYM(init_scwprintf) ";\t.scl\t3;\t.type\t32;\t.endef\n"
ASM_SYM(init_scwprintf) ":\n\t"
"pushal\n\t"
"call\t" ASM_SYM(resolve_scwprintf) "\n\t"
"popal\n\t"
/* fallthrough */
".globl\t" ASM_SYM(_scwprintf) "\n\t"
".def\t" ASM_SYM(_scwprintf) ";\t.scl\t2;\t.type\t32;\t.endef\n"
ASM_SYM(_scwprintf) ":\n\t"
"jmp\t*" ASM_SYM(__MINGW_IMP_SYMBOL(_scwprintf))
);
#endif

View File

@ -0,0 +1,87 @@
/**
* This file has no copyright assigned and is placed in the Public Domain.
* This file is part of the mingw-w64 runtime package.
* No warranty is given; refer to the file DISCLAIMER.PD within this package.
*/
#include <windows.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <wchar.h>
#include <errno.h>
/* emulation of _vscwprintf() via _vsnwprintf() */
static int __cdecl emu_vscwprintf(const wchar_t * __restrict__ format, va_list arglist)
{
wchar_t *buffer, *new_buffer;
size_t size;
int ret = -1;
/* if format is a null pointer, _vscwprintf() returns -1 and sets errno to EINVAL */
if (!format) {
errno = EINVAL;
return -1;
}
/* size for _vsnwprintf() must be non-zero and buffer must have place for terminating null character */
size = (wcslen(format) * 2 + 1) * sizeof(wchar_t);
buffer = malloc(size);
if (!buffer) {
errno = ENOMEM;
return -1;
}
/* if the number of characters to write is greater than size, _vsnwprintf() returns -1 */
while (size < SIZE_MAX/2 && (ret = _vsnwprintf(buffer, size, format, arglist)) < 0) {
/* in this case try with larger buffer */
size *= 2;
new_buffer = realloc(buffer, size);
if (!new_buffer)
break;
buffer = new_buffer;
}
free(buffer);
if (ret < 0) {
errno = ENOMEM;
return -1;
}
return ret;
}
#ifndef __LIBMSVCRT_OS__
int __attribute__ ((alias ("emu_vscwprintf"))) __cdecl _vscwprintf(const wchar_t * __restrict__, va_list);
int (__cdecl *__MINGW_IMP_SYMBOL(_vscwprintf))(const wchar_t * __restrict__, va_list) = _vscwprintf;
#else
#include <msvcrt.h>
static int __cdecl init_vscwprintf(const wchar_t * __restrict__ format, va_list arglist);
int (__cdecl *__MINGW_IMP_SYMBOL(_vscwprintf))(const wchar_t * __restrict__, va_list) = init_vscwprintf;
static int __cdecl init_vscwprintf(const wchar_t * __restrict__ format, va_list arglist)
{
HMODULE msvcrt = __mingw_get_msvcrt_handle();
int (__cdecl *func)(const wchar_t * __restrict__, va_list) = NULL;
if (msvcrt)
func = (int (__cdecl *)(const wchar_t * __restrict__, va_list))GetProcAddress(msvcrt, "_vscwprintf");
if (!func)
func = emu_vscwprintf;
return (__MINGW_IMP_SYMBOL(_vscwprintf) = func)(format, arglist);
}
int __cdecl _vscwprintf(const wchar_t * __restrict__ format, va_list arglist)
{
return __MINGW_IMP_SYMBOL(_vscwprintf)(format, arglist);
}
#endif