For now gdb does not request it as it still believes it's running a normal application. It will however, as soon as we advertise support for qXfer:features:read request and reply with a custom register set.
This also introduces packet_reply_open_xfer / packet_reply_close_xfer function to allow partial replies. It always allocate the full reply for simplicity and then truncates to the requested offset and size.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com ---
v2: This now reads the target process memory after checking if the file is mapped as MEM_IMAGE to get the file headers and iterate over the sections and report them individually to gdb.
Other non-PE modules are reported as a single segment, starting at the module base address.
This appears to be enough for gdb to be happy. It still struggles to get source/line information for non-PE builtins but they tend to disappear anyway, and otherwise it gets the backtraces right.
The whole thing works both for 32bit wine/winedbg debugging 32bit app, 64bit wine/winedbg debugging 32bit/64bit app, but not really for 32bit wine/winedbg debugging 64bit app. From my tests it also seems to be a gdb limitation as it chokes on startup on apparently valid replies. Still big usability improvement I believe.
Breakpoints work because gdb emulates them using memory read/writes, and the last missing piece is watchpoint support, which I will send later.
programs/winedbg/gdbproxy.c | 139 ++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+)
diff --git a/programs/winedbg/gdbproxy.c b/programs/winedbg/gdbproxy.c index 7c57caf9d1e8..0c154d0b21c7 100644 --- a/programs/winedbg/gdbproxy.c +++ b/programs/winedbg/gdbproxy.c @@ -767,6 +767,35 @@ static void packet_reply_close(struct gdb_context* gdbctx) gdbctx->out_curr_packet = -1; }
+static void packet_reply_open_xfer(struct gdb_context* gdbctx) +{ + packet_reply_open(gdbctx); + packet_reply_add(gdbctx, "m"); +} + +static void packet_reply_close_xfer(struct gdb_context* gdbctx, int off, int len) +{ + int begin = gdbctx->out_curr_packet + 1; + int plen; + + if (begin + off < gdbctx->out_len) + { + gdbctx->out_len -= off; + memmove(gdbctx->out_buf + begin, gdbctx->out_buf + begin + off, gdbctx->out_len); + } + else + { + gdbctx->out_buf[gdbctx->out_curr_packet] = 'l'; + gdbctx->out_len = gdbctx->out_curr_packet + 1; + } + + plen = gdbctx->out_len - begin; + if (len >= 0 && plen > len) gdbctx->out_len -= (plen - len); + else gdbctx->out_buf[gdbctx->out_curr_packet] = 'l'; + + packet_reply_close(gdbctx); +} + static enum packet_return packet_reply(struct gdb_context* gdbctx, const char* packet) { packet_reply_open(gdbctx); @@ -1396,8 +1425,107 @@ static enum packet_return packet_query_remote_command(struct gdb_context* gdbctx return packet_reply_error(gdbctx, EINVAL); }
+static BOOL CALLBACK packet_query_libraries_cb(PCSTR mod_name, DWORD64 base, PVOID ctx) +{ + struct gdb_context* gdbctx = ctx; + MEMORY_BASIC_INFORMATION mbi; + IMAGE_SECTION_HEADER *sec; + IMAGE_DOS_HEADER *dos = NULL; + IMAGE_NT_HEADERS *nth = NULL; + IMAGEHLP_MODULE64 mod; + SIZE_T size, i; + BOOL is_wow64; + char buffer[0x400]; + + mod.SizeOfStruct = sizeof(mod); + SymGetModuleInfo64(gdbctx->process->handle, base, &mod); + + packet_reply_add(gdbctx, "<library name=""); + if (strcmp(mod.LoadedImageName, "[vdso].so") == 0) + packet_reply_add(gdbctx, "linux-vdso.so.1"); + else if (mod.LoadedImageName[0] == '/') + packet_reply_add(gdbctx, mod.LoadedImageName); + else + { + UNICODE_STRING nt_name; + ANSI_STRING ansi_name; + char *unix_path, *tmp; + + RtlInitAnsiString(&ansi_name, mod.LoadedImageName); + RtlAnsiStringToUnicodeString(&nt_name, &ansi_name, TRUE); + + if ((unix_path = wine_get_unix_file_name(nt_name.Buffer))) + { + if (IsWow64Process(gdbctx->process->handle, &is_wow64) && + is_wow64 && (tmp = strstr(unix_path, "system32"))) + memcpy(tmp, "syswow64", 8); + packet_reply_add(gdbctx, unix_path); + } + else + packet_reply_add(gdbctx, mod.LoadedImageName); + + HeapFree(GetProcessHeap(), 0, unix_path); + RtlFreeUnicodeString(&nt_name); + } + packet_reply_add(gdbctx, "">"); + + size = sizeof(buffer); + if (VirtualQueryEx(gdbctx->process->handle, (void *)(UINT_PTR)mod.BaseOfImage, &mbi, sizeof(mbi)) >= sizeof(mbi) && + mbi.Type == MEM_IMAGE && mbi.State != MEM_FREE) + { + if (ReadProcessMemory(gdbctx->process->handle, (void *)(UINT_PTR)mod.BaseOfImage, buffer, size, &size) && + size >= sizeof(IMAGE_DOS_HEADER)) + dos = (IMAGE_DOS_HEADER *)buffer; + + if (dos && dos->e_magic == IMAGE_DOS_SIGNATURE && dos->e_lfanew < size) + nth = (IMAGE_NT_HEADERS *)(buffer + dos->e_lfanew); + + if (nth && memcmp(&nth->Signature, "PE\0\0", 4)) + nth = NULL; + } + + if (!nth) memset(buffer, 0, sizeof(buffer)); + + /* if the module is not PE we have cleared buffer with 0, this makes + * the following computation valid in all cases. */ + dos = (IMAGE_DOS_HEADER *)buffer; + nth = (IMAGE_NT_HEADERS *)(buffer + dos->e_lfanew); + if (IsWow64Process(gdbctx->process->handle, &is_wow64) && is_wow64) + sec = IMAGE_FIRST_SECTION((IMAGE_NT_HEADERS32 *)nth); + else + sec = IMAGE_FIRST_SECTION((IMAGE_NT_HEADERS64 *)nth); + + for (i = 0; i < max(nth->FileHeader.NumberOfSections, 1); ++i) + { + if ((char *)(sec + i) >= buffer + size) break; + packet_reply_add(gdbctx, "<segment address="0x"); + packet_reply_val(gdbctx, mod.BaseOfImage + sec[i].VirtualAddress, sizeof(unsigned long)); + packet_reply_add(gdbctx, ""/>"); + } + + packet_reply_add(gdbctx, "</library>"); + + return TRUE; +} + +static void packet_query_libraries(struct gdb_context* gdbctx) +{ + BOOL opt; + + /* this will resynchronize builtin dbghelp's internal ELF module list */ + SymLoadModule(gdbctx->process->handle, 0, 0, 0, 0, 0); + + packet_reply_add(gdbctx, "<library-list>"); + opt = SymSetExtendedOption(SYMOPT_EX_WINE_NATIVE_MODULES, TRUE); + SymEnumerateModules64(gdbctx->process->handle, packet_query_libraries_cb, gdbctx); + SymSetExtendedOption(SYMOPT_EX_WINE_NATIVE_MODULES, opt); + packet_reply_add(gdbctx, "</library-list>"); +} + static enum packet_return packet_query(struct gdb_context* gdbctx) { + int off, len; + switch (gdbctx->in_packet[0]) { case 'f': @@ -1485,6 +1613,7 @@ static enum packet_return packet_query(struct gdb_context* gdbctx) { packet_reply_open(gdbctx); packet_reply_add(gdbctx, "QStartNoAckMode+;"); + packet_reply_add(gdbctx, "qXfer:libraries:read+;"); if (*target_xml) packet_reply_add(gdbctx, "PacketSize=400;qXfer:features:read+"); packet_reply_close(gdbctx); return packet_done; @@ -1516,6 +1645,16 @@ static enum packet_return packet_query(struct gdb_context* gdbctx) } break; case 'X': + if (sscanf(gdbctx->in_packet, "Xfer:libraries:read::%x,%x", &off, &len) == 2) + { + if (!gdbctx->process) return packet_error; + + packet_reply_open_xfer(gdbctx); + packet_query_libraries(gdbctx); + packet_reply_close_xfer(gdbctx, off, len); + return packet_done; + } + if (*target_xml && strncmp(gdbctx->in_packet, "Xfer:features:read:target.xml", 29) == 0) return packet_reply(gdbctx, target_xml); break;