QEMU-Devel Archive mirror
 help / color / mirror / Atom feed
From: Gerd Hoffmann <kraxel@redhat.com>
To: qemu-devel@nongnu.org
Cc: "Kevin Wolf" <kwolf@redhat.com>, "Thomas Huth" <thuth@redhat.com>,
	berrange@redhat.com, qemu-block@nongnu.org,
	"Michael S. Tsirkin" <mst@redhat.com>,
	"Michael Roth" <michael.roth@amd.com>,
	"Cornelia Huck" <cohuck@redhat.com>, "Peter Lieven" <pl@kamp.de>,
	"Markus Armbruster" <armbru@redhat.com>,
	"Max Reitz" <mreitz@redhat.com>,
	"Halil Pasic" <pasic@linux.ibm.com>,
	"Marc-André Lureau" <marcandre.lureau@redhat.com>,
	qemu-s390x@nongnu.org, "Gerd Hoffmann" <kraxel@redhat.com>,
	"Ronnie Sahlberg" <ronniesahlberg@gmail.com>,
	"Samuel Thibault" <samuel.thibault@ens-lyon.org>,
	"Paolo Bonzini" <pbonzini@redhat.com>,
	"David Hildenbrand" <david@redhat.com>,
	"Richard Henderson" <richard.henderson@linaro.org>,
	"Eric Blake" <eblake@redhat.com>,
	"Christian Borntraeger" <borntraeger@de.ibm.com>
Subject: [PATCH v2 03/18] modules: add qemu-modinfo utility
Date: Thu, 10 Jun 2021 07:57:40 +0200	[thread overview]
Message-ID: <20210610055755.538119-4-kraxel@redhat.com> (raw)
In-Reply-To: <20210610055755.538119-1-kraxel@redhat.com>

Scan .modinfo sections of qemu modules,
write module metadata to modinfo.json.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 qemu-modinfo.c | 270 +++++++++++++++++++++++++++++++++++++++++++++++++
 meson.build    |  11 ++
 2 files changed, 281 insertions(+)
 create mode 100644 qemu-modinfo.c

diff --git a/qemu-modinfo.c b/qemu-modinfo.c
new file mode 100644
index 000000000000..611dbdb00683
--- /dev/null
+++ b/qemu-modinfo.c
@@ -0,0 +1,270 @@
+/*
+ * QEMU module parser
+ *
+ * read modules, find modinfo section, parse & store metadata.
+ *
+ * Copyright Red Hat, Inc. 2021
+ *
+ * Authors:
+ *     Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#include "qemu/osdep.h"
+#include "elf.h"
+#include <stdint.h>
+#include <dirent.h>
+
+#include "qapi/qapi-types-modules.h"
+#include "qapi/qapi-visit-modules.h"
+#include "qapi/qobject-output-visitor.h"
+#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qstring.h"
+
+#if INTPTR_MAX == INT32_MAX
+# define Elf_Ehdr Elf32_Ehdr
+# define Elf_Shdr Elf32_Shdr
+# define ELFCLASS ELFCLASS32
+#elif INTPTR_MAX == INT64_MAX
+# define Elf_Ehdr Elf64_Ehdr
+# define Elf_Shdr Elf64_Shdr
+# define ELFCLASS ELFCLASS64
+#else
+# error Huh?  Neither 32-bit nor 64-bit host.
+#endif
+
+static const char *moddir = CONFIG_QEMU_MODDIR;
+static const char *dsosuf = CONFIG_HOST_DSOSUF;
+
+static ModuleInfo *modinfo(const char *module, char *info, size_t size)
+{
+    ModuleInfo *modinfo;
+    strList *sl;
+    size_t pos = 0, len;
+
+    modinfo = g_new0(ModuleInfo, 1);
+    modinfo->name = g_strdup(module);
+
+    if (info) {
+        do {
+            if (strncmp(info + pos, "obj=", 4) == 0) {
+                sl = g_new0(strList, 1);
+                sl->value = g_strdup(info + pos + 4);
+                sl->next = modinfo->objs;
+                modinfo->objs = sl;
+                modinfo->has_objs = true;
+            } else if (strncmp(info + pos, "dep=", 4) == 0) {
+                sl = g_new0(strList, 1);
+                sl->value = g_strdup(info + pos + 4);
+                sl->next = modinfo->deps;
+                modinfo->deps = sl;
+                modinfo->has_deps = true;
+            } else if (strncmp(info + pos, "arch=", 5) == 0) {
+                modinfo->arch = g_strdup(info + pos + 5);
+                modinfo->has_arch = true;
+            } else if (strncmp(info + pos, "opts=", 5) == 0) {
+                modinfo->opts = g_strdup(info + pos + 5);
+                modinfo->has_opts = true;
+            } else {
+                fprintf(stderr, "unknown tag: %s\n", info + pos);
+                exit(1);
+            }
+            len = strlen(info + pos) + 1;
+            pos += len;
+        } while (pos < size);
+    }
+
+    return modinfo;
+}
+
+static void elf_read_section_hdr(FILE *fp, Elf_Ehdr *ehdr,
+                                 int section, Elf_Shdr *shdr)
+{
+    size_t pos, len;
+    int ret;
+
+    pos = ehdr->e_shoff + section * ehdr->e_shentsize;
+    len = MIN(ehdr->e_shentsize, sizeof(*shdr));
+
+    ret = fseek(fp, pos, SEEK_SET);
+    if (ret != 0) {
+        fprintf(stderr, "seek error\n");
+        exit(1);
+    }
+
+    memset(shdr, 0, sizeof(*shdr));
+    ret = fread(shdr, len, 1, fp);
+    if (ret != 1) {
+        fprintf(stderr, "read error\n");
+        exit(1);
+    }
+}
+
+static void *elf_read_section(FILE *fp, Elf_Ehdr *ehdr,
+                              int section, size_t *size)
+{
+    Elf_Shdr shdr;
+    void *data;
+    int ret;
+
+    elf_read_section_hdr(fp, ehdr, section, &shdr);
+    if (shdr.sh_offset && shdr.sh_size) {
+        ret = fseek(fp, shdr.sh_offset, SEEK_SET);
+        if (ret != 0) {
+            fprintf(stderr, "seek error\n");
+            exit(1);
+        }
+
+        data = g_malloc(shdr.sh_size);
+        ret = fread(data, shdr.sh_size, 1, fp);
+        if (ret != 1) {
+            fprintf(stderr, "read error\n");
+            exit(1);
+        }
+        *size = shdr.sh_size;
+    } else {
+        data = NULL;
+        *size = 0;
+    }
+    return data;
+}
+
+static ModuleInfo *elf_parse_module(const char *module,
+                                    const char *filename)
+{
+    Elf_Ehdr ehdr;
+    Elf_Shdr shdr;
+    FILE *fp;
+    int ret, i;
+    char *str;
+    size_t str_size;
+    char *info;
+    size_t info_size;
+
+    fp = fopen(filename, "r");
+    if (NULL == fp) {
+        fprintf(stderr, "open %s: %s\n", filename, strerror(errno));
+        exit(1);
+    }
+
+    ret = fread(&ehdr, sizeof(ehdr), 1, fp);
+    if (ret != 1) {
+        fprintf(stderr, "read error (%s)\n", filename);
+        exit(1);
+    }
+
+    if (ehdr.e_ident[EI_MAG0] != ELFMAG0 ||
+        ehdr.e_ident[EI_MAG1] != ELFMAG1 ||
+        ehdr.e_ident[EI_MAG2] != ELFMAG2 ||
+        ehdr.e_ident[EI_MAG3] != ELFMAG3) {
+        fprintf(stderr, "not an elf file (%s)\n", filename);
+        exit(1);
+    }
+    if (ehdr.e_ident[EI_CLASS] != ELFCLASS64) {
+        fprintf(stderr, "elf class mismatch (%s)\n", filename);
+        exit(1);
+    }
+    if (ehdr.e_shoff == 0) {
+        fprintf(stderr, "no section header (%s)\n", filename);
+        exit(1);
+    }
+
+    /* read string table */
+    if (ehdr.e_shstrndx == 0) {
+        fprintf(stderr, "no section strings (%s)\n", filename);
+        exit(1);
+    }
+    str = elf_read_section(fp, &ehdr, ehdr.e_shstrndx, &str_size);
+    if (NULL == str) {
+        fprintf(stderr, "no section strings (%s)\n", filename);
+        exit(1);
+    }
+
+    /* find and read modinfo section */
+    info = NULL;
+    for (i = 0; i < ehdr.e_shnum; i++) {
+        elf_read_section_hdr(fp, &ehdr, i, &shdr);
+        if (!shdr.sh_name) {
+            continue;
+        }
+        if (strcmp(str + shdr.sh_name, ".modinfo") == 0) {
+            info = elf_read_section(fp, &ehdr, i, &info_size);
+        }
+    }
+    fclose(fp);
+
+    return modinfo(module, info, info_size);
+}
+
+int main(int argc, char **argv)
+{
+    DIR *dir;
+    FILE *fp;
+    ModuleInfo *modinfo;
+    ModuleInfoList *modlist;
+    Modules *modules;
+    Visitor *v;
+    QObject *obj;
+    Error *errp = NULL;
+    struct dirent *ent;
+    char *ext, *file, *name;
+    GString *gjson;
+    QString *qjson;
+    const char *json;
+
+    if (argc > 1) {
+        moddir = argv[1];
+    }
+
+    dir = opendir(moddir);
+    if (dir == NULL) {
+        fprintf(stderr, "opendir(%s): %s\n", moddir, strerror(errno));
+        exit(1);
+    }
+
+    modules = g_new0(Modules, 1);
+    while (NULL != (ent = readdir(dir))) {
+        ext = strrchr(ent->d_name, '.');
+        if (!ext) {
+            continue;
+        }
+        if (strcmp(ext, dsosuf) != 0) {
+            continue;
+        }
+
+        name = g_strndup(ent->d_name, ext - ent->d_name);
+        file = g_strdup_printf("%s/%s", moddir, ent->d_name);
+        modinfo = elf_parse_module(name, file);
+        g_free(file);
+        g_free(name);
+
+        modlist = g_new0(ModuleInfoList, 1);
+        modlist->value = modinfo;
+        modlist->next = modules->list;
+        modules->list = modlist;
+    }
+    closedir(dir);
+
+    v = qobject_output_visitor_new(&obj);
+    visit_type_Modules(v, NULL, &modules, &errp);
+    visit_complete(v, &obj);
+    visit_free(v);
+
+    gjson = qobject_to_json(obj);
+    qjson = qstring_from_gstring(gjson);
+    json = qstring_get_str(qjson);
+
+    file = g_strdup_printf("%s/modinfo.json", moddir);
+    fp = fopen(file, "w");
+    if (fp == NULL) {
+        fprintf(stderr, "open(%s): %s\n", file, strerror(errno));
+        exit(1);
+    }
+    fprintf(fp, "%s", json);
+    fclose(fp);
+
+    printf("%s written\n", file);
+    g_free(file);
+    return 0;
+}
diff --git a/meson.build b/meson.build
index d2a9ce91f556..9823c5889140 100644
--- a/meson.build
+++ b/meson.build
@@ -2380,6 +2380,17 @@ if xkbcommon.found()
                            dependencies: [qemuutil, xkbcommon], install: have_tools)
 endif
 
