From: Gustavo Sverzut Barbieri Date: Mon, 19 Dec 2011 23:23:13 +0000 (-0200) Subject: elf: add get_symbols() X-Git-Tag: v3~87 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=45e6db9c016b7802f2013b22ad63e7005f5683ef;p=thirdparty%2Fkmod.git elf: add get_symbols() Similar to module-init-tools load_symbols(), it will try .symtab and .strtab for symbols starting with __crc_, if they are found their crc is read from ELF's Elf_Sym::st_value. If not found, then it will fallback to __ksymtab_strings. --- diff --git a/libkmod/libkmod-elf.c b/libkmod/libkmod-elf.c index eeb17c6a..b4d6882a 100644 --- a/libkmod/libkmod-elf.c +++ b/libkmod/libkmod-elf.c @@ -627,3 +627,195 @@ int kmod_elf_strip_vermagic(struct kmod_elf *elf) ELFDBG(elf, "no vermagic found in .modinfo\n"); return -ENOENT; } + + +static int kmod_elf_get_symbols_symtab(const struct kmod_elf *elf, struct kmod_modversion **array) +{ + uint64_t i, last, size; + const void *buf; + const char *strings; + char *itr; + struct kmod_modversion *a; + int count, err; + + *array = NULL; + + err = kmod_elf_get_section(elf, "__ksymtab_strings", &buf, &size); + if (err < 0) + return err; + strings = buf; + if (strings == NULL || size == 0) + return 0; + + /* skip zero padding */ + while (strings[0] == '\0' && size > 1) { + strings++; + size--; + } + if (size <= 1) + return 0; + + last = 0; + for (i = 0, count = 0; i < size; i++) { + if (strings[i] == '\0') { + if (last == i) { + last = i + 1; + continue; + } + count++; + last = i + 1; + } + } + if (strings[i - 1] != '\0') + count++; + + *array = a = malloc(size + 1 + sizeof(struct kmod_modversion) * count); + if (*array == NULL) + return -errno; + + itr = (char *)(a + count); + last = 0; + for (i = 0, count = 0; i < size; i++) { + if (strings[i] == '\0') { + size_t slen = i - last; + if (last == i) { + last = i + 1; + continue; + } + a[count].crc = 0; + a[count].symbol = itr; + memcpy(itr, strings + last, slen); + itr[slen] = '\0'; + itr += slen + 1; + count++; + last = i + 1; + } + } + if (strings[i - 1] != '\0') { + size_t slen = i - last; + a[count].crc = 0; + a[count].symbol = itr; + memcpy(itr, strings + last, slen); + itr[slen] = '\0'; + itr += slen + 1; + count++; + } + + return count; +} + +/* array will be allocated with strings in a single malloc, just free *array */ +int kmod_elf_get_symbols(const struct kmod_elf *elf, struct kmod_modversion **array) +{ + static const char crc_str[] = "__crc_"; + static const size_t crc_strlen = sizeof(crc_str) - 1; + uint64_t strtablen, symtablen, str_off, sym_off; + const void *strtab, *symtab; + struct kmod_modversion *a; + char *itr; + size_t slen, symlen; + int i, count, symcount, err; + + err = kmod_elf_get_section(elf, ".strtab", &strtab, &strtablen); + if (err < 0) { + ELFDBG(elf, "no .strtab found.\n"); + goto fallback; + } + + err = kmod_elf_get_section(elf, ".symtab", &symtab, &symtablen); + if (err < 0) { + ELFDBG(elf, "no .symtab found.\n"); + goto fallback; + } + + 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); + goto fallback; + } + + symcount = symtablen / symlen; + count = 0; + slen = 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; + uint32_t name_off; + +#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); + } else { + Elf64_Sym *s; + name_off = READV(st_name); + } +#undef READV + if (name_off >= strtablen) { + ELFDBG(elf, ".strtab is %"PRIu64" bytes, but .symtab entry %d wants to access offset %"PRIu32".\n", strtablen, i, name_off); + goto fallback; + } + + name = elf_get_mem(elf, str_off + name_off); + + if (strncmp(name, crc_str, crc_strlen) != 0) + continue; + slen += strlen(name + crc_strlen) + 1; + count++; + } + + if (count == 0) + goto fallback; + + *array = a = malloc(sizeof(struct kmod_modversion) * count + slen); + if (*array == NULL) + 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; + uint32_t name_off; + uint64_t crc; + +#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); + crc = READV(st_value); + } else { + Elf64_Sym *s; + name_off = READV(st_name); + crc = READV(st_value); + } +#undef READV + name = elf_get_mem(elf, str_off + name_off); + if (strncmp(name, crc_str, crc_strlen) != 0) + continue; + name += crc_strlen; + + a[count].crc = crc; + a[count].symbol = itr; + slen = strlen(name); + memcpy(itr, name, slen); + itr[slen] = '\0'; + itr += slen + 1; + count++; + } + return count; + +fallback: + ELFDBG(elf, "Falling back to __ksymtab_strings!\n"); + return kmod_elf_get_symbols_symtab(elf, array); +} diff --git a/libkmod/libkmod-module.c b/libkmod/libkmod-module.c index 58787f69..089c61ed 100644 --- a/libkmod/libkmod-module.c +++ b/libkmod/libkmod-module.c @@ -1896,3 +1896,168 @@ KMOD_EXPORT void kmod_module_versions_free_list(struct kmod_list *list) list = kmod_list_remove(list); } } + +struct kmod_module_symbol { + uint64_t crc; + char symbol[]; +}; + +static struct kmod_module_symbol *kmod_module_symbols_new(uint64_t crc, const char *symbol) +{ + struct kmod_module_symbol *mv; + size_t symbollen = strlen(symbol) + 1; + + mv = malloc(sizeof(struct kmod_module_symbol) + symbollen); + if (mv == NULL) + return NULL; + + mv->crc = crc; + memcpy(mv->symbol, symbol, symbollen); + return mv; +} + +static void kmod_module_symbol_free(struct kmod_module_symbol *symbol) +{ + free(symbol); +} + +/** + * kmod_module_get_symbols: + * @mod: kmod module + * @list: where to return list of module symbols. Use + * kmod_module_symbols_get_symbol() and + * kmod_module_symbols_get_crc(). Release this list with + * kmod_module_symbols_unref_list() + * + * Get a list of entries in ELF section ".symtab" or "__ksymtab_strings". + * + * After use, free the @list by calling kmod_module_symbols_free_list(). + * + * Returns: 0 on success or < 0 otherwise. + */ +KMOD_EXPORT int kmod_module_get_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_symbols(elf, &symbols); + if (count < 0) { + ret = count; + goto get_strings_error; + } + + for (i = 0; i < count; i++) { + struct kmod_module_symbol *mv; + struct kmod_list *n; + + mv = kmod_module_symbols_new(symbols[i].crc, symbols[i].symbol); + if (mv == NULL) { + ret = -errno; + kmod_module_symbols_free_list(*list); + *list = NULL; + goto list_error; + } + + n = kmod_list_append(*list, mv); + if (n != NULL) + *list = n; + else { + kmod_module_symbol_free(mv); + kmod_module_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_symbols_get_symbol: + * @entry: a list entry representing a kmod module symbols + * + * Get the symbol of a kmod module symbols. + * + * Returns: the symbol of this kmod module symbols on success or NULL + * on failure. The string is owned by the symbols, do not free it. + */ +KMOD_EXPORT const char *kmod_module_symbol_get_symbol(const struct kmod_list *entry) +{ + struct kmod_module_symbol *symbol; + + if (entry == NULL) + return NULL; + + symbol = entry->data; + return symbol->symbol; +} + +/** + * kmod_module_symbol_get_crc: + * @entry: a list entry representing a kmod module symbol + * + * Get the crc of a kmod module symbol. + * + * Returns: the crc of this kmod module symbol on success or NULL on + * failure. The string is owned by the symbol, do not free it. + */ +KMOD_EXPORT uint64_t kmod_module_symbol_get_crc(const struct kmod_list *entry) +{ + struct kmod_module_symbol *symbol; + + if (entry == NULL) + return 0; + + symbol = entry->data; + return symbol->crc; +} + +/** + * kmod_module_symbols_free_list: + * @list: kmod module symbols list + * + * Release the resources taken by @list + */ +KMOD_EXPORT void kmod_module_symbols_free_list(struct kmod_list *list) +{ + while (list) { + kmod_module_symbol_free(list->data); + list = kmod_list_remove(list); + } +} diff --git a/libkmod/libkmod-private.h b/libkmod/libkmod-private.h index 4b0d84f6..d32b6d79 100644 --- a/libkmod/libkmod-private.h +++ b/libkmod/libkmod-private.h @@ -152,6 +152,7 @@ void kmod_elf_unref(struct kmod_elf *elf) __attribute__((nonnull(1))); const void *kmod_elf_get_memory(const struct kmod_elf *elf) __must_check __attribute__((nonnull(1))); 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_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 38b81cfd..cc3de084 100644 --- a/libkmod/libkmod.h +++ b/libkmod/libkmod.h @@ -150,6 +150,11 @@ const char *kmod_module_version_get_symbol(const struct kmod_list *entry); uint64_t kmod_module_version_get_crc(const struct kmod_list *entry); void kmod_module_versions_free_list(struct kmod_list *list); +int kmod_module_get_symbols(const struct kmod_module *mod, struct kmod_list **list); +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); + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/libkmod/libkmod.sym b/libkmod/libkmod.sym index c61ad7e0..bf937297 100644 --- a/libkmod/libkmod.sym +++ b/libkmod/libkmod.sym @@ -62,4 +62,11 @@ global: kmod_module_version_get_symbol; kmod_module_version_get_crc; kmod_module_versions_free_list; -} LIBKMOD_1; + + kmod_module_get_symbols; + kmod_module_symbol_get_symbol; + kmod_module_symbol_get_crc; + kmod_module_symbols_free_list; +local: + *; +}; diff --git a/test/test-lookup.c b/test/test-lookup.c index 620237eb..70cdb5c7 100644 --- a/test/test-lookup.c +++ b/test/test-lookup.c @@ -160,6 +160,20 @@ int main(int argc, char *argv[]) kmod_module_versions_free_list(pre); } + pre = NULL; + err = kmod_module_get_symbols(mod, &pre); + if (err > 0) { + puts("\t\tsymbols:"); + kmod_list_foreach(d, pre) { + const char *symbol; + uint64_t crc; + symbol = kmod_module_symbol_get_symbol(d); + crc = kmod_module_symbol_get_crc(d); + printf("\t\t\t%s: %#"PRIx64"\n", symbol, crc); + } + kmod_module_symbols_free_list(pre); + } + kmod_module_unref(mod); }