Skip to content

lib/CMakeLists.txt passes /WHOLEARCHIVE:hermesvm_a as a bare name; lld-link cannot open it, so the shared hermesvm (and therefore shermes) does not link under clang-cl #2175

Description

@altween

lib/CMakeLists.txt:246-252 forces the whole static VM into the shared library:

if(APPLE)
  target_link_libraries(hermesvm PRIVATE -force_load hermesvm_a)
  target_link_libraries(hermesvmlean PRIVATE -force_load hermesvmlean_a)
elseif(MSVC)
  target_link_libraries(hermesvm PRIVATE hermesvm_a)
  target_link_options(hermesvm PRIVATE /WHOLEARCHIVE:hermesvm_a)
  target_link_libraries(hermesvmlean PRIVATE hermesvmlean_a)
  target_link_options(hermesvmlean PRIVATE /WHOLEARCHIVE:hermesvmlean_a)

The argument to /WHOLEARCHIVE: at :250 is the bare CMake target name,
with no path and no extension. link.exe may resolve that; lld-link — which
is what a clang-cl toolchain reaches for — does not
:

FAILED: lib/hermesvm.dll lib/hermesvm.lib
lld-link: error: could not open 'hermesvm_a': no such file or directory

The archive itself is built and present; the full link line CMake generates has
it twice — once as the bare /WHOLEARCHIVE: argument and once as a real
relative path:

lld-link.exe /nologo lib\CMakeFiles\hermesvm.dir\dummy.cpp.obj /out:lib\hermesvm.dll
  /implib:lib\hermesvm.lib /pdb:lib\hermesvm.pdb /dll /version:0.0 /machine:x64
  /INCREMENTAL:NO  /WHOLEARCHIVE:hermesvm_a  lib\hermesvm_a.lib  ...

and only the first spelling is the one that fails. lld-link does not search
the library path for a /WHOLEARCHIVE: operand, and does not append .lib to
it.
Measured by re-running that exact command by hand, twice:

+ /LIBPATH:lib, argument left as the bare name  -> could not open 'hermesvm_a'
  /WHOLEARCHIVE:lib\hermesvm_a.lib              -> links, 4,887,552-byte DLL

So adding a search path is not a workaround, and the path spelling is a complete
fix.

Consequence beyond the shared library: tools/shermes links hermesvm, so
the AOT compiler's own dependency target (shermes-dep,
tools/shermes/CMakeLists.txt) cannot be built, and shermes's default link
line — which ends in -lhermesvm — has no library to name.
hermesvm_a (static) and every tool that uses it — hermes, hermesc, and a
JSI embedder linking hermesvm_a directly — build and run perfectly; it is only
the SHARED library that fails. (A shermes-compiled program CAN be linked
against the static archives instead, by hand, via the LDFLAGS environment
variable. That is a
workaround for a downstream consumer, not a reason to leave the bare name.)

Repro

git clone https://github.com/facebook/hermes.git hermes-static
cd hermes-static
git checkout 5cee10abc93667ea5538caecaf0a457c66fa5bdc

call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat"
set CLANGCL=C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/Llvm/x64/bin/clang-cl.exe

cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release ^
  -DCMAKE_C_COMPILER="%CLANGCL%" -DCMAKE_CXX_COMPILER="%CLANGCL%" ^
  -DHERMES_ENABLE_TEST_SUITE=OFF -DHERMES_ENABLE_NAPI=OFF

(HERMES_ENABLE_TEST_SUITE=OFF and HERMES_ENABLE_NAPI=OFF only shorten the
build; neither is load-bearing for the failure below.) Then:

cmake --build build --target hermesvm

Expected: links lib/hermesvm.dll. Actual: lld-link: error: could not open 'hermesvm_a': no such file or directory, after lib/hermesvm_a.lib has been
built successfully.

Environment

Windows 11 Pro 10.0.26200, x86-64
CMake 3.31.6-msvc6 (the one Visual Studio 2022 ships), Ninja 1.13.2
Visual Studio 2022 Community

> clang-cl --version
clang version 19.1.5
Target: x86_64-pc-windows-msvc
Thread model: posix
InstalledDir: C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\Llvm\x64\bin

Proposed fix (one line each)

Pass a path the linker can open, using the generator expression CMake already
provides:

target_link_options(hermesvm PRIVATE "/WHOLEARCHIVE:$<TARGET_FILE:hermesvm_a>")
target_link_options(hermesvmlean PRIVATE "/WHOLEARCHIVE:$<TARGET_FILE:hermesvmlean_a>")

The same applies to /WHOLEARCHIVE:hermesNapi at lib/CMakeLists.txt:271 and
:273, which is reached when HERMES_ENABLE_NAPI is on and has not been
exercised here.

The comment above the block notes that LINK_LIBRARY generator-expression
support was waiting on CMake 3.24; $<TARGET_FILE:...> needs nothing newer than
the tree's existing cmake_minimum_required(VERSION 3.21.0).

Related issues

Where this came from

Measured while embedding static_h as the second runtime of a native game host on Windows (the repository is private at the time of filing, so no links). That host links the static hermesvm_a and is unaffected; this issue is what stopped it taking an AOT reading through the default shermes link line. No file under the Hermes checkout was edited, and no workaround for this one was found from outside it — the bare name is not on any search path the linker consults.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions