diff --git a/helpers/inspect-library.c b/helpers/inspect-library.c index 50a229dc1af55e84e6275f63feb94f926bde03d9..aa4918c1163715966c2d8287b1df86b234d536d4 100644 --- a/helpers/inspect-library.c +++ b/helpers/inspect-library.c @@ -75,10 +75,12 @@ do { \ enum { OPTION_HELP = 1, + OPTION_DEB_SYMBOLS, }; struct option long_options[] = { + { "deb-symbols", no_argument, NULL, OPTION_DEB_SYMBOLS }, { "help", no_argument, NULL, OPTION_HELP }, { NULL, 0, NULL, 0 } }; @@ -123,12 +125,17 @@ main (int argc, size_t len = 0; ssize_t chars; bool first; + bool deb_symbols = false; int opt; while ((opt = getopt_long (argc, argv, "", long_options, NULL)) != -1) { switch (opt) { + case OPTION_DEB_SYMBOLS: + deb_symbols = true; + break; + case OPTION_HELP: usage (0); break; @@ -174,6 +181,10 @@ main (int argc, if (argc >= optind + 2) { + size_t soname_len = strlen (soname); + bool found_our_soname = false; + bool in_our_soname = false; + if (strcmp(argv[optind + 1], "-") == 0) fp = stdin; else @@ -197,10 +208,58 @@ main (int argc, /* Skip any empty line */ if (chars > 1) { - char *pointer_into_line = line; + char *pointer_into_line; + + if (deb_symbols) + { + if (line[0] == '#' || line[0] == '*' || line[0] == '|') + { + /* comment or metadata lines, ignore: + * "# comment" + * "* Field: Value" + * "| alternative-dependency" */ + continue; + } + else if (line[0] == ' ') + { + /* this line represents a symbol: + * " symbol@Base 1.2-3~" */ + if (!in_our_soname) + { + /* this is a symbol from a different library, + * ignore */ + continue; + } + } + else + { + /* This line introduces a new SONAME, which might + * be the one we are interested in: + * "libz.so.1 zlib1g #MINVER#" */ + if (strncmp (soname, line, soname_len) == 0 + && (line[soname_len] == ' ' || line[soname_len] == '\t')) + { + found_our_soname = true; + in_our_soname = true; + } + else + { + in_our_soname = false; + } + + /* This is not a symbol */ + continue; + } + + pointer_into_line = &line[1]; + } + else + { + pointer_into_line = line; + } symbol = strsep (&pointer_into_line, "@"); - version = strsep (&pointer_into_line, "@"); + version = strsep (&pointer_into_line, deb_symbols ? "@ \t" : "@"); if (symbol == NULL) { fprintf (stderr, "Probably the symbol@version pair is mispelled."); @@ -235,6 +294,12 @@ main (int argc, free (line); fclose (fp); + if (deb_symbols && !found_our_soname) + { + fprintf (stderr, "Warning: \"%s\" does not describe ABI of \"%s\"\n", + argv[optind + 1], soname); + } + first = true; entry = 0; printf (",\n \"missing_symbols\": ["); diff --git a/steam-runtime-tools/library.c b/steam-runtime-tools/library.c index 26b4983e588ce833618d6420aae6a84e1b6bf22c..d0b39fc8db3f9088b25a72816dd96fc0bd54ce3a 100644 --- a/steam-runtime-tools/library.c +++ b/steam-runtime-tools/library.c @@ -398,10 +398,10 @@ srt_library_get_misversioned_symbols (SrtLibrary *self) * srt_check_library_presence: * @soname: (type filename): The `SONAME` of a shared library, for example `libjpeg.so.62` * @multiarch: A multiarch tuple like %SRT_ABI_I386, representing an ABI. - * @symbols_path: (nullable): (type filename): The filename of a file with one - * symbol per line, in the format `jpeg_input_complete@LIBJPEG_6.2` for - * versioned symbols or `DGifOpen@Base` for symbols not associated with a version, - * or %NULL if we do not know which symbols the library is meant to contain. + * @symbols_path: (nullable): (type filename): The filename of a file listing + * symbols, or %NULL if we do not know which symbols the library is meant to + * contain. + * @symbols_format: The format of @symbols_path. * @more_details_out: (out) (optional) (transfer full): Used to return an * #SrtLibrary object representing the shared library provided by @soname. * Free with `g_object_unref()`. @@ -409,6 +409,15 @@ srt_library_get_misversioned_symbols (SrtLibrary *self) * Attempt to load @soname into a helper subprocess, and check whether it conforms * to the ABI provided in `symbols_path`. * + * If @symbols_format is %SRT_LIBRARY_SYMBOLS_FORMAT_PLAIN, @symbols_path must + * list one symbol per line, in the format `jpeg_input_complete@LIBJPEG_6.2` + * for versioned symbols or `DGifOpen@Base` (or just `DGifOpen`) for symbols + * not associated with a version. + * + * If @symbols_format is %SRT_LIBRARY_SYMBOLS_FORMAT_DEB_SYMBOLS, @symbols_path + * must be in deb-symbols(5) format. It may list symbols for more SONAMEs than + * just @soname; if so, they are ignored. + * * Returns: A bitfield containing problems, or %SRT_LIBRARY_ISSUES_NONE * if no problems were found. */ @@ -417,12 +426,16 @@ SrtLibraryIssues srt_check_library_presence (const char *soname, const char *multiarch, const char *symbols_path, + SrtLibrarySymbolsFormat symbols_format, SrtLibrary **more_details_out) { gchar *helper = NULL; gchar *output = NULL; gchar *absolute_path = NULL; - const gchar *argv[] = { "inspect-library", soname, symbols_path, NULL }; + const gchar *argv[] = + { + "inspect-library", "--deb-symbols", soname, symbols_path, NULL + }; int exit_status = -1; JsonParser *parser = NULL; JsonNode *node = NULL; @@ -440,6 +453,22 @@ srt_check_library_presence (const char *soname, g_return_val_if_fail (multiarch != NULL, SRT_LIBRARY_ISSUES_CANNOT_LOAD); g_return_val_if_fail (more_details_out == NULL || *more_details_out == NULL, SRT_LIBRARY_ISSUES_CANNOT_LOAD); + switch (symbols_format) + { + case SRT_LIBRARY_SYMBOLS_FORMAT_PLAIN: + argv[1] = soname; + argv[2] = symbols_path; + argv[3] = NULL; + break; + + case SRT_LIBRARY_SYMBOLS_FORMAT_DEB_SYMBOLS: + /* do nothing, argv is already set up for this */ + break; + + default: + g_return_val_if_reached (SRT_LIBRARY_ISSUES_CANNOT_LOAD); + } + helper = g_strdup_printf ("%s/%s-inspect-library", _srt_get_helpers_path (), multiarch); argv[0] = helper; diff --git a/steam-runtime-tools/library.h b/steam-runtime-tools/library.h index e6fc5c944b5a1a8df3ba744d71c0c3a0b70237ca..4a3db271f4242f6a652a1d337d32e0d2550fbffd 100644 --- a/steam-runtime-tools/library.h +++ b/steam-runtime-tools/library.h @@ -67,6 +67,19 @@ typedef enum SRT_LIBRARY_ISSUES_NONE = 0 } SrtLibraryIssues; +/** + * SrtLibrarySymbolsFormat: + * @SRT_LIBRARY_SYMBOLS_FORMAT_PLAIN: One symbol per line + * @SRT_LIBRARY_SYMBOLS_FORMAT_DEB_SYMBOLS: deb-symbols(5) format + * + * The format of a file listing expected symbols. + */ +typedef enum +{ + SRT_LIBRARY_SYMBOLS_FORMAT_PLAIN = 0, + SRT_LIBRARY_SYMBOLS_FORMAT_DEB_SYMBOLS +} SrtLibrarySymbolsFormat; + const char *srt_library_get_absolute_path (SrtLibrary *self); const char *srt_library_get_soname (SrtLibrary *self); const char *srt_library_get_multiarch_tuple (SrtLibrary *self); @@ -77,4 +90,5 @@ const char * const *srt_library_get_dependencies (SrtLibrary *self); SrtLibraryIssues srt_check_library_presence (const char *soname, const char *multiarch, const char *symbols_path, + SrtLibrarySymbolsFormat symbols_format, SrtLibrary **more_details_out); diff --git a/tests/library.c b/tests/library.c index c2168610d299d9bd24e225ef7d2014165b8f6f63..78cdd4833d5c77b12738ec3c5ce4a1ef572ad40c 100644 --- a/tests/library.c +++ b/tests/library.c @@ -237,6 +237,7 @@ test_presence (Fixture *f, issues = srt_check_library_presence ("libz.so.1", _SRT_MULTIARCH, tmp_file, + SRT_LIBRARY_SYMBOLS_FORMAT_PLAIN, &library); g_assert_cmpint (issues, ==, SRT_LIBRARY_ISSUES_NONE); @@ -270,6 +271,77 @@ test_presence (Fixture *f, g_clear_error (&error); } +/* + * Test parsing a deb-symbols(5) file. + */ +static void +test_deb_symbols (Fixture *f, + gconstpointer context) +{ + gboolean result; + char *tmp_file = NULL; + static const char expected_symbols[] = + "# A comment.\n" + /* Obviously this library doesn't really exist: it's here to check + * that we do the right thing for files that describe more than one + * SONAME, like libglib2.0-0:*.symbols. */ + "libzextra.so.0 libzextra0 #MINVER#\n" + " some_fictitious_symbol@Base 1:2.0\n" + "libz.so.1 zlib1g #MINVER#\n" + "| libz1 #MINVER\n" + "* Build-Depends-Package: zlib1g-dev\n" + " adler32@Base 1:1.1.4\n" + " inflateBack@ZLIB_1.2.0 1:1.2.0\n" + /* These symbols don't exist: they're here to check that we don't miss + * checking for symbols in the correct section of the file. */ + " nope@MISSING 1:1.2.0\n" + " also_nope@Base 1:1.2.0\n" + /* This library doesn't exist either. */ + "libzmore.so.0 libzmore0 #MINVER#\n" + " some_other_fictitious_symbol@Base 1:2.0\n"; + SrtLibrary *library = NULL; + SrtLibraryIssues issues; + const char * const *missing_symbols; + int fd; + GError *error = NULL; + + if (strcmp (_SRT_MULTIARCH, "") == 0) + { + g_test_skip ("Unsupported architecture"); + return; + } + + fd = g_file_open_tmp ("library-XXXXXX", &tmp_file, &error); + g_assert_no_error (error); + g_assert_cmpint (fd, !=, -1); + close (fd); + + result = g_file_set_contents (tmp_file, expected_symbols, -1, &error); + g_assert_no_error (error); + g_assert_true (result); + + issues = srt_check_library_presence ("libz.so.1", + _SRT_MULTIARCH, + tmp_file, + SRT_LIBRARY_SYMBOLS_FORMAT_DEB_SYMBOLS, + &library); + g_assert_cmpint (issues, ==, SRT_LIBRARY_ISSUES_MISSING_SYMBOLS); + + /* If we had mistakenly parsed the sections that refer to libzextra.so.0 + * and libzmore.so.0, then we would see more missing symbols than this. + * If we had not parsed the libz.so.1 section, we would see fewer. */ + missing_symbols = srt_library_get_missing_symbols (library); + g_assert_nonnull (missing_symbols); + g_assert_cmpstr (missing_symbols[0], ==, "nope@MISSING"); + g_assert_cmpstr (missing_symbols[1], ==, "also_nope"); + g_assert_cmpstr (missing_symbols[2], ==, NULL); + + g_unlink (tmp_file); + g_free (tmp_file); + g_object_unref (library); + g_clear_error (&error); +} + /* * Test the presence of empty lines in expected symbols file. */ @@ -312,6 +384,7 @@ test_empty_line (Fixture *f, issues = srt_check_library_presence ("libz.so.1", _SRT_MULTIARCH, tmp_file, + SRT_LIBRARY_SYMBOLS_FORMAT_PLAIN, &library); g_assert_cmpint (issues, ==, SRT_LIBRARY_ISSUES_NONE); @@ -371,6 +444,7 @@ test_missing_symbols (Fixture *f, issues = srt_check_library_presence ("libz.so.1", _SRT_MULTIARCH, tmp_file, + SRT_LIBRARY_SYMBOLS_FORMAT_PLAIN, &library); g_assert_cmpint (issues, ==, SRT_LIBRARY_ISSUES_MISSING_SYMBOLS); @@ -449,6 +523,7 @@ test_misversioned_symbols (Fixture *f, issues = srt_check_library_presence ("libz.so.1", _SRT_MULTIARCH, tmp_file, + SRT_LIBRARY_SYMBOLS_FORMAT_PLAIN, &library); g_assert_cmpint (issues, ==, SRT_LIBRARY_ISSUES_MISVERSIONED_SYMBOLS); @@ -529,6 +604,7 @@ test_missing_symbols_and_versions (Fixture *f, issues = srt_check_library_presence ("libz.so.1", _SRT_MULTIARCH, tmp_file, + SRT_LIBRARY_SYMBOLS_FORMAT_PLAIN, &library); g_assert_cmpint (issues & SRT_LIBRARY_ISSUES_MISSING_SYMBOLS, !=, 0); g_assert_cmpint (issues & SRT_LIBRARY_ISSUES_MISVERSIONED_SYMBOLS, !=, 0); @@ -592,12 +668,14 @@ test_missing_library (Fixture *f, issues = srt_check_library_presence ("libMISSING.so.62", _SRT_MULTIARCH, NULL, + SRT_LIBRARY_SYMBOLS_FORMAT_PLAIN, NULL); g_assert_cmpint (issues, ==, SRT_LIBRARY_ISSUES_CANNOT_LOAD); issues = srt_check_library_presence ("libMISSING.so.62", _SRT_MULTIARCH, NULL, + SRT_LIBRARY_SYMBOLS_FORMAT_PLAIN, &library); g_assert_cmpint (issues, ==, SRT_LIBRARY_ISSUES_CANNOT_LOAD); g_assert_cmpstr (srt_library_get_absolute_path (library), ==, NULL); @@ -633,6 +711,7 @@ test_missing_arch (Fixture *f, issues = srt_check_library_presence ("libz.so.1", "hal9000-linux-gnu", NULL, + SRT_LIBRARY_SYMBOLS_FORMAT_PLAIN, &library); g_assert_cmpint (issues, ==, SRT_LIBRARY_ISSUES_CANNOT_LOAD); g_assert_cmpstr (srt_library_get_absolute_path (library), ==, NULL); @@ -662,6 +741,8 @@ main (int argc, setup, test_object, teardown); g_test_add ("/presence", Fixture, NULL, setup, test_presence, teardown); + g_test_add ("/deb_symbols", Fixture, NULL, setup, test_deb_symbols, + teardown); g_test_add ("/empty_line", Fixture, NULL, setup, test_empty_line, teardown); g_test_add ("/missing_symbol", Fixture, NULL, setup,