Add new DJGPP-specific commands and document them in the manual.

Make the function which reads memory regions be more defensive
about buggy DPMI providers.
This commit is contained in:
Eli Zaretskii 2001-07-23 16:10:24 +00:00
parent 0772685173
commit 9f20bf26f8
4 changed files with 447 additions and 8 deletions

View 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".

View 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.

View 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

View 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