[PATCH v2 6/8] winedbg: buffer and escape output of GDB qXfer commands.

Jinoh Kang jinoh.kang.kr at gmail.com
Mon Nov 1 00:41:12 CDT 2021


Signed-off-by: Jinoh Kang <jinoh.kang.kr at gmail.com>
---
 programs/winedbg/gdbproxy.c | 284 +++++++++++++++++++++++++++---------
 1 file changed, 211 insertions(+), 73 deletions(-)

diff --git a/programs/winedbg/gdbproxy.c b/programs/winedbg/gdbproxy.c
index bdb73659ade..caf64983c49 100644
--- a/programs/winedbg/gdbproxy.c
+++ b/programs/winedbg/gdbproxy.c
@@ -54,6 +54,13 @@ struct gdb_xpoint
     unsigned int value;
 };
 
+struct vl_buffer
+{
+    void *base;
+    size_t len;
+    size_t alloc;
+};
+
 struct gdb_context
 {
     /* gdb information */
@@ -82,6 +89,7 @@ struct gdb_context
     /* Unix environment */
     ULONG_PTR                   wine_segs[3];   /* load addresses of the ELF wine exec segments (text, bss and data) */
     BOOL                        no_ack_mode;
+    struct vl_buffer            qxfer_buffer;
 };
 
 /* assume standard signal and errno values */
@@ -224,6 +232,102 @@ static void hex_to(char* dst, const void* src, size_t len)
     }
 }
 
