mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-24 18:44:20 +08:00
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:
parent
0772685173
commit
9f20bf26f8
@ -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".
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
||||
|
300
gdb/go32-nat.c
300
gdb/go32-nat.c
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user