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.
This commit is contained in:
Gustavo Sverzut Barbieri 2011-12-19 21:23:13 -02:00 committed by Lucas De Marchi
parent 9bbb72df05
commit 45e6db9c01
6 changed files with 385 additions and 1 deletions

View 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);
}

View 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);
}
}

View 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)));

View 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

View 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:
*;
};

View 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);
}