From 9f20bf26f8399f4edb5ea771ac571ed64cbce865 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Mon, 23 Jul 2001 16:10:24 +0000 Subject: [PATCH] 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. --- gdb/ChangeLog | 10 ++ gdb/doc/ChangeLog | 5 + gdb/doc/gdb.texinfo | 140 +++++++++++++++++++++ gdb/go32-nat.c | 300 ++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 447 insertions(+), 8 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index ed349ffbe77..0124250065e 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,13 @@ +2001-07-23 Eli Zaretskii + + * 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 * i386gnu-nat.c: Include "i387-nat.h". diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index 3966b4689af..00afcecf325 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,8 @@ +2001-07-23 Eli Zaretskii + + * gdb.tex (DJGPP Native): New node, with descriptions of + DJGPP-specific commands. + 2001-07-19 John R. Moore * gdbint.texinfo: Three misspellings. diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index ea9dd2d24c2..fc592c89eab 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -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 diff --git a/gdb/go32-nat.c b/gdb/go32-nat.c index 56ab8cb4e13..fce7a0f4815 100644 --- a/gdb/go32-nat.c +++ b/gdb/go32-nat.c @@ -47,6 +47,7 @@ #undef disable #include #include +#include #include #include #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 . */ +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 -- 2.30.2