Hello Richard,
Thank you for the detailed report, reproducer, and patch.
The OpenSSL and V context was particularly useful.
I confirmed the issue and pushed the fix to the mob branch:
31020bf9 win32: handle quoted LIBRARY names in def files
Your diagnosis and the two essential parts of your patch were correct: the
quotes must not be stored in the PE import name, and a bare module name must
receive the default .dll extension.
I didn't apply the patch verbatim because its context is from the older
line-based DEF parser, while the current parser is token-based. I also wanted
to address a few related edge cases:
- The LIBRARY argument is now scanned as a quote-aware token, so quoted names
may contain whitespace and may be followed by options such as BASE=address.
- Unterminated or otherwise malformed quotes are rejected instead of being
copied into the import name.
- The former 80-byte dllname buffer was replaced with dynamic allocation,
avoiding silent truncation of long names or of the appended .dll suffix.
- An existing extension is preserved.
I added a Windows regression test which builds "def library test.dll", links
through a DEF file containing LIBRARY "def library test", checks that the PE
import table contains the exact name "def library test.dll", and runs the
resulting executable.
The complete Windows test suite passes. I also built and ran the complete test
suite on Linux with both GCC and Clang, built the PE cross-targets from Linux,
and exercised quoted/unquoted names, present/missing extensions, long names,
trailing BASE=, and malformed quotes through the Linux-hosted PE compiler.
Thanks again for identifying this and helping with the fix.
Regards,
Mounir IDRASSI
From: Richard Wheeler <[email protected]>
To: "[email protected]"<[email protected]>
Date: Thu, 23 Jul 2026 05:42:04 +0900
Subject: [Tinycc-devel] [BUG] pe_load_def() doesn't strip quotes or default the
.dll extension on a LIBRARY directive
> Hi,
> When linking against a .def file whose LIBRARY directive is a quoted
> string with no file extension (a form MS's own LINK.exe/lib.exe accept,
> and which real-world .def files - e.g. those shipped in OpenSSL's
> official Win64 installer - actually use), pe_load_def() in tccpe.c
> embeds the directive's value literally, quote characters included and
> with no .dll suffix appended, as the DLL name the resulting executable
> must load at startup. Since '"' is not a legal Windows filename
> character, no file can ever satisfy that embedded name, and the
> program fails to even start, with the Windows loader reporting
> STATUS_DLL_NOT_FOUND (0xC0000135) - regardless of whether the correctly
> named DLL is present and on PATH.
> Repro:
> // mylib.c -> compiled to mylib-1-x64.dll, exporting myfunc
> __declspec(dllexport) int myfunc(void) { return 42; }
> // mylib-1-x64.def
> LIBRARY "mylib-1-x64"
> EXPORTS
> myfunc @1
> tcc -shared mylib.c -o mylib-1-x64.dll
> tcc prog.c mylib-1-x64.def -o prog.exe
> prog.exe fails to launch (STATUS_DLL_NOT_FOUND), even though
> mylib-1-x64.dll exists exactly where it should. Inspecting prog.exe's
> import table shows the imported module name as the literal string
> "mylib-1-x64" (15 chars, quotes included, no .dll suffix), which cannot
> match any real file. MSVC's link.exe handles the identical .def file
> correctly, embedding the correct mylib-1-x64.dll reference.
> Root cause: in pe_load_def(), the LIBRARY value is extracted via a
> plain whitespace-delimited tokenizer (trimfront/pstrcpy) that neither
> strips a surrounding quote pair nor defaults a missing extension,
> unlike MSVC's own DEF-file handling, which does both.
> Confirmed present on the current mob branch, and reproduced identically
> on commit 9eef33993ade2d3b964d19b1081978ceae5d359d (2020-06-05) - this
> is not a regression, it's a long-standing, still-open gap.
> Found via V (vlang/v), whose Windows CI links crypto/ecdsa,
> crypto/rsa_pss, and net/quic against OpenSSL. OpenSSL's official
> Windows installer (Win64OpenSSL, e.g. version 4.0.1) ships
> lib\VC\x64\MD\libcrypto.def / libssl.def with exactly this quoted,
> unsuffixed format (LIBRARY "libcrypto-4-x64"), so every tcc-linked
> executable built against -lcrypto on Windows with this (very common,
> officially distributed) OpenSSL package fails to launch.
> Patch attached: strip a matched pair of surrounding quote characters
> from the LIBRARY name, then default a missing extension to .dll,
> mirroring MSVC's own DEF LIBRARY statement semantics. Verified against
> the repro above (prog.exe now runs and returns 42), and re-tested the
> previously-working unquoted and quoted-with-extension forms to confirm
> no regression.
>
> Thanks,
>
> Richard Wheeler
>
> From df132956f0bf6fcd56f0e63811c51066ae9793a9 Mon Sep 17 00:00:00 2001
> From: Richard Wheeler <[email protected]>
> Date: Wed, 22 Jul 2026 16:21:13 -0400
> Subject: [PATCH] tccpe: strip quotes and default .dll extension in DEF
> LIBRARY
> statements
>
> pe_load_def() copied the LIBRARY directive's value 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 a16411c7..0a434d05 100644
> --- a/tccpe.c
> +++ b/tccpe.c
> @@ -1701,7 +1701,7 @@ static char *trimback(char *a, char *e)
> /* ------------------------------------------------------------- */
> 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 line[400], dllname[80], *p, *x;
> FILE *fp;
>
> @@ -1716,7 +1716,17 @@ static int pe_load_def(TCCState *s1, int fd)
> case 0:
> if (0 != strnicmp(p, "LIBRARY", 7))
> goto quit;
> - pstrcpy(dllname, sizeof dllname, trimfront(p+7));
> + x = trimfront(p+7);
> + 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;
> continue;
>
> --
> 2.53.0.windows.2
_______________________________________________
Tinycc-devel mailing list
[email protected]
https://lists.nongnu.org/mailman/listinfo/tinycc-devel