getmod a application of KGTP (https://kgtp.googlecode.com).

It can get Linux Kernel modules info from sysfs and output them as GDB
command add-symbol-file format.  Then you can call "source output_file_name"
inside GDB to let it load all the debug info of Linux Kernel modules.
It will help debug Linux Kernel modules with GDB with gdbstub such as
KGTP, KGDB, QEMU.

It was written by C and built with -static make it friendly to the embedded
system.
Following part is the help info of getmod:
Usage: ./getmod [option]
  -s dir    Add dir to module search directory list.
            This options can use more than once.
  -S        Add /lib/modules/3.2.0-57-generic/kernel to module search directory 
list.
  -r dir    Add dir to replace the directory.
            This options can use more than once.
  -n        No search the directory of the module
            file directory.
  -h        Display this information.

Signed-off-by: Hui Zhu <teawa...@gmail.com>
---
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -16,6 +16,7 @@ help:
        @echo '  vm         - misc vm tools'
        @echo '  x86_energy_perf_policy - Intel energy policy tool'
        @echo '  tmon       - thermal monitoring and tuning tool'
+       @echo '  getmod     - Output LKM info in GDB add-symbol-file format.'
        @echo ''
        @echo 'You can do:'
        @echo ' $$ make -C tools/ <tool>_install'
@@ -36,7 +37,7 @@ help:
 cpupower: FORCE
        $(call descend,power/$@)
-cgroup firewire guest usb virtio vm net: FORCE
+cgroup firewire guest usb virtio vm net getmod: FORCE
        $(call descend,$@)
liblk: FORCE
@@ -77,7 +78,7 @@ install: cgroup_install cpupower_install
 cpupower_clean:
        $(call descend,power/cpupower,clean)
-cgroup_clean firewire_clean lguest_clean usb_clean virtio_clean vm_clean net_clean:
+cgroup_clean firewire_clean lguest_clean usb_clean virtio_clean vm_clean 
net_clean getmod_clean:
        $(call descend,$(@:_clean=),clean)
liblk_clean:
@@ -97,6 +98,7 @@ tmon_clean:
clean: cgroup_clean cpupower_clean firewire_clean lguest_clean perf_clean \
                selftests_clean turbostat_clean usb_clean virtio_clean \
-               vm_clean net_clean x86_energy_perf_policy_clean tmon_clean
+               vm_clean net_clean x86_energy_perf_policy_clean tmon_clean \
+               getmod_clean
.PHONY: FORCE
--- /dev/null
+++ b/tools/getmod/Makefile
@@ -0,0 +1,11 @@
+prefix = /usr
+
+all : getmod
+
+getmod : CFLAGS = -Wall -O2 -static
+
+clean :
+       rm -rf getmod
+
+install :
+       install getmod $(prefix)/sbin/
--- /dev/null
+++ b/tools/getmod/getmod.c
@@ -0,0 +1,407 @@
+/*
+ * Output Linux Kernel modules info in GDB add-symbol-file format.
+ *
+ * Copyright(C) KGTP team (https://kgtp.googlecode.com), 2011-2013
+ * Licensed under the GNU General Public License, version 2.0 (GPLv2)
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <sys/utsname.h>
+#include <dirent.h>
+
+#define MOD_DIR                "/lib/modules"
+#define PROC_MOD       "/proc/modules"
+#define SYS_MOD                "/sys/module"
+
+#define SDIR_MAX       16
+
+int    sdir_number = 0;
+char   *sdir[SDIR_MAX];
+
+int    rdir_number = 0;
+int    rdir_current = 0;
+char   *rdir[SDIR_MAX];
+
+char   got_dir[512];
+
+int    no_search_mod = 0;
+
+int
+search_mod_1(char *dir, char *file)
+{
+       DIR             *dp;
+       struct dirent   *ptr;
+       int             ret = 0;
+
+       dp = opendir(dir);
+       if (!dp) {
+               fprintf(stderr, "#Cannot open %s: %s.\n", dir,
+                       strerror(errno));
+               ret = -1;
+               goto out;
+       }
+       while ((ptr = readdir(dp)) != NULL) {
+               char    cdir[512];
+
+               if (ptr->d_type == DT_DIR) {
+                       if (strcmp(ptr->d_name, ".") == 0)
+                               continue;
+                       if (strcmp(ptr->d_name, "..") == 0)
+                               continue;
+                       snprintf(cdir, 512, "%s/%s", dir, ptr->d_name);
+                       if (search_mod_1(cdir, file)) {
+                               ret = 1;
+                               break;
+                       }
+               } else {
+                       int     i;
+
+                       snprintf(cdir, 512, "%s", ptr->d_name);
+                       for (i = 0; i < strlen(cdir); i++) {
+                               if (cdir[i] == '_')
+                                       cdir[i] = '-';
+                       }
+                       if (strcmp(cdir, file) == 0) {
+                               snprintf(got_dir, 512, "%s/%s", dir,
+                                        ptr->d_name);
+                               ret = 1;
+                               break;
+                       }
+               }
+       }
+       closedir(dp);
+
+out:
+       return ret;
+}
+
+int
+search_mod(char *dir, char *file)
+{
+       int     ret;
+       char    tmp_dir[512];
+
+       ret = search_mod_1(dir, file);
+       if (ret <= 0)
+               return ret;
+
+       if (rdir_number == 0)
+               return 1;
+
+       if (rdir_current >= rdir_number)
+               rdir_current--;
+
+       strcpy(tmp_dir, got_dir);
+       snprintf(got_dir, 512, "%s%s", rdir[rdir_current],
+                tmp_dir + strlen(dir));
+
+       rdir_current++;
+       return 1;
+}
+
+void
+print_mod(char *name, char *addr)
+{
+       int             i;
+       char            mod_dir[256];
+       struct stat     sbuf;
+       char            file[64];
+       DIR             *dp;
+       struct dirent   *ptr;
+
+       snprintf(file, 64, "%s.ko", name);
+
+       if (no_search_mod)
+               printf("add-symbol-file %s %s", file, addr);
+       else {
+               for (i = 0; i < strlen(file); i++) {
+                       if (file[i] == '_')
+                               file[i] = '-';
+               }
+               for (i = 0; i < sdir_number; i++) {
+                       int     ret;
+
+                       if (sdir[i] == NULL)
+                               continue;
+                       ret = search_mod(sdir[i], file);
+                       if (ret < 0)
+                               sdir[i] = NULL;
+                       if (ret > 0)
+                               break;
+               }
+               if (i >= sdir_number) {
+                       for (i = 0; i < sdir_number; i++) {
+                               if (sdir[i])
+                                       break;
+                       }
+                       if (i >= sdir_number) {
+                               no_search_mod = 1;
+                               fprintf(stderr,
+                                       "#Cannot open any module search directories. 
 Auto open -n.\n");
+                       } else
+                               fprintf(stderr,
+                                       "#Cannot find file %s in the module search 
directories.  Just output the command with filename.\n",
+                                       file);
+                       printf("#add-symbol-file %s %s", file, addr);
+               } else
+                       printf("add-symbol-file %s %s", got_dir, addr);
+       }
+
+       snprintf(mod_dir, 256, "%s/%s/sections", SYS_MOD, name);
+       /* Check mod_dir.  */
+       if (stat(mod_dir, &sbuf) || !S_ISDIR(sbuf.st_mode)) {
+               fprintf(stderr, "%s is not a right directory.\n", mod_dir);
+               exit(-1);
+       }
+
+       dp = opendir(mod_dir);
+       if (!dp) {
+               fprintf(stderr, "Cannot open %s: %s.\n", mod_dir,
+                       strerror(errno));
+               exit(-errno);
+       }
+       while ((ptr = readdir(dp)) != NULL) {
+               if (ptr->d_type == DT_REG) {
+                       char    section_file_name[512];
+                       FILE    *fp;
+                       char    line[256];
+                       size_t  size;
+
+                       if (strcmp(ptr->d_name, ".text") == 0
+                           || strcmp(ptr->d_name, ".symtab") == 0
+                           || strcmp(ptr->d_name, ".strtab") == 0)
+                               continue;
+
+                       snprintf(section_file_name, 512, "%s/%s", mod_dir,
+                                ptr->d_name);
+                       fp = fopen(section_file_name, "r");
+                       if (!fp) {
+                               perror(section_file_name);
+                               exit(-errno);
+                       }
+                       if (fgets(line, 256, fp) == NULL) {
+                               perror(section_file_name);
+                               exit(-errno);
+                       }
+                       fclose(fp);
+                       size = strlen(line);
+                       if (size == 0) {
+                               fprintf(stderr, "format of %s is not right.\n",
+                                       section_file_name);
+                               exit(-errno);
+                       }
+                       if (line[size - 1] == '\n')
+                               line[size - 1] = '\0';
+                       printf(" -s %s %s", ptr->d_name, line);
+               }
+       }
+       closedir(dp);
+
+       printf("\n");
+}
+
+int
+check_sdir(char *dir)
+{
+       struct stat     sbuf;
+
+       if (stat(dir, &sbuf) || !S_ISDIR(sbuf.st_mode)) {
+               fprintf(stderr,
+                       "#%s is not a right directory.  Ignore it.\n",
+                       dir);
+               return 0;
+       }
+
+       return 1;
+}
+
+void
+add_sdir(char *dir)
+{
+       if (sdir_number < SDIR_MAX) {
+               if (check_sdir(dir)) {
+                       sdir[sdir_number] = dir;
+                       sdir_number++;
+               }
+       } else {
+               fprintf(stderr, "Set too much module search directory.");
+               exit(-1);
+       }
+}
+
+void
+add_rdir(char *dir)
+{
+       if (rdir_number < SDIR_MAX) {
+               rdir[rdir_number] = dir;
+               rdir_number++;
+       } else {
+               fprintf(stderr, "Set too much module search directory.");
+               exit(-1);
+       }
+}
+
+char *
+get_default_sdir(void)
+{
+       static int      need_init = 1;
+       static char     dir[512];
+
+       if (need_init) {
+               struct utsname  ubuf;
+
+               if (uname(&ubuf)) {
+                       fprintf(stderr, "Fail to get kernel version.");
+                       exit(-errno);
+               }
+               snprintf(dir, 512, "%s/%s/kernel", MOD_DIR, ubuf.release);
+       }
+
+       return dir;
+}
+
+void
+print_usage(char *arg)
+{
+       printf("Output LKM info in GDB add-symbol-file format.\n"
+              "Usage: %s [option]\n\n"
+
+              "  -s dir    Add dir to module search directory list.\n"
+              "            This options can use more than once.\n\n"
+
+              "  -S        Add %s to module search directory list.\n\n"
+
+              "  -r dir    Add dir to replace the directory.\n"
+              "            This options can use more than once.\n\n"
+
+              "  -n        No search the directory of the module\n"
+              "            file directory.\n\n"
+
+              "  -h        Display this information.\n",
+              arg, get_default_sdir());
+
+       exit(0);
+}
+
+int
+main(int argc, char *argv[], char *envp[])
+{
+       struct stat     sbuf;
+       FILE            *fp;
+       char            line[4096];
+       int             c;
+       int             default_sdir_isset = 0;
+
+       if (geteuid() != 0) {
+               fprintf(stderr,
+                       "Only root can get the right address of modules.\n");
+               exit(-1);
+       }
+
+       while ((c = getopt(argc, argv, "s:Sr:nh")) != -1) {
+               switch (c) {
+               case 's':
+                       add_sdir(optarg);
+                       break;
+               case 'S':
+                       if (!default_sdir_isset)
+                               add_sdir(get_default_sdir());
+                       break;
+               case 'r':
+                       add_rdir(optarg);
+                       break;
+               case 'n':
+                       no_search_mod = 1;
+                       break;
+               case 'h':
+               default:
+                       print_usage(argv[0]);
+                       break;
+               }
+       }
+
+       if (!no_search_mod && sdir_number == 0)
+               add_sdir(get_default_sdir());
+       if (!no_search_mod && sdir_number == 0) {
+               no_search_mod = 1;
+               fprintf(stderr,
+                       "#Cannot open any module search directories.  Auto open 
-n.\n");
+       }
+
+       /* Check PROC_MOD.  */
+       if (stat(PROC_MOD, &sbuf) || !S_ISREG(sbuf.st_mode)) {
+               fprintf(stderr, "%s is not right.\n", PROC_MOD);
+               exit(-1);
+       }
+
+       /* Get module name and address from PROC_MOD.  */
+       fp = fopen(PROC_MOD, "r");
+       if (!fp) {
+               perror(PROC_MOD);
+               exit(-errno);
+       }
+       while (fgets(line, 4096, fp)) {
+               int     i;
+               size_t  size = strlen(line);
+               int     is_not_digit = 0;
+
+               if (line[size - 1] != '\n') {
+                       fprintf(stderr,
+                               "line:%s is too big to parse by getmod.\n",
+                               line);
+                       exit(-1);
+               }
+
+               /* This part get the name.  */
+               for (i = 0; i < size; i++) {
+                       if (line[i] == ' ') {
+                               line[i] = '\0';
+                               break;
+                       }
+               }
+               /* Following part will get the addr.  */
+               if (i == size) {
+                       fprintf(stderr,
+                               "The format of \"%s\" is not right.\n", line);
+                       exit(-1);
+               }
+               if (line[size - 1] == '\n')
+                       line[size - 1] = '\0';
+               for (i = size - 2; i >= 0; i--) {
+                       if (line[i] == ' ') {
+                               if (is_not_digit) {
+                                       line[i] = '\0';
+                                       is_not_digit = 0;
+                               } else
+                                       break;
+                       } else {
+                               if (line[i] != 'x' && line[i] != 'X'
+                                   && (line[i] < '0' || line[i] > '9')
+                                   && (line[i] < 'a' || line[i] > 'f')
+                                   && (line[i] < 'A' || line[i] > 'F'))
+                                       is_not_digit = 1;
+                       }
+               }
+               if (i < 0) {
+                       fprintf(stderr, "The format of \"%s\" is not right.\n",
+                               line);
+                       exit(-1);
+               }
+               print_mod(line, line + i + 1);
+       }
+       if (ferror(fp)) {
+               perror(PROC_MOD);
+               exit(-errno);
+       }
+       fclose(fp);
+
+       return 0;
+}
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to