]> git.ipfire.org Git - thirdparty/kmod.git/commitdiff
elf: add get_symbols()
authorGustavo Sverzut Barbieri <barbieri@profusion.mobi>
Mon, 19 Dec 2011 23:23:13 +0000 (21:23 -0200)
committerLucas De Marchi <lucas.demarchi@profusion.mobi>
Sat, 24 Dec 2011 03:44:31 +0000 (01:44 -0200)
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.

libkmod/libkmod-elf.c
libkmod/libkmod-module.c
libkmod/libkmod-private.h
libkmod/libkmod.h
libkmod/libkmod.sym
test/test-lookup.c

index eeb17c6ac0002d39b038429c7562509a46c1948a..b4d6882a044e914473d12df879cc72f5134dcfa2 100644 (file)
@@ -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);
+}
index 58787f6921e0a03e72b7f08c041f75e1250e2909..089c61ed7f76db9295e06b07f6b36ca90f613fa8 100644 (file)
@@ -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);
+       }
+}
index 4b0d84f6a3e38c4c51692b2c65909ed57f71950f..d32b6d799ea5e3617e3423ca80a63cc90d0ec656 100644 (file)
@@ -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)));
 
index 38b81cfd44f19fab6b30d90d7f4a700096bd387e..cc3de084d1b1800974d82a17ec879b4269d27b74 100644 (file)
@@ -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
index c61ad7e010b7cf17d61ac6f054f452ffdc8725d1..bf937297df96469e148606a6a5bfd5d57d8afe9c 100644 (file)
@@ -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:
+        *;
+};
index 620237eb2beeb375a32e5b402871b4117f31e538..70cdb5c7881ff39e747ffbebf7bc09db39282afb 100644 (file)
@@ -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);
        }