+static void* buffer_realloc(void* buf, size_t size);
+
+static void vl_resize(struct vl_buffer* vlbuf, size_t alloc)
+{
+    vlbuf->alloc = alloc;
+    vlbuf->base = buffer_realloc(vlbuf->base, vlbuf->alloc);
+}
+
+static void vl_empty(struct vl_buffer *vlbuf)
+{
+    vlbuf->len = 0;
+    vl_resize(vlbuf, 0);
+}
+
+static void vl_grow(struct vl_buffer* vlbuf, size_t size)
+{
+    if (vlbuf->alloc < vlbuf->len + size)
+        vl_resize(vlbuf, ((vlbuf->len + size) / 32 + 1) * 32);
+}
+
+static void vl_append(struct vl_buffer* vlbuf, const void *data, size_t size)
+{
+    vl_grow(vlbuf, size);
+    memcpy((void *)((unsigned char *)vlbuf->base + vlbuf->len), data, size);
+    vlbuf->len += size;
+}
+
+static inline void vl_append_str(struct vl_buffer* vlbuf, const char* str)
+{
+    vl_append(vlbuf, (const void *)str, strlen(str));
+}
+
+static inline void vl_append_uinthex(struct vl_buffer* vlbuf, ULONG_PTR val, int len)
+{
+    char buf[sizeof(ULONG_PTR) * 2], *ptr;
+
+    assert(len <= sizeof(ULONG_PTR));
+
+    for (ptr = buf + len * 2; ptr != buf; val >>= 4)
+        *--ptr = hex_to0(val & 0x0F);
+
+    vl_append(vlbuf, ptr, len * 2);
+}
+
+static const unsigned char xml_special_chars_lookup_table[16] = {
+    /* The characters should be sorted by its value modulo table length. */
+
+    0x00,       /* NUL */
+    0,
+    0x22,       /* ": 0010|0010 */
+    0, 0, 0,
+    0x26,       /* &: 0010|0110 */
+    0x27,       /* ': 0010|0111 */
+    0, 0, 0, 0,
+    0x3C,       /* <: 0011|1100 */
+    0,
+    0x3E,       /* >: 0011|1110 */
+    0
+};
+
+static inline BOOL is_nul_or_xml_special_char(unsigned char val)
+{
+    /* Note: strcspn() uses lookup tables as well, but as of Wine 6.19
+     * msvcrt!strcspn allocates 1024 bytes (sizeof(BOOL)*256) of table
+     * on the stack and populates it on the fly.  It would be slower and less
+     * cache-friendly than a preallocated, tiny static lookup table.
+     */
+
+    const size_t length = ARRAY_SIZE(xml_special_chars_lookup_table);
+    return xml_special_chars_lookup_table[val % length] == val;
+}
+
+static void vl_append_xmlstr(struct vl_buffer* vlbuf, const char *str)
+{
+    const char *ptr = str, *sptr;
+
+    for (;;)
+    {
+        for (sptr = ptr; !is_nul_or_xml_special_char((unsigned char)*sptr); sptr++)
+            ;
+
+        vl_append(vlbuf, ptr, sptr - ptr);
+        ptr = sptr;
+
+        switch (*ptr++)
+        {
+        case '"': vl_append_str(vlbuf, """); break;
+        case '&': vl_append_str(vlbuf, "&"); break;
+        case '\'': vl_append_str(vlbuf, "'"); break;
+        case '<': vl_append_str(vlbuf, "<"); break;
+        case '>': vl_append_str(vlbuf, ">"); break;
+        default: return;
+        }
+    }
+}
+
 static unsigned char checksum(const char* ptr, int len)
 {
     unsigned cksum = 0;
@@ -833,34 +937,6 @@ 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, unsigned int off, unsigned 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 (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)
 {
@@ -891,6 +967,38 @@ static inline void packet_reply_register_hex_to(struct gdb_context* gdbctx, dbg_
     packet_reply_hex_to(gdbctx, cpu_register_ptr(gdbctx, ctx, idx), cpu_register_map[idx].length);
 }
 
+static void packet_reply_xfer(
+    struct gdb_context* gdbctx,
+    const void *data,
+    size_t datalen,
+    unsigned int off,
+    unsigned int len,
+    BOOL *more_p
+)
+{
+    BOOL nonempty, more;
+    size_t trunc_len;
+
+    packet_reply_open(gdbctx);
+
+    nonempty = (size_t)off < datalen;
+    more = nonempty && (size_t)off + len < datalen;
+    if (more)
+        packet_reply_add(gdbctx, "m");
+    else
+        packet_reply_add(gdbctx, "l");
+
+    if (nonempty)
+    {
+        trunc_len = min((size_t)len, datalen - off);
+        packet_reply_add_raw(gdbctx, (const unsigned char *)data + off, trunc_len);
+    }
+
+    packet_reply_close(gdbctx);
+
+    *more_p = more;
+}
+
 /* =============================================== *
  *          P A C K E T   H A N D L E R S          *
  * =============================================== *
@@ -1587,6 +1695,7 @@ static enum packet_return packet_query_remote_command(struct gdb_context* gdbctx
 static BOOL CALLBACK packet_query_libraries_cb(PCSTR mod_name, DWORD64 base, PVOID ctx)
 {
     struct gdb_context* gdbctx = ctx;
+    struct vl_buffer* vlbuf = &gdbctx->qxfer_buffer;
     MEMORY_BASIC_INFORMATION mbi;
     IMAGE_SECTION_HEADER *sec;
     IMAGE_DOS_HEADER *dos = NULL;
@@ -1599,11 +1708,11 @@ static BOOL CALLBACK packet_query_libraries_cb(PCSTR mod_name, DWORD64 base, PVO
     mod.SizeOfStruct = sizeof(mod);
     SymGetModuleInfo64(gdbctx->process->handle, base, &mod);
 
-    packet_reply_add(gdbctx, "<library name=\"");
+    vl_append_str(vlbuf, "<library name=\"");
     if (strcmp(mod.LoadedImageName, "[vdso].so") == 0)
-        packet_reply_add(gdbctx, "linux-vdso.so.1");
+        vl_append_xmlstr(vlbuf, "linux-vdso.so.1");
     else if (mod.LoadedImageName[0] == '/')
-        packet_reply_add(gdbctx, mod.LoadedImageName);
+        vl_append_xmlstr(vlbuf, mod.LoadedImageName);
     else
     {
         UNICODE_STRING nt_name;
@@ -1618,15 +1727,15 @@ static BOOL CALLBACK packet_query_libraries_cb(PCSTR mod_name, DWORD64 base, PVO
             if (IsWow64Process(gdbctx->process->handle, &is_wow64) &&
                 is_wow64 && (tmp = strstr(unix_path, "system32")))
                 memcpy(tmp, "syswow64", 8);
-            packet_reply_add(gdbctx, unix_path);
+            vl_append_xmlstr(vlbuf, unix_path);
         }
         else
-            packet_reply_add(gdbctx, mod.LoadedImageName);
+            vl_append_xmlstr(vlbuf, mod.LoadedImageName);
 
         HeapFree(GetProcessHeap(), 0, unix_path);
         RtlFreeUnicodeString(&nt_name);
     }
-    packet_reply_add(gdbctx, "\">");
+    vl_append_str(vlbuf, "\">");
 
     size = sizeof(buffer);
     if (VirtualQueryEx(gdbctx->process->handle, (void *)(UINT_PTR)mod.BaseOfImage, &mbi, sizeof(mbi)) >= sizeof(mbi) &&
@@ -1657,72 +1766,75 @@ static BOOL CALLBACK packet_query_libraries_cb(PCSTR mod_name, DWORD64 base, PVO
     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(ULONG_PTR));
-        packet_reply_add(gdbctx, "\"/>");
+        vl_append_str(vlbuf, "<segment address=\"0x");
+        vl_append_uinthex(vlbuf, mod.BaseOfImage + sec[i].VirtualAddress, sizeof(ULONG_PTR));
+        vl_append_str(vlbuf, "\"/>");
     }
 
-    packet_reply_add(gdbctx, "</library>");
+    vl_append_str(vlbuf, "</library>");
 
     return TRUE;
 }
 
 static void packet_query_libraries(struct gdb_context* gdbctx)
 {
+    struct vl_buffer *vlbuf = &gdbctx->qxfer_buffer;
     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>");
+    vl_append_str(vlbuf, "<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>");
+    vl_append_str(vlbuf, "</library-list>");
 }
 
 static void packet_query_threads(struct gdb_context* gdbctx)
 {
+    struct vl_buffer *vlbuf = &gdbctx->qxfer_buffer;
     struct dbg_process* process = gdbctx->process;
     struct dbg_thread* thread;
 
-    packet_reply_add(gdbctx, "<threads>");
+    vl_append_str(vlbuf, "<threads>");
     LIST_FOR_EACH_ENTRY(thread, &process->threads, struct dbg_thread, entry)
     {
-        packet_reply_add(gdbctx, "<thread ");
-        packet_reply_add(gdbctx, "id=\"");
-        packet_reply_val(gdbctx, thread->tid, 4);
-        packet_reply_add(gdbctx, "\" name=\"");
-        packet_reply_add(gdbctx, thread->name);
-        packet_reply_add(gdbctx, "\"/>");
+        vl_append_str(vlbuf, "<thread ");
+        vl_append_str(vlbuf, "id=\"");
+        vl_append_uinthex(vlbuf, thread->tid, 4);
+        vl_append_str(vlbuf, "\" name=\"");
+        vl_append_str(vlbuf, thread->name);
+        vl_append_str(vlbuf, "\"/>");
     }
-    packet_reply_add(gdbctx, "</threads>");
+    vl_append_str(vlbuf, "</threads>");
 }
 
 static void packet_query_target_xml(struct gdb_context* gdbctx, struct backend_cpu* cpu)
 {
+    struct vl_buffer *vlbuf = &gdbctx->qxfer_buffer;
     const char* feature_prefix = NULL;
     const char* feature = NULL;
     char buffer[256];
     int i;
 
-    packet_reply_add(gdbctx, "<target>");
+    vl_append_str(vlbuf, "<target>");
     switch (cpu->machine)
     {
     case IMAGE_FILE_MACHINE_AMD64:
-        packet_reply_add(gdbctx, "<architecture>i386:x86-64</architecture>");
+        vl_append_str(vlbuf, "<architecture>i386:x86-64</architecture>");
         feature_prefix = "org.gnu.gdb.i386.";
         break;
     case IMAGE_FILE_MACHINE_I386:
-        packet_reply_add(gdbctx, "<architecture>i386</architecture>");
+        vl_append_str(vlbuf, "<architecture>i386</architecture>");
         feature_prefix = "org.gnu.gdb.i386.";
         break;
     case IMAGE_FILE_MACHINE_ARMNT:
-        packet_reply_add(gdbctx, "<architecture>arm</architecture>");
+        vl_append_str(vlbuf, "<architecture>arm</architecture>");
         feature_prefix = "org.gnu.gdb.arm.";
         break;
     case IMAGE_FILE_MACHINE_ARM64:
-        packet_reply_add(gdbctx, "<architecture>aarch64</architecture>");
+        vl_append_str(vlbuf, "<architecture>aarch64</architecture>");
         feature_prefix = "org.gnu.gdb.aarch64.";
         break;
     }
@@ -1731,17 +1843,17 @@ static void packet_query_target_xml(struct gdb_context* gdbctx, struct backend_c
     {
         if (cpu->gdb_register_map[i].feature)
         {
-            if (feature) packet_reply_add(gdbctx, "</feature>");
+            if (feature) vl_append_str(vlbuf, "</feature>");
             feature = cpu->gdb_register_map[i].feature;
 
-            packet_reply_add(gdbctx, "<feature name=\"");
-            if (feature_prefix) packet_reply_add(gdbctx, feature_prefix);
-            packet_reply_add(gdbctx, feature);
-            packet_reply_add(gdbctx, "\">");
+            vl_append_str(vlbuf, "<feature name=\"");
+            if (feature_prefix) vl_append_str(vlbuf, feature_prefix);
+            vl_append_str(vlbuf, feature);
+            vl_append_str(vlbuf, "\">");
 
             if (strcmp(feature_prefix, "org.gnu.gdb.i386.") == 0 &&
                 strcmp(feature, "core") == 0)
-                packet_reply_add(gdbctx, "<flags id=\"i386_eflags\" size=\"4\">"
+                vl_append_str(vlbuf, "<flags id=\"i386_eflags\" size=\"4\">"
                                          "<field name=\"CF\" start=\"0\" end=\"0\"/>"
                                          "<field name=\"\" start=\"1\" end=\"1\"/>"
                                          "<field name=\"PF\" start=\"2\" end=\"2\"/>"
@@ -1763,7 +1875,7 @@ static void packet_query_target_xml(struct gdb_context* gdbctx, struct backend_c
 
             if (strcmp(feature_prefix, "org.gnu.gdb.i386.") == 0 &&
                 strcmp(feature, "sse") == 0)
-                packet_reply_add(gdbctx, "<vector id=\"v4f\" type=\"ieee_single\" count=\"4\"/>"
+                vl_append_str(vlbuf, "<vector id=\"v4f\" type=\"ieee_single\" count=\"4\"/>"
                                          "<vector id=\"v2d\" type=\"ieee_double\" count=\"2\"/>"
                                          "<vector id=\"v16i8\" type=\"int8\" count=\"16\"/>"
                                          "<vector id=\"v8i16\" type=\"int16\" count=\"8\"/>"
@@ -1798,20 +1910,20 @@ static void packet_query_target_xml(struct gdb_context* gdbctx, struct backend_c
 
         snprintf(buffer, ARRAY_SIZE(buffer), "<reg name=\"%s\" bitsize=\"%Iu\"",
                  cpu->gdb_register_map[i].name, 8 * cpu->gdb_register_map[i].length);
-        packet_reply_add(gdbctx, buffer);
+        vl_append_str(vlbuf, buffer);
 
         if (cpu->gdb_register_map[i].type)
         {
-            packet_reply_add(gdbctx, " type=\"");
-            packet_reply_add(gdbctx, cpu->gdb_register_map[i].type);
-            packet_reply_add(gdbctx, "\"");
+            vl_append_str(vlbuf, " type=\"");
+            vl_append_str(vlbuf, cpu->gdb_register_map[i].type);
+            vl_append_str(vlbuf, "\"");
         }
 
-        packet_reply_add(gdbctx, "/>");
+        vl_append_str(vlbuf, "/>");
     }
 
-    if (feature) packet_reply_add(gdbctx, "</feature>");
-    packet_reply_add(gdbctx, "</target>");
+    if (feature) vl_append_str(vlbuf, "</feature>");
+    vl_append_str(vlbuf, "</target>");
 }
 
 static enum packet_return packet_query(struct gdb_context* gdbctx)
@@ -1941,32 +2053,56 @@ static enum packet_return packet_query(struct gdb_context* gdbctx)
     case 'X':
         if (sscanf(gdbctx->in_packet, "Xfer:libraries:read::%x,%x", &off, &len) == 2)
         {
+            BOOL more;
+
             if (!gdbctx->process) return packet_error;
 
-            packet_reply_open_xfer(gdbctx);
+            vl_empty(&gdbctx->qxfer_buffer);
             packet_query_libraries(gdbctx);
-            packet_reply_close_xfer(gdbctx, off, len);
+            packet_reply_xfer(
+                gdbctx,
+                gdbctx->qxfer_buffer.base,
+                gdbctx->qxfer_buffer.len,
+                off, len, &more
+            );
+            vl_empty(&gdbctx->qxfer_buffer);
             return packet_done;
         }
 
         if (sscanf(gdbctx->in_packet, "Xfer:threads:read::%x,%x", &off, &len) == 2)
         {
+            BOOL more;
+
             if (!gdbctx->process) return packet_error;
 
-            packet_reply_open_xfer(gdbctx);
+            vl_empty(&gdbctx->qxfer_buffer);
             packet_query_threads(gdbctx);
-            packet_reply_close_xfer(gdbctx, off, len);
+            packet_reply_xfer(
+                gdbctx,
+                gdbctx->qxfer_buffer.base,
+                gdbctx->qxfer_buffer.len,
+                off, len, &more
+            );
+            vl_empty(&gdbctx->qxfer_buffer);
             return packet_done;
         }
 
         if (sscanf(gdbctx->in_packet, "Xfer:features:read:target.xml:%x,%x", &off, &len) == 2)
         {
+            BOOL more;
+
             if (!gdbctx->process) return packet_error;
             if (!(cpu = gdbctx->process->be_cpu)) return packet_error;
 
-            packet_reply_open_xfer(gdbctx);
+            vl_empty(&gdbctx->qxfer_buffer);
             packet_query_target_xml(gdbctx, cpu);
-            packet_reply_close_xfer(gdbctx, off, len);
+            packet_reply_xfer(
+                gdbctx,
+                gdbctx->qxfer_buffer.base,
+                gdbctx->qxfer_buffer.len,
+                off, len, &more
+            );
+            vl_empty(&gdbctx->qxfer_buffer);
             return packet_done;
         }
         break;
@@ -2283,6 +2419,8 @@ static BOOL gdb_init_context(struct gdb_context* gdbctx, unsigned flags, unsigne
     for (i = 0; i < ARRAY_SIZE(gdbctx->wine_segs); i++)
         gdbctx->wine_segs[i] = 0;
 
+    memset(&gdbctx->qxfer_buffer, 0, sizeof(gdbctx->qxfer_buffer));
+
     /* wait for first trap */
     while (WaitForDebugEvent(&gdbctx->de, INFINITE))
     {
-- 
2.31.1




More information about the wine-devel mailing list