/* Classification of ELF files.
Copyright (C) 2019 Red Hat, Inc.
+ Copyright (C) 2025 Mark J. Wielaard <mark@klomp.org>
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
/* Set by parse_opt. */
static int verbose;
-/* Set by the main function. */
-static const char *current_path;
+/* Set by the main function and check_ar_members. */
+static char *current_path;
/* Set by open_file. */
static int file_fd = -1;
/* Set by parse_opt. */
static bool flag_only_regular_files;
+static bool flag_any_ar_member;
static bool
open_file (void)
classify_flag_no_print,
classify_flag_matching,
classify_flag_not_matching,
+ classify_flag_any_ar_member
};
static bool
flag_only_regular_files = true;
break;
+ case classify_flag_any_ar_member:
+ flag_any_ar_member = true;
+ break;
+
case classify_flag_stdin:
flag_stdin = do_stdin;
break;
return 0;
}
+static bool
+check_checks ()
+{
+ bool checks_passed = true;
+ bool checks[] =
+ {
+ [classify_elf] = is_elf (),
+ [classify_elf_file] = is_elf_file (),
+ [classify_elf_archive] = is_elf_archive (),
+ [classify_core] = is_core (),
+ [classify_unstripped] = is_unstripped (),
+ [classify_executable] = is_executable (),
+ [classify_program] = is_program (),
+ [classify_shared] = is_shared (),
+ [classify_library] = is_library (),
+ [classify_linux_kernel_module] = is_linux_kernel_module (),
+ [classify_debug_only] = is_debug_only (),
+ [classify_loadable] = is_loadable (),
+ };
+
+ if (verbose > 1)
+ {
+ if (checks[classify_elf])
+ fprintf (stderr, "debug: %s: elf\n", current_path);
+ if (checks[classify_elf_file])
+ fprintf (stderr, "debug: %s: elf_file\n", current_path);
+ if (checks[classify_elf_archive])
+ fprintf (stderr, "debug: %s: elf_archive\n", current_path);
+ if (checks[classify_core])
+ fprintf (stderr, "debug: %s: core\n", current_path);
+ if (checks[classify_unstripped])
+ fprintf (stderr, "debug: %s: unstripped\n", current_path);
+ if (checks[classify_executable])
+ fprintf (stderr, "debug: %s: executable\n", current_path);
+ if (checks[classify_program])
+ fprintf (stderr, "debug: %s: program\n", current_path);
+ if (checks[classify_shared])
+ fprintf (stderr, "debug: %s: shared\n", current_path);
+ if (checks[classify_library])
+ fprintf (stderr, "debug: %s: library\n", current_path);
+ if (checks[classify_linux_kernel_module])
+ fprintf (stderr, "debug: %s: linux kernel module\n", current_path);
+ if (checks[classify_debug_only])
+ fprintf (stderr, "debug: %s: debug-only\n", current_path);
+ if (checks[classify_loadable])
+ fprintf (stderr, "debug: %s: loadable\n", current_path);
+ }
+
+ for (enum classify_check check = 0;
+ check <= classify_check_last; ++check)
+ switch (requirements[check])
+ {
+ case required:
+ if (!checks[check])
+ checks_passed = false;
+ break;
+ case forbidden:
+ if (checks[check])
+ checks_passed = false;
+ break;
+ case do_not_care:
+ break;
+ }
+
+ return checks_passed;
+}
+
+static bool
+check_ar_members ()
+{
+ char *ar_path = current_path;
+ Elf *ar_elf = elf;
+ bool checks_passed = false;
+
+ /* Guess some storage space for the "ar_path[member_path]" string so
+ we have to hopefully only allocate once. */
+ size_t path_size = 2 * strlen (ar_path) + 24;
+ char *full_path = malloc (path_size);
+ if (full_path == NULL)
+ {
+ issue (ENOMEM, N_("allocating a member string name storage"));
+ current_path = ar_path;
+ return false;
+ }
+
+ int cmd = ELF_C_READ;
+ bool bad_ar = false;
+ while ((elf = elf_begin (file_fd, cmd, ar_elf)) != NULL)
+ {
+ Elf_Arhdr *arhdr = elf_getarhdr (elf);
+ if (arhdr == NULL)
+ {
+ elf_issue (N_("getting ar header"));
+ elf_end (elf);
+ bad_ar = true;
+ break;
+ }
+
+ char *ar_name = arhdr->ar_name ?: "<unknown>";
+ if (path_size < strlen (ar_path) + strlen (ar_name) + 3)
+ {
+ path_size = strlen (ar_path) + strlen (ar_name) + 24;
+ char *new_path = realloc (current_path, path_size);
+ if (new_path == NULL)
+ {
+ issue (ENOMEM, N_("allocating a member string name storage"));
+ elf_end (elf);
+ bad_ar = true;
+ break;
+ }
+ full_path = new_path;
+ }
+
+ if (sprintf (full_path, "%s[%s]", ar_path, ar_name) < 0)
+ {
+ issue (0, N_("constructing ar member string name"));
+ elf_end (elf);
+ bad_ar = true;
+ break;
+ }
+
+ /* One member (and no errors) is all it takes to pass. But we
+ check all members to make sure it is a valid archive. */
+ current_path = full_path;
+ if (run_classify () && check_checks ())
+ checks_passed = true;
+
+ cmd = elf_next (elf);
+
+ if (elf_end (elf) != 0)
+ {
+ elf_issue (N_("closing ar member"));
+ bad_ar = true;
+ }
+
+ if (bad_ar)
+ break;
+ }
+
+ if (bad_ar)
+ checks_passed = false;
+
+ elf = ar_elf;
+ free (full_path);
+ current_path = ar_path;
+ return checks_passed;
+}
+
/* Perform requested checks against the file at current_path. If
necessary, sets *STATUS to 1 if checks failed. */
static void
process_current_path (int *status)
{
bool checks_passed = true;
+ bool elf_opened = open_elf ();
- if (open_elf () && run_classify ())
- {
- bool checks[] =
- {
- [classify_elf] = is_elf (),
- [classify_elf_file] = is_elf_file (),
- [classify_elf_archive] = is_elf_archive (),
- [classify_core] = is_core (),
- [classify_unstripped] = is_unstripped (),
- [classify_executable] = is_executable (),
- [classify_program] = is_program (),
- [classify_shared] = is_shared (),
- [classify_library] = is_library (),
- [classify_linux_kernel_module] = is_linux_kernel_module (),
- [classify_debug_only] = is_debug_only (),
- [classify_loadable] = is_loadable (),
- };
-
- if (verbose > 1)
- {
- if (checks[classify_elf])
- fprintf (stderr, "debug: %s: elf\n", current_path);
- if (checks[classify_elf_file])
- fprintf (stderr, "debug: %s: elf_file\n", current_path);
- if (checks[classify_elf_archive])
- fprintf (stderr, "debug: %s: elf_archive\n", current_path);
- if (checks[classify_core])
- fprintf (stderr, "debug: %s: core\n", current_path);
- if (checks[classify_unstripped])
- fprintf (stderr, "debug: %s: unstripped\n", current_path);
- if (checks[classify_executable])
- fprintf (stderr, "debug: %s: executable\n", current_path);
- if (checks[classify_program])
- fprintf (stderr, "debug: %s: program\n", current_path);
- if (checks[classify_shared])
- fprintf (stderr, "debug: %s: shared\n", current_path);
- if (checks[classify_library])
- fprintf (stderr, "debug: %s: library\n", current_path);
- if (checks[classify_linux_kernel_module])
- fprintf (stderr, "debug: %s: linux kernel module\n", current_path);
- if (checks[classify_debug_only])
- fprintf (stderr, "debug: %s: debug-only\n", current_path);
- if (checks[classify_loadable])
- fprintf (stderr, "debug: %s: loadable\n", current_path);
- }
-
- for (enum classify_check check = 0;
- check <= classify_check_last; ++check)
- switch (requirements[check])
- {
- case required:
- if (!checks[check])
- checks_passed = false;
- break;
- case forbidden:
- if (checks[check])
- checks_passed = false;
- break;
- case do_not_care:
- break;
- }
- }
+ if (elf_opened && flag_any_ar_member && run_classify () && is_elf_archive ())
+ checks_passed = check_ar_members ();
+ else if (elf_opened && !flag_any_ar_member && run_classify ())
+ checks_passed = check_checks ();
else if (file_fd == -1)
checks_passed = false; /* There is nothing to check, bad file. */
else
{
+ /* Not a bad file, but couldn't open it as an ELF file or trying
+ to run some classify tests on it produced an error. Check if
+ there were any required tests (normally there is at least the
+ default --elf, but the user could have used --not-elf). */
for (enum classify_check check = 0;
check <= classify_check_last; ++check)
if (requirements[check] == required)
{ NULL, 0, NULL, OPTION_DOC, N_("Input flags"), 2 },
{ "file", 'f', NULL, 0,
N_("Only classify regular (not symlink nor special device) files"), 2 },
+ { "any-ar-member", classify_flag_any_ar_member, NULL, 0,
+ N_("Input is an ar file, classification options apply to ar member"), 2 },
{ "stdin", classify_flag_stdin, NULL, 0,
N_("Also read file names to process from standard input, \
separated by newlines"), 2 },
Determine the type of an ELF file.\
\n\n\
All of the classification options must apply at the same time to a \
-particular file. Classification options can be negated using a \
+particular file. Or if --any-ar-member is given the file must be an \
+ELF archive and the classification options must apply to at least one \
+archive member. Classification options can be negated using a \
\"--not-\" prefix.\
\n\n\
Since modern ELF does not clearly distinguish between programs and \