Skip to content
Snippets Groups Projects
Commit 39c10151 authored by Simon McVittie's avatar Simon McVittie
Browse files

Merge branch 'wip/smcv/deb-symbols' into 'master'

inspect-library: Add support for consuming deb-symbols(5) files

See merge request steam/steam-runtime-tools!21
parents 3e7e9536 24ac6293
No related branches found
No related tags found
2 merge requests!21inspect-library: Add support for consuming deb-symbols(5) files,!20inspect-library: various fixes
Pipeline #1291 passed
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include <argz.h> #include <argz.h>
#include <dlfcn.h> #include <dlfcn.h>
#include <errno.h> #include <errno.h>
#include <getopt.h>
#include <link.h> #include <link.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
...@@ -71,6 +72,39 @@ do { \ ...@@ -71,6 +72,39 @@ do { \
oom (); \ oom (); \
} while (0) } while (0)
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 }
};
static void usage (int code) __attribute__((__noreturn__));
/*
* Print usage information and exit with status @code.
*/
static void
usage (int code)
{
FILE *fp;
if (code == 0)
fp = stdout;
else
fp = stderr;
fprintf (fp, "Usage: %s [OPTIONS] SONAME [SYMBOLS_FILENAME]\n",
program_invocation_short_name);
exit (code);
}
int int
main (int argc, main (int argc,
char **argv) char **argv)
...@@ -88,18 +122,40 @@ main (int argc, ...@@ -88,18 +122,40 @@ main (int argc,
size_t missing_n = 0; size_t missing_n = 0;
size_t misversioned_n = 0; size_t misversioned_n = 0;
char *line = NULL; char *line = NULL;
char *r = NULL;
size_t len = 0; size_t len = 0;
ssize_t chars; ssize_t chars;
bool first; bool first;
bool deb_symbols = false;
int opt;
if (argc < 2 || argc > 3) while ((opt = getopt_long (argc, argv, "", long_options, NULL)) != -1)
{ {
fprintf (stderr, "Expected, as argument, one SONAME and optionally a filename for symbols.\n"); switch (opt)
return 1; {
case OPTION_DEB_SYMBOLS:
deb_symbols = true;
break;
case OPTION_HELP:
usage (0);
break;
case '?':
default:
usage (1);
break; /* not reached */
case -1:
break;
}
} }
soname = argv[1]; if (argc < optind + 1 || argc > optind + 2)
{
usage (1);
}
soname = argv[optind];
printf ("{\n"); printf ("{\n");
printf (" \""); printf (" \"");
print_json_string_content (soname); print_json_string_content (soname);
...@@ -123,18 +179,23 @@ main (int argc, ...@@ -123,18 +179,23 @@ main (int argc,
print_json_string_content (the_library->l_name); print_json_string_content (the_library->l_name);
printf ("\""); printf ("\"");
if (argc == 3) if (argc >= optind + 2)
{ {
if (strcmp(argv[2], "-") == 0) size_t soname_len = strlen (soname);
bool found_our_soname = false;
bool in_our_soname = false;
if (strcmp(argv[optind + 1], "-") == 0)
fp = stdin; fp = stdin;
else else
fp = fopen(argv[2], "r"); fp = fopen(argv[optind + 1], "r");
if (fp == NULL) if (fp == NULL)
{ {
int saved_errno = errno; int saved_errno = errno;
fprintf (stderr, "Error reading \"%s\": %s\n", argv[2], strerror (saved_errno)); fprintf (stderr, "Error reading \"%s\": %s\n",
argv[optind + 1], strerror (saved_errno));
clean_exit (handle); clean_exit (handle);
return 1; return 1;
} }
...@@ -147,20 +208,69 @@ main (int argc, ...@@ -147,20 +208,69 @@ main (int argc,
/* Skip any empty line */ /* Skip any empty line */
if (chars > 1) if (chars > 1)
{ {
r = line; char *pointer_into_line;
symbol = strsep(&line, "@");
version = strsep(&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, deb_symbols ? "@ \t" : "@");
if (symbol == NULL) if (symbol == NULL)
{ {
fprintf (stderr, "Probably the symbol@version pair is mispelled."); fprintf (stderr, "Probably the symbol@version pair is mispelled.");
free (r); free (line);
free (missing_symbols); free (missing_symbols);
free (misversioned_symbols); free (misversioned_symbols);
clean_exit (handle); clean_exit (handle);
return 1; return 1;
} }
if (strcmp (version, BASE) == 0) if (version == NULL || strcmp (version, BASE) == 0)
{ {
if (!has_symbol (handle, symbol)) if (!has_symbol (handle, symbol))
argz_add_or_die (&missing_symbols, &missing_n, symbol); argz_add_or_die (&missing_symbols, &missing_n, symbol);
...@@ -179,12 +289,17 @@ main (int argc, ...@@ -179,12 +289,17 @@ main (int argc,
free (merged_string); free (merged_string);
} }
} }
free (r);
} }
} }
free (line); free (line);
fclose (fp); 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; first = true;
entry = 0; entry = 0;
printf (",\n \"missing_symbols\": ["); printf (",\n \"missing_symbols\": [");
......
...@@ -398,10 +398,10 @@ srt_library_get_misversioned_symbols (SrtLibrary *self) ...@@ -398,10 +398,10 @@ srt_library_get_misversioned_symbols (SrtLibrary *self)
* srt_check_library_presence: * srt_check_library_presence:
* @soname: (type filename): The `SONAME` of a shared library, for example `libjpeg.so.62` * @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. * @multiarch: A multiarch tuple like %SRT_ABI_I386, representing an ABI.
* @symbols_path: (nullable): (type filename): The filename of a file with one * @symbols_path: (nullable): (type filename): The filename of a file listing
* symbol per line, in the format `jpeg_input_complete@LIBJPEG_6.2` for * symbols, or %NULL if we do not know which symbols the library is meant to
* versioned symbols or `DGifOpen@Base` for symbols not associated with a version, * contain.
* 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 * @more_details_out: (out) (optional) (transfer full): Used to return an
* #SrtLibrary object representing the shared library provided by @soname. * #SrtLibrary object representing the shared library provided by @soname.
* Free with `g_object_unref()`. * Free with `g_object_unref()`.
...@@ -409,6 +409,15 @@ srt_library_get_misversioned_symbols (SrtLibrary *self) ...@@ -409,6 +409,15 @@ srt_library_get_misversioned_symbols (SrtLibrary *self)
* Attempt to load @soname into a helper subprocess, and check whether it conforms * Attempt to load @soname into a helper subprocess, and check whether it conforms
* to the ABI provided in `symbols_path`. * 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 * Returns: A bitfield containing problems, or %SRT_LIBRARY_ISSUES_NONE
* if no problems were found. * if no problems were found.
*/ */
...@@ -417,12 +426,16 @@ SrtLibraryIssues ...@@ -417,12 +426,16 @@ SrtLibraryIssues
srt_check_library_presence (const char *soname, srt_check_library_presence (const char *soname,
const char *multiarch, const char *multiarch,
const char *symbols_path, const char *symbols_path,
SrtLibrarySymbolsFormat symbols_format,
SrtLibrary **more_details_out) SrtLibrary **more_details_out)
{ {
gchar *helper = NULL; gchar *helper = NULL;
gchar *output = NULL; gchar *output = NULL;
gchar *absolute_path = 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; int exit_status = -1;
JsonParser *parser = NULL; JsonParser *parser = NULL;
JsonNode *node = NULL; JsonNode *node = NULL;
...@@ -440,6 +453,22 @@ srt_check_library_presence (const char *soname, ...@@ -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 (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); 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", helper = g_strdup_printf ("%s/%s-inspect-library",
_srt_get_helpers_path (), multiarch); _srt_get_helpers_path (), multiarch);
argv[0] = helper; argv[0] = helper;
......
...@@ -67,6 +67,19 @@ typedef enum ...@@ -67,6 +67,19 @@ typedef enum
SRT_LIBRARY_ISSUES_NONE = 0 SRT_LIBRARY_ISSUES_NONE = 0
} SrtLibraryIssues; } 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_absolute_path (SrtLibrary *self);
const char *srt_library_get_soname (SrtLibrary *self); const char *srt_library_get_soname (SrtLibrary *self);
const char *srt_library_get_multiarch_tuple (SrtLibrary *self); const char *srt_library_get_multiarch_tuple (SrtLibrary *self);
...@@ -77,4 +90,5 @@ const char * const *srt_library_get_dependencies (SrtLibrary *self); ...@@ -77,4 +90,5 @@ const char * const *srt_library_get_dependencies (SrtLibrary *self);
SrtLibraryIssues srt_check_library_presence (const char *soname, SrtLibraryIssues srt_check_library_presence (const char *soname,
const char *multiarch, const char *multiarch,
const char *symbols_path, const char *symbols_path,
SrtLibrarySymbolsFormat symbols_format,
SrtLibrary **more_details_out); SrtLibrary **more_details_out);
...@@ -227,6 +227,7 @@ test_presence (Fixture *f, ...@@ -227,6 +227,7 @@ test_presence (Fixture *f,
expected_symbols = g_string_new ("inflateCopy@ZLIB_1.2.0\n" expected_symbols = g_string_new ("inflateCopy@ZLIB_1.2.0\n"
"inflateBack@ZLIB_1.2.0\n" "inflateBack@ZLIB_1.2.0\n"
"adler32\n"
"gzopen@Base"); "gzopen@Base");
result = g_file_set_contents (tmp_file, expected_symbols->str, -1, &error); result = g_file_set_contents (tmp_file, expected_symbols->str, -1, &error);
...@@ -236,6 +237,7 @@ test_presence (Fixture *f, ...@@ -236,6 +237,7 @@ test_presence (Fixture *f,
issues = srt_check_library_presence ("libz.so.1", issues = srt_check_library_presence ("libz.so.1",
_SRT_MULTIARCH, _SRT_MULTIARCH,
tmp_file, tmp_file,
SRT_LIBRARY_SYMBOLS_FORMAT_PLAIN,
&library); &library);
g_assert_cmpint (issues, ==, SRT_LIBRARY_ISSUES_NONE); g_assert_cmpint (issues, ==, SRT_LIBRARY_ISSUES_NONE);
...@@ -269,6 +271,77 @@ test_presence (Fixture *f, ...@@ -269,6 +271,77 @@ test_presence (Fixture *f,
g_clear_error (&error); 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. * Test the presence of empty lines in expected symbols file.
*/ */
...@@ -311,6 +384,7 @@ test_empty_line (Fixture *f, ...@@ -311,6 +384,7 @@ test_empty_line (Fixture *f,
issues = srt_check_library_presence ("libz.so.1", issues = srt_check_library_presence ("libz.so.1",
_SRT_MULTIARCH, _SRT_MULTIARCH,
tmp_file, tmp_file,
SRT_LIBRARY_SYMBOLS_FORMAT_PLAIN,
&library); &library);
g_assert_cmpint (issues, ==, SRT_LIBRARY_ISSUES_NONE); g_assert_cmpint (issues, ==, SRT_LIBRARY_ISSUES_NONE);
...@@ -370,6 +444,7 @@ test_missing_symbols (Fixture *f, ...@@ -370,6 +444,7 @@ test_missing_symbols (Fixture *f,
issues = srt_check_library_presence ("libz.so.1", issues = srt_check_library_presence ("libz.so.1",
_SRT_MULTIARCH, _SRT_MULTIARCH,
tmp_file, tmp_file,
SRT_LIBRARY_SYMBOLS_FORMAT_PLAIN,
&library); &library);
g_assert_cmpint (issues, ==, SRT_LIBRARY_ISSUES_MISSING_SYMBOLS); g_assert_cmpint (issues, ==, SRT_LIBRARY_ISSUES_MISSING_SYMBOLS);
...@@ -448,6 +523,7 @@ test_misversioned_symbols (Fixture *f, ...@@ -448,6 +523,7 @@ test_misversioned_symbols (Fixture *f,
issues = srt_check_library_presence ("libz.so.1", issues = srt_check_library_presence ("libz.so.1",
_SRT_MULTIARCH, _SRT_MULTIARCH,
tmp_file, tmp_file,
SRT_LIBRARY_SYMBOLS_FORMAT_PLAIN,
&library); &library);
g_assert_cmpint (issues, ==, SRT_LIBRARY_ISSUES_MISVERSIONED_SYMBOLS); g_assert_cmpint (issues, ==, SRT_LIBRARY_ISSUES_MISVERSIONED_SYMBOLS);
...@@ -528,6 +604,7 @@ test_missing_symbols_and_versions (Fixture *f, ...@@ -528,6 +604,7 @@ test_missing_symbols_and_versions (Fixture *f,
issues = srt_check_library_presence ("libz.so.1", issues = srt_check_library_presence ("libz.so.1",
_SRT_MULTIARCH, _SRT_MULTIARCH,
tmp_file, tmp_file,
SRT_LIBRARY_SYMBOLS_FORMAT_PLAIN,
&library); &library);
g_assert_cmpint (issues & SRT_LIBRARY_ISSUES_MISSING_SYMBOLS, !=, 0); g_assert_cmpint (issues & SRT_LIBRARY_ISSUES_MISSING_SYMBOLS, !=, 0);
g_assert_cmpint (issues & SRT_LIBRARY_ISSUES_MISVERSIONED_SYMBOLS, !=, 0); g_assert_cmpint (issues & SRT_LIBRARY_ISSUES_MISVERSIONED_SYMBOLS, !=, 0);
...@@ -591,12 +668,14 @@ test_missing_library (Fixture *f, ...@@ -591,12 +668,14 @@ test_missing_library (Fixture *f,
issues = srt_check_library_presence ("libMISSING.so.62", issues = srt_check_library_presence ("libMISSING.so.62",
_SRT_MULTIARCH, _SRT_MULTIARCH,
NULL, NULL,
SRT_LIBRARY_SYMBOLS_FORMAT_PLAIN,
NULL); NULL);
g_assert_cmpint (issues, ==, SRT_LIBRARY_ISSUES_CANNOT_LOAD); g_assert_cmpint (issues, ==, SRT_LIBRARY_ISSUES_CANNOT_LOAD);
issues = srt_check_library_presence ("libMISSING.so.62", issues = srt_check_library_presence ("libMISSING.so.62",
_SRT_MULTIARCH, _SRT_MULTIARCH,
NULL, NULL,
SRT_LIBRARY_SYMBOLS_FORMAT_PLAIN,
&library); &library);
g_assert_cmpint (issues, ==, SRT_LIBRARY_ISSUES_CANNOT_LOAD); g_assert_cmpint (issues, ==, SRT_LIBRARY_ISSUES_CANNOT_LOAD);
g_assert_cmpstr (srt_library_get_absolute_path (library), ==, NULL); g_assert_cmpstr (srt_library_get_absolute_path (library), ==, NULL);
...@@ -632,6 +711,7 @@ test_missing_arch (Fixture *f, ...@@ -632,6 +711,7 @@ test_missing_arch (Fixture *f,
issues = srt_check_library_presence ("libz.so.1", issues = srt_check_library_presence ("libz.so.1",
"hal9000-linux-gnu", "hal9000-linux-gnu",
NULL, NULL,
SRT_LIBRARY_SYMBOLS_FORMAT_PLAIN,
&library); &library);
g_assert_cmpint (issues, ==, SRT_LIBRARY_ISSUES_CANNOT_LOAD); g_assert_cmpint (issues, ==, SRT_LIBRARY_ISSUES_CANNOT_LOAD);
g_assert_cmpstr (srt_library_get_absolute_path (library), ==, NULL); g_assert_cmpstr (srt_library_get_absolute_path (library), ==, NULL);
...@@ -661,6 +741,8 @@ main (int argc, ...@@ -661,6 +741,8 @@ main (int argc,
setup, test_object, teardown); setup, test_object, teardown);
g_test_add ("/presence", Fixture, NULL, setup, test_presence, g_test_add ("/presence", Fixture, NULL, setup, test_presence,
teardown); teardown);
g_test_add ("/deb_symbols", Fixture, NULL, setup, test_deb_symbols,
teardown);
g_test_add ("/empty_line", Fixture, NULL, setup, test_empty_line, g_test_add ("/empty_line", Fixture, NULL, setup, test_empty_line,
teardown); teardown);
g_test_add ("/missing_symbol", Fixture, NULL, setup, g_test_add ("/missing_symbol", Fixture, NULL, setup,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment