abitofevrything / dart_ncurses

MIT License
0 stars 1 forks source link

Failed to load ncursesw.so (linux) #4

Open yvs2014 opened 1 year ago

yvs2014 commented 1 year ago

Hello, got exception below trying to test it for the first time (os: linux, ubuntu). Is there something obvious that I've missed?

Unhandled exception:
Invalid argument(s): Failed to load dynamic library 'libncursesw.so': /lib/x86_64-linux-gnu/libncursesw.so: file too short
#0      _open (dart:ffi-patch/ffi_dynamic_library_patch.dart:11:43)
#1      new DynamicLibrary.open (dart:ffi-patch/ffi_dynamic_library_patch.dart:22:12)
#2      defaultLibrary.<anonymous closure> (package:ncurses/src/wrapper.dart:20:34)
#3      defaultLibrary (package:ncurses/src/wrapper.dart:22:2)
#4      defaultLibrary (package:ncurses/src/wrapper.dart)
#5      new Screen.<anonymous closure> (package:ncurses/src/wrapper.dart:797:19)
#6      wrap (package:ncurses/src/helpers.dart:17:20)
#7      new Screen (package:ncurses/src/wrapper.dart:794:17)

libncursesw6 is present in system and works

yvs2014 commented 1 year ago

inspecting that a bit: given pkg-config -libs ncursesw says -lncursesw -ltinfo and content of /lib/x86_64-linux-gnu/libncursesw.so is INPUT(libncursesw.so.6 -ltinfo)

and after putting a small correction in wrapper, it works. Just don't know howto fix it properly in a dart way of lib loading

--- a/lib/src/wrapper.dart
+++ b/lib/src/wrapper.dart
@@ -12,7 +12,7 @@ String get defaultLibraryPath {
   } else if (Platform.isMacOS) {
     return 'libncursesw.dylib';
   } else {
-    return 'libncursesw.so';
+    return 'libncursesw.so.6';
   }
 }
abitofevrything commented 1 year ago

Hi, The content of your libncursesw.so file is a linker instruction for people statically linking ncurses with -lncurses. It does not work with the runtime dynamic library loader.

I had this exact issue during development - my libncurses.so redirects to INPUT(-lncursesw), which is the the library uses libncursesw instead of libncurses. You'll have to fix/change your installation of ncurses so that libncursesw.so is a symlink to libncursesw.so.6, which is how it is on my system: ```bash $ file /usr/lib/libncursesw.so /usr/lib/libncursesw.so: symbolic link to libncursesw.so.6



Changing the dependency to be specifically version 6 isn't great as it breaks the package for anyone not using ncurses 6 even if the API is compatible.
yvs2014 commented 1 year ago

You'll have to fix/change your installation of ncurses so that libncursesw.so is a symlink to libncursesw.so.6

and that's exactly a problem, because it's not "my" custom installation, it's a distribution default (libncurses-dev: /usr/lib/x86_64-linux-gnu/libncursesw.so and putting some ld script in .so file with implicit linker format at packaging ncurses{w}), so that asking all other people to make manual changes in their target systems, I'd not consider that as a good solution

abitofevrything commented 1 year ago

Very sorry about the late reply, GitHub apparently didn't save the reply I wrote a few days ago...

The best way to resolve this would be for you to get in touch with the distro maintainers to find out why ncurses is set up this way and figure out the correct way to handle it on our end. I can provide a bit of help in implementing anything on our end.

Alternatively, I would also accept a PR which changes the code to first try and load libncursesw.so and then only if that fails try and load libncursesw.so.6.

yvs2014 commented 1 year ago

Hi,

The best way to resolve this would be for you to get in touch with the distro maintainers to find out why ncurses is set up this way

I don't think so, maybe all linux distributions with gnu ld linker use ld scripting when it needs (that scripting is its own way and that is noted in documentation, for example gnu ld(1) says If the linker cannot recognize the format of an object file, it will assume that it is a linker script). Returning to the dart lib loading, by idea for dynamic loading you need to use 'so.N' shared objs, otherwise if it tries to load '.so' then it needs to do it properly. But in dart guides (for example here https://dart.dev/guides/libraries/c-interop) there's a loading of '.so' file without notes about possible scripting there, it's a bit questionable. I've asked it on https://github.com/dart-lang/samples/issues/185, no replies yet.

the code to first try and load libncursesw.so and then only if that fails try and load libncursesw.so.6

yeah, recently I also did something like that for linux target of dart dynamic loading (in a small wrapper of couple curses functions), but firstly .so.N then .so https://github.com/yvs2014/dmtr/blob/0dc0d4353cac5e1bc73fcf1087b0b2f49f30ebea/bin/libncurses.dart#L16-L37

that's kinda workaround for this case

abitofevrything commented 1 year ago

I don't think so, maybe all linux distributions with gnu ld linker use ld scripting when it needs

You're reading the documentation for ld, the compile time linker. The runtime linker (which is what is used to dynamically load *.so files) varies depending on your system, but for me it's /usr/lib/ld-linux-x86-64.so.2.

Knowing this, we can verify that the runtime linker does not support linker scripts. Remembering that libncurses.so is on my system a linker script:

➜  ~ cat /usr/lib/libncurses.so
INPUT(-lncursesw)
➜  ~ /usr/lib/ld-linux-x86-64.so.2 --verify /usr/lib/libncurses.so || echo 'Not a valid script'
Not a valid script

The runtime linker does not support linker scripts (which makes sense, considering that it's what it used by Dart to load dynamic libraries - hence why we get errors when trying to use DynamicLibrary.open on a linker script). It's intended only for the linker used at compile time.

yvs2014 commented 1 year ago

The runtime linker does not support linker scripts

and there's a couple ways how to get around that. The remaining question is what a recommended one by dart

abitofevrything commented 1 year ago

I'd be interested to know what those ways around it are and why they aren't part of the runtime linker yet :)

yvs2014 commented 1 year ago

I'd be interested to know what those ways around it

depends on which side it can be done, from developer tools side it could be a wrapper, from an end dart user - compiled proxy lib (and that lib is linked with proper libs)

why they aren't part of the runtime linker yet

I think it doesn't supposed to be there, if you look at ldd obj, there are so.N references, and no '.so'. I.e. '.so' resolving it's a job of developer tools.

Another one good argument not to use '.so' at runtime: '.so' files often go with '-dev' package only, when '.so.N' files are present in main packages. In result there will be 'no such file' with these '.so' runtime references.