The practical issue I am trying to solve is the following scenario (encountered with a game using some Uplay plugin) which has regressed with apisets implementation in Wine. The game imports ucrtbase.dll. Then, a plugin calls GetModuleHandle("api-ms-win-crt-runtime-l1-1-0.dll") and expects GetProcAddress("_crt_atexit") to succeed on the returned handle. The problem is that the game has ucrtbase.dll in its .exe directory. That one currently gets loaded. Then, find_dll_file() resolves api-ms-win-crt-runtime-l1-1-0.dll to the ucrtbase with a full path in system32 and tries to find that one (this part matches Windows behaviour as far as tests show). Since we have ucrtbase loaded with another path this results in NULL module handle.
Windows maintains the list of "Known DLLs" (probably aimed for speeding up processes startup by using pre-mapped dll images for commonly used system dlls). On Windows, known dlls have specifics in load path resolution when dll name doesn't contain path (relative or full): - they are loaded from system directory even if there is another dll with the same name in priority search path; - they are found even if library search paths do not contain system directory;
Those "Known DLLs" are basically listed in HKLM\System\CurrentControlSet\Control\Session Manager\KnownDLLs registry key. But the actual list of "known dlls" is bigger (probably includes all dependencies of those listed under registry key). The full actual list of "known dlls" is in '\KnownDlls' directory object which contains image sections under dll names. Most notably, ucrtbase.dll (which is of the concern in the regression) is not listed in registry but is present in \KnownDlls. Technically nothing prevents us from implementing these mechanics and creating directory object with mapped load dlls, also resolving the explicitly listed dlls' dependencies. But it is not apparent to me if that is going to improve load times noticably in Wine, and also that is worth the inherent complications until anything actually depends on that \KnownDlls sections. So instead of implementing all of that throughout the loader I added a few missing dlls to registry. The current list in this pat chset corresponds to the contents of \KnownDlls directory on an up to date Windows 10.
-- v4: ntdll: Load known dlls from system directory. loader/wine.inf: Add known dlls key. ntdll: Factor out prepend_system_dir() function. kernel32/tests: Add tests for known dlls load specifics.