diff mbox

[RFC,v4] monitor: add memory search commands s, sp

Message ID 1429879188-28888-1-git-send-email-hw.claudio@gmail.com
State New
Headers show

Commit Message

Claudio Fontana April 24, 2015, 12:39 p.m. UTC
From: Claudio Fontana <claudio.fontana@huawei.com>

usage is similar to the commands x, xp.

Example with string: looking for "ELF" header in memory:

(qemu) s/1000000cb 0x40001000 "ELF"
searching memory area [0000000040001000-00000000400f5240]
0000000040090001
(qemu) x/20b 0x40090000
0000000040090000: '\x7f' 'E' 'L' 'F' '\x02' '\x01' '\x01' '\x03'
0000000040090008: '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00'
0000000040090010: '\x02' '\x00' '\xb7' '\x00'

Example with value: looking for 64bit variable value 0x990088

(qemu) s/1000000xg 0xffff900042000000 0x990088
searching memory area [ffff900042000000-ffff9000427a1200]
ffff9000424b3000
ffff9000424c1000

Signed-off-by: Claudio Fontana <claudio.fontana@huawei.com>
---
 hmp-commands.hx |  28 ++++++++++++
 monitor.c       | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 168 insertions(+)

changes from v3:
initialize pointer variable to NULL to finally get rid of spurious warning

changes from v2:
move code to try to address spurious warning

changes from v1:
make checkpatch happy by adding braces here and there.

Comments

Luiz Capitulino May 1, 2015, 5:15 p.m. UTC | #1
On Fri, 24 Apr 2015 14:39:48 +0200
hw.claudio@gmail.com wrote:

> From: Claudio Fontana <claudio.fontana@huawei.com>
> 
> usage is similar to the commands x, xp.
> 
> Example with string: looking for "ELF" header in memory:
> 
> (qemu) s/1000000cb 0x40001000 "ELF"
> searching memory area [0000000040001000-00000000400f5240]
> 0000000040090001
> (qemu) x/20b 0x40090000
> 0000000040090000: '\x7f' 'E' 'L' 'F' '\x02' '\x01' '\x01' '\x03'
> 0000000040090008: '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00'
> 0000000040090010: '\x02' '\x00' '\xb7' '\x00'
> 
> Example with value: looking for 64bit variable value 0x990088
> 
> (qemu) s/1000000xg 0xffff900042000000 0x990088
> searching memory area [ffff900042000000-ffff9000427a1200]
> ffff9000424b3000
> ffff9000424c1000
> 
> Signed-off-by: Claudio Fontana <claudio.fontana@huawei.com>

Applied to the qmp branch, thanks.

