networkupstools / nut

The Network UPS Tools repository. UPS management protocol Informational RFC 9271 published by IETF at https://www.rfc-editor.org/info/rfc9271 Please star NUT on GitHub, this helps with sponsorships!
https://networkupstools.org/
Other
2.03k stars 349 forks source link

Loading DLLs from one dir, without copies near every EXE "module" (see common.c) #1482

Closed jimklimov closed 2 years ago

jimklimov commented 2 years ago

Currently Windows builds of binaries (drivers, tools, daemons...) seem to require that the numerous DLL files involved (from libupsclient to libsnmp, libusb, etc.) should be either in same directory as the binary or perhaps in the caller's working directory - they were the same in manual testing.

Part of the issue may be handled in common.c method get_libname_in_dir() (for LTDL codebase such as nut-scanner) where we want to try pathnames relative to the (installed) binary, but seem to have no idea where that is - especially as this may be beneficial in general to distribute loosely-linked NUT bundles. For Windows it might be addressed with GetModuleFileName() as seen in getfullpath() but for general case there is no portable solution other than maintaining a variable to save a resolved realpath() of argv[0] in every main(). Some libc implementations offer a solution (some have vars, others have methods) for that, which we might benefit from instead of custom logic, but generally there is no single fix.

Another part of the issue specifically for WIN32 builds is to investigate how libtool, ld or whatever else used during build sets up the search paths to DLL files in the binaries. Maybe some simple argument along the lines of RPATH="..\lib" would help?

jimklimov commented 2 years ago

Does not seem fully possible (without barging into caller's PATH, which has lowest priority - and we do not have init-script equivalent to tweak this easily anyway) due to MS Windows DLL loading approach: https://stackoverflow.com/a/25407687

Currently the PoC script was modified to deliver DLLs into "bin" and hardlink needed ones into "sbin" (detecting by itself what is "needed"). A practical simpler approach might be to dump all tools, drivers, daemons etc. into one "bin" dir along with the libraries, by using appropriate configure script options.

jimklimov commented 2 years ago

https://stackoverflow.com/questions/3272383/linking-with-r-and-rpath-switches-on-windows

https://ibob.bg/blog/2018/12/16/windows-rpath/

jimklimov commented 2 years ago

One half-practical solution might be to lt_dlopen() everything explicitly, and maybe even statically include libltdl to each binary? But then we'd need to know all exact DLL filenames relevant for current binary (using paths as resolved by get_libname() or fallen back to system resolver). Probably ETOOCOMPLEX and error-prone, if it would work at all :\

jimklimov commented 2 years ago

For other platforms, the recently added support for LD_LIBRARY_PATH{,_32,_64} (as much as it is frowned upon) in get_libname() method (#1548) allows nut-scanner and similar dynamic optional library consumers to customize where they find libs, in a way similar to how "persistent" shared objects are linked by the OS loader.