[PATCH v3 6/9] winedbg: Buffer and escape output of GDB qXfer commands.
Jinoh Kang
jinoh.kang.kr at gmail.com
Tue Nov 16 10:51:43 CST 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