Hi,
Two independent win32 fixes, found while chasing down why current V
(vlang/v) programs built with tcc + Boehm GC crash on startup.
Patch 1/2 is a standalone DEF-file parsing bug: pe_load_def() embeds
a .def file's quoted/unsuffixed LIBRARY name verbatim into the import
table, so any executable linked against a .def file like
LIBRARY "libcrypto-4-x64"
EXPORTS
...
(exactly what OpenSSL's official Win64 installer ships) fails to
start with STATUS_DLL_NOT_FOUND, since '"' is not a legal filename
character. Confirmed present since at least 2020 (commit 9eef339),
still present on current mob. Fix: strip a matched pair of surrounding
quotes, and default a missing extension to .dll, matching MSVC's own
DEF LIBRARY statement semantics. Verified against the exact repro
(quoted, unsuffixed LIBRARY name now links and runs correctly), and
re-tested the previously-working unquoted/quoted-with-extension forms
to confirm no regression.
Patch 2/2 is what actually surfaced while testing patch 1 end-to-end:
any tcc-compiled program built with -bt (backtrace support, which is
what tccbin's own recommended Windows build config enables by default)
that calls OutputDebugString - directly, or via a library, e.g. Boehm
GC's Windows logging path - crashes immediately. cpu_exception_handler()
in tccrun.c falls into its default case, and therefore terminates the
process, for any exception code it doesn't explicitly recognize.
DBG_PRINTEXCEPTION_C (0x40010006) is raised internally by Windows as
part of OutputDebugString()'s own protocol specifically so an attached
debugger can read the string before the process continues; it's
supposed to be silently passed through when no debugger is attached,
not treated as a fault. Fix: add the missing DBG_PRINTEXCEPTION_C /
DBG_RIPEXCEPTION constants to winnt.h (there was a literal gap between
DBG_CONTROL_C and DBG_CONTROL_BREAK), and pass DBG_PRINTEXCEPTION_C
through via EXCEPTION_CONTINUE_SEARCH instead of reporting+exiting.
Verified genuine faults are unaffected - a real null-pointer
dereference still reports "invalid memory access" and exits fatally
as before; only this one known-benign debugger-notification code is
no longer misclassified as a crash. Also verified end-to-end: a V
program linking Boehm GC (which needed its own separate fix, unrelated
to tcc, for a different old-tinycc-vs-new-tinycc ABI mismatch around
setjmp()) now builds and runs correctly with tcc's default -bt/-bcheck
flags, with no workaround flags needed.
Both patches tested natively on Windows (not under emulation), against
current mob (commit d9d02c56 at the time of testing).

Thanks,

Richard Wheeler

Patch1

From c10de82cde1a27305ea2b99742c8306b8e233d70 Mon Sep 17 00:00:00 2001
From: Richard Wheeler <[email protected]>
Date: Wed, 22 Jul 2026 17:40:38 -0400
Subject: [PATCH 1/2] tccpe: strip quotes and default .dll extension in DEF
LIBRARY statements

pe_load_def() copied the LIBRARY directive's value (via get_token())
verbatim into dllname, with no handling for a quoted string and no
fallback to append .dll when the name has no extension.

Real-world .def files commonly quote the LIBRARY name and omit the
extension (this is exactly what MSVC's own DEF-file parser accepts,
and what OpenSSL's official Windows installer ships, e.g.
LIBRARY "libcrypto-4-x64"). Since '"' is not a legal filename
character, the resulting import table entry can never match any
real DLL, and the linked executable fails to start with
STATUS_DLL_NOT_FOUND.

Strip a matched pair of surrounding quotes, then default a missing
extension to .dll, mirroring MSVC's DEF LIBRARY statement semantics.
---
tccpe.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/tccpe.c b/tccpe.c
index c076d084..c15dada9 100644
--- a/tccpe.c
+++ b/tccpe.c
@@ -1777,7 +1777,7 @@ static char *get_token(char **s, char *f)

static int pe_load_def(TCCState *s1, int fd)
{
- int state = 0, ret = -1, dllindex = 0, ord;
+ int state = 0, ret = -1, dllindex = 0, ord, len;
char dllname[80], *buf, *line, *p, *x, next;

buf = tcc_load_text(fd);
@@ -1792,7 +1792,17 @@ static int pe_load_def(TCCState *s1, int fd)
case 0:
if (0 != stricmp(p, "LIBRARY") || next == '\n')
goto quit;
- pstrcpy(dllname, sizeof dllname, get_token(&line, &next));
+ x = get_token(&line, &next);
+ len = strlen(x);
+ if (len >= 2 && '"' == x[0] && '"' == x[len - 1]) {
+ x[len - 1] = 0;
+ ++x;
+ }
+ pstrcpy(dllname, sizeof dllname, x);
+ /* DEF LIBRARY statements may give a bare module name with no
+ extension; MSVC's own DEF parser assumes .dll in that case. */
+ if (0 == *tcc_fileextension(dllname))
+ pstrcat(dllname, sizeof dllname, ".dll");
++state;
break;
case 1:
-- 2.53.0.windows.2
Patch2

From 6036c98c5c2baea96b21e98dfb510c5214f19c4b Mon Sep 17 00:00:00 2001
From: Richard Wheeler <[email protected]>
Date: Thu, 23 Jul 2026 12:06:08 -0400
Subject: [PATCH 2/2] win32: don't treat DBG_PRINTEXCEPTION_C as a fatal crash
under -bt

cpu_exception_handler(), installed via AddVectoredExceptionHandler()
whenever backtrace support is active, fell into its default case -
and therefore terminated the process via rt_exit() - for any
exception code it didn't explicitly recognize.

DBG_PRINTEXCEPTION_C (0x40010006) is raised internally by Windows as
part of OutputDebugString()'s own protocol, so that an attached
debugger can read the string before the process continues; it is
expected to be silently passed through when no debugger is present.
Any program compiled with -bt that calls OutputDebugString (directly,
or via a library - e.g. Boehm GC's Windows logging path) currently
gets killed by this false positive instead.

Add the missing DBG_PRINTEXCEPTION_C/DBG_RIPEXCEPTION constants to
winnt.h, and pass DBG_PRINTEXCEPTION_C through via
EXCEPTION_CONTINUE_SEARCH instead of reporting and exiting. Genuine
faults (access violation, stack overflow, divide-by-zero, etc.) are
unaffected - only this one known-benign debugger-notification code is
no longer misclassified as fatal.
---
tccrun.c | 5 +++++
win32/include/winapi/winnt.h | 2 ++
2 files changed, 7 insertions(+)

diff --git a/tccrun.c b/tccrun.c
index 4ff511df..2873d562 100644
--- a/tccrun.c
+++ b/tccrun.c
@@ -1409,6 +1409,11 @@ static long __stdcall 
cpu_exception_handler(EXCEPTION_POINTERS *ex_info)
f.ip = *(addr_t*)f.sp;
rt_error(&f, "breakpoint/single-step exception:");
return EXCEPTION_CONTINUE_SEARCH;
+ case DBG_PRINTEXCEPTION_C:
+ /* raised by OutputDebugString(); benign and expected to be
+ ignored when no debugger is attached - do not treat it as
+ a fatal crash. */
+ return EXCEPTION_CONTINUE_SEARCH;
default:
rt_error(&f, "caught exception %08x", code);
break;
diff --git a/win32/include/winapi/winnt.h b/win32/include/winapi/winnt.h
index baace123..1e9876a8 100644
--- a/win32/include/winapi/winnt.h
+++ b/win32/include/winapi/winnt.h
@@ -789,6 +789,8 @@ typedef DWORD LCID;
#define DBG_TERMINATE_THREAD ((DWORD)0x40010003L)
#define DBG_TERMINATE_PROCESS ((DWORD)0x40010004L)
#define DBG_CONTROL_C ((DWORD)0x40010005L)
+#define DBG_PRINTEXCEPTION_C ((DWORD)0x40010006L)
+#define DBG_RIPEXCEPTION ((DWORD)0x40010007L)
#define DBG_CONTROL_BREAK ((DWORD)0x40010008L)
#define DBG_COMMAND_EXCEPTION ((DWORD)0x40010009L)
#define STATUS_GUARD_PAGE_VIOLATION ((DWORD)0x80000001L)
-- 2.53.0.windows.2

Attachment: 0002-win32-don-t-treat-DBG_PRINTEXCEPTION_C-as-a-fatal-cr.patch
Description: Binary data

Attachment: 0001-tccpe-strip-quotes-and-default-.dll-extension-in-DEF.patch
Description: Binary data

_______________________________________________
Tinycc-devel mailing list
[email protected]
https://lists.nongnu.org/mailman/listinfo/tinycc-devel

Reply via email to