From d3d888c943f8f27c83d214b0569e33c4cccf8982 Mon Sep 17 00:00:00 2001 From: Simon McVittie <smcv@collabora.com> Date: Wed, 18 Sep 2019 13:03:36 +0100 Subject: [PATCH] capture-libs: Treat unversioned libraries as in indeterminate order On at least Debian, Ubuntu and Manjaro, libgcc_s.so.1 is a regular file, not a symlink to a versioned name (libgcc_s.so.1.2.3) like most shared libraries. However, on Fedora 30 it is a symlink to a versioned name like libgcc_s-9-20190827.so.1. The name used in Fedora happens to be less than libgcc_s.so.1 in strverscmp() order, causing capsule-capture-libs to prefer the Debian/Manjaro libgcc_s.so.1, even if the container is older. This can cause problems if an old Debian-derived container like the Steam Runtime is used on a newer Fedora host, with host graphics drivers imported by using capsule-capture-libs: the container's libgcc_s.so.1 does not satisfy the versioned symbol requirements of the graphics driver, causing loading to fail. Treat a regular file libgcc_s.so.1 as indeterminate order (or "equal"), so that we fall back to the default behaviour, which is currently to take the host version of the library in the case of a tie. This is a stopgap solution: ideally we would consider the versioned symbols exported by both libraries, and take whichever one has a superset of the version definitions exported by the other, if there is a strict superset in either direction (in the case of libgcc, in fact there is). Signed-off-by: Simon McVittie <smcv@collabora.com> --- utils/capture-libs.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/utils/capture-libs.c b/utils/capture-libs.c index f2e2d28c3..156603403 100644 --- a/utils/capture-libs.c +++ b/utils/capture-libs.c @@ -356,6 +356,41 @@ library_cmp_by_name( const char *soname, "\"%s\" from \"%s\"", soname, left_basename, left_from, right_basename, right_from ); + if( strcmp( left_basename, right_basename ) == 0 ) + { + DEBUG( DEBUG_TOOL, + "Name of %s \"%s\" from \"%s\" compares the same as " + "\"%s\" from \"%s\"", + soname, left_basename, left_from, right_basename, right_from ); + return 0; + } + + if( strcmp( soname, left_basename ) == 0 ) + { + /* In some distributions (Debian, Ubuntu, Manjaro) + * libgcc_s.so.1 is a plain file, not a symlink to a + * version-suffixed version. We cannot know just from the name + * whether that's older or newer, so assume equal. The caller is + * responsible for figuring out which one to prefer. */ + DEBUG( DEBUG_TOOL, + "Unversioned %s \"%s\" from \"%s\" cannot be compared with " + "\"%s\" from \"%s\"", + soname, left_basename, left_from, + right_basename, right_from ); + return 0; + } + + if( strcmp( soname, right_basename ) == 0 ) + { + /* The same, but the other way round */ + DEBUG( DEBUG_TOOL, + "%s \"%s\" from \"%s\" cannot be compared with " + "unversioned \"%s\" from \"%s\"", + soname, left_basename, left_from, + right_basename, right_from ); + return 0; + } + return ( strverscmp( left_basename, right_basename ) ); } @@ -489,6 +524,12 @@ capture_one( const char *soname, capture_flags flags, { const char *needed_path_in_container = container.needed[0].path; + /* TODO: Ideally we would actually dlopen the libraries and + * inspect their symbol tables, either in ambiguous cases + * or always - but that's easier said than done, because + * private symbols exist. For now we just use + * library_cmp_by_name(). */ + /* If equal, we prefer the provider over the container */ if( library_cmp_by_name( needed_name, needed_path_in_container, -- GitLab