Skip to content

Commit f338903

Browse files
maurerlucasdemarchi
authored andcommitted
libkmod: Support EXTENDED_MODVERSIONS
In order to allow longer names, the kernel is taking a new MODVERSIONS format option which stores the names in a strtab-like section and the crcs in an array-like one. This patch makes `kmod_elf_get_modversions` use either format, preferring the new one where present. Signed-off-by: Matthew Maurer <[email protected]> Link: #270 Signed-off-by: Lucas De Marchi <[email protected]>
1 parent 486d678 commit f338903

File tree

1 file changed

+86
-4
lines changed

1 file changed

+86
-4
lines changed

libkmod/libkmod-elf.c

Lines changed: 86 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ enum kmod_elf_section {
3333
KMOD_ELF_SECTION_STRTAB,
3434
KMOD_ELF_SECTION_SYMTAB,
3535
KMOD_ELF_SECTION_VERSIONS,
36+
KMOD_ELF_SECTION_VERSION_EXT_NAMES,
37+
KMOD_ELF_SECTION_VERSION_EXT_CRCS,
3638
KMOD_ELF_SECTION_MAX,
3739
};
3840

@@ -42,6 +44,8 @@ static const char *const section_name_map[] = {
4244
[KMOD_ELF_SECTION_STRTAB] = ".strtab",
4345
[KMOD_ELF_SECTION_SYMTAB] = ".symtab",
4446
[KMOD_ELF_SECTION_VERSIONS] = "__versions",
47+
[KMOD_ELF_SECTION_VERSION_EXT_NAMES] = "__version_ext_names",
48+
[KMOD_ELF_SECTION_VERSION_EXT_CRCS] = "__version_ext_crcs",
4549
};
4650

4751
struct kmod_elf {
@@ -538,17 +542,15 @@ static inline void elf_get_modversion_lengths(const struct kmod_elf *elf, size_t
538542
}
539543
}
540544

541-
/* array will be allocated with strings in a single malloc, just free *array */
542-
int kmod_elf_get_modversions(const struct kmod_elf *elf, struct kmod_modversion **array)
545+
static int kmod_elf_get_basic_modversions(const struct kmod_elf *elf,
546+
struct kmod_modversion **array)
543547
{
544548
size_t i, count, crclen, namlen, verlen;
545549
uint64_t off, sec_off, size;
546550
struct kmod_modversion *a;
547551

548552
elf_get_modversion_lengths(elf, &verlen, &crclen, &namlen);
549553

550-
*array = NULL;
551-
552554
sec_off = elf->sections[KMOD_ELF_SECTION_VERSIONS].offset;
553555
size = elf->sections[KMOD_ELF_SECTION_VERSIONS].size;
554556
if (sec_off == 0)
@@ -591,6 +593,86 @@ int kmod_elf_get_modversions(const struct kmod_elf *elf, struct kmod_modversion
591593
return count;
592594
}
593595

596+
static int kmod_elf_get_extended_modversions(const struct kmod_elf *elf,
597+
struct kmod_modversion **array)
598+
{
599+
uint64_t name_off, name_size, crc_off, crc_size;
600+
const char *name_cursor;
601+
int count, i, ret;
602+
603+
name_off = elf->sections[KMOD_ELF_SECTION_VERSION_EXT_NAMES].offset;
604+
name_size = elf->sections[KMOD_ELF_SECTION_VERSION_EXT_NAMES].size;
605+
crc_off = elf->sections[KMOD_ELF_SECTION_VERSION_EXT_CRCS].offset;
606+
crc_size = elf->sections[KMOD_ELF_SECTION_VERSION_EXT_CRCS].size;
607+
608+
if ((name_off == 0) || (crc_off == 0))
609+
return -ENODATA;
610+
611+
if (crc_size == 0)
612+
return 0;
613+
614+
if (crc_size % sizeof(uint32_t) != 0)
615+
return -EINVAL;
616+
617+
count = crc_size / sizeof(uint32_t);
618+
*array = malloc(sizeof(struct kmod_modversion) * count);
619+
if (*array == NULL)
620+
return -errno;
621+
622+
for (name_cursor = elf_get_mem(elf, name_off), i = 0; i < count; i++) {
623+
size_t len;
624+
uint32_t crc;
625+
626+
if (name_size == 0) {
627+
ELFDBG(elf, "fewer symbol names than crcs\n");
628+
ret = -EINVAL;
629+
goto release_array;
630+
}
631+
len = strnlen(name_cursor, name_size);
632+
if (len == name_size) {
633+
ELFDBG(elf, "last symbol name not null-terminated\n");
634+
ret = -EINVAL;
635+
goto release_array;
636+
}
637+
crc = elf_get_uint(elf, crc_off, sizeof(uint32_t));
638+
639+
/* PPC sometimes prefixes names with `.` */
640+
if (name_cursor[0] == '.') {
641+
name_cursor++;
642+
len--;
643+
}
644+
645+
(*array)[i].crc = crc;
646+
(*array)[i].bind = KMOD_SYMBOL_UNDEF;
647+
(*array)[i].symbol = name_cursor;
648+
649+
/* len doesn't include the NUL, but our pointers and size do */
650+
name_cursor += len + 1;
651+
name_size -= len + 1;
652+
crc_off += sizeof(uint32_t);
653+
}
654+
655+
return count;
656+
657+
release_array:
658+
free(*array);
659+
return ret;
660+
}
661+
662+
/* array will be allocated with strings in a single malloc, just free *array */
663+
int kmod_elf_get_modversions(const struct kmod_elf *elf, struct kmod_modversion **array)
664+
{
665+
int ret;
666+
667+
*array = NULL;
668+
669+
ret = kmod_elf_get_extended_modversions(elf, array);
670+
/* if no extended modversions, fall back, otherwise propagate error */
671+
if (ret != -ENODATA)
672+
return ret;
673+
return kmod_elf_get_basic_modversions(elf, array);
674+
}
675+
594676
static int elf_strip_versions_section(const struct kmod_elf *elf, uint8_t *changed)
595677
{
596678
uint64_t off, size;

0 commit comments

Comments
 (0)