KMOD_ELF_MSB = (1 << 4)
};
+/* as defined in module-init-tools */
+struct kmod_modversion32 {
+ uint32_t crc;
+ char name[64 - sizeof(uint32_t)];
+};
+
+struct kmod_modversion64 {
+ uint64_t crc;
+ char name[64 - sizeof(uint64_t)];
+};
+
#ifdef WORDS_BIGENDIAN
static const enum kmod_elf_class native_endianess = KMOD_ELF_MSB;
#else
uint64_t offset;
uint32_t nameoff; /* offset in strings itself */
} strings;
+ uint16_t machine;
} header;
};
elf->header.section.offset = READV(e_shoff); \
elf->header.section.count = READV(e_shnum); \
elf->header.section.entry_size = READV(e_shentsize); \
- elf->header.strings.section = READV(e_shstrndx)
+ elf->header.strings.section = READV(e_shstrndx); \
+ elf->header.machine = READV(e_machine)
if (elf->class & KMOD_ELF_32) {
const Elf32_Ehdr *hdr = elf_get_mem(elf, 0);
LOAD_HEADER;
const void *buf;
char *itr;
int i, count, err;
- struct kmod_modversion32 {
- uint32_t crc;
- char name[64 - sizeof(uint32_t)];
- };
- struct kmod_modversion64 {
- uint64_t crc;
- char name[64 - sizeof(uint64_t)];
- };
#define MODVERSION_SEC_SIZE (sizeof(struct kmod_modversion64))
assert(sizeof(struct kmod_modversion64) ==
symbol++;
a[i].crc = crc;
- a[i].bind = KMOD_MODVERSION_UNDEF;
+ a[i].bind = KMOD_SYMBOL_UNDEF;
a[i].symbol = itr;
symbollen = strlen(symbol) + 1;
memcpy(itr, symbol, symbollen);
continue;
}
a[count].crc = 0;
- a[count].bind = KMOD_MODVERSION_GLOBAL;
+ a[count].bind = KMOD_SYMBOL_GLOBAL;
a[count].symbol = itr;
memcpy(itr, strings + last, slen);
itr[slen] = '\0';
if (strings[i - 1] != '\0') {
size_t slen = i - last;
a[count].crc = 0;
- a[count].bind = KMOD_MODVERSION_GLOBAL;
+ a[count].bind = KMOD_SYMBOL_GLOBAL;
a[count].symbol = itr;
memcpy(itr, strings + last, slen);
itr[slen] = '\0';
return count;
}
-static inline uint8_t kmod_modversion_bind_from_elf(uint8_t elf_value)
+static inline uint8_t kmod_symbol_bind_from_elf(uint8_t elf_value)
{
switch (elf_value) {
case STB_LOCAL:
- return KMOD_MODVERSION_LOCAL;
+ return KMOD_SYMBOL_LOCAL;
case STB_GLOBAL:
- return KMOD_MODVERSION_GLOBAL;
+ return KMOD_SYMBOL_GLOBAL;
case STB_WEAK:
- return KMOD_MODVERSION_WEAK;
+ return KMOD_SYMBOL_WEAK;
default:
- return KMOD_MODVERSION_NONE;
+ return KMOD_SYMBOL_NONE;
}
}
bind = ELF64_ST_BIND(info);
a[count].crc = crc;
- a[count].bind = kmod_modversion_bind_from_elf(bind);
+ a[count].bind = kmod_symbol_bind_from_elf(bind);
a[count].symbol = itr;
slen = strlen(name);
memcpy(itr, name, slen);
ELFDBG(elf, "Falling back to __ksymtab_strings!\n");
return kmod_elf_get_symbols_symtab(elf, array);
}
+
+static int kmod_elf_crc_find(const struct kmod_elf *elf, const void *versions, uint64_t versionslen, const char *name, uint64_t *crc)
+{
+ size_t verlen, crclen, off;
+ uint64_t i;
+
+ if (elf->class & KMOD_ELF_32) {
+ struct kmod_modversion32 *mv;
+ verlen = sizeof(*mv);
+ crclen = sizeof(mv->crc);
+ } else {
+ struct kmod_modversion64 *mv;
+ verlen = sizeof(*mv);
+ crclen = sizeof(mv->crc);
+ }
+
+ off = (const uint8_t *)versions - elf->memory;
+ for (i = 0; i < versionslen; i += verlen) {
+ const char *symbol = elf_get_mem(elf, off + i + crclen);
+ if (!streq(name, symbol))
+ continue;
+ *crc = elf_get_uint(elf, off + i, crclen);
+ return i / verlen;
+ }
+
+ ELFDBG(elf, "could not find crc for symbol '%s'\n", name);
+ *crc = 0;
+ return -1;
+}
+
+/* from module-init-tools:elfops_core.c */
+#ifndef STT_REGISTER
+#define STT_REGISTER 13 /* Global register reserved to app. */
+#endif
+
+/* array will be allocated with strings in a single malloc, just free *array */
+int kmod_elf_get_dependency_symbols(const struct kmod_elf *elf, struct kmod_modversion **array)
+{
+ uint64_t versionslen, strtablen, symtablen, str_off, sym_off, ver_off;
+ const void *versions, *strtab, *symtab;
+ struct kmod_modversion *a;
+ char *itr;
+ size_t slen, verlen, symlen, crclen;
+ int i, count, symcount, vercount, err;
+ bool handle_register_symbols;
+ uint8_t *visited_versions;
+ uint64_t *symcrcs;
+
+ err = kmod_elf_get_section(elf, "__versions", &versions, &versionslen);
+ if (err < 0) {
+ versions = NULL;
+ versionslen = 0;
+ verlen = 0;
+ crclen = 0;
+ } else {
+ if (elf->class & KMOD_ELF_32) {
+ struct kmod_modversion32 *mv;
+ verlen = sizeof(*mv);
+ crclen = sizeof(mv->crc);
+ } else {
+ struct kmod_modversion64 *mv;
+ verlen = sizeof(*mv);
+ crclen = sizeof(mv->crc);
+ }
+ if (versionslen % verlen != 0) {
+ ELFDBG(elf, "unexpected __versions of length %"PRIu64", not multiple of %zd as expected.\n", versionslen, verlen);
+ versions = NULL;
+ versionslen = 0;
+ }
+ }
+
+ err = kmod_elf_get_section(elf, ".strtab", &strtab, &strtablen);
+ if (err < 0) {
+ ELFDBG(elf, "no .strtab found.\n");
+ return -EINVAL;
+ }
+
+ err = kmod_elf_get_section(elf, ".symtab", &symtab, &symtablen);
+ if (err < 0) {
+ ELFDBG(elf, "no .symtab found.\n");
+ return -EINVAL;
+ }
+
+ if (elf->class & KMOD_ELF_32)
+ symlen = sizeof(Elf32_Sym);
+ else
+ symlen = sizeof(Elf64_Sym);
+
+ if (symtablen % symlen != 0) {
+ ELFDBG(elf, "unexpected .symtab of length %"PRIu64", not multiple of %"PRIu64" as expected.\n", symtablen, symlen);
+ return -EINVAL;
+ }
+
+ if (versionslen == 0) {
+ vercount = 0;
+ visited_versions = NULL;
+ } else {
+ vercount = versionslen / verlen;
+ visited_versions = calloc(vercount, sizeof(uint8_t));
+ if (visited_versions == NULL)
+ return -ENOMEM;
+ }
+
+ handle_register_symbols = (elf->header.machine == EM_SPARC ||
+ elf->header.machine == EM_SPARCV9);
+
+ symcount = symtablen / symlen;
+ count = 0;
+ slen = 0;
+ str_off = (const uint8_t *)strtab - elf->memory;
+ sym_off = (const uint8_t *)symtab - elf->memory + symlen;
+
+ symcrcs = calloc(symcount, sizeof(uint64_t));
+ if (symcrcs == NULL) {
+ free(visited_versions);
+ return -ENOMEM;
+ }
+
+ for (i = 1; i < symcount; i++, sym_off += symlen) {
+ const char *name;
+ uint64_t crc;
+ uint32_t name_off;
+ uint16_t secidx;
+ uint8_t info;
+ int idx;
+
+#define READV(field) \
+ elf_get_uint(elf, sym_off + offsetof(typeof(*s), field),\
+ sizeof(s->field))
+ if (elf->class & KMOD_ELF_32) {
+ Elf32_Sym *s;
+ name_off = READV(st_name);
+ secidx = READV(st_shndx);
+ info = READV(st_info);
+ } else {
+ Elf64_Sym *s;
+ name_off = READV(st_name);
+ secidx = READV(st_shndx);
+ info = READV(st_info);
+ }
+#undef READV
+ if (secidx != SHN_UNDEF)
+ continue;
+
+ if (handle_register_symbols) {
+ uint8_t type;
+ if (elf->class & KMOD_ELF_32)
+ type = ELF32_ST_TYPE(info);
+ else
+ type = ELF64_ST_TYPE(info);
+
+ /* Not really undefined: sparc gcc 3.3 creates
+ * U references when you have global asm
+ * variables, to avoid anyone else misusing
+ * them.
+ */
+ if (type == STT_REGISTER)
+ continue;
+ }
+
+ if (name_off >= strtablen) {
+ ELFDBG(elf, ".strtab is %"PRIu64" bytes, but .symtab entry %d wants to access offset %"PRIu32".\n", strtablen, i, name_off);
+ free(visited_versions);
+ free(symcrcs);
+ return -EINVAL;
+ }
+
+ name = elf_get_mem(elf, str_off + name_off);
+ if (name[0] == '\0') {
+ ELFDBG(elf, "empty symbol name at index %"PRIu64"\n", i);
+ continue;
+ }
+
+ slen += strlen(name) + 1;
+ count++;
+
+ idx = kmod_elf_crc_find(elf, versions, versionslen, name, &crc);
+ if (idx >= 0 && visited_versions != NULL)
+ visited_versions[idx] = 1;
+ symcrcs[i] = crc;
+ }
+
+ if (visited_versions != NULL) {
+ /* module_layout/struct_module are not visited, but needed */
+ ver_off = (const uint8_t *)versions - elf->memory;
+ for (i = 0; i < vercount; i++) {
+ if (visited_versions[i] == 0) {
+ const char *name;
+ name = elf_get_mem(elf, ver_off + i * verlen + crclen);
+ slen += strlen(name) + 1;
+
+ count++;
+ }
+ }
+ }
+
+ if (count == 0) {
+ free(visited_versions);
+ free(symcrcs);
+ return 0;
+ }
+
+ *array = a = malloc(sizeof(struct kmod_modversion) * count + slen);
+ if (*array == NULL) {
+ free(visited_versions);
+ free(symcrcs);
+ return -errno;
+ }
+
+ itr = (char *)(a + count);
+ count = 0;
+ str_off = (const uint8_t *)strtab - elf->memory;
+ sym_off = (const uint8_t *)symtab - elf->memory + symlen;
+ for (i = 1; i < symcount; i++, sym_off += symlen) {
+ const char *name;
+ uint64_t crc;
+ uint32_t name_off;
+ uint16_t secidx;
+ uint8_t info, bind;
+
+#define READV(field) \
+ elf_get_uint(elf, sym_off + offsetof(typeof(*s), field),\
+ sizeof(s->field))
+ if (elf->class & KMOD_ELF_32) {
+ Elf32_Sym *s;
+ name_off = READV(st_name);
+ secidx = READV(st_shndx);
+ info = READV(st_info);
+ } else {
+ Elf64_Sym *s;
+ name_off = READV(st_name);
+ secidx = READV(st_shndx);
+ info = READV(st_info);
+ }
+#undef READV
+ if (secidx != SHN_UNDEF)
+ continue;
+
+ if (handle_register_symbols) {
+ uint8_t type;
+ if (elf->class & KMOD_ELF_32)
+ type = ELF32_ST_TYPE(info);
+ else
+ type = ELF64_ST_TYPE(info);
+
+ /* Not really undefined: sparc gcc 3.3 creates
+ * U references when you have global asm
+ * variables, to avoid anyone else misusing
+ * them.
+ */
+ if (type == STT_REGISTER)
+ continue;
+ }
+
+ name = elf_get_mem(elf, str_off + name_off);
+ if (name[0] == '\0') {
+ ELFDBG(elf, "empty symbol name at index %"PRIu64"\n", i);
+ continue;
+ }
+
+ if (elf->class & KMOD_ELF_32)
+ bind = ELF32_ST_BIND(info);
+ else
+ bind = ELF64_ST_BIND(info);
+ if (bind == STB_WEAK)
+ bind = KMOD_SYMBOL_WEAK;
+ else
+ bind = KMOD_SYMBOL_UNDEF;
+
+ slen = strlen(name);
+ crc = symcrcs[i];
+
+ a[count].crc = crc;
+ a[count].bind = bind;
+ a[count].symbol = itr;
+ memcpy(itr, name, slen);
+ itr[slen] = '\0';
+ itr += slen + 1;
+
+ count++;
+ }
+
+ free(symcrcs);
+
+ if (visited_versions == NULL)
+ return count;
+
+ /* add unvisited (module_layout/struct_module) */
+ ver_off = (const uint8_t *)versions - elf->memory;
+ for (i = 0; i < vercount; i++) {
+ const char *name;
+ uint64_t crc;
+
+ if (visited_versions[i] != 0)
+ continue;
+
+ name = elf_get_mem(elf, ver_off + i * verlen + crclen);
+ slen = strlen(name);
+ crc = elf_get_uint(elf, ver_off + i * verlen, crclen);
+
+ a[count].crc = crc;
+ a[count].bind = KMOD_SYMBOL_UNDEF;
+ a[count].symbol = itr;
+ memcpy(itr, name, slen);
+ itr[slen] = '\0';
+ itr += slen + 1;
+
+ count++;
+ }
+ free(visited_versions);
+ return count;
+}
list = kmod_list_remove(list);
}
}
+
+struct kmod_module_dependency_symbol {
+ uint64_t crc;
+ uint8_t bind;
+ char symbol[];
+};
+
+static struct kmod_module_dependency_symbol *kmod_module_dependency_symbols_new(uint64_t crc, uint8_t bind, const char *symbol)
+{
+ struct kmod_module_dependency_symbol *mv;
+ size_t symbollen = strlen(symbol) + 1;
+
+ mv = malloc(sizeof(struct kmod_module_dependency_symbol) + symbollen);
+ if (mv == NULL)
+ return NULL;
+
+ mv->crc = crc;
+ mv->bind = bind;
+ memcpy(mv->symbol, symbol, symbollen);
+ return mv;
+}
+
+static void kmod_module_dependency_symbol_free(struct kmod_module_dependency_symbol *dependency_symbol)
+{
+ free(dependency_symbol);
+}
+
+/**
+ * kmod_module_get_dependency_symbols:
+ * @mod: kmod module
+ * @list: where to return list of module dependency_symbols. Use
+ * kmod_module_dependency_symbol_get_symbol() and
+ * kmod_module_dependency_symbol_get_crc(). Release this list with
+ * kmod_module_dependency_symbols_free_list()
+ *
+ * Get a list of entries in ELF section ".symtab" or "__ksymtab_strings".
+ *
+ * After use, free the @list by calling
+ * kmod_module_dependency_symbols_free_list().
+ *
+ * Returns: 0 on success or < 0 otherwise.
+ */
+KMOD_EXPORT int kmod_module_get_dependency_symbols(const struct kmod_module *mod, struct kmod_list **list)
+{
+ struct kmod_file *file;
+ struct kmod_elf *elf;
+ const char *path;
+ const void *mem;
+ struct kmod_modversion *symbols;
+ size_t size;
+ int i, count, ret = 0;
+
+ if (mod == NULL || list == NULL)
+ return -ENOENT;
+
+ assert(*list == NULL);
+
+ path = kmod_module_get_path(mod);
+ if (path == NULL)
+ return -ENOENT;
+
+ file = kmod_file_open(path);
+ if (file == NULL)
+ return -errno;
+
+ size = kmod_file_get_size(file);
+ mem = kmod_file_get_contents(file);
+
+ elf = kmod_elf_new(mem, size);
+ if (elf == NULL) {
+ ret = -errno;
+ goto elf_open_error;
+ }
+
+ count = kmod_elf_get_dependency_symbols(elf, &symbols);
+ if (count < 0) {
+ ret = count;
+ goto get_strings_error;
+ }
+
+ for (i = 0; i < count; i++) {
+ struct kmod_module_dependency_symbol *mv;
+ struct kmod_list *n;
+
+ mv = kmod_module_dependency_symbols_new(symbols[i].crc,
+ symbols[i].bind,
+ symbols[i].symbol);
+ if (mv == NULL) {
+ ret = -errno;
+ kmod_module_dependency_symbols_free_list(*list);
+ *list = NULL;
+ goto list_error;
+ }
+
+ n = kmod_list_append(*list, mv);
+ if (n != NULL)
+ *list = n;
+ else {
+ kmod_module_dependency_symbol_free(mv);
+ kmod_module_dependency_symbols_free_list(*list);
+ *list = NULL;
+ ret = -ENOMEM;
+ goto list_error;
+ }
+ }
+ ret = count;
+
+list_error:
+ free(symbols);
+get_strings_error:
+ kmod_elf_unref(elf);
+elf_open_error:
+ kmod_file_unref(file);
+
+ return ret;
+}
+
+/**
+ * kmod_module_dependency_symbol_get_symbol:
+ * @entry: a list entry representing a kmod module dependency_symbols
+ *
+ * Get the dependency symbol of a kmod module
+ *
+ * Returns: the symbol of this kmod module dependency_symbols on success or NULL
+ * on failure. The string is owned by the dependency_symbols, do not free it.
+ */
+KMOD_EXPORT const char *kmod_module_dependency_symbol_get_symbol(const struct kmod_list *entry)
+{
+ struct kmod_module_dependency_symbol *dependency_symbol;
+
+ if (entry == NULL)
+ return NULL;
+
+ dependency_symbol = entry->data;
+ return dependency_symbol->symbol;
+}
+
+/**
+ * kmod_module_dependency_symbol_get_crc:
+ * @entry: a list entry representing a kmod module dependency_symbol
+ *
+ * Get the crc of a kmod module dependency_symbol.
+ *
+ * Returns: the crc of this kmod module dependency_symbol on success or NULL on
+ * failure. The string is owned by the dependency_symbol, do not free it.
+ */
+KMOD_EXPORT uint64_t kmod_module_dependency_symbol_get_crc(const struct kmod_list *entry)
+{
+ struct kmod_module_dependency_symbol *dependency_symbol;
+
+ if (entry == NULL)
+ return 0;
+
+ dependency_symbol = entry->data;
+ return dependency_symbol->crc;
+}
+
+/**
+ * kmod_module_dependency_symbol_get_bind:
+ * @entry: a list entry representing a kmod module dependency_symbol
+ *
+ * Get the bind type of a kmod module dependency_symbol.
+ *
+ * Returns: the bind of this kmod module dependency_symbol on success
+ * or < 0 on failure.
+ */
+KMOD_EXPORT int kmod_module_dependency_symbol_get_bind(const struct kmod_list *entry)
+{
+ struct kmod_module_dependency_symbol *dependency_symbol;
+
+ if (entry == NULL)
+ return 0;
+
+ dependency_symbol = entry->data;
+ return dependency_symbol->bind;
+}
+
+/**
+ * kmod_module_dependency_symbols_free_list:
+ * @list: kmod module dependency_symbols list
+ *
+ * Release the resources taken by @list
+ */
+KMOD_EXPORT void kmod_module_dependency_symbols_free_list(struct kmod_list *list)
+{
+ while (list) {
+ kmod_module_dependency_symbol_free(list->data);
+ list = kmod_list_remove(list);
+ }
+}