When linking against a.deffile whoseLIBRARYdirective is aquotedstring withno
file extension(a form Microsoft's ownLINK.exe/lib.exeaccept, and which
real-world.deffiles — e.g. those shipped in OpenSSL's official Win64 installer
— actually use), TCC's PE import-table writer embeds the directive's
valueliterally, quote characters included, with no.dllsuffix appended, as the
DLL name the resulting executable must load at startup.
Since"is not a legal character in a Windows filename, no file can ever satisfy
that embedded name. Every program linked this way fails to even start, with the
Windows loader reportingSTATUS_DLL_NOT_FOUND(0xC0000135) — regardless of
whether the correctly-named real DLL is present and onPATH.
See attached .md file for fuller details
Richard Wheeler
# TinyCC (Windows/PE target): `.def` file `LIBRARY "name"` directive not unquoted, and no default `.dll` extension â produces an unloadable executable (STATUS_DLL_NOT_FOUND)
**Suggested subject line (if posting to the tinycc-devel mailing list):**
`[BUG] pe_load_def() doesn't strip quotes or default the .dll extension on a LIBRARY directive`
## Summary
When linking against a `.def` file whose `LIBRARY` directive is a **quoted** string
with **no file extension** (a form Microsoft'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), TCC's PE import-table writer embeds the directive's
value **literally, quote characters included, with no `.dll` suffix appended**, as
the DLL name the resulting executable must load at startup.
Since `"` is not a legal character in a Windows filename, no file can ever satisfy
that embedded name. Every program linked this way fails to even start, with the
Windows loader reporting `STATUS_DLL_NOT_FOUND` (0xC0000135) â regardless of
whether the correctly-named real DLL is present and on `PATH`.
## Environment
- Confirmed present in TinyCC mob branch commit `9eef33993ade2d3b964d19b1081978ceae5d359d`
(2020-06-05) and reproduced identically by reading the **current** mob branch's
`tccpe.c` (`pe_load_def`, tokenizing via the `get_token()` helper) â this is not
a regression, it appears to be a long-standing, still-open gap.
- Windows 64-bit PE target (`tcc -m64`, or any Win64 tcc build).
## Steps to reproduce (minimal, self-contained)
1. Build any tiny DLL that exports one function, and name the file with a
version-suffixed name (a very common real-world convention â this is exactly
how OpenSSL 3.x/4.x ship their Windows runtime DLLs):
```c
// mylib.c -> compiled to mylib-1-x64.dll, exporting myfunc
__declspec(dllexport) int myfunc(void) { return 42; }
```
2. Write a `.def` file for it whose `LIBRARY` directive is **quoted** and has
**no extension** â again, exactly the format real tooling produces (see
"Real-world occurrence" below):
```
LIBRARY "mylib-1-x64"
EXPORTS
myfunc @1
```
3. Compile/link a trivial consumer against it with tcc:
```
tcc -L. -lmylib prog.c -o prog.exe
```
4. Run `prog.exe` with `mylib-1-x64.dll` present in the same directory (or on `PATH`).
### Actual behavior
`prog.exe` fails to launch. The Windows loader reports `STATUS_DLL_NOT_FOUND`
(0xC0000135) â the process never reaches `main()`, so there is no C-level error
message, just an OS-level launch failure â **even though `mylib-1-x64.dll` exists**
exactly where it should.
Inspecting `prog.exe`'s import table (e.g. via `dumpbin /imports` or any PE viewer)
shows the imported module name as the literal 15-character string
`"mylib-1-x64"` â quote characters included, no `.dll` suffix â which cannot match
any real file.
### Expected behavior
`prog.exe` runs and calls `myfunc()` successfully, the same as it would if linked
with MSVC's `link.exe` against an import library built from the identical `.def`
file (confirmed: MSVC handles this exact `.def` format correctly, reading the
quoted/unsuffixed name via its own DEF-file parser and embedding the correct,
loadable `mylib-1-x64.dll` reference).
## Root cause
In `tccpe.c`'s `pe_load_def()`, the `LIBRARY` directive's value is extracted via
`get_token()` (in older snapshots, inlined as `trimfront(p + 7)`) and copied
directly into `dllname` with `pstrcpy()`:
```c
case 0:
if (0 != stricmp(p, "LIBRARY") || next == '\n')
goto quit;
pstrcpy(dllname, sizeof dllname, get_token(&line, &next));
++state;
break;
```
`get_token()` is a plain whitespace-delimited tokenizer â it does not recognize
or strip a surrounding pair of `"` characters. The resulting `dllname` is then
used as-is (`tcc_add_dllref(s1, dllname, 0)`), with **no fallback to append
`.dll`** if the name has no recognized extension â unlike MSVC's own DEF-file
handling, which does both.
## Suggested fix
In `pe_load_def()`, after extracting the `LIBRARY` token:
1. If the token starts and ends with `"`, strip both quote characters.
2. If the (now-unquoted) name has no `.` in its final path component (i.e. no
file extension), append `.dll` before use.
This mirrors the DEF-file `LIBRARY` statement semantics documented by Microsoft
(quoting is optional and stripped; a bare module name is assumed to be the DLL's
base name). I have not written/tested a patch against the actual TinyCC source
tree myself â the above is a diagnosis from reading the published source, not a
verified patch â but the fix looks localized to this one extraction site.
## Real-world occurrence (motivation for filing this)
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"
EXPORTS
...
```
Every TCC-linked executable built against `-lcrypto` on Windows with this
(very common, officially-distributed) OpenSSL package fails to launch with
`STATUS_DLL_NOT_FOUND`, even with `libcrypto-4-x64.dll` correctly installed and
on `PATH`. Confirmed via GitHub Actions CI logs (empty process output, exit code
`-1073741515` = `0xC0000135`).
## Workaround in use (for reference, not a fix)
Since the embedded name contains an illegal filename character, no file-aliasing
workaround is possible on the consuming side. We're currently working around this
in CI by rewriting the `.def` file's `LIBRARY` line (stripping quotes, appending
`.dll`) before invoking tcc â a source-side patch, not a real fix for TCC users
who can't control how their `.def` files are generated.
_______________________________________________
Tinycc-devel mailing list
[email protected]
https://lists.nongnu.org/mailman/listinfo/tinycc-devel