Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fails to find libxcb on NetBSD #785

Closed
ids1024 opened this issue Jan 1, 2023 · 7 comments · Fixed by #788
Closed

Fails to find libxcb on NetBSD #785

ids1024 opened this issue Jan 1, 2023 · 7 comments · Fixed by #788

Comments

@ids1024
Copy link
Contributor

ids1024 commented Jan 1, 2023

I thought I'd try rust-windowing/winit#2614 in my NetBSD VM, and got "failed to open library 'libxcb.so.1'".

It seems this exists as /usr/libX11R7/lib/libxcb.so.2? Not sure why it's .2 rather than .1...

Would it make sense to just load it as libxcb.so, or use a different name by platform? Hopefully the bindings should all be correct since the xcb header files used on different OSes should be the same.

@psychon
Copy link
Owner

psychon commented Jan 2, 2023

Relevant code:

// TODO: Names for non-unix platforms
#[cfg(unix)]
const LIB_NAME: &str = "libxcb.so.1";
#[cfg(not(unix))]
compile_error!("dl-libxcb feature is not supported on non-unix");

Would it make sense to just load it as libxcb.so

Not really. On debian, libxcb.so is in the package libxcb1-dev, i.e. only meant as a build-time dependency (and used when passing -lxcb to the linker; the linker then sees that this file contains a SONAME of libxcb.so.1 and uses that for the final binary), not runtime. libxcb.so.1 and libxcb.so.1.1.0 come from the package libxcb1.

So... dunno what the correct way is, but so far I have no better idea than playing whack-a-mole with this.

@ids1024 Do you want to submit a PR that fixes the above linked code to work on NetBSD?

(Also, one could just not use the dl-libxcb feature. That way, we link against libxcb.so at build time and the linker then does its usual magic to find the correct run-time library...)

Edit: Does libtool work differently on NetBSD?!? In libxcb's source code, there is just a single version info that does not vary based on plattform: https://gitlab.freedesktop.org/xorg/lib/libxcb/-/blob/cb8c70f5a65b4bd68b449dcaa637c3c4753e2f81/src/Makefile.am#L14-21

@psychon
Copy link
Owner

psychon commented Jan 2, 2023

Another idea could be to try multiple names. If loading libxcb.so.1 fails, try again with libxcb.so. This has a higher chance of working everywhere and could turn into less of a "whack-a-mole".

Quick attempt to get this to work:

