ELF: initial support for modinfo and strip of modversions and vermagic.

Needs testing, but should work.
This commit is contained in:
Gustavo Sverzut Barbieri 2011-12-18 01:25:06 -02:00 committed by Lucas De Marchi
parent e5a7f6ac79
commit 708624a4eb
8 changed files with 1064 additions and 9 deletions

View File

@ -37,7 +37,8 @@ libkmod_libkmod_la_SOURCES =\
libkmod/libkmod-index.c \
libkmod/libkmod-index.h \
libkmod/libkmod-module.c \
libkmod/libkmod-file.c
libkmod/libkmod-file.c \
libkmod/libkmod-elf.c
EXTRA_DIST += libkmod/libkmod.sym
EXTRA_DIST += libkmod/COPYING libkmod/README

View File

@ -22,6 +22,7 @@ AC_PROG_CC_C99
AC_C_TYPEOF
AM_PROG_CC_C_O
AC_PROG_GCC_TRADITIONAL
AC_C_BIGENDIAN
required_private_libs=""

619
libkmod/libkmod-elf.c Normal file
View File

@ -0,0 +1,619 @@
/*
* libkmod - interface to kernel module operations
*
* Copyright (C) 2011 ProFUSION embedded systems
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <elf.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include "libkmod.h"
#include "libkmod-private.h"
enum kmod_elf_class {
KMOD_ELF_32 = (1 << 1),
KMOD_ELF_64 = (1 << 2),
KMOD_ELF_LSB = (1 << 3),
KMOD_ELF_MSB = (1 << 4)
};
#ifdef WORDS_BIGENDIAN
static const enum kmod_elf_class native_endianess = KMOD_ELF_MSB;
#else
static const enum kmod_elf_class native_endianess = KMOD_ELF_LSB;
#endif
struct kmod_elf {
const uint8_t *memory;
uint8_t *changed;
uint64_t size;
enum kmod_elf_class class;
struct kmod_elf_header {
struct {
uint64_t offset;
uint16_t count;
uint16_t entry_size;
} section;
struct {
uint16_t section; /* index of the strings section */
uint64_t size;
uint64_t offset;
uint32_t nameoff; /* offset in strings itself */
} strings;
} header;
};
//#define ENABLE_ELFDBG 1
#if defined(ENABLE_LOGGING) && defined(ENABLE_ELFDBG)
#define ELFDBG(elf, ...) \
_elf_dbg(elf, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__);
static inline void _elf_dbg(const struct kmod_elf *elf, const char *fname, unsigned line, const char *func, const char *fmt, ...)
{
va_list args;
fprintf(stderr, "ELFDBG-%d%c: %s:%u %s() ",
(elf->class & KMOD_ELF_32) ? 32 : 64,
(elf->class & KMOD_ELF_MSB) ? 'M' : 'L',
fname, line, func);
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
}
#else
#define ELFDBG(elf, ...)
#endif
static int elf_identify(const void *memory, uint64_t size)
{
const uint8_t *p = memory;
int class = 0;
if (size <= EI_NIDENT || memcmp(p, ELFMAG, SELFMAG) != 0)
return -ENOEXEC;
switch (p[EI_CLASS]) {
case ELFCLASS32:
if (size <= sizeof(Elf32_Ehdr))
return -EINVAL;
class |= KMOD_ELF_32;
break;
case ELFCLASS64:
if (size <= sizeof(Elf64_Ehdr))
return -EINVAL;
class |= KMOD_ELF_64;
break;
default:
return -EINVAL;
}
switch (p[EI_DATA]) {
case ELFDATA2LSB:
class |= KMOD_ELF_LSB;
break;
case ELFDATA2MSB:
class |= KMOD_ELF_MSB;
break;
default:
return -EINVAL;
}
return class;
}
static inline uint64_t elf_get_uint(const struct kmod_elf *elf, uint64_t offset, uint16_t size)
{
const uint8_t *p;
uint64_t ret = 0;
size_t i;
assert(size <= sizeof(uint64_t));
assert(offset + size <= elf->size);
if (offset + size > elf->size) {
ELFDBG(elf, "out of bounds: %"PRIu64" + %"PRIu16" = %"PRIu64"> %"PRIu64" (ELF size)\n",
offset, size, offset + size, elf->size);
return (uint64_t)-1;
}
p = elf->memory + offset;
if (elf->class & KMOD_ELF_MSB) {
for (i = 0; i < size; i++)
ret = (ret << 8) | p[size];
} else {
for (i = 1; i <= size; i++)
ret = (ret << 8) | p[size - i];
}
ELFDBG(elf, "size=%"PRIu16" offset=%"PRIu64" value=%"PRIu64"\n",
size, offset, ret);
return ret;
}
static inline int elf_set_uint(struct kmod_elf *elf, uint64_t offset, uint64_t size, uint64_t value)
{
uint8_t *p;
size_t i;
ELFDBG(elf, "size=%"PRIu16" offset=%"PRIu64" value=%"PRIu64" write memory=%p\n",
size, offset, value, elf->changed);
assert(size <= sizeof(uint64_t));
assert(offset + size <= elf->size);
if (offset + size > elf->size) {
ELFDBG(elf, "out of bounds: %"PRIu64" + %"PRIu16" = %"PRIu64"> %"PRIu64" (ELF size)\n",
offset, size, offset + size, elf->size);
return -1;
}
if (elf->changed == NULL) {
elf->changed = malloc(elf->size);
if (elf->changed == NULL)
return -errno;
memcpy(elf->changed, elf->memory, elf->size);
elf->memory = elf->changed;
ELFDBG(elf, "copied memory to allow writing.\n");
}
p = elf->changed + offset;
if (elf->class & KMOD_ELF_MSB) {
for (i = 1; i <= size; i++) {
p[size - i] = value & 0xff;
value = (value & 0xffffffffffffff00) >> 8;
}
} else {
for (i = 0; i < size; i++) {
p[i] = value & 0xff;
value = (value & 0xffffffffffffff00) >> 8;
}
}
return 0;
}
static inline const void *elf_get_mem(const struct kmod_elf *elf, uint64_t offset)
{
assert(offset < elf->size);
if (offset >= elf->size) {
ELFDBG(elf, "out-of-bounds: %"PRIu64" >= %"PRIu64" (ELF size)\n",
offset, elf->size);
return NULL;
}
return elf->memory + offset;
}
static inline const void *elf_get_section_header(const struct kmod_elf *elf, uint16_t idx)
{
assert(idx != SHN_UNDEF);
assert(idx < elf->header.section.count);
if (idx == SHN_UNDEF || idx >= elf->header.section.count) {
ELFDBG(elf, "invalid section number: %"PRIu16", last=%"PRIu16"\n",
idx, elf->header.section.count);
return NULL;
}
return elf_get_mem(elf, elf->header.section.offset +
idx * elf->header.section.entry_size);
}
static inline int elf_get_section_info(const struct kmod_elf *elf, uint16_t idx, uint64_t *offset, uint64_t *size, uint32_t *nameoff)
{
const uint8_t *p = elf_get_section_header(elf, idx);
uint64_t min_size, off = p - elf->memory;
if (p == NULL) {
ELFDBG(elf, "no section at %"PRIu16"\n", idx);
*offset = 0;
*size = 0;
*nameoff = 0;
return -EINVAL;
}
#define READV(field) \
elf_get_uint(elf, off + offsetof(typeof(*hdr), field), sizeof(hdr->field))
if (elf->class & KMOD_ELF_32) {
const Elf32_Shdr *hdr = (const Elf32_Shdr *)p;
*size = READV(sh_size);
*offset = READV(sh_offset);
*nameoff = READV(sh_name);
} else {
const Elf64_Shdr *hdr = (const Elf64_Shdr *)p;
*size = READV(sh_size);
*offset = READV(sh_offset);
*nameoff = READV(sh_name);
}
#undef READV
min_size = *offset + *size;
if (min_size >= elf->size) {
ELFDBG(elf, "out-of-bounds: %"PRIu64" >= %"PRIu64" (ELF size)\n",
min_size, elf->size);
return -EINVAL;
}
ELFDBG(elf, "section=%"PRIu16" is: offset=%"PRIu64" size=%"PRIu64" nameoff=%"PRIu32"\n",
idx, *offset, *size, *nameoff);
return 0;
}
static const char *elf_get_strings_section(const struct kmod_elf *elf, uint64_t *size)
{
*size = elf->header.strings.size;
return elf_get_mem(elf, elf->header.strings.offset);
}
struct kmod_elf *kmod_elf_new(const void *memory, off_t size)
{
struct kmod_elf *elf;
size_t hdr_size, shdr_size, min_size;
int class;
assert(sizeof(uint16_t) == sizeof(Elf32_Half));
assert(sizeof(uint16_t) == sizeof(Elf64_Half));
assert(sizeof(uint32_t) == sizeof(Elf32_Word));
assert(sizeof(uint32_t) == sizeof(Elf64_Word));
class = elf_identify(memory, size);
if (class < 0) {
errno = -class;
return NULL;
}
elf = malloc(sizeof(struct kmod_elf));
if (elf == NULL) {
return NULL;
}
elf->memory = memory;
elf->changed = NULL;
elf->size = size;
elf->class = class;
#define READV(field) \
elf_get_uint(elf, offsetof(typeof(*hdr), field), sizeof(hdr->field))
#define LOAD_HEADER \
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)
if (elf->class & KMOD_ELF_32) {
const Elf32_Ehdr *hdr = elf_get_mem(elf, 0);
LOAD_HEADER;
hdr_size = sizeof(Elf32_Ehdr);
shdr_size = sizeof(Elf32_Shdr);
} else {
const Elf64_Ehdr *hdr = elf_get_mem(elf, 0);
LOAD_HEADER;
hdr_size = sizeof(Elf64_Ehdr);
shdr_size = sizeof(Elf64_Shdr);
}
#undef LOAD_HEADER
#undef READV
ELFDBG(elf, "section: offset=%"PRIu64" count=%"PRIu16" entry_size=%"PRIu16" strings index=%"PRIu16"\n",
elf->header.section.offset,
elf->header.section.count,
elf->header.section.entry_size,
elf->header.strings.section);
if (elf->header.section.entry_size != shdr_size) {
ELFDBG(elf, "unexpected section entry size: %"PRIu16", expected %"PRIu16"\n",
elf->header.section.entry_size, shdr_size);
goto invalid;
}
min_size = hdr_size * shdr_size * elf->header.section.count;
if (min_size >= elf->size) {
ELFDBG(elf, "file is too short to hold sections\n");
goto invalid;
}
if (elf_get_section_info(elf, elf->header.strings.section,
&elf->header.strings.offset,
&elf->header.strings.size,
&elf->header.strings.nameoff) < 0) {
ELFDBG(elf, "could not get strings section\n");
goto invalid;
} else {
uint64_t slen;
const char *s = elf_get_strings_section(elf, &slen);
if (slen == 0 || s[slen - 1] != '\0') {
ELFDBG(elf, "strings section does not ends with \\0\n");
goto invalid;
}
}
return elf;
invalid:
free(elf);
errno = EINVAL;
return NULL;
}
void kmod_elf_unref(struct kmod_elf *elf)
{
free(elf->changed);
free(elf);
}
const void *kmod_elf_get_memory(const struct kmod_elf *elf)
{
return elf->memory;
}
static int kmod_elf_get_section(const struct kmod_elf *elf, const char *section, const void **buf, size_t *buf_size)
{
uint64_t nameslen;
const char *names = elf_get_strings_section(elf, &nameslen);
uint16_t i;
*buf = NULL;
*buf_size = 0;
for (i = 1; i < elf->header.section.count; i++) {
uint64_t off, size;
uint32_t nameoff;
const char *n;
int err = elf_get_section_info(elf, i, &off, &size, &nameoff);
if (err < 0)
continue;
if (nameoff >= nameslen)
continue;
n = names + nameoff;
if (!streq(section, n))
continue;
*buf = elf_get_mem(elf, off);
*buf_size = size;
return 0;
}
return -ENOENT;
}
/* array will be allocated with strings in a single malloc, just free *array */
int kmod_elf_get_strings(const struct kmod_elf *elf, const char *section, char ***array)
{
uint64_t i, last, size;
const void *buf;
const char *strings;
char *itr, **a;
int count, err;
*array = NULL;
err = kmod_elf_get_section(elf, section, &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;
for (i = 0, count = 0; i < size; i++) {
if (strings[i] == '\0')
count++;
}
if (strings[i - 1] != '\0')
count++;
*array = a = malloc(size + 1 + sizeof(char *) * 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;
a[count] = 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] = 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_modversions(const struct kmod_elf *elf, struct kmod_modversion **array)
{
uint64_t size, secsize, slen, off;
struct kmod_modversion *a;
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)];
};
*array = NULL;
err = kmod_elf_get_section(elf, "__versions", &buf, &size);
if (err < 0)
return err;
if (buf == NULL || size == 0)
return 0;
if (elf->class & KMOD_ELF_32)
secsize = sizeof(struct kmod_modversion32);
else
secsize = sizeof(struct kmod_modversion64);
if (size % secsize != 0)
return -EINVAL;
count = size / secsize;
off = (const uint8_t *)buf - elf->memory;
slen = 0;
for (i = 0; i < count; i++, off += secsize) {
const char *symbol;
if (elf->class & KMOD_ELF_32) {
struct kmod_modversion32 *mv;
symbol = elf_get_mem(elf, off + sizeof(mv->crc));
} else {
struct kmod_modversion64 *mv;
symbol = elf_get_mem(elf, off + sizeof(mv->crc));
}
slen += strlen(symbol) + 1;
}
*array = a = malloc(sizeof(struct kmod_modversion) * count + slen);
if (*array == NULL)
return -errno;
itr = (char *)(a + count);
off = (const uint8_t *)buf - elf->memory;
for (i = 0; i < count; i++, off += secsize) {
uint64_t crc;
const char *symbol;
size_t symbollen;
if (elf->class & KMOD_ELF_32) {
struct kmod_modversion32 *mv;
crc = elf_get_uint(elf, off, sizeof(mv->crc));
symbol = elf_get_mem(elf, off + sizeof(mv->crc));
} else {
struct kmod_modversion64 *mv;
crc = elf_get_uint(elf, off, sizeof(mv->crc));
symbol = elf_get_mem(elf, off + sizeof(mv->crc));
}
a[i].crc = crc;
a[i].symbol = itr;
symbollen = strlen(symbol) + 1;
memcpy(itr, symbol, symbollen);
itr += symbollen;
}
return count;
}
int kmod_elf_strip_section(struct kmod_elf *elf, const char *section)
{
uint64_t size, off;
const void *buf;
int err = kmod_elf_get_section(elf, section, &buf, &size);
if (err < 0)
return err;
off = (const uint8_t *)buf - elf->memory;
#define WRITEV(field, value) \
elf_set_uint(elf, off + offsetof(typeof(*hdr), field), sizeof(hdr->field), value)
if (elf->class & KMOD_ELF_32) {
const Elf32_Shdr *hdr = buf;
uint32_t val = ~(uint32_t)SHF_ALLOC;
return WRITEV(sh_flags, val);
} else {
const Elf64_Shdr *hdr = buf;
uint64_t val = ~(uint64_t)SHF_ALLOC;
return WRITEV(sh_flags, val);
}
#undef WRITEV
}
int kmod_elf_strip_vermagic(struct kmod_elf *elf)
{
uint64_t i, size;
const void *buf;
const char *strings;
int err;
err = kmod_elf_get_section(elf, ".modinfo", &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;
for (i = 0; i < size; i++) {
const char *s;
size_t off, len;
if (strings[i] == '\0')
continue;
if (i + 1 >= size)
continue;
s = strings + i;
len = sizeof("vermagic=") - 1;
if (i + len >= size)
continue;
if (strncmp(s, "vermagic=", len) != 0) {
i += strlen(s);
continue;
}
s += len;
off = (const uint8_t *)s - elf->memory;
if (elf->changed == NULL) {
elf->changed = malloc(elf->size);
if (elf->changed == NULL)
return -errno;
memcpy(elf->changed, elf->memory, elf->size);
elf->memory = elf->changed;
ELFDBG(elf, "copied memory to allow writing.\n");
}
len = strlen(s);
ELFDBG(elf, "clear .modinfo vermagic \"%s\" (%zd bytes)\n",
s, len);
memset(elf->changed + off, '\0', len);
return 0;
}
ELFDBG(elf, "no vermagic found in .modinfo\n");
return -ENOENT;
}

View File

@ -682,7 +682,7 @@ KMOD_EXPORT int kmod_module_remove_module(struct kmod_module *mod,
return 0;
}
extern long init_module(void *mem, unsigned long len, const char *args);
extern long init_module(const void *mem, unsigned long len, const char *args);
/**
* kmod_module_insert_module:
@ -701,23 +701,23 @@ KMOD_EXPORT int kmod_module_insert_module(struct kmod_module *mod,
const char *options)
{
int err;
void *mem;
const void *mem;
off_t size;
struct kmod_file *file;
struct kmod_elf *elf = NULL;
const char *path;
const char *args = options ? options : "";
if (mod == NULL)
return -ENOENT;
if (mod->path == NULL) {
path = kmod_module_get_path(mod);
if (path == NULL) {
ERR(mod->ctx, "Not supported to load a module by name yet\n");
return -ENOSYS;
}
if (flags != 0)
INFO(mod->ctx, "Flags are not implemented yet\n");
file = kmod_file_open(mod->path);
file = kmod_file_open(path);
if (file == NULL) {
err = -errno;
return err;
@ -726,10 +726,35 @@ KMOD_EXPORT int kmod_module_insert_module(struct kmod_module *mod,
size = kmod_file_get_size(file);
mem = kmod_file_get_contents(file);
if (flags & (KMOD_INSERT_FORCE_VERMAGIC | KMOD_INSERT_FORCE_MODVERSION)) {
elf = kmod_elf_new(mem, size);
if (elf == NULL) {
err = -errno;
goto elf_failed;
}
if (flags & KMOD_INSERT_FORCE_MODVERSION) {
err = kmod_elf_strip_section(elf, "__versions");
if (err < 0)
INFO(mod->ctx, "Failed to strip modversion: %s\n", strerror(-err));
}
if (flags & KMOD_INSERT_FORCE_VERMAGIC) {
err = kmod_elf_strip_vermagic(elf);
if (err < 0)
INFO(mod->ctx, "Failed to strip vermagic: %s\n", strerror(-err));
}
mem = kmod_elf_get_memory(elf);
}
err = init_module(mem, size, args);
if (err < 0)
ERR(mod->ctx, "Failed to insert module '%s'\n", mod->path);
ERR(mod->ctx, "Failed to insert module '%s'\n", path);
if (elf != NULL)
kmod_elf_unref(elf);
elf_failed:
kmod_file_unref(file);
return err;
@ -1506,3 +1531,351 @@ KMOD_EXPORT void kmod_module_section_free_list(struct kmod_list *list)
list = kmod_list_remove(list);
}
}
struct kmod_module_info {
char *key;
char value[];
};
static struct kmod_module_info *kmod_module_info_new(const char *key, size_t keylen, const char *value, size_t valuelen)
{
struct kmod_module_info *info;
info = malloc(sizeof(struct kmod_module_info) + keylen + valuelen + 2);
if (info == NULL)
return NULL;
info->key = (char *)info + sizeof(struct kmod_module_info)
+ valuelen + 1;
memcpy(info->key, key, keylen);
info->key[keylen] = '\0';
memcpy(info->value, value, valuelen);
info->value[valuelen] = '\0';
return info;
}
static void kmod_module_info_free(struct kmod_module_info *info)
{
free(info);
}
/**
* kmod_module_get_info:
* @mod: kmod module
* @list: where to return list of module information. Use
* kmod_module_info_get_key() and
* kmod_module_info_get_value(). Release this list with
* kmod_module_info_unref_list()
*
* Get a list of entries in ELF section ".modinfo", these contain
* alias, license, depends, vermagic and other keys with respective
* values.
*
* After use, free the @list by calling kmod_module_info_free_list().
*
* Returns: 0 on success or < 0 otherwise.
*/
KMOD_EXPORT int kmod_module_get_info(const struct kmod_module *mod, struct kmod_list **list)
{
struct kmod_file *file;
struct kmod_elf *elf;
const char *path;
const void *mem;
char **strings;
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_strings(elf, ".modinfo", &strings);
if (count < 0) {
ret = count;
goto get_strings_error;
}
for (i = 0; i < count; i++) {
struct kmod_module_info *info;
struct kmod_list *n;
const char *key, *value;
size_t keylen, valuelen;
key = strings[i];
value = strchr(key, '=');
if (value == NULL) {
keylen = strlen(key);
valuelen = 0;
} else {
keylen = value - key;
value++;
valuelen = strlen(value);
}
info = kmod_module_info_new(key, keylen, value, valuelen);
if (info == NULL) {
ret = -errno;
kmod_module_info_free_list(*list);
*list = NULL;
goto list_error;
}
n = kmod_list_append(*list, info);
if (n != NULL)
*list = n;
else {
kmod_module_info_free(info);
kmod_module_info_free_list(*list);
*list = NULL;
ret = -ENOMEM;
goto list_error;
}
}
ret = count;
list_error:
free(strings);
get_strings_error:
kmod_elf_unref(elf);
elf_open_error:
kmod_file_unref(file);
return ret;
}
/**
* kmod_module_info_get_key:
* @entry: a list entry representing a kmod module info
*
* Get the key of a kmod module info.
*
* Returns: the key of this kmod module info on success or NULL on
* failure. The string is owned by the info, do not free it.
*/
KMOD_EXPORT const char *kmod_module_info_get_key(const struct kmod_list *entry)
{
struct kmod_module_info *info;
if (entry == NULL)
return NULL;
info = entry->data;
return info->key;
}
/**
* kmod_module_info_get_value:
* @entry: a list entry representing a kmod module info
*
* Get the value of a kmod module info.
*
* Returns: the value of this kmod module info on success or NULL on
* failure. The string is owned by the info, do not free it.
*/
KMOD_EXPORT const char *kmod_module_info_get_value(const struct kmod_list *entry)
{
struct kmod_module_info *info;
if (entry == NULL)
return NULL;
info = entry->data;
return info->value;
}
/**
* kmod_module_info_free_list:
* @list: kmod module info list
*
* Release the resources taken by @list
*/
KMOD_EXPORT void kmod_module_info_free_list(struct kmod_list *list)
{
while (list) {
kmod_module_info_free(list->data);
list = kmod_list_remove(list);
}
}
struct kmod_module_version {
uint64_t crc;
char symbol[];
};
static struct kmod_module_version *kmod_module_versions_new(uint64_t crc, const char *symbol)
{
struct kmod_module_version *mv;
size_t symbollen = strlen(symbol) + 1;
mv = malloc(sizeof(struct kmod_module_version) + symbollen);
if (mv == NULL)
return NULL;
mv->crc = crc;
memcpy(mv->symbol, symbol, symbollen);
return mv;
}
static void kmod_module_version_free(struct kmod_module_version *version)
{
free(version);
}
/**
* kmod_module_get_versions:
* @mod: kmod module
* @list: where to return list of module versions. Use
* kmod_module_versions_get_symbol() and
* kmod_module_versions_get_crc(). Release this list with
* kmod_module_versions_unref_list()
*
* Get a list of entries in ELF section "__versions".
*
* After use, free the @list by calling kmod_module_versions_free_list().
*
* Returns: 0 on success or < 0 otherwise.
*/
KMOD_EXPORT int kmod_module_get_versions(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 *versions;
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_modversions(elf, &versions);
if (count < 0) {
ret = count;
goto get_strings_error;
}
for (i = 0; i < count; i++) {
struct kmod_module_version *mv;
struct kmod_list *n;
mv = kmod_module_versions_new(versions[i].crc, versions[i].symbol);
if (mv == NULL) {
ret = -errno;
kmod_module_versions_free_list(*list);
*list = NULL;
goto list_error;
}
n = kmod_list_append(*list, mv);
if (n != NULL)
*list = n;
else {
kmod_module_version_free(mv);
kmod_module_versions_free_list(*list);
*list = NULL;
ret = -ENOMEM;
goto list_error;
}
}
ret = count;
list_error:
free(versions);
get_strings_error:
kmod_elf_unref(elf);
elf_open_error:
kmod_file_unref(file);
return ret;
}
/**
* kmod_module_versions_get_symbol:
* @entry: a list entry representing a kmod module versions
*
* Get the symbol of a kmod module versions.
*
* Returns: the symbol of this kmod module versions on success or NULL
* on failure. The string is owned by the versions, do not free it.
*/
KMOD_EXPORT const char *kmod_module_version_get_symbol(const struct kmod_list *entry)
{
struct kmod_module_version *version;
if (entry == NULL)
return NULL;
version = entry->data;
return version->symbol;
}
/**
* kmod_module_version_get_crc:
* @entry: a list entry representing a kmod module version
*
* Get the crc of a kmod module version.
*
* Returns: the crc of this kmod module version on success or NULL on
* failure. The string is owned by the version, do not free it.
*/
KMOD_EXPORT uint64_t kmod_module_version_get_crc(const struct kmod_list *entry)
{
struct kmod_module_version *version;
if (entry == NULL)
return 0;
version = entry->data;
return version->crc;
}
/**
* kmod_module_versions_free_list:
* @list: kmod module versions list
*
* Release the resources taken by @list
*/
KMOD_EXPORT void kmod_module_versions_free_list(struct kmod_list *list)
{
while (list) {
kmod_module_version_free(list->data);
list = kmod_list_remove(list);
}
}

View File

@ -140,6 +140,20 @@ void *kmod_file_get_contents(const struct kmod_file *file) __must_check __attrib
off_t kmod_file_get_size(const struct kmod_file *file) __must_check __attribute__((nonnull(1)));
void kmod_file_unref(struct kmod_file *file) __attribute__((nonnull(1)));
/* libkmod-elf.c */
struct kmod_elf;
struct kmod_modversion {
uint64_t crc;
char *symbol;
};
struct kmod_elf *kmod_elf_new(const void *memory, off_t size) __must_check __attribute__((nonnull(1)));
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_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)));
/* util functions */
char *getline_wrapped(FILE *fp, unsigned int *linenum) __attribute__((nonnull(1)));

