* Makefile.in: Add i386lynx-tdep to the right places.
authorStu Grossman <grossman@cygnus>
Wed, 22 Sep 1993 15:23:53 +0000 (15:23 +0000)
committerStu Grossman <grossman@cygnus>
Wed, 22 Sep 1993 15:23:53 +0000 (15:23 +0000)
(TARDIRS):  Add gdbserver.

* exec.c (print_section_info):  Print entry point.
* i386lynx-nat.c (i386lynx_saved_pc_after_call):  Move into
i386lynx-tdep.c.  Add core file support.
* i386lynx-tdep.c:  New module for Lynx/386 target dependant code.
* maint.c:  Add `maint info sections' command to print info about all
sections that BFD knows about for exec and core files.
* sparc-tdep.c (sparc_push_dummy_frame):  Update stack pointer
before putting frame on the stack.  Consolidate writes to reduce
traffic for remote debugging.
* config/i386/i386lynx.mh (NATDEPFILES):  Remove exec.o.
* config/i386/i386lynx.mt (TDEPFILES):  Add exec.o, i386lynx-tdep.o.
* config/i386/nm-i386lynx.h:  Add target_pid_to_str().
* config/i386/tm-i386lynx.h:  Remove target_pid_to_str().
* sparclite/Makefile.in:  Add deps to keep Sun make happy.

gdb/ChangeLog
gdb/config/i386/i386lynx.mh
gdb/config/i386/i386lynx.mt
gdb/config/i386/nm-i386lynx.h
gdb/config/i386/tm-i386lynx.h
gdb/exec.c
gdb/i386lynx-nat.c
gdb/i386lynx-tdep.c [new file with mode: 0644]
gdb/maint.c

index 3008322e394db0b1036c4e6018ade30cc3559248..5bb5c79b69bbf2c7ac6e54085f5ef29b5276a980 100644 (file)
@@ -1,3 +1,23 @@
+Wed Sep 22 08:02:57 1993  Stu Grossman  (grossman at cygnus.com)
+
+       * Makefile.in:  Add i386lynx-tdep to the right places.
+       (TARDIRS):  Add gdbserver.
+
+       * exec.c (print_section_info):  Print entry point.
+       * i386lynx-nat.c (i386lynx_saved_pc_after_call):  Move into
+       i386lynx-tdep.c.  Add core file support.
+       * i386lynx-tdep.c:  New module for Lynx/386 target dependant code.
+       * maint.c:  Add `maint info sections' command to print info about all
+       sections that BFD knows about for exec and core files.
+       * sparc-tdep.c (sparc_push_dummy_frame):  Update stack pointer
+       before putting frame on the stack.  Consolidate writes to reduce
+       traffic for remote debugging.
+       * config/i386/i386lynx.mh (NATDEPFILES):  Remove exec.o.
+       * config/i386/i386lynx.mt (TDEPFILES):  Add exec.o, i386lynx-tdep.o.
+       * config/i386/nm-i386lynx.h:  Add target_pid_to_str().
+       * config/i386/tm-i386lynx.h:  Remove target_pid_to_str().
+       * sparclite/Makefile.in:  Add deps to keep Sun make happy.
+
 Tue Sep 21 17:48:14 1993  Jim Kingdon  (kingdon@lioth.cygnus.com)
 
        * breakpoint.h, breakpoint.c (bpstat_stop_status): Add new argument
index 2e34bd81688c532166750fbe8b1bcd9fd75cf267..51b70baa9ca773ebab1e586fec93fe305bad5bac 100644 (file)
@@ -1,6 +1,6 @@
 # Host: Intel 386 running Lynx
 XDEPFILES= 
-NATDEPFILES= exec.o fork-child.o infptrace.o inftarg.o corelow.o i386lynx-nat.o
+NATDEPFILES= fork-child.o infptrace.o inftarg.o corelow.o i386lynx-nat.o
 XM_FILE= xm-i386lynx.h
 NAT_FILE= nm-i386lynx.h
 REGEX=regex.o
index 0af5657a2e9a122f36ab1a4e7b3ff4ff27023f7f..2b83ad9f6cb574ad5f56ba3f0c2130dd8b553002 100644 (file)
@@ -1,3 +1,3 @@
 # Target: Intel 386 running Lynx
-TDEPFILES= i386-tdep.o i386-pinsn.o
+TDEPFILES= exec.o i386-tdep.o i386-pinsn.o i386lynx-tdep.o
 TM_FILE= tm-i386lynx.h
