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:
parent
9bbb72df05
commit
45e6db9c01
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)));
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
*;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue