From 9c9e2983c6a01dc07b4a9c5bfd44e11111f4e99d Mon Sep 17 00:00:00 2001 From: Michael Tremer Date: Fri, 3 Jan 2025 10:17:48 +0000 Subject: [PATCH] ELF: Parse any runtime dependencies Signed-off-by: Michael Tremer --- src/pakfire/build.c | 17 +++++++ src/pakfire/elf.c | 99 +++++++++++++++++++++++++++++++++++++++ src/pakfire/elf.h | 4 ++ src/scripts/find-requires | 45 ------------------ 4 files changed, 120 insertions(+), 45 deletions(-) diff --git a/src/pakfire/build.c b/src/pakfire/build.c index 57ba341ab..14a1d5b5f 100644 --- a/src/pakfire/build.c +++ b/src/pakfire/build.c @@ -617,6 +617,7 @@ ERROR: static int pakfire_build_find_elf_requires( struct pakfire_ctx* ctx, struct pakfire_file* file, struct pakfire_find_deps_ctx* deps) { struct pakfire_elf* elf = NULL; + char** requires = NULL; int r; // Try to open the ELF file @@ -641,7 +642,23 @@ static int pakfire_build_find_elf_requires( goto ERROR; } + // Fetch all requires + r = pakfire_elf_requires(elf, &requires); + if (r < 0) + goto ERROR; + + // Add them all to the package + if (requires) { + for (char** s = requires; *s; s++) { + r = pakfire_package_add_dep(deps->pkg, PAKFIRE_PKG_REQUIRES, "%s", *s); + if (r < 0) + goto ERROR; + } + } + ERROR: + if (requires) + pakfire_strings_free(requires); if (elf) pakfire_elf_unref(elf); diff --git a/src/pakfire/elf.c b/src/pakfire/elf.c index 49f9a41eb..c9c1dadd7 100644 --- a/src/pakfire/elf.c +++ b/src/pakfire/elf.c @@ -239,6 +239,10 @@ int pakfire_elf_machine(struct pakfire_elf* self) { return self->ehdr.e_machine; } +int pakfire_elf_is_elf64(struct pakfire_elf* self) { + return self->ehdr.e_ident[EI_CLASS] = ELFCLASS64; +} + int pakfire_elf_endianess(struct pakfire_elf* self) { return self->ehdr.e_ident[EI_DATA]; } @@ -913,6 +917,101 @@ int pakfire_elf_is_stripped(struct pakfire_elf* self) { return 1; } +static int pakfire_elf_find_requires(struct pakfire_elf* self, + Elf_Scn* section, const GElf_Shdr* shdr, void* data) { + GElf_Verneed verneed = {}; + GElf_Vernaux vernaux = {}; + const char* version = NULL; + const char* name = NULL; + char*** requires = data; + GElf_Dyn dyn = {}; + int r; + + // Fetch the section data + Elf_Data* d = elf_getdata(section, NULL); + + switch (shdr->sh_type) { + case SHT_DYNAMIC: + for (size_t i = 0; i < shdr->sh_size / shdr->sh_entsize; i++) { + // Fetch the symbol + if (!gelf_getdyn(d, i, &dyn)) + continue; + + switch (dyn.d_tag) { + case DT_NEEDED: + name = elf_strptr(self->elf, shdr->sh_link, dyn.d_un.d_val); + + // Skip empty names (this should not happen) + if (!name || !*name) + continue; + + DEBUG(self->ctx, "%s depends on %s\n", self->path, name); + + // Add it to the list + r = pakfire_strings_appendf(requires, "%s()%s", + name, pakfire_elf_is_elf64(self) ? "(64bit)" : ""); + if (r < 0) + return r; + break; + } + } + break; + + case SHT_GNU_verneed: + size_t offset = 0; + + while (offset < shdr->sh_size) { + if (!gelf_getverneed(d, offset, &verneed)) + break; + + // Fetch the library name + name = elf_strptr(self->elf, shdr->sh_link, verneed.vn_file); + if (!name || !*name) + continue; + + size_t aux_offset = verneed.vn_aux; + + for (int i = 0; i < verneed.vn_cnt; i++) { + if (!gelf_getvernaux(d, aux_offset, &vernaux)) + break; + + // Fetch the version string + version = elf_strptr(self->elf, shdr->sh_link, vernaux.vna_name); + + // Skip empty versions + if (!version || !*version) + continue; + + // Add it to the list + r = pakfire_strings_appendf(requires, "%s(%s)%s", + name, version, pakfire_elf_is_elf64(self) ? "(64bit)" : ""); + if (r < 0) + return r; + + if (!vernaux.vna_next) + break; + + aux_offset += vernaux.vna_next; + } + + if (!verneed.vn_next) + break; + + offset += verneed.vn_next; + } + break; + + default: + break; + } + + return 0; +} + +int pakfire_elf_requires(struct pakfire_elf* self, char*** requires) { + return pakfire_elf_foreach_section(self, SHT_NULL, pakfire_elf_find_requires, requires); +} + /* libdw does not seem to export the error codes in their header files, although there is a function to retrieve them... diff --git a/src/pakfire/elf.h b/src/pakfire/elf.h index a1d591797..450e7c481 100644 --- a/src/pakfire/elf.h +++ b/src/pakfire/elf.h @@ -40,6 +40,7 @@ struct pakfire_elf* pakfire_elf_unref(struct pakfire_elf* self); const char* pakfire_elf_path(struct pakfire_elf* self); int pakfire_elf_type(struct pakfire_elf* self); int pakfire_elf_machine(struct pakfire_elf* self); +int pakfire_elf_is_elf64(struct pakfire_elf* self); int pakfire_elf_endianess(struct pakfire_elf* self); const char* pakfire_elf_build_id(struct pakfire_elf* self); const char* pakfire_elf_debuglink(struct pakfire_elf* self); @@ -62,6 +63,9 @@ enum { PAKFIRE_ELF_MISSING_SHSTK = (1 << 3), }; +// Dependencies +int pakfire_elf_requires(struct pakfire_elf* self, char*** requires); + // Source Files typedef int (*pakfire_elf_foreach_source_file_callback) (struct pakfire_ctx* ctx, struct pakfire_elf* elf, const char* filename, void* data); diff --git a/src/scripts/find-requires b/src/scripts/find-requires index 3fcbfd478..b2e14bd2f 100644 --- a/src/scripts/find-requires +++ b/src/scripts/find-requires @@ -43,41 +43,6 @@ find_elf_interpreter() { return 0 } -find_weak_symbols() { - local file="${1}" - - local suffix - - # Is this a 64 bit file? - if file -L "${file}" 2>/dev/null | grep -q "ELF 64-bit"; then - suffix="(64bit)" - fi - - # List all weak symbol versions - objdump -p "${file}" 2>/dev/null | \ - awk \ - 'BEGIN { START=0; LIBNAME=""; } - /^$/ { START=0; } - /^Dynamic Section:$/ { START=1; } - (START==1) && /NEEDED/ { - if ("'${suffix}'" != "") { - sub(/$/, "()'${suffix}'", $2); - } - print $2; - } - (START==2) && /^[A-Za-z]/ { START=3; } - /^Version References:$/ { START=2; } - (START==2) && /required from/ { - sub(/:/, "", $3); - LIBNAME=$3; - } - (START==2) && (LIBNAME!="") && ($4!="") { - print LIBNAME "(" $4 ")'${suffix}'"; - }' - - return 0 -} - find_script_interpreter() { local file="${1}" @@ -182,16 +147,6 @@ main() { continue fi - # Is this an ELF file? - if file -L "${path}" 2>/dev/null | grep -q "ELF"; then - # Find weak symbols - if ! find_weak_symbols "${path}"; then - return 1 - fi - - continue - fi - # Add script interpreters if ! find_script_interpreter "${path}"; then return 1 -- 2.47.2