index 3a2d318ef706cecdfaca4fab62aecf4286a0d6e9..e67ec1dc9cb59df5f765c839dc8668e461bd0dda 100644 (file)
@@ -20,6 +20,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #ifndef NM_I386LYNX_H
 #define NM_I386LYNX_H
 
+#include <sys/conf.h>
 #include <sys/kernel.h>
 #include <sys/mem.h>
 #include <sys/signal.h>
@@ -55,4 +56,13 @@ i386_register_u_addr PARAMS ((int, int));
 #define CHILD_WAIT
 extern int child_wait PARAMS ((int *status));
 
+/* Lynx needs a special definition of this so that we can
+   print out the pid and thread number seperatly.  */
+
+#undef target_pid_to_str
+
+#define target_pid_to_str(PID) \
+       i386lynx_pid_to_str (PID)
+extern char *i386lynx_pid_to_str PARAMS ((int pid));
+
 #endif /* NM_I386LYNX_H */
index adf5c7c98d8964f4efbdb311fb6d13afc17b70a7..d5db133848a5604d5ff191cf470193f5d9c1b93e 100644 (file)
@@ -31,13 +31,4 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #define SAVED_PC_AFTER_CALL i386lynx_saved_pc_after_call
 CORE_ADDR i386lynx_saved_pc_after_call ();
 
-/* Lynx needs a special definition of this so that we can
-   print out the pid and thread number seperatly.  */
-
-#undef target_pid_to_str
-
-#define target_pid_to_str(PID) \
-       i386lynx_pid_to_str (PID)
-extern char *i386lynx_pid_to_str PARAMS ((int pid));
-
 #endif /* TM_I386LYNX_H */