+if config_host.has_key('CONFIG_MODULES')
+   qemu_modinfo = executable('qemu-modinfo', files('qemu-modinfo.c') + genh,
+                             dependencies: [glib, qemuutil], install: have_tools)
+   custom_target('modinfo.json',
+                 input: [ softmmu_mods, block_mods ],
+                 output: 'modinfo.json',
+                 install: true,
+                 install_dir: qemu_moddir,
+                 command: [ qemu_modinfo, '.' ])
+endif
+
 if have_tools
   qemu_img = executable('qemu-img', [files('qemu-img.c'), hxdep],
              dependencies: [authz, block, crypto, io, qom, qemuutil], install: true)
-- 
2.31.1



  parent reply	other threads:[~2021-06-10  6:05 UTC|newest]

Thread overview: 50+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-06-10  5:57 [PATCH v2 00/18] modules: add metadata database Gerd Hoffmann
2021-06-10  5:57 ` [PATCH v2 01/18] modules: add metadata macros, add qxl module annotations Gerd Hoffmann
2021-06-10  5:57 ` [PATCH v2 02/18] qapi: add ModuleInfo schema Gerd Hoffmann
2021-06-14  7:48   ` Markus Armbruster
2021-06-14 15:21     ` Gerd Hoffmann
2021-06-14 16:53       ` Markus Armbruster
2021-06-10  5:57 ` Gerd Hoffmann [this message]
2021-06-10 13:04   ` [PATCH v2 03/18] modules: add qemu-modinfo utility Gerd Hoffmann
2021-06-10 13:13     ` Daniel P. Berrangé
2021-06-14  8:34       ` Paolo Bonzini
2021-06-14 14:36         ` Paolo Bonzini
2021-06-15  4:49           ` Gerd Hoffmann
2021-06-15  7:56             ` Gerd Hoffmann
2021-06-15 13:07               ` Gerd Hoffmann
2021-06-15 13:35                 ` Paolo Bonzini
2021-06-16  9:16                   ` Gerd Hoffmann
2021-06-16 10:53                     ` Paolo Bonzini
2021-06-14 15:01         ` Gerd Hoffmann
2021-06-14 15:08           ` Daniel P. Berrangé
2021-06-15  4:54             ` Gerd Hoffmann
2021-06-15  9:27               ` Daniel P. Berrangé
2021-06-10  5:57 ` [PATCH v2 04/18] modules: add virtio-gpu module annotations Gerd Hoffmann
2021-06-10  5:57 ` [PATCH v2 05/18] modules: add chardev " Gerd Hoffmann
2021-06-10  5:57 ` [PATCH v2 06/18] modules: add audio " Gerd Hoffmann
2021-06-10  5:57 ` [PATCH v2 07/18] modules: add usb-redir " Gerd Hoffmann
2021-06-10  5:57 ` [PATCH v2 08/18] modules: add ccid " Gerd Hoffmann
2021-06-10  5:57 ` [PATCH v2 09/18] modules: add ui " Gerd Hoffmann
2021-06-10  5:57 ` [PATCH v2 10/18] modules: add s390x " Gerd Hoffmann
2021-06-10  5:57 ` [PATCH v2 11/18] modules: add block " Gerd Hoffmann
2021-06-10  5:57 ` [PATCH v2 12/18] modules: add module_load_path_init helper Gerd Hoffmann
2021-06-10  5:57 ` [PATCH v2 13/18] modules: load modinfo.json Gerd Hoffmann
2021-06-10  5:57 ` [PATCH v2 14/18] modules: use modinfo for dependencies Gerd Hoffmann
2021-06-10  5:57 ` [PATCH v2 15/18] modules: use modinfo for qom load Gerd Hoffmann
2021-06-10  5:57 ` [PATCH v2 16/18] modules: use modinfo for qemu opts load Gerd Hoffmann
2021-06-10  5:57 ` [PATCH v2 17/18] modules: check arch and block load on mismatch Gerd Hoffmann
2021-06-10 12:35   ` Daniel P. Berrangé
2021-06-10 12:57     ` Gerd Hoffmann
2021-06-10 13:06       ` Daniel P. Berrangé
2021-06-10 14:03         ` Gerd Hoffmann
2021-06-14 11:23           ` Claudio Fontana
2021-06-14 13:44             ` Gerd Hoffmann
2021-06-14 13:52               ` Daniel P. Berrangé
2021-06-14 14:10                 ` Gerd Hoffmann
2021-06-14 11:19     ` Claudio Fontana
2021-06-10  5:57 ` [PATCH v2 18/18] [fixup] module_load_modinfo Gerd Hoffmann
2021-06-10  6:24   ` Gerd Hoffmann
2021-06-10  8:32 ` [PATCH v2 00/18] modules: add metadata database Claudio Fontana
2021-06-10  9:54   ` Gerd Hoffmann
2021-06-14 11:31     ` Claudio Fontana
2021-06-14 14:07       ` Gerd Hoffmann

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210610055755.538119-4-kraxel@redhat.com \
    --to=kraxel@redhat.com \
    --cc=armbru@redhat.com \
    --cc=berrange@redhat.com \
    --cc=borntraeger@de.ibm.com \
    --cc=cohuck@redhat.com \
    --cc=david@redhat.com \
    --cc=eblake@redhat.com \
    --cc=kwolf@redhat.com \
    --cc=marcandre.lureau@redhat.com \
    --cc=michael.roth@amd.com \
    --cc=mreitz@redhat.com \
    --cc=mst@redhat.com \
    --cc=pasic@linux.ibm.com \
    --cc=pbonzini@redhat.com \
    --cc=pl@kamp.de \
    --cc=qemu-block@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    --cc=qemu-s390x@nongnu.org \
    --cc=richard.henderson@linaro.org \
    --cc=ronniesahlberg@gmail.com \
    --cc=samuel.thibault@ens-lyon.org \
    --cc=thuth@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).