From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 17327 invoked by alias); 13 Jun 2009 14:29:53 -0000 Received: (qmail 17314 invoked by uid 22791); 13 Jun 2009 14:29:51 -0000 X-SWARE-Spam-Status: No, hits=-1.8 required=5.0 tests=AWL,BAYES_00,J_CHICKENPOX_14,J_CHICKENPOX_55,J_CHICKENPOX_93,RCVD_IN_DNSWL_LOW,SPF_PASS,URIBL_RHS_DOB X-Spam-Check-By: sourceware.org Received: from sif.is.scarlet.be (HELO sif.is.scarlet.be) (193.74.71.28) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Sat, 13 Jun 2009 14:29:43 +0000 Received: from [172.17.1.10] (ip-81-11-222-7.dsl.scarlet.be [81.11.222.7]) by sif.is.scarlet.be (8.14.2/8.14.2) with ESMTP id n5DETbbB014025; Sat, 13 Jun 2009 16:29:38 +0200 Subject: Re: Patch : gdbserver get_image_name on CE From: Danny Backx Reply-To: danny.backx@scarlet.be To: gdb-patches@sourceware.org Content-Type: multipart/mixed; boundary="=-N+ek63Sd5DbA9YZguL51" Date: Sat, 13 Jun 2009 14:29:00 -0000 Message-Id: <1244903385.20290.9.camel@pavilion> Mime-Version: 1.0 X-DCC-scarlet.be-Metrics: sif 20001; Body=2 Fuz1=2 Fuz2=2 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2009-06/txt/msg00357.txt.bz2 --=-N+ek63Sd5DbA9YZguL51 Content-Type: text/plain Content-Transfer-Encoding: 7bit Content-length: 1086 As I said gdbserver works with this patch, on CE on an x86 clone. For the record, I'm testing this with a Via Eden ULV processor on Windows Embedded CE 6.0 . Several issues solved with this patch : 1. Initialize the breakpoint structure to the right instruction. 2. Handle a case where the inferior refuses to start. See the code after WaitForDebugEvent in win32-low.c . 3. Setjmp won't work on this platform, I've "#if 0"-ed it out. Clearly not the right solution. Comment please. 4. Handle the failing call to GetThreadContext. 5. Read the DLL names in a way that works on x86. The patch I've attached is relative to gdb-6.8 . The configure.srv bit in the attachment was sent (by me) and committed (by Pedro) earlier, don't pay attention to it. The patch for #5 was also recently sent by me, probably not committed yet. After comments on this, I'll transform this into a real patch. Note that gdb also requires a bit of work, I've not polluted this patch with that. I have it working too. Danny -- Danny Backx ; danny.backx - at - scarlet.be ; http://danny.backx.info --=-N+ek63Sd5DbA9YZguL51 Content-Disposition: attachment; filename="gdbserver-diffs" Content-Type: text/x-patch; name="gdbserver-diffs"; charset="UTF-8" Content-Transfer-Encoding: 8bit Content-length: 11300 diff -c /home/danny/src/gdb/gdb/gdb-6.8.orig/gdb/gdbserver/configure.srv ./configure.srv *** /home/danny/src/gdb/gdb/gdb-6.8.orig/gdb/gdbserver/configure.srv 2008-02-11 23:00:31.000000000 +0100 --- ./configure.srv 2009-04-17 19:49:46.000000000 +0200 *************** *** 65,70 **** --- 65,79 ---- srv_linux_regsets=yes srv_linux_thread_db=yes ;; + i[34567]86-*-mingw*ce*) + srv_regobj=reg-i386.o + srv_tgtobj="win32-low.o win32-i386-low.o" + srv_tgtobj="${srv_tgtobj} wincecompat.o" + # hostio_last_error implementation is in win32-low.c + srv_hostio_err_objs="" + srv_mingw=yes + srv_mingwce=yes + ;; i[34567]86-*-mingw*) srv_regobj=reg-i386.o srv_tgtobj="win32-low.o win32-i386-low.o" srv_mingw=yes diff -c /home/danny/src/gdb/gdb/gdb-6.8.orig/gdb/gdbserver/server.c ./server.c *** /home/danny/src/gdb/gdb/gdb-6.8.orig/gdb/gdbserver/server.c 2008-02-19 22:36:54.000000000 +0100 --- ./server.c 2009-06-13 11:57:58.000000000 +0200 *************** *** 1061,1071 **** --- 1061,1073 ---- continue; } + #if 0 if (setjmp (toplevel)) { fprintf (stderr, "Exiting\n"); exit (1); } + #endif port = *next_arg; next_arg++; *************** *** 1141,1153 **** shared library event" notice on gdb side. */ dlls_changed = 0; if (setjmp (toplevel)) { fprintf (stderr, "Killing inferior\n"); kill_inferior (); exit (1); } ! if (status == 'W' || status == 'X') was_running = 0; else --- 1143,1156 ---- shared library event" notice on gdb side. */ dlls_changed = 0; + #if 0 if (setjmp (toplevel)) { fprintf (stderr, "Killing inferior\n"); kill_inferior (); exit (1); } ! #endif if (status == 'W' || status == 'X') was_running = 0; else *************** *** 1164,1169 **** --- 1167,1173 ---- remote_open (port); restart: + #if 0 if (setjmp (toplevel) != 0) { /* An error occurred. */ *************** *** 1173,1178 **** --- 1177,1183 ---- putpkt (own_buf); } } + #endif disable_async_io (); while (!exit_requested) Only in .: spu-low.c~ diff -c /home/danny/src/gdb/gdb/gdb-6.8.orig/gdb/gdbserver/utils.c ./utils.c *** /home/danny/src/gdb/gdb/gdb-6.8.orig/gdb/gdbserver/utils.c 2008-01-01 23:53:14.000000000 +0100 --- ./utils.c 2009-05-31 15:38:07.000000000 +0200 *************** *** 65,71 **** --- 65,75 ---- fflush (stdout); vfprintf (stderr, string, args); fprintf (stderr, "\n"); + #ifdef __MINGW32CE__ + exit(1); + #else longjmp (toplevel, 1); + #endif } /* Print an error message and exit reporting failure. diff -c /home/danny/src/gdb/gdb/gdb-6.8.orig/gdb/gdbserver/win32-i386-low.c ./win32-i386-low.c *** /home/danny/src/gdb/gdb/gdb-6.8.orig/gdb/gdbserver/win32-i386-low.c 2008-01-01 23:53:14.000000000 +0100 --- ./win32-i386-low.c 2009-06-13 11:50:19.000000000 +0200 *************** *** 36,51 **** debug_registers_used = 0; } static void i386_get_thread_context (win32_thread_info *th, DEBUG_EVENT* current_event) { ! th->context.ContextFlags = \ ! CONTEXT_FULL | \ ! CONTEXT_FLOATING_POINT | \ ! CONTEXT_EXTENDED_REGISTERS | \ CONTEXT_DEBUG_REGISTERS; ! GetThreadContext (th->h, &th->context); debug_registers_changed = 0; --- 36,71 ---- debug_registers_used = 0; } + /* + * According to Mike Stall's .net debugging blog + * (http://blogs.msdn.com/jmstall/archive/2005/01/18/355697.aspx) + * the CONTEXT_EXTENDED_REGISTERS flag must be omitted if hardware doesn't + * support it. So I guess the only reasonable thing to do is just try. + */ static void i386_get_thread_context (win32_thread_info *th, DEBUG_EVENT* current_event) { ! /* try all flags */ ! th->context.ContextFlags = ! CONTEXT_FULL | ! CONTEXT_FLOATING_POINT | ! CONTEXT_EXTENDED_REGISTERS | CONTEXT_DEBUG_REGISTERS; ! if (GetThreadContext (th->h, &th->context) == 0) { ! DWORD e = GetLastError(); ! ! if (e == ERROR_INVALID_PARAMETER) { ! /* try limited set */ ! th->context.ContextFlags = CONTEXT_FULL | ! CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS; ! if (GetThreadContext (th->h, &th->context) == 0) { ! DWORD e = GetLastError(); ! printf("GetThreadContext failure %d\n", e); ! return; ! } ! } ! } debug_registers_changed = 0; *************** *** 190,195 **** --- 210,236 ---- collect_register (r, context_offset); } + /* + * The INT 3 instruction is traditionally used for x86 platform breakpointing. + * Microsoft also appears to use a DebugBreak function, which probably does the same. + * Gas translates "int $3" (or "int3") to a one-byte instruction : 0xCC . + * + * From Wikipedia : + * + * The INT 3 instruction is defined for use by debuggers to temporarily replace + * an instruction in a running program, in order to set a breakpoint. Other INT + * instructions are encoded using two bytes. This makes them unsuitable for use + * in patching instructions (which can be one byte long). + * + * The opcode for INT 3 is 0xCC, as opposite from the opcode for INT immediate, + * which is 0xCD imm8. According to Intel documentation: "Intel and Microsoft + * assemblers will not generate the CD03 opcode from any mnemonic" and 0xCC + * has some special features, which are not shared by "the normal 2-byte + * opcode for INT 3 (CD03)" [IA-32 Arch. Software Developer’s Manual. Vol. 2A] + */ + static const unsigned char i386_wince_breakpoint = 0xCC; + #define i386_wince_breakpoint_len 1 + struct win32_target_ops the_low_target = { sizeof (mappings) / sizeof (mappings[0]), i386_initial_stuff, *************** *** 199,205 **** i386_fetch_inferior_register, i386_store_inferior_register, i386_single_step, ! NULL, /* breakpoint */ ! 0, /* breakpoint_len */ "i386" /* arch_string */ }; --- 240,246 ---- i386_fetch_inferior_register, i386_store_inferior_register, i386_single_step, ! &i386_wince_breakpoint, /* breakpoint */ ! i386_wince_breakpoint_len, /* breakpoint_len */ "i386" /* arch_string */ }; diff -c /home/danny/src/gdb/gdb/gdb-6.8.orig/gdb/gdbserver/win32-low.c ./win32-low.c *** /home/danny/src/gdb/gdb/gdb-6.8.orig/gdb/gdbserver/win32-low.c 2008-02-14 23:41:39.000000000 +0100 --- ./win32-low.c 2009-06-13 16:18:48.000000000 +0200 *************** *** 894,907 **** loaded_dll (buf2, load_addr); } static char * get_image_name (HANDLE h, void *address, int unicode) { ! static char buf[(2 * MAX_PATH) + 1]; DWORD size = unicode ? sizeof (WCHAR) : sizeof (char); char *address_ptr; int len = 0; ! char b[2]; DWORD done; /* Attempt to read the name of the dll that was detected. --- 894,912 ---- loaded_dll (buf2, load_addr); } + /* + * Warning : some parts of this function rely on sizeof(WCHAR) == 2 + */ static char * get_image_name (HANDLE h, void *address, int unicode) { ! static char buf[(2 * MAX_PATH) + 1]; /* here */ DWORD size = unicode ? sizeof (WCHAR) : sizeof (char); char *address_ptr; + #ifndef _WIN32_WCE int len = 0; ! char b[2]; /* here */ ! #endif DWORD done; /* Attempt to read the name of the dll that was detected. *************** *** 924,932 **** return NULL; #endif /* Find the length of the string */ while (ReadProcessMemory (h, address_ptr + len++ * size, &b, size, &done) ! && (b[0] != 0 || b[size - 1] != 0) && done == size) continue; if (!unicode) --- 929,956 ---- return NULL; #endif + #ifdef _WIN32_WCE + /* Always unicode */ + /* Assume you can read it all in one go, or otherwise the done variable will + * tell you how far you've read. + */ + WCHAR *wbuf = alloca ((MAX_PATH + 1) * size); + ReadProcessMemory (h, address_ptr, wbuf, MAX_PATH * size, &done); + if (done < 0 || done > MAX_PATH * size) + buf[0] = '\0'; + else { + int n; + n = wcstombs (buf, wbuf, done); + if (n == (size_t)-1) + buf[0] = '\0'; + /* No need to address the length limit case of the wcstombs call, + * buf has been allocated large enough. */ + } + return buf; + #else /* Find the length of the string */ while (ReadProcessMemory (h, address_ptr + len++ * size, &b, size, &done) ! && (b[0] != 0 || b[size - 1] != 0) && done == size) /* here */ continue; if (!unicode) *************** *** 936,946 **** WCHAR *unicode_address = (WCHAR *) alloca (len * sizeof (WCHAR)); ReadProcessMemory (h, address_ptr, unicode_address, len * sizeof (WCHAR), &done); - WideCharToMultiByte (CP_ACP, 0, unicode_address, len, buf, len, 0, 0); } - return buf; } typedef BOOL (WINAPI *winapi_EnumProcessModules) (HANDLE, HMODULE *, --- 960,969 ---- WCHAR *unicode_address = (WCHAR *) alloca (len * sizeof (WCHAR)); ReadProcessMemory (h, address_ptr, unicode_address, len * sizeof (WCHAR), &done); WideCharToMultiByte (CP_ACP, 0, unicode_address, len, buf, len, 0, 0); } return buf; + #endif } typedef BOOL (WINAPI *winapi_EnumProcessModules) (HANDLE, HMODULE *, *************** *** 990,997 **** DWORD cbNeeded; BOOL ok; ! if (!load_psapi ()) goto failed; cbNeeded = 0; ok = (*win32_EnumProcessModules) (current_process_handle, --- 1013,1021 ---- DWORD cbNeeded; BOOL ok; ! if (!load_psapi ()) { goto failed; + } cbNeeded = 0; ok = (*win32_EnumProcessModules) (current_process_handle, *************** *** 1144,1149 **** --- 1168,1174 ---- /* Windows does not report the image name of the dlls in the debug event on attaches. We resort to iterating over the list of loaded dlls looking for a match by image base. */ + /* Note : no psapi.dll on CE, fall back to get_image_name below. */ if (!psapi_get_dll_name ((DWORD) event->lpBaseOfDll, dll_buf)) { if (!server_waiting) *************** *** 1368,1373 **** --- 1393,1399 ---- happen is the user will see a spurious breakpoint. */ current_event.dwDebugEventCode = 0; + OUTMSG2(("attaching: before WaitForDebugEvent\n")); if (!WaitForDebugEvent (¤t_event, 0)) { OUTMSG2(("no attach events left\n")); *************** *** 1383,1390 **** /* Keep the wait time low enough for confortable remote interruption, but high enough so gdbserver doesn't become a bottleneck. */ ! if (!WaitForDebugEvent (¤t_event, 250)) return 0; } gotevent: --- 1409,1427 ---- /* Keep the wait time low enough for confortable remote interruption, but high enough so gdbserver doesn't become a bottleneck. */ ! if (!WaitForDebugEvent (¤t_event, 250)) { ! /* ! * Sometimes an application will just not start up. ! * Detect this here, return in such a way that the loop ends. ! */ ! DWORD e = GetLastError(); ! ! if (e == ERROR_PIPE_NOT_CONNECTED) { ! ourstatus->kind = TARGET_WAITKIND_EXITED; ! return 1; /* break the loop in our caller */ ! } return 0; + } } gotevent: --=-N+ek63Sd5DbA9YZguL51--