> ---
>  hmp-commands.hx |  28 ++++++++++++
>  monitor.c       | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 168 insertions(+)
> 
> changes from v3:
> initialize pointer variable to NULL to finally get rid of spurious warning
> 
> changes from v2:
> move code to try to address spurious warning
> 
> changes from v1:
> make checkpatch happy by adding braces here and there.
> 
> diff --git a/hmp-commands.hx b/hmp-commands.hx
> index d5022d8..2bf5737 100644
> --- a/hmp-commands.hx
> +++ b/hmp-commands.hx
> @@ -432,6 +432,34 @@ Start gdbserver session (default @var{port}=1234)
>  ETEXI
>  
>      {
> +        .name       = "s",
> +        .args_type  = "fmt:/,addr:l,data:s",
> +        .params     = "/fmt addr data",
> +        .help       = "search virtual memory starting at 'addr' for 'data'",
> +        .mhandler.cmd = hmp_memory_search,
> +    },
> +
> +STEXI
> +@item s/fmt @var{addr} @var{data}
> +@findex s
> +Virtual memory search starting at @var{addr} for data described by @var{data}.
> +ETEXI
> +
> +    {
> +        .name       = "sp",
> +        .args_type  = "fmt:/,addr:l,data:s",
> +        .params     = "/fmt addr data",
> +        .help       = "search physical memory starting at 'addr' for 'data'",
> +        .mhandler.cmd = hmp_physical_memory_search,
> +    },
> +
> +STEXI
> +@item sp/fmt @var{addr} @var{data}
> +@findex sp
> +Physical memory search starting at @var{addr} for data described by @var{data}.
> +ETEXI
> +
> +    {
>          .name       = "x",
>          .args_type  = "fmt:/,addr:l",
>          .params     = "/fmt addr",
> diff --git a/monitor.c b/monitor.c
> index c86a89e..b648dd2 100644
> --- a/monitor.c
> +++ b/monitor.c
> @@ -1208,6 +1208,124 @@ static void monitor_printc(Monitor *mon, int c)
>      monitor_printf(mon, "'");
>  }
>  
> +static void monitor_print_addr(Monitor *mon, hwaddr addr, bool is_physical)
> +{
> +    if (is_physical) {
> +        monitor_printf(mon, TARGET_FMT_plx "\n", addr);
> +    } else {
> +        monitor_printf(mon, TARGET_FMT_lx "\n", (target_ulong)addr);
> +    }
> +}
> +
> +/* simple memory search for a byte sequence. The sequence is generated from
> + * a numeric value to look for in guest memory, or from a string.
> + */
> +static void memory_search(Monitor *mon, int count, int format, int wsize,
> +                          hwaddr addr, const char *data_str, bool is_physical)
> +{
> +    int pos, len;              /* pos in the search area, len of area */
> +    char *hay;                 /* buffer for haystack */
> +    int hay_size;              /* haystack size. Needle size is wsize. */
> +    const char *needle = NULL; /* needle to search in the haystack */
> +    const char *format_str;    /* numeric input format string */
> +    char value_raw[8];         /* numeric input converted to raw data */
> +#define MONITOR_S_CHUNK_SIZE 16000
> +
> +    len = wsize * count;
> +    if (len < 1) {
> +        monitor_printf(mon, "invalid search area length.\n");
> +        return;
> +    }
> +    switch (format) {
> +    case 'i':
> +        monitor_printf(mon, "format '%c' not supported.\n", format);
> +        return;
> +    case 'c':
> +        needle = data_str;
> +        wsize = strlen(data_str);
> +        if (wsize > MONITOR_S_CHUNK_SIZE) {
> +            monitor_printf(mon, "search string too long [max %d].\n",
> +                           MONITOR_S_CHUNK_SIZE);
> +            return;
> +        }
> +        break;
> +    case 'o':
> +        format_str = "%" SCNo64;
> +        break;
> +    default:
> +    case 'x':
> +        format_str = "%" SCNx64;
> +        break;
> +    case 'u':
> +        format_str = "%" SCNu64;
> +        break;
> +    case 'd':
> +        format_str = "%" SCNd64;
> +        break;
> +    }
> +    if (format != 'c') {
> +        uint64_t value;      /* numeric input value */
> +        void *from = &value;
> +        if (sscanf(data_str, format_str, &value) != 1) {
> +            monitor_printf(mon, "could not parse search string "
> +                           "\"%s\" as format '%c'.\n", data_str, format);
> +            return;
> +        }
> +#if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN)
> +        value = bswap64(value);
> +#endif
> +#if defined(TARGET_WORDS_BIGENDIAN)
> +        from += 8 - wsize;
> +#endif
> +        memcpy(value_raw, from, wsize);
> +        needle = value_raw;
> +    }
> +    monitor_printf(mon, "searching memory area ");
> +    if (is_physical) {
> +        monitor_printf(mon, "[" TARGET_FMT_plx "-" TARGET_FMT_plx "]\n",
> +                       addr, addr + len);
> +    } else {
> +        monitor_printf(mon, "[" TARGET_FMT_lx "-" TARGET_FMT_lx "]\n",
> +                       (target_ulong)addr, (target_ulong)addr + len);
> +    }
> +    hay_size = len < MONITOR_S_CHUNK_SIZE ? len : MONITOR_S_CHUNK_SIZE;
> +    hay = g_malloc0(hay_size);
> +
> +    for (pos = 0; pos < len;) {
> +        char *mark, *match; /* mark new starting position, eventual match */
> +        int l, todo;        /* total length to be processed in current chunk */
> +        l = len - pos;
> +        if (l > hay_size) {
> +            l = hay_size;
> +        }
> +        if (is_physical) {
> +            cpu_physical_memory_read(addr, hay, l);
> +        } else if (cpu_memory_rw_debug(ENV_GET_CPU(mon_get_cpu()), addr,
> +                                       (uint8_t *)hay, l, 0) < 0) {
> +            monitor_printf(mon, " Cannot access memory\n");
> +            break;
> +        }
> +        for (mark = hay, todo = l; todo >= wsize;) {
> +            match = memmem(mark, todo, needle, wsize);
> +            if (!match) {
> +                break;
> +            }
> +            monitor_print_addr(mon, addr + (match - hay), is_physical);
> +            mark = match + 1;
> +            todo = mark - hay;
> +        }
> +        if (pos + l < len) {
> +            /* catch potential matches across chunks. */
> +            pos += l - (wsize - 1);
> +            addr += l - (wsize - 1);
> +        } else {
> +            pos += l;
> +            addr += l;
> +        }
> +    }
> +    g_free(hay);
> +}
> +
>  static void memory_dump(Monitor *mon, int count, int format, int wsize,
>                          hwaddr addr, int is_physical)
>  {
> @@ -1332,6 +1450,28 @@ static void memory_dump(Monitor *mon, int count, int format, int wsize,
>      }
>  }
>  
> +static void hmp_memory_search(Monitor *mon, const QDict *qdict)
> +{
> +    int count = qdict_get_int(qdict, "count");
> +    int format = qdict_get_int(qdict, "format");
> +    int size = qdict_get_int(qdict, "size");
> +    target_long addr = qdict_get_int(qdict, "addr");
> +    const char *data_str = qdict_get_str(qdict, "data");
> +
> +    memory_search(mon, count, format, size, addr, data_str, false);
> +}
> +
> +static void hmp_physical_memory_search(Monitor *mon, const QDict *qdict)
> +{
> +    int count = qdict_get_int(qdict, "count");
> +    int format = qdict_get_int(qdict, "format");
> +    int size = qdict_get_int(qdict, "size");
> +    hwaddr addr = qdict_get_int(qdict, "addr");
> +    const char *data_str = qdict_get_str(qdict, "data");
> +
> +    memory_search(mon, count, format, size, addr, data_str, true);
> +}
> +
>  static void hmp_memory_dump(Monitor *mon, const QDict *qdict)
>  {
>      int count = qdict_get_int(qdict, "count");
Luiz Capitulino May 11, 2015, 2:16 p.m. UTC | #2
On Fri, 24 Apr 2015 14:39:48 +0200
hw.claudio@gmail.com wrote:

> From: Claudio Fontana <claudio.fontana@huawei.com>
> 
> usage is similar to the commands x, xp.
> 
> Example with string: looking for "ELF" header in memory:
> 
> (qemu) s/1000000cb 0x40001000 "ELF"
> searching memory area [0000000040001000-00000000400f5240]
> 0000000040090001
> (qemu) x/20b 0x40090000
> 0000000040090000: '\x7f' 'E' 'L' 'F' '\x02' '\x01' '\x01' '\x03'
> 0000000040090008: '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00'
> 0000000040090010: '\x02' '\x00' '\xb7' '\x00'
> 
> Example with value: looking for 64bit variable value 0x990088
> 
> (qemu) s/1000000xg 0xffff900042000000 0x990088
> searching memory area [ffff900042000000-ffff9000427a1200]
> ffff9000424b3000
> ffff9000424c1000
> 
> Signed-off-by: Claudio Fontana <claudio.fontana@huawei.com>

I had to drop this patch because it doesn't build for w32. You can
find instructions on how to build for w32 at:

 http://wiki.qemu.org/Hosts/W32

> ---
>  hmp-commands.hx |  28 ++++++++++++
>  monitor.c       | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 168 insertions(+)
> 
> changes from v3:
> initialize pointer variable to NULL to finally get rid of spurious warning
> 
> changes from v2:
> move code to try to address spurious warning
> 
> changes from v1:
> make checkpatch happy by adding braces here and there.
> 
> diff --git a/hmp-commands.hx b/hmp-commands.hx
> index d5022d8..2bf5737 100644
> --- a/hmp-commands.hx
> +++ b/hmp-commands.hx
> @@ -432,6 +432,34 @@ Start gdbserver session (default @var{port}=1234)
>  ETEXI
>  
>      {
> +        .name       = "s",
> +        .args_type  = "fmt:/,addr:l,data:s",
> +        .params     = "/fmt addr data",
> +        .help       = "search virtual memory starting at 'addr' for 'data'",
> +        .mhandler.cmd = hmp_memory_search,
> +    },
> +
> +STEXI
> +@item s/fmt @var{addr} @var{data}
> +@findex s
> +Virtual memory search starting at @var{addr} for data described by @var{data}.
> +ETEXI
> +
> +    {
> +        .name       = "sp",
> +        .args_type  = "fmt:/,addr:l,data:s",
> +        .params     = "/fmt addr data",
> +        .help       = "search physical memory starting at 'addr' for 'data'",
> +        .mhandler.cmd = hmp_physical_memory_search,
> +    },
> +
> +STEXI
> +@item sp/fmt @var{addr} @var{data}
> +@findex sp
> +Physical memory search starting at @var{addr} for data described by @var{data}.
> +ETEXI
> +
> +    {
>          .name       = "x",
>          .args_type  = "fmt:/,addr:l",
>          .params     = "/fmt addr",
> diff --git a/monitor.c b/monitor.c
> index c86a89e..b648dd2 100644
> --- a/monitor.c
> +++ b/monitor.c
> @@ -1208,6 +1208,124 @@ static void monitor_printc(Monitor *mon, int c)
>      monitor_printf(mon, "'");
>  }
>  
> +static void monitor_print_addr(Monitor *mon, hwaddr addr, bool is_physical)
> +{
> +    if (is_physical) {
> +        monitor_printf(mon, TARGET_FMT_plx "\n", addr);
> +    } else {
> +        monitor_printf(mon, TARGET_FMT_lx "\n", (target_ulong)addr);
> +    }
> +}
> +
> +/* simple memory search for a byte sequence. The sequence is generated from
> + * a numeric value to look for in guest memory, or from a string.
> + */
> +static void memory_search(Monitor *mon, int count, int format, int wsize,
> +                          hwaddr addr, const char *data_str, bool is_physical)
> +{
> +    int pos, len;              /* pos in the search area, len of area */
> +    char *hay;                 /* buffer for haystack */
> +    int hay_size;              /* haystack size. Needle size is wsize. */
> +    const char *needle = NULL; /* needle to search in the haystack */
> +    const char *format_str;    /* numeric input format string */
> +    char value_raw[8];         /* numeric input converted to raw data */
> +#define MONITOR_S_CHUNK_SIZE 16000
> +
> +    len = wsize * count;
> +    if (len < 1) {
> +        monitor_printf(mon, "invalid search area length.\n");
> +        return;
> +    }
> +    switch (format) {
> +    case 'i':
> +        monitor_printf(mon, "format '%c' not supported.\n", format);
> +        return;
> +    case 'c':
> +        needle = data_str;
> +        wsize = strlen(data_str);
> +        if (wsize > MONITOR_S_CHUNK_SIZE) {
> +            monitor_printf(mon, "search string too long [max %d].\n",
> +                           MONITOR_S_CHUNK_SIZE);
> +            return;
> +        }
> +        break;
> +    case 'o':
> +        format_str = "%" SCNo64;
> +        break;
> +    default:
> +    case 'x':
> +        format_str = "%" SCNx64;
> +        break;
> +    case 'u':
> +        format_str = "%" SCNu64;
> +        break;
> +    case 'd':
> +        format_str = "%" SCNd64;
> +        break;
> +    }
> +    if (format != 'c') {
> +        uint64_t value;      /* numeric input value */
> +        void *from = &value;
> +        if (sscanf(data_str, format_str, &value) != 1) {
> +            monitor_printf(mon, "could not parse search string "
> +                           "\"%s\" as format '%c'.\n", data_str, format);
> +            return;
> +        }
> +#if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN)
> +        value = bswap64(value);
> +#endif
> +#if defined(TARGET_WORDS_BIGENDIAN)
> +        from += 8 - wsize;
> +#endif
> +        memcpy(value_raw, from, wsize);
> +        needle = value_raw;
> +    }
> +    monitor_printf(mon, "searching memory area ");
> +    if (is_physical) {
> +        monitor_printf(mon, "[" TARGET_FMT_plx "-" TARGET_FMT_plx "]\n",
> +                       addr, addr + len);
> +    } else {
> +        monitor_printf(mon, "[" TARGET_FMT_lx "-" TARGET_FMT_lx "]\n",
> +                       (target_ulong)addr, (target_ulong)addr + len);
> +    }
> +    hay_size = len < MONITOR_S_CHUNK_SIZE ? len : MONITOR_S_CHUNK_SIZE;
> +    hay = g_malloc0(hay_size);
> +
> +    for (pos = 0; pos < len;) {
> +        char *mark, *match; /* mark new starting position, eventual match */
> +        int l, todo;        /* total length to be processed in current chunk */
> +        l = len - pos;
> +        if (l > hay_size) {
> +            l = hay_size;
> +        }
> +        if (is_physical) {
> +            cpu_physical_memory_read(addr, hay, l);
> +        } else if (cpu_memory_rw_debug(ENV_GET_CPU(mon_get_cpu()), addr,
> +                                       (uint8_t *)hay, l, 0) < 0) {
> +            monitor_printf(mon, " Cannot access memory\n");
> +            break;
> +        }
> +        for (mark = hay, todo = l; todo >= wsize;) {
> +            match = memmem(mark, todo, needle, wsize);
> +            if (!match) {
> +                break;
> +            }
> +            monitor_print_addr(mon, addr + (match - hay), is_physical);
> +            mark = match + 1;
> +            todo = mark - hay;
> +        }
> +        if (pos + l < len) {
> +            /* catch potential matches across chunks. */
> +            pos += l - (wsize - 1);
> +            addr += l - (wsize - 1);
> +        } else {
> +            pos += l;
> +            addr += l;
> +        }
> +    }
> +    g_free(hay);
> +}
> +
>  static void memory_dump(Monitor *mon, int count, int format, int wsize,
>                          hwaddr addr, int is_physical)
>  {
> @@ -1332,6 +1450,28 @@ static void memory_dump(Monitor *mon, int count, int format, int wsize,
>      }
>  }
>  
> +static void hmp_memory_search(Monitor *mon, const QDict *qdict)
> +{
> +    int count = qdict_get_int(qdict, "count");
> +    int format = qdict_get_int(qdict, "format");
> +    int size = qdict_get_int(qdict, "size");
> +    target_long addr = qdict_get_int(qdict, "addr");
> +    const char *data_str = qdict_get_str(qdict, "data");
> +
> +    memory_search(mon, count, format, size, addr, data_str, false);
> +}
> +
> +static void hmp_physical_memory_search(Monitor *mon, const QDict *qdict)
> +{
> +    int count = qdict_get_int(qdict, "count");
> +    int format = qdict_get_int(qdict, "format");
> +    int size = qdict_get_int(qdict, "size");
> +    hwaddr addr = qdict_get_int(qdict, "addr");
> +    const char *data_str = qdict_get_str(qdict, "data");
> +
> +    memory_search(mon, count, format, size, addr, data_str, true);
> +}
> +
>  static void hmp_memory_dump(Monitor *mon, const QDict *qdict)
>  {
>      int count = qdict_get_int(qdict, "count");
Claudio Fontana May 12, 2015, 3:03 p.m. UTC | #3
On 11.05.2015 16:16, Luiz Capitulino wrote:
> On Fri, 24 Apr 2015 14:39:48 +0200
> hw.claudio@gmail.com wrote:
> 
>> From: Claudio Fontana <claudio.fontana@huawei.com>
>>
>> usage is similar to the commands x, xp.
>>
>> Example with string: looking for "ELF" header in memory:
>>
>> (qemu) s/1000000cb 0x40001000 "ELF"
>> searching memory area [0000000040001000-00000000400f5240]
>> 0000000040090001
>> (qemu) x/20b 0x40090000
>> 0000000040090000: '\x7f' 'E' 'L' 'F' '\x02' '\x01' '\x01' '\x03'
>> 0000000040090008: '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00'
>> 0000000040090010: '\x02' '\x00' '\xb7' '\x00'
>>
>> Example with value: looking for 64bit variable value 0x990088
>>
>> (qemu) s/1000000xg 0xffff900042000000 0x990088
>> searching memory area [ffff900042000000-ffff9000427a1200]
>> ffff9000424b3000
>> ffff9000424c1000
>>
>> Signed-off-by: Claudio Fontana <claudio.fontana@huawei.com>
> 
> I had to drop this patch because it doesn't build for w32. You can
> find instructions on how to build for w32 at:
> 
>  http://wiki.qemu.org/Hosts/W32
> 

I see, will take some time to figure out I think, due to my lack of familiarity with the Windows compilation environment.

Incidentally, if somebody knows of memmem equivalents in the Windows API, or how function replacements are usually handled, please let me know.

I guess we could add a replacement function in util/ to compile #ifdef _WIN32?
Basically it would be a duplicate of the work already done in gnulib...
are all other supported targets ok with using the GNU function memmem of string.h ?

Thanks,

Claudio

>> ---
>>  hmp-commands.hx |  28 ++++++++++++
>>  monitor.c       | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>  2 files changed, 168 insertions(+)
>>
>> changes from v3:
>> initialize pointer variable to NULL to finally get rid of spurious warning
>>
>> changes from v2:
>> move code to try to address spurious warning
>>
>> changes from v1:
>> make checkpatch happy by adding braces here and there.
>>
>> diff --git a/hmp-commands.hx b/hmp-commands.hx
>> index d5022d8..2bf5737 100644
>> --- a/hmp-commands.hx
>> +++ b/hmp-commands.hx
>> @@ -432,6 +432,34 @@ Start gdbserver session (default @var{port}=1234)
>>  ETEXI
>>  
>>      {
>> +        .name       = "s",
>> +        .args_type  = "fmt:/,addr:l,data:s",
>> +        .params     = "/fmt addr data",
>> +        .help       = "search virtual memory starting at 'addr' for 'data'",
>> +        .mhandler.cmd = hmp_memory_search,
>> +    },
>> +
>> +STEXI
>> +@item s/fmt @var{addr} @var{data}
>> +@findex s
>> +Virtual memory search starting at @var{addr} for data described by @var{data}.
>> +ETEXI
>> +
>> +    {
>> +        .name       = "sp",
>> +        .args_type  = "fmt:/,addr:l,data:s",
>> +        .params     = "/fmt addr data",
>> +        .help       = "search physical memory starting at 'addr' for 'data'",
>> +        .mhandler.cmd = hmp_physical_memory_search,
>> +    },
>> +
>> +STEXI
>> +@item sp/fmt @var{addr} @var{data}
>> +@findex sp
>> +Physical memory search starting at @var{addr} for data described by @var{data}.
>> +ETEXI
>> +
>> +    {
>>          .name       = "x",
>>          .args_type  = "fmt:/,addr:l",
>>          .params     = "/fmt addr",
>> diff --git a/monitor.c b/monitor.c
>> index c86a89e..b648dd2 100644
>> --- a/monitor.c
>> +++ b/monitor.c
>> @@ -1208,6 +1208,124 @@ static void monitor_printc(Monitor *mon, int c)
>>      monitor_printf(mon, "'");
>>  }
>>  
>> +static void monitor_print_addr(Monitor *mon, hwaddr addr, bool is_physical)
>> +{
>> +    if (is_physical) {
>> +        monitor_printf(mon, TARGET_FMT_plx "\n", addr);
>> +    } else {
>> +        monitor_printf(mon, TARGET_FMT_lx "\n", (target_ulong)addr);
>> +    }
>> +}
>> +
>> +/* simple memory search for a byte sequence. The sequence is generated from
>> + * a numeric value to look for in guest memory, or from a string.
>> + */
>> +static void memory_search(Monitor *mon, int count, int format, int wsize,
>> +                          hwaddr addr, const char *data_str, bool is_physical)
>> +{
>> +    int pos, len;              /* pos in the search area, len of area */
>> +    char *hay;                 /* buffer for haystack */
>> +    int hay_size;              /* haystack size. Needle size is wsize. */
>> +    const char *needle = NULL; /* needle to search in the haystack */
>> +    const char *format_str;    /* numeric input format string */
>> +    char value_raw[8];         /* numeric input converted to raw data */
>> +#define MONITOR_S_CHUNK_SIZE 16000
>> +
>> +    len = wsize * count;
>> +    if (len < 1) {
>> +        monitor_printf(mon, "invalid search area length.\n");
>> +        return;
>> +    }
>> +    switch (format) {
>> +    case 'i':
>> +        monitor_printf(mon, "format '%c' not supported.\n", format);
>> +        return;
>> +    case 'c':
>> +        needle = data_str;
>> +        wsize = strlen(data_str);
>> +        if (wsize > MONITOR_S_CHUNK_SIZE) {
>> +            monitor_printf(mon, "search string too long [max %d].\n",
>> +                           MONITOR_S_CHUNK_SIZE);
>> +            return;
>> +        }
>> +        break;
>> +    case 'o':
>> +        format_str = "%" SCNo64;
>> +        break;
>> +    default:
>> +    case 'x':
>> +        format_str = "%" SCNx64;
>> +        break;
>> +    case 'u':
>> +        format_str = "%" SCNu64;
>> +        break;
>> +    case 'd':
>> +        format_str = "%" SCNd64;
>> +        break;
>> +    }
>> +    if (format != 'c') {
>> +        uint64_t value;      /* numeric input value */
>> +        void *from = &value;
>> +        if (sscanf(data_str, format_str, &value) != 1) {
>> +            monitor_printf(mon, "could not parse search string "
>> +                           "\"%s\" as format '%c'.\n", data_str, format);
>> +            return;
>> +        }
>> +#if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN)
>> +        value = bswap64(value);
>> +#endif
>> +#if defined(TARGET_WORDS_BIGENDIAN)
>> +        from += 8 - wsize;
>> +#endif
>> +        memcpy(value_raw, from, wsize);
>> +        needle = value_raw;
>> +    }
>> +    monitor_printf(mon, "searching memory area ");
>> +    if (is_physical) {
>> +        monitor_printf(mon, "[" TARGET_FMT_plx "-" TARGET_FMT_plx "]\n",
>> +                       addr, addr + len);
>> +    } else {
>> +        monitor_printf(mon, "[" TARGET_FMT_lx "-" TARGET_FMT_lx "]\n",
>> +                       (target_ulong)addr, (target_ulong)addr + len);
>> +    }
>> +    hay_size = len < MONITOR_S_CHUNK_SIZE ? len : MONITOR_S_CHUNK_SIZE;
>> +    hay = g_malloc0(hay_size);
>> +
>> +    for (pos = 0; pos < len;) {
>> +        char *mark, *match; /* mark new starting position, eventual match */
>> +        int l, todo;        /* total length to be processed in current chunk */
>> +        l = len - pos;
>> +        if (l > hay_size) {
>> +            l = hay_size;
>> +        }
>> +        if (is_physical) {
>> +            cpu_physical_memory_read(addr, hay, l);
>> +        } else if (cpu_memory_rw_debug(ENV_GET_CPU(mon_get_cpu()), addr,
>> +                                       (uint8_t *)hay, l, 0) < 0) {
>> +            monitor_printf(mon, " Cannot access memory\n");
>> +            break;
>> +        }
>> +        for (mark = hay, todo = l; todo >= wsize;) {
>> +            match = memmem(mark, todo, needle, wsize);
>> +            if (!match) {
>> +                break;
>> +            }
>> +            monitor_print_addr(mon, addr + (match - hay), is_physical);
>> +            mark = match + 1;
>> +            todo = mark - hay;
>> +        }
>> +        if (pos + l < len) {
>> +            /* catch potential matches across chunks. */
>> +            pos += l - (wsize - 1);
>> +            addr += l - (wsize - 1);
>> +        } else {
>> +            pos += l;
>> +            addr += l;
>> +        }
>> +    }
>> +    g_free(hay);
>> +}
>> +
>>  static void memory_dump(Monitor *mon, int count, int format, int wsize,
>>                          hwaddr addr, int is_physical)
>>  {
>> @@ -1332,6 +1450,28 @@ static void memory_dump(Monitor *mon, int count, int format, int wsize,
>>      }
>>  }
>>  
>> +static void hmp_memory_search(Monitor *mon, const QDict *qdict)
>> +{
>> +    int count = qdict_get_int(qdict, "count");
>> +    int format = qdict_get_int(qdict, "format");
>> +    int size = qdict_get_int(qdict, "size");
>> +    target_long addr = qdict_get_int(qdict, "addr");
>> +    const char *data_str = qdict_get_str(qdict, "data");
>> +
>> +    memory_search(mon, count, format, size, addr, data_str, false);
>> +}
>> +
>> +static void hmp_physical_memory_search(Monitor *mon, const QDict *qdict)
>> +{
>> +    int count = qdict_get_int(qdict, "count");
>> +    int format = qdict_get_int(qdict, "format");
>> +    int size = qdict_get_int(qdict, "size");
>> +    hwaddr addr = qdict_get_int(qdict, "addr");
>> +    const char *data_str = qdict_get_str(qdict, "data");
>> +
>> +    memory_search(mon, count, format, size, addr, data_str, true);
>> +}
>> +
>>  static void hmp_memory_dump(Monitor *mon, const QDict *qdict)
>>  {
>>      int count = qdict_get_int(qdict, "count");
>
Paolo Bonzini May 13, 2015, 3:19 p.m. UTC | #4
On 12/05/2015 17:03, Claudio Fontana wrote:
> I see, will take some time to figure out I think, due to my lack of familiarity with the Windows compilation environment.
> 
> Incidentally, if somebody knows of memmem equivalents in the Windows API, or how function replacements are usually handled, please let me know.
> 
> I guess we could add a replacement function in util/ to compile #ifdef _WIN32?

Yes, you can use $(CONFIG_WIN32) in util/Makefile.objs.  It doesn't have
to be fancy, a simple O(n^2) search would do.

Later we could use Boyer-Moore or some other optimization.

> Basically it would be a duplicate of the work already done in gnulib...

Yes.  The good thing is that it would be easy to test without a
cross-compiler, since symbols in an executable override symbols in glibc.

> are all other supported targets ok with using the GNU function memmem of string.h ?

Until they complain, they are...

Paolo
diff mbox

Patch

diff --git a/hmp-commands.hx b/hmp-commands.hx
index d5022d8..2bf5737 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -432,6 +432,34 @@  Start gdbserver session (default @var{port}=1234)
 ETEXI
 
     {
+        .name       = "s",
+        .args_type  = "fmt:/,addr:l,data:s",
+        .params     = "/fmt addr data",
+        .help       = "search virtual memory starting at 'addr' for 'data'",
+        .mhandler.cmd = hmp_memory_search,
+    },
+
+STEXI
+@item s/fmt @var{addr} @var{data}
+@findex s
+Virtual memory search starting at @var{addr} for data described by @var{data}.
+ETEXI
+
+    {
+        .name       = "sp",
+        .args_type  = "fmt:/,addr:l,data:s",
+        .params     = "/fmt addr data",
+        .help       = "search physical memory starting at 'addr' for 'data'",
+        .mhandler.cmd = hmp_physical_memory_search,
+    },
+
+STEXI
+@item sp/fmt @var{addr} @var{data}
+@findex sp
+Physical memory search starting at @var{addr} for data described by @var{data}.
+ETEXI
+
+    {
         .name       = "x",
         .args_type  = "fmt:/,addr:l",
         .params     = "/fmt addr",
diff --git a/monitor.c b/monitor.c
index c86a89e..b648dd2 100644
--- a/monitor.c
+++ b/monitor.c
@@ -1208,6 +1208,124 @@  static void monitor_printc(Monitor *mon, int c)
     monitor_printf(mon, "'");
 }
 
+static void monitor_print_addr(Monitor *mon, hwaddr addr, bool is_physical)
+{
+    if (is_physical) {
+        monitor_printf(mon, TARGET_FMT_plx "\n", addr);
+    } else {
+        monitor_printf(mon, TARGET_FMT_lx "\n", (target_ulong)addr);
+    }
+}
+
+/* simple memory search for a byte sequence. The sequence is generated from
+ * a numeric value to look for in guest memory, or from a string.
+ */
+static void memory_search(Monitor *mon, int count, int format, int wsize,
+                          hwaddr addr, const char *data_str, bool is_physical)
+{
+    int pos, len;              /* pos in the search area, len of area */
+    char *hay;                 /* buffer for haystack */
+    int hay_size;              /* haystack size. Needle size is wsize. */
+    const char *needle = NULL; /* needle to search in the haystack */
+    const char *format_str;    /* numeric input format string */
+    char value_raw[8];         /* numeric input converted to raw data */
+#define MONITOR_S_CHUNK_SIZE 16000
+
+    len = wsize * count;
+    if (len < 1) {
+        monitor_printf(mon, "invalid search area length.\n");
+        return;
+    }
+    switch (format) {
+    case 'i':
+        monitor_printf(mon, "format '%c' not supported.\n", format);
+        return;
+    case 'c':
+        needle = data_str;
+        wsize = strlen(data_str);
+        if (wsize > MONITOR_S_CHUNK_SIZE) {
+            monitor_printf(mon, "search string too long [max %d].\n",
+                           MONITOR_S_CHUNK_SIZE);
+            return;
+        }
+        break;
+    case 'o':
+        format_str = "%" SCNo64;
+        break;
+    default:
+    case 'x':
+        format_str = "%" SCNx64;
+        break;
+    case 'u':
+        format_str = "%" SCNu64;
+        break;
+    case 'd':
+        format_str = "%" SCNd64;
+        break;
+    }
+    if (format != 'c') {
+        uint64_t value;      /* numeric input value */
+        void *from = &value;
+        if (sscanf(data_str, format_str, &value) != 1) {
+            monitor_printf(mon, "could not parse search string "
+                           "\"%s\" as format '%c'.\n", data_str, format);
+            return;
+        }
+#if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN)
+        value = bswap64(value);
+#endif
+#if defined(TARGET_WORDS_BIGENDIAN)
+        from += 8 - wsize;
+#endif
+        memcpy(value_raw, from, wsize);
+        needle = value_raw;
+    }
+    monitor_printf(mon, "searching memory area ");
+    if (is_physical) {
+        monitor_printf(mon, "[" TARGET_FMT_plx "-" TARGET_FMT_plx "]\n",
+                       addr, addr + len);
+    } else {
+        monitor_printf(mon, "[" TARGET_FMT_lx "-" TARGET_FMT_lx "]\n",
+                       (target_ulong)addr, (target_ulong)addr + len);
+    }
+    hay_size = len < MONITOR_S_CHUNK_SIZE ? len : MONITOR_S_CHUNK_SIZE;
+    hay = g_malloc0(hay_size);
+
+    for (pos = 0; pos < len;) {
+        char *mark, *match; /* mark new starting position, eventual match */
+        int l, todo;        /* total length to be processed in current chunk */
+        l = len - pos;
+        if (l > hay_size) {
+            l = hay_size;
+        }
+        if (is_physical) {
+            cpu_physical_memory_read(addr, hay, l);
+        } else if (cpu_memory_rw_debug(ENV_GET_CPU(mon_get_cpu()), addr,
+                                       (uint8_t *)hay, l, 0) < 0) {
+            monitor_printf(mon, " Cannot access memory\n");
+            break;
+        }
+        for (mark = hay, todo = l; todo >= wsize;) {
+            match = memmem(mark, todo, needle, wsize);
+            if (!match) {
+                break;
+            }
+            monitor_print_addr(mon, addr + (match - hay), is_physical);
+            mark = match + 1;
+            todo = mark - hay;
+        }
+        if (pos + l < len) {
+            /* catch potential matches across chunks. */
+            pos += l - (wsize - 1);
+            addr += l - (wsize - 1);
+        } else {
+            pos += l;
+            addr += l;
+        }
+    }
+    g_free(hay);
+}
+
 static void memory_dump(Monitor *mon, int count, int format, int wsize,
                         hwaddr addr, int is_physical)
 {
@@ -1332,6 +1450,28 @@  static void memory_dump(Monitor *mon, int count, int format, int wsize,
     }
 }
 
+static void hmp_memory_search(Monitor *mon, const QDict *qdict)
+{
+    int count = qdict_get_int(qdict, "count");
+    int format = qdict_get_int(qdict, "format");
+    int size = qdict_get_int(qdict, "size");
+    target_long addr = qdict_get_int(qdict, "addr");
+    const char *data_str = qdict_get_str(qdict, "data");
+
+    memory_search(mon, count, format, size, addr, data_str, false);
+}
+
+static void hmp_physical_memory_search(Monitor *mon, const QDict *qdict)
+{
+    int count = qdict_get_int(qdict, "count");
+    int format = qdict_get_int(qdict, "format");
+    int size = qdict_get_int(qdict, "size");
+    hwaddr addr = qdict_get_int(qdict, "addr");
+    const char *data_str = qdict_get_str(qdict, "data");
+
+    memory_search(mon, count, format, size, addr, data_str, true);
+}
+
 static void hmp_memory_dump(Monitor *mon, const QDict *qdict)
 {
     int count = qdict_get_int(qdict, "count");