libnix fixPath

13 jun 2024

motivation

fixPath, a tool created for making nix store concepts possible on Windows 10, see series libnix

fixPath

fixPath is a tool to modify the path to a certain DLLs (Dynamic Shared Objects) for Microsoft Windows Executables 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 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 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 https://nibblestew.blogspot.com/2019/05/emulating-rpath-on-windows-via-binary.html. thanks Jussi!

for details, read https://github.com/nixcloud/fixPath

PE header

the picture below shows the SectionsHeader in ImHex with the newly added .fixpath section:

the picture below shows the DLLName hint in ImHex somewhere in .rdata:

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 https://github.com/llvm/llvm-project/compare/release/18.x...qknight:llvm-project:libnix_PE-fixPath, you can get the `.fixPath PE section added automatically!

how to use fixPath

show library status:

$ 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:

$ 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:

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!

article source