diff --git a/x11rb/src/xcb_ffi/raw_ffi/ffi.rs b/x11rb/src/xcb_ffi/raw_ffi/ffi.rs
index 9b5ffe8..b9a159d 100644
--- a/x11rb/src/xcb_ffi/raw_ffi/ffi.rs
+++ b/x11rb/src/xcb_ffi/raw_ffi/ffi.rs
@@ -33,12 +33,23 @@ pub(crate) mod libxcb_library {
         unsafe fn load() -> Result<Self, LibxcbLoadError> {
             // TODO: Names for non-unix platforms
             #[cfg(unix)]
-            const LIB_NAME: &str = "libxcb.so.1";
+            const LIB_NAMES: &[&str] = &["libxcb.so.1", "libxcb.so"];
             #[cfg(not(unix))]
             compile_error!("dl-libxcb feature is not supported on non-unix");
 
-            let library = libloading::Library::new(LIB_NAME)
-                .map_err(|e| LibxcbLoadError::OpenLibError(LIB_NAME.into(), e.to_string()))?;
+            // Go through LIB_NAMES and try to find a loadable library. If none is found, the error
+            // from loading the last entry is returned.
+            unsafe fn find_library() -> Result<libloading::Library, LibxcbLoadError> {
+                let mut last_error = None;
+                for name in LIB_NAMES {
+                    match libloading::Library::new(name) {
+                        Ok(lib) => { return Ok(lib); }
+                        Err(e) => { last_error = Some(LibxcbLoadError::OpenLibError(name.into(), e.to_string())); }
+                    }
+                }
+                Err(last_error.expect("LIB_NAMES is not empty"))
+            }
+            let library = find_library()?;
             let funcs = LibxcbFuncs::new(&library).map_err(|(symbol, e)| {
                 LibxcbLoadError::GetSymbolError(symbol.into(), e.to_string())
             })?;

Opinions?

Edit: Waaaaay simpler patch:

diff --git a/x11rb/src/xcb_ffi/raw_ffi/ffi.rs b/x11rb/src/xcb_ffi/raw_ffi/ffi.rs
index 9b5ffe8..f4de319 100644
--- a/x11rb/src/xcb_ffi/raw_ffi/ffi.rs
+++ b/x11rb/src/xcb_ffi/raw_ffi/ffi.rs
@@ -38,6 +38,7 @@ pub(crate) mod libxcb_library {
             compile_error!("dl-libxcb feature is not supported on non-unix");
 
             let library = libloading::Library::new(LIB_NAME)
+                .or_else(|_| libloading::Library::new("libxcb.so"))
                 .map_err(|e| LibxcbLoadError::OpenLibError(LIB_NAME.into(), e.to_string()))?;
             let funcs = LibxcbFuncs::new(&library).map_err(|(symbol, e)| {
                 LibxcbLoadError::GetSymbolError(symbol.into(), e.to_string())

@eduardosm
Copy link
Collaborator

The unsuffixed version (libxcb.so) might also be unavailable. For example, in ubuntu I will only have /usr/lib/x86_64-linux-gnu/libxcb.so.1 unless I install the development packages. The unsuffixed is usually there for the linker (ld) to find the library.

Additionally, if a system happens to have more than one libxcb.so.* version (for whatever reason), loading the incorrect one could cause issues due to ABI incompatibility.

I think the correct solution would be to cfg-gate the constant for each OS. We just need to figure out how the library is suffixed on each unix OS. It would be also nice to know why it is suffixed with .2 instead of .1 on NetBSD. Did the ABI break at some point?

@psychon
Copy link
Owner

psychon commented Jan 2, 2023

It would be also nice to know why it is suffixed with .2 instead of .1 on NetBSD. Did the ABI break at some point?

Looking at http://cvsweb.netbsd.org/bsdweb.cgi/pkgsrc/x11/libxcb/#dirlist, I did not find anything special. This seems to just build libxcb.

Next I looked at libtool. https://git.savannah.gnu.org/cgit/libtool.git/tree/m4/libtool.m4 is lots of fun. Assuming I did this correctly, netbsd* uses version_type=sunos. Linux uses version_type=linux. What's the meaning of this? This is answered by https://git.savannah.gnu.org/cgit/libtool.git/tree/build-aux/ltmain.in:

	linux) # correct to gnu/linux during the next big refactor
	  func_arith $current - $age
	  major=.$func_arith_result
	  versuffix=$major.$age.$revision
	  ;;

and

	sunos)
	  major=.$current
	  versuffix=.$current.$revision
	  ;;

libxcb uses -version-info 2:0:1, so current - age is 2 - 1=1. That's why we get libxcb.so.1. Everything using the sunos scheme will instead end up with libxcb.so.2.

Sadly, I do not know which values of $host_os really map to which systems and I certainly do not want to support AIX. Darwin has version_type=darwin, FreeBSD has version_type=freebsd-$objformat. Haiku uses version_type=linux. Both NetBSD and OpenBSD have version_type=sunos. That's the stuff that caught my eye from a quick scroll.

Darwin is the same as Linux:

	darwin)
	  # Like Linux, but with the current version available in
	  # verstring for coding it into the library header
	  func_arith $current - $age
	  major=.$func_arith_result

...and so is freebsd-elf:

	freebsd-elf | midnightbsd-elf)
	  func_arith $current - $age
	  major=.$func_arith_result

Soooo.... if I understand correctly, then only NetBSD and OpenBSD are affected and they should have libxcb.so.2, right?

psychon added a commit that referenced this issue Jan 2, 2023
Turns out that libtool uses a different logic for turning its
"-version-info" into a file name on OpenBSD and NetBSD than on Linux,
Darwin, and FreeBSD. On the affected BSDs, this just takes "current",
which is the first argument to "-version-info". Everywhere else, the
logic is "current - age".

Since libxcb uses "-version-info 2:0:1", this means that we end up with
libxcb.so.2 on OpenBSD and NetBSD instead of the libxcb.so.1 that the
code expects. Thus, we failed to load libxcb.

Fix this by correcting the library name on OpenBSD and NetBSD.

Fixes: #785
Signed-off-by: Uli Schlachter <psychon@znc.in>
@eduardosm
Copy link
Collaborator

Knowing now that it is a libtool thing, I found this: https://lists.gnu.org/archive/html/bug-libtool/2011-05/msg00007.html
TL;DR Looks like some historical bug converted to "feature" to avoid breaking things that have been wrong for too long.

@ids1024
Copy link
Contributor Author

ids1024 commented Jan 3, 2023

Oh good, I was hoping we could find out exactly why the numbering is different, and on what platforms it is an issue, since allowing it to link an ABI incompatible version of the library would be a quite bad thing. (Not that a SONAME bump of libxcb seems very likely at this point.)

and I certainly do not want to support AIX.

Wait, AIX still exists? And Rust has a tier 3 powerpc64-ibm-aix target? I had only heard of AIX from looking at the arcane GNU scripts...

psychon added a commit that referenced this issue Jan 3, 2023
Turns out that libtool uses a different logic for turning its
"-version-info" into a file name on OpenBSD and NetBSD than on Linux,
Darwin, and FreeBSD. On the affected BSDs, this just takes "current",
which is the first argument to "-version-info". Everywhere else, the
logic is "current - age".

Since libxcb uses "-version-info 2:0:1", this means that we end up with
libxcb.so.2 on OpenBSD and NetBSD instead of the libxcb.so.1 that the
code expects. Thus, we failed to load libxcb.

Fix this by correcting the library name on OpenBSD and NetBSD.

Additionally, the folder is apparently not in the default search path,
so we need an absolute path. No idea whether this is also needed on
OpenBSD. I will wait for someone to complain/test.

Fixes: #785
Signed-off-by: Uli Schlachter <psychon@znc.in>
psychon added a commit that referenced this issue Jan 3, 2023
Turns out that libtool uses a different logic for turning its
"-version-info" into a file name on OpenBSD and NetBSD than on Linux,
Darwin, and FreeBSD. On the affected BSDs, this just takes "current",
which is the first argument to "-version-info". Everywhere else, the
logic is "current - age".

Since libxcb uses "-version-info 2:0:1", this means that we end up with
libxcb.so.2 on OpenBSD and NetBSD instead of the libxcb.so.1 that the
code expects. Thus, we failed to load libxcb.

Fix this by correcting the library name on OpenBSD and NetBSD.

Additionally, the folder is apparently not in the default search path,
so we need an absolute path. No idea whether this is also needed on
OpenBSD. I will wait for someone to complain/test.

Fixes: #785
Signed-off-by: Uli Schlachter <psychon@znc.in>
@mergify mergify bot closed this as completed in #788 Jan 3, 2023
@psychon
Copy link
Owner

psychon commented Jan 3, 2023

Does anyone have some time to prepare a PR with an update to the changelog? I guess it is time for a point release (and if we had any breaking changes, then I guess we need a branch without those breaking changes).

I will (likely) only have time for this on the weekend.

psychon added a commit that referenced this issue Jan 4, 2023
Turns out that libtool uses a different logic for turning its
"-version-info" into a file name on OpenBSD and NetBSD than on Linux,
Darwin, and FreeBSD. On the affected BSDs, this just takes "current",
which is the first argument to "-version-info". Everywhere else, the
logic is "current - age".

Since libxcb uses "-version-info 2:0:1", this means that we end up with
libxcb.so.2 on OpenBSD and NetBSD instead of the libxcb.so.1 that the
code expects. Thus, we failed to load libxcb.

Fix this by correcting the library name on OpenBSD and NetBSD.

Additionally, the folder is apparently not in the default search path,
so we need an absolute path. No idea whether this is also needed on
OpenBSD. I will wait for someone to complain/test.

Fixes: #785
Signed-off-by: Uli Schlachter <psychon@znc.in>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants