Add new DJGPP-specific commands and document them in the manual.
authorEli Zaretskii <eliz@gnu.org>
Mon, 23 Jul 2001 16:10:24 +0000 (16:10 +0000)
committerEli Zaretskii <eliz@gnu.org>
Mon, 23 Jul 2001 16:10:24 +0000 (16:10 +0000)
Make the function which reads memory regions be more defensive
about buggy DPMI providers.

gdb/ChangeLog
gdb/doc/ChangeLog
gdb/doc/gdb.texinfo
gdb/go32-nat.c

index ed349ffbe77affef571dad6519d6a8a053583f43..0124250065ee8836ddbb27621d947b8aba1e4586 100644 (file)
@@ -1,3 +1,13 @@
+2001-07-23  Eli Zaretskii  <eliz@is.elta.co.il>
+
+       * go32-nat.c (read_memory_region): Set the granularity bit of the
+       allocated segment according to its size, and adjust the limit to
+       be page-aligned if the segment is page-granular.
+
+       * (get_cr3, get_pde, get_pte, display_ptable_entry, go32_pde) 
+       (display_page_table, go32_pte, go32_pte_for_address): New functions.
+       (_initialize_go32_nat): Initialize and document them.
+
 2001-07-22  Mark Kettenis  <kettenis@gnu.org>
 
        * i386gnu-nat.c: Include "i387-nat.h".
index 3966b4689af77a002b2da9f24748be3b9224a80e..00afcecf3258a97aa33dcf23c8a1dbfb790ad228 100644 (file)
@@ -1,3 +1,8 @@
+2001-07-23  Eli Zaretskii  <eliz@is.elta.co.il>
+
+       * gdb.tex (DJGPP Native): New node, with descriptions of
+       DJGPP-specific commands.
+
 2001-07-19  John R. Moore  <jmoore@redhat.com>
 
        * gdbint.texinfo: Three misspellings.
index ea9dd2d24c249861209faece5d3148bbb3ce6663..fc592c89eab656b1a5764b09853d670622356f8b 100644 (file)
@@ -10741,6 +10741,7 @@ configurations.
 @menu
 * HP-UX::                       HP-UX
 * SVR4 Process Information::    SVR4 process information
+* DJGPP Native::                Features specific to the DJGPP port
 @end menu
 
 @node HP-UX
@@ -10795,6 +10796,145 @@ received.
 Show all the above information about the process.
 @end table
 
