# fails the build if a PE binary statically imports a forbidden DLL. # # some DLLs must never end up in spice's static import table, for two reasons: # # 1. user-overridable DLLs (e.g. DXVK's d3d9.dll): users drop their own copy # into the modules directory to replace the system one. a static import # forces the loader to load the SYSTEM copy at process startup - before the # modules directory is added to the DLL search path and before the game DLL # loads - so the user-supplied override never takes effect (see issue #779). # # 2. DLLs that break games when present (e.g. Media Foundation: mf/mfplat/ # mfreadwrite): a static import loads them eagerly and breaks Unity games. # # in both cases the DLL must instead be loaded dynamically (libutils::try_library # / GetProcAddress / delay load) so it is only pulled in when actually needed. # # invoked via `cmake -P` from a POST_BUILD step. required -D variables: # OBJDUMP - path to objdump (CMAKE_OBJDUMP) # TARGET_FILE - path to the PE binary to inspect # FORBIDDEN - semicolon-separated list of lowercase DLL names to reject if(NOT OBJDUMP OR NOT EXISTS "${OBJDUMP}") message(WARNING "check_no_static_dll_imports: objdump not found, skipping import check for ${TARGET_FILE}") return() endif() execute_process( COMMAND "${OBJDUMP}" -p "${TARGET_FILE}" OUTPUT_VARIABLE dump_output RESULT_VARIABLE dump_result ERROR_VARIABLE dump_error) if(NOT dump_result EQUAL 0) message(WARNING "check_no_static_dll_imports: objdump failed for ${TARGET_FILE}: ${dump_error}") return() endif() # both GNU objdump and llvm-objdump print one "DLL Name: " line per # statically imported DLL in their PE private-header dump. string(REGEX MATCHALL "DLL Name:[ \t]*[^\n\r]+" dll_lines "${dump_output}") set(violations "") foreach(line IN LISTS dll_lines) string(REGEX REPLACE "DLL Name:[ \t]*" "" dll_name "${line}") string(STRIP "${dll_name}" dll_name) string(TOLOWER "${dll_name}" dll_name_lower) if(dll_name_lower IN_LIST FORBIDDEN) list(APPEND violations "${dll_name}") endif() endforeach() if(violations) list(REMOVE_DUPLICATES violations) string(REPLACE ";" ", " violations_str "${violations}") message(FATAL_ERROR "static DLL import check FAILED for ${TARGET_FILE}\n" " forbidden static imports found: ${violations_str}\n" "\n" " these DLLs must never be statically imported by spice:\n" " * user-overridable DLLs (e.g. DXVK d3d9.dll) - a static import loads the\n" " system copy at startup and preempts the modules override (issue #779).\n" " * Media Foundation DLLs (mf/mfplat/mfreadwrite) - a static import breaks\n" " Unity games.\n" "\n" " fix: load the DLL dynamically instead - replace the direct API call with a\n" " libutils::try_library() + libutils::try_proc() lookup (or a delay load), then\n" " call through the resolved function pointer.") endif() message(STATUS "static DLL import check passed for ${TARGET_FILE}")