From: Gustavo Sverzut Barbieri Date: Tue, 20 Dec 2011 13:54:53 +0000 (-0200) Subject: elf: implement kmod_module_get_dependency_symbols() X-Git-Tag: v3~84 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=674f8590e3aac67e0decd3da41407091c2d6a8d5;p=platform%2Fupstream%2Fkmod.git elf: implement kmod_module_get_dependency_symbols() Uses kmod_elf_get_dependency_symbols() that looks into ".symtab" for UNDEF symbols and matches the name from ".strtab" to "__versions" to get crc. Likely the public API should unify the symbol information getters and list release, they are almost the same. --- diff --git a/TODO b/TODO index d23a4d8..cbc5440 100644 --- a/TODO +++ b/TODO @@ -18,6 +18,24 @@ Features: * provide 1:1 compatibility with module-init-tools's modprobe - dump configuration +* provide depmod: + - add missing libkmod-elf.c functions: + - fetch_tables() + - deref_sym() (used by fetch_tables) + - add index writing functions to kmod-depmod.c + - 1:1 compatible kmod-depmod.c + +* review API, maybe unify all of these setters: + - kmod_module_version_get_symbol() + - kmod_module_version_get_crc() + - kmod_module_symbol_get_symbol() + - kmod_module_symbol_get_crc() + - kmod_module_dependency_symbol_get_symbol() + - kmod_module_dependency_symbol_get_crc() + - kmod_module_versions_free_list() + - kmod_module_symbols_free_list() + - kmod_module_dependency_symbols_free_list() + * provide modules.archive, a cache file with all modules compressed and a fast access. It's like a tar.gz, but with each entry compressed as opposed to the whole tar compressed, easy to pick diff --git a/libkmod/libkmod-elf.c b/libkmod/libkmod-elf.c index 8c39c06..8052e4e 100644 --- a/libkmod/libkmod-elf.c +++ b/libkmod/libkmod-elf.c @@ -34,6 +34,17 @@ enum kmod_elf_class { 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 @@ -57,6 +68,7 @@ struct kmod_elf { uint64_t offset; uint32_t nameoff; /* offset in strings itself */ } strings; + uint16_t machine; } header; }; @@ -296,7 +308,8 @@ struct kmod_elf *kmod_elf_new(const void *memory, off_t size) 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; @@ -473,14 +486,6 @@ int kmod_elf_get_modversions(const struct kmod_elf *elf, struct kmod_modversion 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) == @@ -533,7 +538,7 @@ int kmod_elf_get_modversions(const struct kmod_elf *elf, struct kmod_modversion 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); @@ -684,7 +689,7 @@ static int kmod_elf_get_symbols_symtab(const struct kmod_elf *elf, struct kmod_m 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'; @@ -696,7 +701,7 @@ static int kmod_elf_get_symbols_symtab(const struct kmod_elf *elf, struct kmod_m 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'; @@ -707,17 +712,17 @@ static int kmod_elf_get_symbols_symtab(const struct kmod_elf *elf, struct kmod_m 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; } } @@ -831,7 +836,7 @@ int kmod_elf_get_symbols(const struct kmod_elf *elf, struct kmod_modversion **ar 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); @@ -845,3 +850,315 @@ fallback: 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; +} diff --git a/libkmod/libkmod-module.c b/libkmod/libkmod-module.c index 089c61e..4d85b76 100644 --- a/libkmod/libkmod-module.c +++ b/libkmod/libkmod-module.c @@ -2061,3 +2061,193 @@ KMOD_EXPORT void kmod_module_symbols_free_list(struct kmod_list *list) 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); + } +} diff --git a/libkmod/libkmod-private.h b/libkmod/libkmod-private.h index 6da8eef..436ebf3 100644 --- a/libkmod/libkmod-private.h +++ b/libkmod/libkmod-private.h @@ -144,13 +144,7 @@ void kmod_file_unref(struct kmod_file *file) __attribute__((nonnull(1))); struct kmod_elf; struct kmod_modversion { uint64_t crc; - enum kmod_modversion_bind { - KMOD_MODVERSION_NONE = '\0', - KMOD_MODVERSION_LOCAL = 'L', - KMOD_MODVERSION_GLOBAL = 'G', - KMOD_MODVERSION_WEAK = 'W', - KMOD_MODVERSION_UNDEF = 'U' - } bind; + enum kmod_symbol_bind bind; char *symbol; }; @@ -160,6 +154,7 @@ const void *kmod_elf_get_memory(const struct kmod_elf *elf) __must_check __attri int kmod_elf_get_strings(const struct kmod_elf *elf, const char *section, char ***array) __must_check __attribute__((nonnull(1,2,3))); int kmod_elf_get_modversions(const struct kmod_elf *elf, struct kmod_modversion **array) __must_check __attribute__((nonnull(1,2))); int kmod_elf_get_symbols(const struct kmod_elf *elf, struct kmod_modversion **array) __must_check __attribute__((nonnull(1,2))); +int kmod_elf_get_dependency_symbols(const struct kmod_elf *elf, struct kmod_modversion **array) __must_check __attribute__((nonnull(1,2))); int kmod_elf_strip_section(struct kmod_elf *elf, const char *section) __must_check __attribute__((nonnull(1,2))); int kmod_elf_strip_vermagic(struct kmod_elf *elf) __must_check __attribute__((nonnull(1))); diff --git a/libkmod/libkmod.h b/libkmod/libkmod.h index cc3de08..ab47095 100644 --- a/libkmod/libkmod.h +++ b/libkmod/libkmod.h @@ -155,6 +155,20 @@ const char *kmod_module_symbol_get_symbol(const struct kmod_list *entry); uint64_t kmod_module_symbol_get_crc(const struct kmod_list *entry); void kmod_module_symbols_free_list(struct kmod_list *list); +enum kmod_symbol_bind { + KMOD_SYMBOL_NONE = '\0', + KMOD_SYMBOL_LOCAL = 'L', + KMOD_SYMBOL_GLOBAL = 'G', + KMOD_SYMBOL_WEAK = 'W', + KMOD_SYMBOL_UNDEF = 'U' +}; + +int kmod_module_get_dependency_symbols(const struct kmod_module *mod, struct kmod_list **list); +const char *kmod_module_dependency_symbol_get_symbol(const struct kmod_list *entry); +int kmod_module_dependency_symbol_get_bind(const struct kmod_list *entry); +uint64_t kmod_module_dependency_symbol_get_crc(const struct kmod_list *entry); +void kmod_module_dependency_symbols_free_list(struct kmod_list *list); + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/libkmod/libkmod.sym b/libkmod/libkmod.sym index bf93729..1a72dd0 100644 --- a/libkmod/libkmod.sym +++ b/libkmod/libkmod.sym @@ -67,6 +67,12 @@ global: kmod_module_symbol_get_symbol; kmod_module_symbol_get_crc; kmod_module_symbols_free_list; + + kmod_module_get_dependency_symbols; + kmod_module_dependency_symbol_get_symbol; + kmod_module_dependency_symbol_get_crc; + kmod_module_dependency_symbol_get_bind; + kmod_module_dependency_symbols_free_list; local: *; }; diff --git a/test/test-elf.c b/test/test-elf.c index 773ca6b..23dd1fd 100644 --- a/test/test-elf.c +++ b/test/test-elf.c @@ -80,6 +80,24 @@ int main(int argc, char *argv[]) kmod_module_symbols_free_list(list); } + list = NULL; + err = kmod_module_get_dependency_symbols(mod, &list); + if (err <= 0) + printf("no dependency symbols! (%s)\n", strerror(-err)); + else { + puts("dependency symbols:"); + kmod_list_foreach(l, list) { + const char *symbol; + uint8_t bind; + uint64_t crc; + symbol = kmod_module_dependency_symbol_get_symbol(l); + bind = kmod_module_dependency_symbol_get_bind(l); + crc = kmod_module_dependency_symbol_get_crc(l); + printf("\t%s %c: %#"PRIx64"\n", symbol, bind, crc); + } + kmod_module_dependency_symbols_free_list(list); + } + kmod_module_unref(mod); module_error: kmod_unref(ctx);