index ef78097ea44760a21f091eec5e53046a0c14465f..444c89ebec04c851f9ce9f2ffed3be68d91243da 100644 (file)
@@ -83,7 +83,9 @@ exec_close (quitting)
      int quitting;
 {
   if (exec_bfd) {
+    char *name = bfd_get_filename (exec_bfd);
     bfd_close (exec_bfd);
+    free (name);
     exec_bfd = NULL;
   }
   if (exec_ops.to_sections) {
@@ -143,7 +145,7 @@ exec_file_command (args, from_tty)
       if (scratch_chan < 0)
        perror_with_name (filename);
 
-      exec_bfd = bfd_fdopenr (scratch_pathname, NULL, scratch_chan);
+      exec_bfd = bfd_fdopenr (scratch_pathname, gnutarget, scratch_chan);
       if (!exec_bfd)
        error ("Could not open `%s' as an executable file: %s",
               scratch_pathname, bfd_errmsg (bfd_error));
@@ -162,7 +164,7 @@ exec_file_command (args, from_tty)
       {
        struct section_table *p;
        for (p = exec_ops.to_sections; p < exec_ops.to_sections_end; p++)
-         if (!strcmp (".text", bfd_section_name (p->bfd, p->sec_ptr)))
+         if (STREQ (".text", bfd_section_name (p->bfd, p->sec_ptr)))
            {
              text_start = p->addr;
              text_end   = p->endaddr;
@@ -341,7 +343,8 @@ print_section_info (t, abfd)
   printf_filtered ("\t`%s', ", bfd_get_filename(abfd));
   wrap_here ("        ");
   printf_filtered ("file type %s.\n", bfd_get_target(abfd));
-
+  printf_filtered ("\tEntry point: %s\n",
+                  local_hex_string (bfd_get_start_address (exec_bfd)));
   for (p = t->to_sections; p < t->to_sections_end; p++) {
     printf_filtered ("\t%s", local_hex_string_custom (p->addr, "08"));
     printf_filtered (" - %s", local_hex_string_custom (p->endaddr, "08"));
@@ -403,6 +406,17 @@ set_section_command (args, from_tty)
   error ("Section %s not found", secprint);
 }
 
+/* If mourn is being called in all the right places, this could be say
+   `gdb internal error' (since generic_mourn calls mark_breakpoints_out).  */
+
+static int
+ignore (addr, contents)
+     CORE_ADDR addr;
+     char *contents;
+{
+  return 0;
+}
+
 struct target_ops exec_ops = {
        "exec", "Local exec file",
        "Use an executable file as a target.\n\
@@ -412,7 +426,7 @@ Specify the filename of the executable file.",
        0, 0, /* fetch_registers, store_registers, */
        0, /* prepare_to_store, */
        xfer_memory, exec_files_info,
-       0, 0, /* insert_breakpoint, remove_breakpoint, */
+       ignore, ignore, /* insert_breakpoint, remove_breakpoint, */
        0, 0, 0, 0, 0, /* terminal stuff */
        0, 0, /* kill, load */
        0, /* lookup sym */
@@ -429,20 +443,23 @@ Specify the filename of the executable file.",
 void
 _initialize_exec()
 {
+  struct cmd_list_element *c;
 
-  add_com ("file", class_files, file_command,
-          "Use FILE as program to be debugged.\n\
+  c = add_cmd ("file", class_files, file_command,
+              "Use FILE as program to be debugged.\n\
 It is read for its symbols, for getting the contents of pure memory,\n\
 and it is the program executed when you use the `run' command.\n\
 If FILE cannot be found as specified, your execution directory path\n\
 ($PATH) is searched for a command of that name.\n\
-No arg means to have no executable file and no symbols.");
+No arg means to have no executable file and no symbols.", &cmdlist);
+  c->completer = filename_completer;
 
-  add_com ("exec-file", class_files, exec_file_command,
+  c = add_cmd ("exec-file", class_files, exec_file_command,
           "Use FILE as program for getting contents of pure memory.\n\
 If FILE cannot be found as specified, your execution directory path\n\
 is searched for a command of that name.\n\
-No arg means have no executable file.");
+No arg means have no executable file.", &cmdlist);
+  c->completer = filename_completer;
 
   add_com ("section", class_files, set_section_command,
    "Change the base address of section SECTION of the exec file to ADDR.\n\
index 204880a41277769119371a434cd5ed7c47064a41..63300f149ecf701c3258333ee78b1e64bffffcb5 100644 (file)
@@ -50,6 +50,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #define        FS      0
 #define        GS      0
 
+static struct target_ops lynx_core_ops;
+
 /* this table must line up with REGISTER_NAMES in m-i386.h */
 static unsigned int regmap[] = 
 {
@@ -184,36 +186,6 @@ store_inferior_registers (regno)
       store_register (regno, offset, inferior_pid);
 }
 
-/* Extract the register values out of the core file and store
-   them where `read_register' will find them.
-
-   CORE_REG_SECT points to the register values themselves, read into memory.
-   CORE_REG_SIZE is the size of that area.
-   WHICH says which set of registers we are handling (0 = int, 2 = float
-         on machines where they are discontiguous).
-   REG_ADDR is the offset from u.u_ar0 to the register values relative to
-            core_reg_sect.  This is used with old-fashioned core files to
-           locate the registers in a large upage-plus-stack ".reg" section.
-           Original upage address X is at location core_reg_sect+x+reg_addr.
- */
-
-void
-fetch_core_registers (core_reg_sect, core_reg_size, which, reg_addr)
-     char *core_reg_sect;
-     unsigned core_reg_size;
-     int which;
-     unsigned reg_addr;
-{
-  struct st_entry s;
-  unsigned int regno, addr;
-
-  for (regno = 0; regno < NUM_REGS; regno++)
-    {
-      addr = register_addr (regno, (char *) &s.ec - (char *) &s);
-      supply_register (regno, core_reg_sect + addr);
-    }
-}
-
 /* Wait for child to do something.  Return pid of child, or -1 in case
    of error; store status through argument pointer STATUS.  */
 
@@ -269,26 +241,6 @@ child_wait (status)
     }
 }
 
-/* Return the PC of the caller from the call frame.  Assumes the subr prologue
-   has already been executed, and the frame pointer setup.  If this is the
-   outermost frame, we check to see if we are in a system call by examining the
-   previous instruction.  If so, then the return PC is actually at SP+4 because
-   system calls use a different calling sequence.  */
-
-CORE_ADDR
-i386lynx_saved_pc_after_call (frame)
-     struct frame_info *frame;
-{
-  char opcode[7];
-  static const char call_inst[] = {0x9a, 0, 0, 0, 0, 8, 0}; /* lcall 0x8,0x0 */
-
-  read_memory (frame->pc - 7, opcode, 7);
-  if (memcmp (opcode, call_inst, 7) == 0)
-    return read_memory_integer (read_register (SP_REGNUM) + 4, 4);
-
-  return read_memory_integer (read_register (SP_REGNUM), 4);
-}
-
 /* Convert a Lynx process ID to a string.  Returns the string in a static
    buffer.  */
 
@@ -302,3 +254,274 @@ i386lynx_pid_to_str (pid)
 
   return buf;
 }
+
+/* Extract the register values out of the core file and store
+   them where `read_register' will find them.
+
+   CORE_REG_SECT points to the register values themselves, read into memory.
+   CORE_REG_SIZE is the size of that area.
+   WHICH says which set of registers we are handling (0 = int, 2 = float
+         on machines where they are discontiguous).
+   REG_ADDR is the offset from u.u_ar0 to the register values relative to
+            core_reg_sect.  This is used with old-fashioned core files to
+           locate the registers in a large upage-plus-stack ".reg" section.
+           Original upage address X is at location core_reg_sect+x+reg_addr.
+ */
+
+void
+fetch_core_registers (core_reg_sect, core_reg_size, which, reg_addr)
+     char *core_reg_sect;
+     unsigned core_reg_size;
+     int which;
+     unsigned reg_addr;
+{
+  struct st_entry s;
+  unsigned int regno, addr;
+
+  for (regno = 0; regno < NUM_REGS; regno++)
+    {
+      addr = register_addr (regno, (char *) &s.ec - (char *) &s);
+      supply_register (regno, core_reg_sect + addr);
+    }
+}
+
+#if 0
+
+/* Discard all vestiges of any previous core file
+   and mark data and stack spaces as empty.  */
+
+/* ARGSUSED */
+static void
+lynx_core_close (quitting)
+     int quitting;
+{
+  if (core_bfd)
+    {
+      free (bfd_get_filename (core_bfd));
+      bfd_close (core_bfd);
+      core_bfd = NULL;
+
+      if (core_ops.to_sections)
+       {
+         free ((PTR)lynx_core_ops.to_sections);
+         lynx_core_ops.to_sections = NULL;
+         lynx_core_ops.to_sections_end = NULL;
+       }
+    }
+}
+
+/* This routine opens and sets up the core file bfd */
+
+static void
+lynx_core_open (filename, from_tty)
+     char *filename;
+     int from_tty;
+{
+  const char *p;
+  int siggy;
+  struct cleanup *old_chain;
+  char *temp;
+  bfd *temp_bfd;
+  int ontop;
+  int scratch_chan;
+
+  target_preopen (from_tty);
+  if (!filename)
+    {
+      error (core_bfd ? 
+            "No core file specified.  (Use `detach' to stop debugging a core file.)"
+            : "No core file specified.");
+    }
+
+  filename = tilde_expand (filename);
+  if (filename[0] != '/')
+    {
+      temp = concat (current_directory, "/", filename, NULL);
+      free (filename);
+      filename = temp;
+    }
+
+  old_chain = make_cleanup (free, filename);
+
+  scratch_chan = open (filename, write_files? O_RDWR: O_RDONLY, 0);
+  if (scratch_chan < 0)
+    perror_with_name (filename);
+
+  temp_bfd = bfd_fdopenr (filename, NULL, scratch_chan);
+  if (temp_bfd == NULL)
+    perror_with_name (filename);
+
+  if (!bfd_check_format (temp_bfd, bfd_core))
+    {
+      /* Do it after the err msg */
+      make_cleanup (bfd_close, temp_bfd);
+      error ("\"%s\" is not a core dump: %s", filename, bfd_errmsg(bfd_error));
+    }
+
+  /* Looks semi-reasonable.  Toss the old core file and work on the new.  */
+
+  discard_cleanups (old_chain);                /* Don't free filename any more */
+  unpush_target (&core_ops);
+  core_bfd = temp_bfd;
+  old_chain = make_cleanup (core_close, core_bfd);
+
+  validate_files ();
+
+  /* Find the data section */
+  if (build_section_table (core_bfd, &core_ops.to_sections,
+                          &core_ops.to_sections_end))
+    error ("Can't find sections in `%s': %s", bfd_get_filename(core_bfd),
+          bfd_errmsg (bfd_error));
+
+  ontop = !push_target (&core_ops);
+  discard_cleanups (old_chain);
+
+  p = bfd_core_file_failing_command (core_bfd);
+  if (p)
+    printf_filtered ("Core was generated by `%s'.\n", p);
+
+  siggy = bfd_core_file_failing_signal (core_bfd);
+  if (siggy > 0)
+    printf_filtered ("Program terminated with signal %d, %s.\n", siggy,
+           safe_strsignal (siggy));
+
+  /* Locate all of the thread register sections.  They have names like .regxx,
+     where xx is the thread-id.  */
+
+  bfd_map_over_sections (core_bfd, grok_register_sections, 
+
+  if (ontop)
+    {
+      /* Fetch all registers from core file */
+      target_fetch_registers (-1);
+
+      /* Now, set up the frame cache, and print the top of stack */
+      set_current_frame (create_new_frame (read_fp (),
+                                          read_pc ()));
+      select_frame (get_current_frame (), 0);
+      print_stack_frame (selected_frame, selected_frame_level, 1);
+    }
+  else
+    {
+      warning (
+              "you won't be able to access this core file until you terminate\n\
+your %s; do ``info files''", current_target->to_longname);
+    }
+}
+
+static void
+lynx_core_detach (args, from_tty)
+     char *args;
+     int from_tty;
+{
+  if (args)
+    error ("Too many arguments");
+  unpush_target (&core_ops);
+  if (from_tty)
+    printf_filtered ("No core file now.\n");
+}
+
+/* Get the registers out of a core file.  This is the machine-
+   independent part.  Fetch_core_registers is the machine-dependent
+   part, typically implemented in the xm-file for each architecture.  */
+
+/* We just get all the registers, so we don't use regno.  */
+/* ARGSUSED */
+static void
+get_core_registers (regno)
+     int regno;
+{
+  sec_ptr reg_sec;
+  unsigned size;
+  char *the_regs;
+  char regsecname[20];
+
+  sprintf (regsecname, ".reg%d", TIDGET (inferior_pid));
+
+  reg_sec = bfd_get_section_by_name (core_bfd, regsecname);
+  if (!reg_sec)
+    goto cant;
+  size = bfd_section_size (core_bfd, reg_sec);
+  the_regs = alloca (size);
+  if (bfd_get_section_contents (core_bfd, reg_sec, the_regs, (file_ptr)0,
+                               size))
+    {
+      fetch_core_registers (the_regs, size, 0,
+                           (unsigned) bfd_section_vma (abfd,reg_sec));
+    }
+  else
+    {
+cant:
+      fprintf_filtered (stderr, "Couldn't fetch registers from core file: %s\n",
+              bfd_errmsg (bfd_error));
+    }
+
+  registers_fetched();
+}
+
+static void
+core_files_info (t)
+  struct target_ops *t;
+{
+  print_section_info (t, core_bfd);
+}
+\f
+/* If mourn is being called in all the right places, this could be say
+   `gdb internal error' (since generic_mourn calls mark_breakpoints_out).  */
+
+static int
+ignore (addr, contents)
+     CORE_ADDR addr;
+     char *contents;
+{
+}
+
+static struct target_ops
+lynx_core_ops =
+{
+  "core",
+  "Local core dump file",
+  "Use a core file as a target.  Specify the filename of the core file.",
+  lynx_core_open,
+  lynx_core_close,
+  find_default_attach,
+  lynx_core_detach,
+  0,
+  0,
+  get_core_registers, 
+  0,
+  0,
+  xfer_memory,
+  lynx_core_files_info,
+  ignore,
+  ignore,
+  0,
+  0,
+  0,
+  0,
+  0,
+  0,
+  0,
+  0,
+  find_default_create_inferior,
+  0,                           /* mourn_inferior */
+  0,                           /* can_run */
+  0,                           /* notice_signals */
+  core_stratum,
+  0,                           /* next */
+  0,
+  1,
+  1,
+  1,
+  0,
+  0,
+  0,
+  OPS_MAGIC,                   /* Always the last thing */
+};
+
+void
+_initialize_i386lynx_nat()
+{
+  add_target (&lynx_core_ops);
+}
+#endif
diff --git a/gdb/i386lynx-tdep.c b/gdb/i386lynx-tdep.c
new file mode 100644 (file)
index 0000000..87b0ac1
--- /dev/null
@@ -0,0 +1,43 @@
+/* Target-dependent code for Lynx running on i386's, for GDB.
+   Copyright 1988, 1989, 1991, 1992, 1993
+   Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include "defs.h"
+#include "inferior.h"
+#include "target.h"
+
+/* Return the PC of the caller from the call frame.  Assumes the subr prologue
+   has already been executed, and the frame pointer setup.  If this is the
+   outermost frame, we check to see if we are in a system call by examining the
+   previous instruction.  If so, then the return PC is actually at SP+4 because
+   system calls use a different calling sequence.  */
+
+CORE_ADDR
+i386lynx_saved_pc_after_call (frame)
+     struct frame_info *frame;
+{
+  char opcode[7];
+  static const char call_inst[] = {0x9a, 0, 0, 0, 0, 8, 0}; /* lcall 0x8,0x0 */
+
+  read_memory (frame->pc - 7, opcode, 7);
+  if (memcmp (opcode, call_inst, 7) == 0)
+    return read_memory_integer (read_register (SP_REGNUM) + 4, 4);
+
+  return read_memory_integer (read_register (SP_REGNUM), 4);
+}
index 9a37fe0f6bd3cd0b076e4f3da74bf103603c9dc7..bc73bdc4c7399c695a29ce63d0ac5c9d75b5a50e 100644 (file)
@@ -29,6 +29,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include "symtab.h"
 #include "gdbtypes.h"
 #include "demangle.h"
+#include "gdbcore.h"
 
 static void
 maintenance_command PARAMS ((char *, int));
@@ -124,6 +125,79 @@ maintenance_info_command (arg, from_tty)
   help_list (maintenanceinfolist, "maintenance info ", -1, stdout);
 }
 
+static void
+print_section_table (abfd, asect, ignore)
+     bfd *abfd;
+     asection *asect;
+     PTR ignore;
+{
+  flagword flags;
+
+  flags = bfd_get_section_flags (abfd, asect);
+
+  printf_filtered ("    %s",
+                  local_hex_string_custom (bfd_section_vma (abfd, asect),
+                                           "08"));
+  printf_filtered ("->%s",
+                  local_hex_string_custom ((bfd_section_vma (abfd, asect)
+                                            + bfd_section_size (abfd, asect)),
+                                           "08"));
+  printf_filtered (" at %s", local_hex_string_custom (asect->filepos, "08"));
+  printf_filtered (": %s", bfd_section_name (abfd, asect));
+
+  if (flags & SEC_ALLOC)
+    printf_filtered (" ALLOC");
+  if (flags & SEC_LOAD)
+    printf_filtered (" LOAD");
+  if (flags & SEC_RELOC)
+    printf_filtered (" RELOC");
+  if (flags & SEC_READONLY)
+    printf_filtered (" READONLY");
+  if (flags & SEC_CODE)
+    printf_filtered (" CODE");
+  if (flags & SEC_DATA)
+    printf_filtered (" DATA");
+  if (flags & SEC_ROM)
+    printf_filtered (" ROM");
+  if (flags & SEC_CONSTRUCTOR)
+    printf_filtered (" CONSTRUCTOR");
+  if (flags & SEC_HAS_CONTENTS)
+    printf_filtered (" HAS_CONTENTS");
+  if (flags & SEC_NEVER_LOAD)
+    printf_filtered (" NEVER_LOAD");
+  if (flags & SEC_SHARED_LIBRARY)
+    printf_filtered (" SHARED_LIBRARY");
+  if (flags & SEC_IS_COMMON)
+    printf_filtered (" IS_COMMON");
+
+  printf_filtered ("\n");
+}
+
+/* ARGSUSED */
+static void
+maintenance_info_sections (arg, from_tty)
+     char *arg;
+     int from_tty;
+{
+  if (exec_bfd)
+    {
+      printf_filtered ("Exec file:\n");
+      printf_filtered ("    `%s', ", bfd_get_filename(exec_bfd));
+      wrap_here ("        ");
+      printf_filtered ("file type %s.\n", bfd_get_target(exec_bfd));
+      bfd_map_over_sections(exec_bfd, print_section_table, 0);
+    }
+
+  if (core_bfd)
+    {
+      printf_filtered ("Core file:\n");
+      printf_filtered ("    `%s', ", bfd_get_filename(core_bfd));
+      wrap_here ("        ");
+      printf_filtered ("file type %s.\n", bfd_get_target(core_bfd));
+      bfd_map_over_sections(core_bfd, print_section_table, 0);
+    }
+}
+
 /* The "maintenance print" command is defined as a prefix, with allow_unknown
    0.  Therefore, its own definition is called only for "maintenance print"
    with no args.  */
@@ -174,6 +248,10 @@ to test internal functions such as the C++ demangler, etc.",
                  &maintenanceinfolist, "maintenance info ", 0,
                  &maintenancelist);
 
+  add_cmd ("sections", class_maintenance, maintenance_info_sections,
+          "List the BFD sections of the exec and core files.",
+          &maintenanceinfolist);
+
   add_prefix_cmd ("print", class_maintenance, maintenance_print_command,
                  "Maintenance command for printing GDB internal state.",
                  &maintenanceprintlist, "maintenance print ", 0,