]> git.ipfire.org Git - thirdparty/kmod.git/commitdiff
libkmod: Add helper to read .note.gnu.build-id section
authorLucas De Marchi <demarchi@kernel.org>
Wed, 25 Mar 2026 04:34:28 +0000 (23:34 -0500)
committerLucas De Marchi <demarchi@kernel.org>
Sat, 20 Jun 2026 16:09:52 +0000 (11:09 -0500)
Read the .note.gnu.build-id section so it can be displayed by modinfo in
a future change. For now this is only added to pretty-print the id, but
later can be refactored to easily compare with the section dump from
/sys/module/<module-name>/.note.gnu.build-id

The section in the elf is potentially available since kernel 4.10 (if
toolchain supports it) and became unconditional since commit
89ff7131f78a ("kbuild: add --hash-style= and --build-id unconditionally")
since minimum toolchain versions got raised.

Signed-off-by: Lucas De Marchi <demarchi@kernel.org>
Reviewed-by: Emil Velikov <emil.l.velikov@gmail.com>
Link: https://github.com/kmod-project/kmod/pull/432
libkmod/libkmod-elf.c
libkmod/libkmod-internal.h

index 7fe6a29c91bf92884cda2344d7ea8ae9c60cb8a7..9d500f1a67e80e8ae0f08b6db15865cd9cbd338c 100644 (file)
@@ -32,6 +32,7 @@ enum kmod_elf_section {
        KMOD_ELF_SECTION_STRTAB,
        KMOD_ELF_SECTION_SYMTAB,
        KMOD_ELF_SECTION_VERSIONS,
+       KMOD_ELF_SECTION_BUILD_ID,
        KMOD_ELF_SECTION_MAX,
 };
 
@@ -41,6 +42,7 @@ static const char *const section_name_map[] = {
        [KMOD_ELF_SECTION_STRTAB] = ".strtab",
        [KMOD_ELF_SECTION_SYMTAB] = ".symtab",
        [KMOD_ELF_SECTION_VERSIONS] = "__versions",
+       [KMOD_ELF_SECTION_BUILD_ID] = ".note.gnu.build-id",
 };
 
 struct kmod_elf {
@@ -1247,3 +1249,62 @@ int kmod_elf_get_dependency_symbols(const struct kmod_elf *elf,
        free(visited_versions);
        return count;
 }
+
+#ifndef NT_GNU_BUILD_ID
+#define NT_GNU_BUILD_ID 3
+#endif
+
+/*
+ * Read the .notes.gnu.build-id section that is added by the linker via --build-id. The
+ * section format follows the one defined in shared/elf-note.h, with name == "GNU\0",
+ * type == NT_GNU_BUILD_ID and desc being the hash.
+ */
+int kmod_elf_get_build_id(const struct kmod_elf *elf, const void **hash, size_t *hash_len)
+{
+       uint32_t namesz, descsz, type;
+       uint64_t off, size;
+       const char *name;
+
+       off = elf->sections[KMOD_ELF_SECTION_BUILD_ID].offset;
+       size = elf->sections[KMOD_ELF_SECTION_BUILD_ID].size;
+       if (off == 0 || size == 0)
+               /*
+                * Conditionally available since 4.10 if toolchain supports it.
+                * Unconditionally available since 5.10:
+                * kernel- commit 89ff7131f78a ("kbuild: add --hash-style= and
+                * --build-id unconditionally"). Just return an error and don't
+                * log anything.
+                */
+               return -ENODATA;
+
+       if (size < 3 * sizeof(uint32_t)) {
+               ELFDBG(elf, "build-id section is too small: %" PRIu64 "\n", size);
+               return -EINVAL;
+       }
+
+       namesz = elf_get_u32(elf, off);
+       off += sizeof(uint32_t);
+       descsz = elf_get_u32(elf, off);
+       off += sizeof(uint32_t);
+       type = elf_get_u32(elf, off);
+       off += sizeof(uint32_t);
+
+       if (size < 3 * sizeof(uint32_t) + namesz + descsz) {
+               ELFDBG(elf, "build-id section is too small: %" PRIu64 "\n", size);
+               return -EINVAL;
+       }
+
+       name = elf_get_mem(elf, off);
+
+       if (type != NT_GNU_BUILD_ID || !streq(name, "GNU")) {
+               ELFDBG(elf, "Invalid build-id section: type %" PRIu32 ", name %.3s\n",
+                      type, name);
+               return -EINVAL;
+       }
+
+       off += namesz;
+       *hash = elf_get_mem(elf, off);
+       *hash_len = descsz;
+
+       return 0;
+}
index cc681e2219c33b5da6a0230038e394418dbe3a13..e85666234d60efe20d0ac215587f9f440c2dbbc3 100644 (file)
@@ -156,6 +156,7 @@ _must_check_ _nonnull_all_ int kmod_elf_get_modinfo_strings(const struct kmod_el
 _must_check_ _nonnull_all_ int kmod_elf_get_modversions(const struct kmod_elf *elf, struct kmod_modversion **array);
 _must_check_ _nonnull_all_ int kmod_elf_get_symbols(const struct kmod_elf *elf, struct kmod_modversion **array);
 _must_check_ _nonnull_all_ int kmod_elf_get_dependency_symbols(const struct kmod_elf *elf, struct kmod_modversion **array);
+_must_check_ _nonnull_all_ int kmod_elf_get_build_id(const struct kmod_elf *elf, const void **hash, size_t *hash_len);
 _must_check_ _nonnull_all_ int kmod_elf_strip(const struct kmod_elf *elf, unsigned int flags, const void **stripped);
 
 /*