+@node DJGPP Native
+@subsection Features for Debugging @sc{djgpp} Programs
+@cindex @sc{djgpp} debugging
+@cindex native @sc{djgpp} debugging
+@cindex MS-DOS-specific commands
+
+@sc{djgpp} is the port of @sc{gnu} development tools to MS-DOS and
+MS-Windows.  @sc{djgpp} programs are 32-bit protected-mode programs
+that use the @dfn{DPMI} (DOS Protected-Mode Interface) API to run on
+top of real-mode DOS systems and their emulations.
+
+@value{GDBN} supports native debugging of @sc{djgpp} programs, and
+defines a few commands specific to the @sc{djgpp} port.  This
+subsection describes those commands.
+
+@table @code
+@kindex info dos
+@item info dos
+This is a prefix of @sc{djgpp}-specific commands which print
+information about the target system and important OS structures.
+
+@kindex sysinfo
+@cindex MS-DOS system info
+@cindex free memory information (MS-DOS)
+@item info dos sysinfo
+This command displays assorted information about the underlying
+platform: the CPU type and features, the OS version and flavor, the
+DPMI version, and the available conventional and DPMI memory.
+
+@cindex GDT
+@cindex LDT
+@cindex IDT
+@cindex segment descriptor tables
+@cindex descriptor tables display
+@item info dos gdt
+@itemx info dos ldt
+@itemx info dos idt
+These 3 commands display entries from, respectively, Global, Local,
+and Interrupt Descriptor Tables (GDT, LDT, and IDT).  The descriptor
+tables are data structures which store a descriptor for each segment
+that is currently in use.  The segment's selector is an index into a
+descriptor table; the table entry for that index holds the
+descriptor's base address and limit, and its attributes and access
+rights.
+
+A typical @sc{djgpp} program uses 3 segments: a code segment, a data
+segment (used for both data and the stack), and a DOS segment (which
+allows access to DOS/BIOS data structures and absolute addresses in
+conventional memory).  However, the DPMI host will usually define
+additional segments in order to support the DPMI environment.
+
+@cindex garbled pointers
+These commands allow to display entries from the descriptor tables.
+Without an argument, all entries from the specified table are
+displayed.  An argument, which should be an integer expression, means
+display a single entry whose index is given by the argument.  For
+example, here's a convenient way to display information about the
+debugged program's data segment:
+
+@smallexample
+(@value{GDBP}) info dos ldt $ds
+0x13f: base=0x11970000 limit=0x0009ffff 32-Bit Data (Read/Write, Exp-up)
+@end smallexample
+
+@noindent
+This comes in handy when you want to see whether a pointer is outside
+the data segment's limit (i.e.@: @dfn{garbled}).
+
+@cindex page tables display (MS-DOS)
+@item info dos pde
+@itemx info dos pte
+These two commands display entries from, respectively, the Page
+Directory and the Page Tables.  Page Directories and Page Tables are
+data structures which control how virtual memory addresses are mapped
+into physical addresses.  A Page Table includes an entry for every
+page of memory that is mapped into the program's address space; there
+may be several Page Tables, each one holding up to 4096 entries.  A
+Page Directory has up to 4096 entries, one each for every Page Table
+that is currently in use.
+
+Without an argument, @kbd{info dos pde} displays the entire Page
+Directory, and @kbd{info dos pte} displays all the entries in all of
+the Page Tables.  An argument, an integer expression, given to the
+@kbd{info dos pde} command means display only that entry from the Page
+Directory table.  An argument given to the @kbd{info dos pte} command
+means display entries from a single Page Table, the one pointed to by
+the specified entry in the Page Directory.
+
+These commands are useful when your program uses @dfn{DMA} (Direct
+Memory Access), which needs physical addresses to program the DMA
+controller.
+
+These commands are supported only with some DPMI servers.
+
+@cindex physical address from linear address
+@item info dos address-pte
+This command displays the Page Table entry for a specified linear
+address.  The argument linear address should already have the
+appropriate segment's base address added to it, because this command
+accepts addresses which may belong to @emph{any} segment.  For
+example, here's how to display the Page Table entry for the page where
+the variable @code{i} is stored:
+
+@smallexample
+(@value{GDBP}) info dos address-pte __djgpp_base_address + (char *)&i
+Page Table entry for address 0x11a00d30:
+Base=0x02698000 Dirty Acc. Not-Cached Write-Back Usr Read-Write +0xd30
+@end smallexample
+
+@noindent
+This says that @code{i} is stored at offset @code{0xd30} from the page
+whose physical base address is @code{0x02698000}, and prints all the
+attributes of that page.
+
+Note that you must cast the addresses of variables to a @code{char *},
+since otherwise the value of @code{__djgpp_base_address}, the base
+address of all variables and functions in a @sc{djgpp} program, will
+be added using the rules of C pointer arithmetics: if @code{i} is
+declared an @code{int}, @value{GDBN} will add 4 times the value of
+@code{__djgpp_base_address} to the address of @code{i}.
+
+Here's another example, it displays the Page Table entry for the
+transfer buffer:
+
+@smallexample
+(@value{GDBP}) info dos address-pte *((unsigned *)&_go32_info_block + 3)
+Page Table entry for address 0x29110:
+Base=0x00029000 Dirty Acc. Not-Cached Write-Back Usr Read-Write +0x110
+@end smallexample
+
+@noindent
+(The @code{+ 3} offset is because the transfer buffer's address is the
+3rd member of the @code{_go32_info_block} structure.)  The output of
+this command clearly shows that addresses in conventional memory are
+mapped 1:1, i.e.@: the physical and linear addresses are identical.
+
+This command is supported only with some DPMI servers.
+@end table
+
 @node Embedded OS
 @section Embedded Operating Systems
 
index 56ab8cb4e138dd03109885c18ce41e416d3709e8..fce7a0f4815a4937266ac37d01ab57b81da639e6 100644 (file)
@@ -47,6 +47,7 @@
 #undef disable
 #include <dpmi.h>
 #include <go32.h>
+#include <sys/farptr.h>
 #include <debug/v2load.h>
 #include <debug/dbgcom.h>
 #if __DJGPP_MINOR__ > 2
@@ -1294,6 +1295,7 @@ static int
 read_memory_region (unsigned long addr, void *dest, size_t len)
 {
   unsigned long dos_ds_limit = __dpmi_get_segment_limit (_dos_ds);
+  int retval = 1;
 
   /* For the low memory, we can simply use _dos_ds.  */
   if (addr <= dos_ds_limit - len)
@@ -1304,14 +1306,40 @@ read_memory_region (unsigned long addr, void *dest, size_t len)
         be able to access that memory.  */
       int sel = __dpmi_allocate_ldt_descriptors (1);
 
-      if (sel <= 0
-         || __dpmi_set_segment_base_address (sel, addr) == -1
-         || __dpmi_set_segment_limit (sel, len - 1) == -1)
-       return 0;
-      movedata (sel, 0, _my_ds (), (unsigned)dest, len);
-      __dpmi_free_ldt_descriptor (sel);
+      if (sel <= 0)
+       retval = 0;
+      else
+       {
+         int access_rights = __dpmi_get_descriptor_access_rights (sel);
+         size_t segment_limit = len - 1;
+
+         /* Make sure the crucial bits in the descriptor access
+            rights are set correctly.  Some DPMI providers might barf
+            if we set the segment limit to something that is not an
+            integral multiple of 4KB pages if the granularity bit is
+            not set to byte-granular, even though the DPMI spec says
+            it's the host's responsibility to set that bit correctly.  */
+         if (len > 1024 * 1024)
+           {
+             access_rights |= 0x8000;
+             /* Page-granular segments should have the low 12 bits of
+                the limit set.  */
+             segment_limit |= 0xfff;
+           }
+         else
+           access_rights &= ~0x8000;
+
+         if (__dpmi_set_segment_base_address (sel, addr) != -1
+             && __dpmi_set_descriptor_access_rights (sel, access_rights) != -1
+             && __dpmi_set_segment_limit (sel, segment_limit) != -1)
+           movedata (sel, 0, _my_ds (), (unsigned)dest, len);
+         else
+           retval = 0;
+
+         __dpmi_free_ldt_descriptor (sel);
+       }
     }
