]> git.ipfire.org Git - pakfire.git/commitdiff
ELF: Parse any runtime dependencies
authorMichael Tremer <michael.tremer@ipfire.org>
Fri, 3 Jan 2025 10:17:48 +0000 (10:17 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Fri, 3 Jan 2025 10:17:48 +0000 (10:17 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/pakfire/build.c
src/pakfire/elf.c
src/pakfire/elf.h
src/scripts/find-requires

index 57ba341ab02122df6876b1b1db997adb89b3edfa..14a1d5b5f8d8ee3db75072c495a6fe0191e23bfe 100644 (file)
@@ -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);
 
index 49f9a41ebdd619531311be8312a63cfdca25d67f..c9c1dadd71b1ed86a3fc6516fd11feee3ad742ca 100644 (file)
@@ -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...
index a1d591797d3cd11d2006749491a7c930f9ad2c79..450e7c481ff2aadb143200576cd90007a7b16014 100644 (file)
@@ -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);
index 3fcbfd478d11a858b1e3cc882cb257a4ed2699d5..b2e14bd2fcf24739841fa8a1cbc1c5d48c932b45 100644 (file)
@@ -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