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

Reply via email to