-  return 1;
+  return retval;
 }
 
 /* Get a segment descriptor stored at index IDX in the descriptor
@@ -1603,7 +1631,7 @@ go32_sidt (char *arg, int from_tty)
        {
          idt_entry = parse_and_eval_long (arg);
          if (idt_entry < 0)
-           error ("Invalid (negative) IDT entry 0x%03x.", idt_entry);
+           error ("Invalid (negative) IDT entry %d.", idt_entry);
        }
     }
 
@@ -1629,6 +1657,243 @@ go32_sidt (char *arg, int from_tty)
     }
 }
 
+/* Cached linear address of the base of the page directory.  For
+   now, available only under CWSDPMI.  Code based on ideas and
+   suggestions from Charles Sandmann <sandmann@clio.rice.edu>.  */
+static unsigned long pdbr;
+
+static unsigned long
+get_cr3 (void)
+{
+  unsigned offset;
+  unsigned taskreg;
+  unsigned long taskbase, cr3;
+  struct dtr_reg gdtr;
+
+  if (pdbr > 0 && pdbr <= 0xfffff)
+    return pdbr;
+
+  /* Get the linear address of GDT and the Task Register.  */
+  __asm__ __volatile__ ("sgdt   %0" : "=m" (gdtr) : /* no inputs */ );
+  __asm__ __volatile__ ("str    %0" : "=m" (taskreg) : /* no inputs */ );
+
+  /* Task Register is a segment selector for the TSS of the current
+     task.  Therefore, it can be used as an index into the GDT to get
+     at the segment descriptor for the TSS.  To get the index, reset
+     the low 3 bits of the selector (which give the CPL).  Add 2 to the
+     offset to point to the 3 low bytes of the base address.  */
+  offset = gdtr.base + (taskreg & 0xfff8) + 2;
+
+
+  /* CWSDPMI's task base is always under the 1MB mark.  */
+  if (offset > 0xfffff)
+    return 0;
+
+  _farsetsel (_dos_ds);
+  taskbase  = _farnspeekl (offset) & 0xffffffU;
+  taskbase += _farnspeekl (offset + 2) & 0xff000000U;
+  if (taskbase > 0xfffff)
+    return 0;
+
+  /* CR3 (a.k.a. PDBR, the Page Directory Base Register) is stored at
+     offset 1Ch in the TSS.  */
+  cr3 = _farnspeekl (taskbase + 0x1c) & ~0xfff;
+  if (cr3 > 0xfffff)
+    {
+      /* The Page Directory is in UMBs.  In that case, CWSDPMI puts
+        the first Page Table right below the Page Directory.  Thus,
+        the first Page Table's entry for its own address and the Page
+        Directory entry for that Page Table will hold the same
+        physical address.  The loop below searches the entire UMB
+        range of addresses for such an occurence.  */
+      unsigned long addr, pte_idx;
+
+      for (addr = 0xb0000, pte_idx = 0xb0;
+          pte_idx < 0xff;
+          addr += 0x1000, pte_idx++)
+       {
+         if (((_farnspeekl (addr + 4 * pte_idx) & 0xfffff027) ==
+              (_farnspeekl (addr + 0x1000) & 0xfffff027))
+             && ((_farnspeekl (addr + 4 * pte_idx + 4) & 0xfffff000) == cr3))
+           {
+             cr3 = addr + 0x1000;
+             break;
+           }
+       }
+
+      if (cr3 > 0xfffff)
+       cr3 = 0;
+    }
+
+  return cr3;
+}
+
+/* Return the N'th Page Directory entry.  */
+static unsigned long
+get_pde (int n)
+{
+  unsigned long pde = 0;
+
+  if (pdbr && n >= 0 && n < 1024)
+    {
+      pde = _farpeekl (_dos_ds, pdbr + 4*n);
+    }
+  return pde;
+}
+
+/* Return the N'th entry of the Page Table whose Page Directory entry
+   is PDE.  */
+static unsigned long
+get_pte (unsigned long pde, int n)
+{
+  unsigned long pte = 0;
+
+  /* pde & 0x80 tests the 4MB page bit.  We don't support 4MB
+     page tables, for now.  */
+  if ((pde & 1) && !(pde & 0x80) && n >= 0 && n < 1024)
+    {
+      pde &= ~0xfff;   /* clear non-address bits */
+      pte = _farpeekl (_dos_ds, pde + 4*n);
+    }
+  return pte;
+}
+
+/* Display a Page Directory or Page Table entry.  IS_DIR, if non-zero,
+   says this is a Page Directory entry.  If FORCE is non-zero, display
+   the entry even if its Present flag is off.  OFF is the offset of the
+   address from the page's base address.  */
+static void
+display_ptable_entry (unsigned long entry, int is_dir, int force, unsigned off)
+{
+  if ((entry & 1) != 0)
+    {
+      printf_filtered ("Base=0x%05lx000", entry >> 12);
+      if ((entry & 0x100) && !is_dir)
+       puts_filtered (" Global");
+      if ((entry & 0x40) && !is_dir)
+       puts_filtered (" Dirty");
+      printf_filtered (" %sAcc.", (entry & 0x20) ? "" : "Not-");
+      printf_filtered (" %sCached", (entry & 0x10) ? "" : "Not-");
+      printf_filtered (" Write-%s", (entry & 8) ? "Thru" : "Back");
+      printf_filtered (" %s", (entry & 4) ? "Usr" : "Sup");
+      printf_filtered (" Read-%s", (entry & 2) ? "Write" : "Only");
+      if (off)
+       printf_filtered (" +0x%x", off);
+      puts_filtered ("\n");
+    }
+  else if (force)
+    printf_filtered ("Page%s not present or not supported; value=0x%lx.\n",
+                    is_dir ? " Table" : "", entry >> 1);
+}
+
+static void
+go32_pde (char *arg, int from_tty)
+{
+  long pde_idx = -1, i;
+
+  if (arg && *arg)
+    {
+      while (*arg && isspace(*arg))
+       arg++;
+
+      if (*arg)
+       {
+         pde_idx = parse_and_eval_long (arg);
+         if (pde_idx < 0 || pde_idx >= 1024)
+           error ("Entry %ld is outside valid limits [0..1023].", pde_idx);
+       }
+    }
+
+  pdbr = get_cr3 ();
+  if (!pdbr)
+    puts_filtered ("Access to Page Directories is not supported on this system.\n");
+  else if (pde_idx >= 0)
+    display_ptable_entry (get_pde (pde_idx), 1, 1, 0);
+  else
+    for (i = 0; i < 1024; i++)
+      display_ptable_entry (get_pde (i), 1, 0, 0);
+}
+
+/* A helper function to display entries in a Page Table pointed to by
+   the N'th entry in the Page Directory.  If FORCE is non-zero, say
+   something even if the Page Table is not accessible.  */
+static void
+display_page_table (long n, int force)
+{
+  unsigned long pde = get_pde (n);
+
+  if ((pde & 1) != 0)
+    {
+      int i;
+
+      printf_filtered ("Page Table pointed to by Page Directory entry 0x%lx:\n", n);
+      for (i = 0; i < 1024; i++)
+       display_ptable_entry (get_pte (pde, i), 0, 0, 0);
+      puts_filtered ("\n");
+    }
+  else if (force)
+    printf_filtered ("Page Table not present; value=0x%lx.\n", pde >> 1);
+}
+
+static void
+go32_pte (char *arg, int from_tty)
+{
+  long pde_idx = -1, i;
+
+  if (arg && *arg)
+    {
+      while (*arg && isspace(*arg))
+       arg++;
+
+      if (*arg)
+       {
+         pde_idx = parse_and_eval_long (arg);
+         if (pde_idx < 0 || pde_idx >= 1024)
+           error ("Entry %d is outside valid limits [0..1023].", pde_idx);
+       }
+    }
+
+  pdbr = get_cr3 ();
+  if (!pdbr)
+    puts_filtered ("Access to Page Tables is not supported on this system.\n");
+  else if (pde_idx >= 0)
+    display_page_table (pde_idx, 1);
+  else
+    for (i = 0; i < 1024; i++)
+      display_page_table (i, 0);
+}
+
+static void
+go32_pte_for_address (char *arg, int from_tty)
+{
+  CORE_ADDR addr = 0, i;
+
+  if (arg && *arg)
+    {
+      while (*arg && isspace(*arg))
+       arg++;
+
+      if (*arg)
+       addr = parse_and_eval_address (arg);
+    }
+  if (!addr)
+    error_no_arg ("linear address");
+
+  pdbr = get_cr3 ();
+  if (!pdbr)
+    puts_filtered ("Access to Page Tables is not supported on this system.\n");
+  else
+    {
+      int pde_idx = (addr >> 22) & 0x3ff;
+      int pte_idx = (addr >> 12) & 0x3ff;
+      unsigned offs = addr & 0xfff;
+
+      printf_filtered ("Page Table entry for address 0x%llx:\n",
+                      (unsigned long long)addr);
+      display_ptable_entry (get_pte (get_pde (pde_idx), pte_idx), 0, 1, offs);
+    }
+}
+
 static struct cmd_list_element *info_dos_cmdlist = NULL;
 
 static void
