[[!meta date="2024-06-13 15:53"]] [[!tag nix nixos libnix fixPath]] [[!series libnix]] [[!summary fixPath on Windows 10 as an alternative to rpath for avoiding DLL hell]] [[!img media/nlnet-logo.gif class="noFancy" style="float: right"]] [[!img posts/libnix/Nix_snowflake_windows.svg class="noFancy" style="float: right" width="200px"]] # motivation **fixPath**, a tool created for **making nix store concepts possible on Windows 10**, see series [libnix](https://lastlog.de/blog/timeline.html?filter=series::libnix) # fixPath > `fixPath` is a tool to modify the path to a certain DLLs (Dynamic Shared Objects) for [Microsoft Windows Executables](https://learn.microsoft.com/en-us/windows/win32/debug/pe-format) by rewriting parts of the executable's PE header, when the `.fixPath` section is present and indicates support for such rewrite, but _without_ having to realign the PE headers. In concept, it is similar to [patchelf](https://github.com/NixOS/patchelf) but instead of changing search paths, fixPath hard-codes each DLL to a particular filepath. see `fixPath` project page: * release with prebuilt fixPath.exe tool + test program: * # why `fixPath`? windows does not have an implementation of the [rpath](https://en.wikipedia.org/wiki/Rpath) concept unix systems have. as a result you have to move the libraries into the same folder as the executable. in theory one could use symlinks instead of copies also. With `fixPath` you can have the libraries in the `c:\nix\store` and only have to modify the PE header similar to patchelf on Linux. a hope of mine is that we as a community can motivate Microsoft to continue work on rpath support for Windows but since this requires changes on the proprietary side of the linker loader there is nothing to be done from outside Microsoft. in that regard `fixPath` is a valuable alternative which luckly works as the library field, i.e. `KERNEL32.dll`, inside the Imports Section supports absolute/relative paths already. * * `nixPath` is the result of a blog post by Jussi, see . thanks Jussi! for details, read # PE header the picture below shows the **SectionsHeader** in ImHex with the newly added `.fixpath` section: [[!img posts/libnix/imhex-fixpath-section.jpg class="noFancy"]] the picture below shows the **DLLName hint** in ImHex somewhere in `.rdata`: [[!img posts/libnix/imhex-filepath-imports.jpg class="noFancy"]] note: the library names have lots of empty space so that later changes don't require PE relocations which are very complicated. # linker support when using the `lld` linker (from LLVM), instead of the default `ld` linker from binutils and using this lld branch , you can get the ``.fixPath` PE section added automatically! # how to use `fixPath` show library status: ```bash $ fixPath.exe -l test_mylib.exe TARGET: - test_mylib.exe - fixPath version: 2 - fix_path_size: 301 IMPORTS - 1, mylib.dll @ 0x4274 - 2, KERNEL32.dll @ 0x43c1 - 3, VCRUNTIME140D.dll @ 0x450e - 4, ucrtbased.dll @ 0x465b DELAYED IMPORTS - 1, delayedlib.dll @ 0x3894 ``` change `mylib.dll` library into absolute path: ```bash $ fixPath.exe -s test_mylib.exe mylib.dll c:\some\location\mylib.dll TARGET: - test_mylib.exe UPDATE mylib.dll @ 0x4274 -> c:\some\location\mylib.dll (modified) DONE ``` show updated library status: ```bash fixPath.exe -l test_mylib.exe TARGET: - test_mylib.exe - fixPath version: 2 - fix_path_size: 301 IMPORTS - 1, mylib.dll @ 0x4274 -> c:\some\location\mylib.dll (modified) - 2, KERNEL32.dll @ 0x43c1 - 3, VCRUNTIME140D.dll @ 0x450e - 4, ucrtbased.dll @ 0x465b DELAYED IMPORTS - 1, delayedlib.dll @ 0x3894 ``` # interesting tests using the `fixPath` concept this works: ``` * [x] dll import supports absolute paths * [x] dll import supports relative paths like `lib\lib.dll` * [x] works with long filename : "c:\t\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.dll" * [x] works with long directory: "c:\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\t.dll" * [x] works with "C:\t\nix\store\zxxialnsgv0ahms5d35sivqzxqg1kicf-libiec61883-1.2.0\lib\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.dll" 213 chars * [x] works with "C:\t\nix\store\zxxialnsgv0ahms5d35sivqzxqg1kicf-libiec61883-1.2.0\lib\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.dll" 269 chars ``` note: an override with absolute/relative path will always cause the linker-loader on Windows 10 to use exactly this library and another copy with the same name in the binary's directory won't override this! # helpful software * * * * ## status * **development of fixPath concept cost me ~5 weeks** * `fixPath` concept work **needs review** * **LLD change needs a PR** to get it upstream * automate fixPath concept by the build system's make install step # summary i hope to get developers interested in `fixPath` concepts and motivate Microsoft to also support `rpath` in PE!