Index: gdb/symfile.c
===================================================================
RCS file: /cvs/src/src/gdb/symfile.c,v
retrieving revision 1.246
diff -u -r1.246 symfile.c
--- gdb/symfile.c	18 Sep 2009 17:33:51 -0000	1.246
+++ gdb/symfile.c	24 Sep 2009 19:27:58 -0000
@@ -2068,6 +2068,194 @@
   ui_out_text (uiout, ".\n");
 }
 
+/*
+  Read symboles of a module file(.ko) of Linux kernel
+*/
+/* taken from kernel source code */
+struct list_head {
+  CORE_ADDR next;
+  CORE_ADDR prev;
+};
+struct module;
+typedef unsigned int __kernel_mode_t;
+typedef __kernel_mode_t mode_t;
+
+struct attribute {
+  const char *name;
+  struct module *owner;
+  mode_t mode;
+};
+
+struct module_attribute {
+  struct attribute attr;
+  ssize_t (*show)(struct module_attribute *, struct module *, char *);
+  ssize_t (*store)(struct module_attribute *, struct module *,
+		   const char *, size_t count);
+  void (*setup)(struct module *, const char *);
+  int (*test)(struct module *);
+  void (*free)(struct module *);
+};
+
+struct attribute_group {
+	const char		*name;
+	mode_t			(*is_visible)();
+	struct attribute	**attrs;
+};
+
+struct module_sect_attr
+{
+	struct module_attribute mattr;
+	char *name;
+	unsigned long address;
+};
+
+struct module_sect_attrs
+{
+	struct attribute_group grp;
+	unsigned int nsections;
+	struct module_sect_attr attrs[0];
+};
+
+#define MODULE_NAME_LEN (64 - sizeof(CORE_ADDR))
+struct module
+{
+  CORE_ADDR unused_state;
+  struct list_head list;
+  char name[MODULE_NAME_LEN];
+};
+#define OFFSET(structure, field) ((CORE_ADDR)(&((struct structure *)0)->field))
+
+static char *
+add_addr_on_linux_module (char *module_name)
+{
+  char *module_base_name, *s;
+  struct expression *expr;
+  struct value *val;
+  int offset_sect_attrs, ret, n;
+  struct symbol *modules;
+  struct module module;
+  CORE_ADDR module_addr, _msas;
+  struct module_sect_attrs msas;
+  struct module_sect_attr *kmsas;
+  char section_name[MODULE_NAME_LEN], *arg_str;
+
+  /* Get base name of module.  */
+  s = strrchr (module_name, '/');
+  if (s == NULL) /* not found  */
+    s = module_name;
+  else
+    s++;
+  module_base_name = xmalloc (strlen(s) + 1);
+  if (module_base_name == NULL)
+    {
+      warning ("can't allocate module_base_name");
+      return NULL;
+    }
+  make_cleanup (xfree, module_base_name);
+  strcpy(module_base_name, s);
+  /* Discard .ko  */
+  s = strchr(module_base_name, '.');
+  if (s != NULL)
+    *s = '\0';
+
+  /* Find whether the module is loaded.  */
+  /* Check to see if Linux is the supported version.  */
+  expr = parse_expression ("&((struct module *)0)->sect_attrs");
+  val = evaluate_expression (expr);
+  offset_sect_attrs = unpack_long (value_type(val), value_contents_all(val));
+  expr = parse_expression ("&((struct module_sect_attrs *)0)->attrs[1]");
+  val = evaluate_expression (expr);
+  if (unpack_long (value_type(val), value_contents_all(val)) !=
+	     (long)&((struct module_sect_attrs *)0)->attrs[1]) {
+    warning ("your target kernel has difference struct module_sect_attrs");
+    return NULL;
+  }
+
+  /* Modules are in the linked list pointed by "modules"  */
+  modules = lookup_symbol ("modules", NULL, VAR_DOMAIN, NULL);
+  if (modules == NULL) {
+    warning ("can't find the address of modules");
+    return NULL;
+  }
+  module_addr = SYMBOL_VALUE_ADDRESS(modules) - OFFSET(module, list.next);
+  do {
+    ret = target_read_memory(module_addr,
+		       (gdb_byte *)&module, sizeof(module));
+    if (ret != 0)
+      {
+	warning("can't read module");
+	return NULL;
+      }
+    if (strcmp(module_base_name, module.name) == 0) /* found the module */
+      break;
+    module_addr = module.list.next - OFFSET(module, list.next);
+  } while (module_addr + OFFSET(module, list.next) !=
+	   SYMBOL_VALUE_ADDRESS(modules));
+  if (module_addr + OFFSET(module, list.next) ==
+      SYMBOL_VALUE_ADDRESS(modules)) /* not found the module */
+    {
+      warning("can't find %s in the linked list of Linux modules",
+	      module_base_name);
+      return NULL;
+    }
+
+  ret = target_read_memory (module_addr + offset_sect_attrs,
+			    (gdb_byte *)&_msas, sizeof(_msas));
+  if (ret != 0)
+    {
+      warning("can't read _msas on %s", module.name);
+      return NULL;
+    }
+  ret = target_read_memory (_msas,
+			    (gdb_byte *)&msas, sizeof(msas));
+  if (ret != 0)
+    {
+      warning("can't read msas on %s", module.name);
+      return NULL;
+    }
+  kmsas = xmalloc (sizeof(struct module_sect_attr) * msas.nsections);
+  if (kmsas == NULL)
+    {
+      warning ("can't allocate kmsas");
+      return NULL;
+    }
+  make_cleanup (xfree, kmsas);
+  ret = target_read_memory (_msas + OFFSET(module_sect_attrs, attrs[0]),
+			    (char *)kmsas,
+			    sizeof(*kmsas) * msas.nsections);
+  if (ret != 0)
+    {
+      warning ("can't read kmsas");
+      return NULL;
+    }
+  /* Look for .text section  */
+  for (n = 0; n < msas.nsections; n++, kmsas++)
+    {
+      ret = target_read_memory((CORE_ADDR)kmsas->name,
+			       section_name, MODULE_NAME_LEN);
+      if (ret != 0)
+	{
+	  warning ("can't read kmsas->name");
+	  return NULL;
+	}
+      if (strcmp(section_name, ".text") == 0)
+	break;
+    }
+  if (n < msas.nsections) /* found .text section */
+    {
+      /* " 0x%016lx" + terminating '\0' */
+      arg_str = xmalloc (strlen(module_name) + 1 + 2 + 16 + 1);
+      if (arg_str == NULL) {
+	warning ("can't allocate arg_str");
+	return NULL;
+      }
+      sprintf (arg_str, "%s 0x%016lx", module_name, kmsas->address);
+      /* No need to free arg_str. make_cleanup_freeargv() takes care. */
+    }
+
+  return arg_str;
+}
+
 /* This function allows the addition of incrementally linked object files.
    It does not modify any state in the target, only in the debugger.  */
 /* Note: ezannoni 2000-04-13 This function/command used to have a
@@ -2110,12 +2298,25 @@
 
   dont_repeat ();
 
-  if (args == NULL)
-    error (_("add-symbol-file takes a file name and an address"));
+  if (args == NULL || *args == '\0')
+    error (_("Usage: add-symbol-file FILE [ADDR [-s <SECT> <SECT_ADDR> -s <SECT> <SECT_ADDR> ...] ]"));
 
   argv = gdb_buildargv (args);
   make_cleanup_freeargv (argv);
 
+  if (argv[1] == NULL) /* no ADDR */
+    {
+      if (remote_break_mode != remote_break_sysrq_g)
+	{
+	  error (_("Don't you forget \"set remotebreak sysrq-g\"?"));
+	}
+      args = add_addr_on_linux_module (args);
+      if (args == NULL)
+	return;
+      argv = gdb_buildargv (args);
+      make_cleanup_freeargv (argv);
+    }
+
   for (arg = argv[0], argcnt = 0; arg != NULL; arg = argv[++argcnt])
     {
       /* Process the argument. */
@@ -2222,8 +2423,11 @@
 	 so we can't determine what section names are valid.  */
     }
 
-  if (from_tty && (!query ("%s", "")))
-    error (_("Not confirmed."));
+  if (remote_break_mode != remote_break_sysrq_g) /* Not module loading */
+    {
+      if (from_tty && (!query ("%s", "")))
+	error (_("Not confirmed."));
+    }
 
   symbol_file_add (filename, from_tty ? SYMFILE_VERBOSE : 0,
                    section_addrs, flags);
