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

Needs testing, but should work.
diff --git a/libkmod/libkmod-module.c b/libkmod/libkmod-module.c
index 0330ffa..a586201 100644
--- a/libkmod/libkmod-module.c
+++ b/libkmod/libkmod-module.c
@@ -682,7 +682,7 @@
 	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 @@
 							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 @@
 	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 @@
 		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);
+	}
+}