intel / intel/simulator-bindings
simics-build-utils emits raw .dll link directives on Windows, breaking MSVC-host builds (LNK1107)
- Dominant language
- Rust
- Stars
- 17
- Forks
- 9
- PR merge metrics
- No merged PRs in 30d
Description
## Summary
`simics_build_utils::emit_link_info()` emits `cargo:rustc-link-lib=dylib:+verbatim=.dll` for `libsimics-common`, `libvtutils`, and the bundled Python DLL on Windows. This works with the MinGW/GNU Rust target (MinGW's `ld`/`lld` can import directly from a PE DLL) but fails on the default `x86_64-pc-windows-msvc` host, because `link.exe` requires a proper import library (`.lib`).
## Reproduction
Default Rust install on Windows (MSVC host), building any crate that depends on `simics-build-utils` — for example, TSFFS:
```powershell
$env:SIMICS_BASE = "C:\...\Simics\simics-6.0.185"
cargo build --release # defaults to x86_64-pc-windows-msvc
```
Result:
```
warning: Using Python environment for linking: PythonEnvironment { ...
lib: C:\...\simics-6.0.185\win64\bin\python310.dll, ... }
error: linking with `link.exe` failed: exit code: 1107
= note: ...\libsimics-common.dll : fatal error LNK1107:
invalid or corrupt file: cannot read at 0x4F8
```
`link.exe` is being handed the raw DLL and chokes on the PE header where it expects a COFF archive.
Workaround: `cargo build --release --target x86_64-pc-windows-gnu` (requires MinGW-w64 installed). This is what TSFFS's docs recommend, but it should not be a *requirement* for consumers on a default Rust Windows install.
## Root cause
In `simics-build-utils/src/lib.rs`, the `#[cfg(windows)]` branch of `emit_link_info()` resolves DLL filenames and emits them verbatim:
```rust
let libsimics_common = bin_dir.join(\"libsimics-common.dll\").canonicalize()?;
// ...
println!(\"cargo:rustc-link-lib=dylib:+verbatim={}\",
libsimics_common.file_name()...); // => libsimics-common.dll
```
`+verbatim` passes the string directly to the linker. MinGW accepts this; MSVC does not.
Similarly, `simics_python_utils::PythonEnvironment::lib_filename()` returns `python310.dll` for the bundled Python case (the banner shows `lib: ...\bin\python310.dll`), when MSVC needs `python310.lib`.
## Proposed fix
Branch on `target_env` in the build script (not on host `cfg(windows)`, since cross-compiles exist):
```rust
// Windows GNU: keep current behavior (link directly against DLL)
// Windows MSVC: emit import-library filenames
#[cfg(all(windows, target_env = \"msvc\"))] // or examine CARGO_CFG_TARGET_ENV
{
println!(\"cargo:rustc-link-lib=dylib:+verbatim=libsimics-common.lib\");
println!(\"cargo:rustc-link-lib=dylib:+verbatim=libvtutils.lib\");
println!(\"cargo:rustc-link-lib=dylib:+verbatim=python310.lib\");
}
```
On my Simics 6.0.185 install, `libsimics-common.lib`, `libvtutils.lib`, and `python310.lib` already exist in `win64\bin` alongside the DLLs, so no extra generation step is needed for this version. For installs where the `.lib` is missing, `lib.exe /def:` from a generated `.def` (via `gendef` or by dumping exports) is the standard workaround.
`simics-python-utils` needs a matching change so `PythonEnvironment::lib_filename()` returns the import lib name under MSVC.
## Caveats (worth flagging, not blockers)
Simics's Windows DLLs ship alongside `libgcc_s_seh-1.dll`, `libstdc++-6.dll`, and `libwinpthread-1.dll`, indicating they were built with MinGW GCC. The linker fix above makes the build *link*, but runtime ABI compatibility for MSVC consumers depends on:
- public Simics API being pure C (appears to be the case);
- no C++ exceptions, `longjmp`, or TLS crossing the DLL boundary;
- CRT consistency (both UCRT — probably fine).
Basic usage should work; edge cases may need validation. Happy to help test if a fix lands.
## Environment
- Windows 11
- Rust 1.90.0, host \`x86_64-pc-windows-msvc\`
- Simics 6.0.185 (bundled Python 3.10)
- \`simics-build-utils\` 0.2.6, \`simics-python-utils\` 0.2.6
- Consumer: tsffs 0.2.4
Contributor guide
Assessment
This issue has not been assessed yet.