View File

@ -140,6 +140,16 @@ const char *kmod_module_get_remove_commands(const struct kmod_module *mod);
int kmod_module_get_softdeps(const struct kmod_module *mod, struct kmod_list **pre, struct kmod_list **post);
int kmod_module_get_info(const struct kmod_module *mod, struct kmod_list **list);
const char *kmod_module_info_get_key(const struct kmod_list *entry);
const char *kmod_module_info_get_value(const struct kmod_list *entry);
void kmod_module_info_free_list(struct kmod_list *list);
int kmod_module_get_versions(const struct kmod_module *mod, struct kmod_list **list);
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);
#ifdef __cplusplus
} /* extern "C" */
#endif

View File

@ -47,6 +47,16 @@ global:
kmod_module_get_install_commands;
kmod_module_get_remove_commands;
kmod_module_get_softdeps;
kmod_module_get_info;
kmod_module_info_get_key;
kmod_module_info_get_value;
kmod_module_info_free_list;
kmod_module_get_versions;
kmod_module_version_get_symbol;
kmod_module_version_get_crc;
kmod_module_versions_free_list;
local:
*;
};

View File

@ -133,6 +133,33 @@ int main(int argc, char *argv[])
}
}
pre = NULL;
err = kmod_module_get_info(mod, &pre);
if (err > 0) {
puts("\t\tmodinfo:");
kmod_list_foreach(d, pre) {
const char *key, *val;
key = kmod_module_info_get_key(d);
val = kmod_module_info_get_value(d);
printf("\t\t\t%s: %s\n", key, val ? val : "");
}
kmod_module_info_free_list(pre);
}
pre = NULL;
err = kmod_module_get_versions(mod, &pre);
if (err > 0) {
puts("\t\tmodversions:");
kmod_list_foreach(d, pre) {
const char *symbol;
uint64_t crc;
symbol = kmod_module_version_get_symbol(d);
crc = kmod_module_version_get_crc(d);
printf("\t\t\t%s: %#"PRIx64"\n", symbol, crc);
}
kmod_module_versions_free_list(pre);
}
kmod_module_unref(mod);
}