@@ -1662,6 +1927,25 @@ _initialize_go32_nat (void)
           "Display entries in the IDT (Interrupt Descriptor Table).\n"
           "Entry number (an expression) as an argument means display only that entry.",
           &info_dos_cmdlist);
+  add_cmd ("pde", class_info, go32_pde,
+          "Display entries in the Page Directory.\n"
+          "Entry number (an expression) as an argument means display only that entry.",
+          &info_dos_cmdlist);
+  add_cmd ("pte", class_info, go32_pte,
+          "Display entries in Page Tables.\n"
+          "Entry number (an expression) as an argument means display only entries\n"
+          "from the Page Table pointed to by the specified Page Directory entry.",
+          &info_dos_cmdlist);
+  add_cmd ("address-pte", class_info, go32_pte_for_address,
+          "Display a Page Table entry for a linear address.\n"
+          "The address argument must be a linear address, after adding to\n"
+          "it the base address of the appropriate segment.\n"
+          "The base address of variables and functions in the debuggee's data\n"
+          "or code segment is stored in the variable __djgpp_base_address,\n"
+          "so use `__djgpp_base_address + (char *)&var' as the argument.\n"
+          "For other segments, look up their base address in the output of\n"
+          "the `info dos ldt' command.",
+          &info_dos_cmdlist);
 }
 
 pid_t