blob: f2ae4ba6a3346086b034e7b47e3638f0c8a31004 [file] [log] [blame]
/*
* gdbfreeplay-i386.c
*
* Target-dependent component of gdbfreeplay for i386.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "gdbfreeplay.h"
/*
* Utility functions
*/
/*
* ix86_hex_to_unsigned_long
*
* Convert target-order ascii bytes to host unsigned long.
* Returns host unsigned long.
*/
static unsigned long
ix86_hex_to_unsigned_long (char *hex)
{
unsigned long ul;
int i;
for (i = 6; i >= 0; i-=2)
{
ul <<= 8;
ul += hex_to_int (hex[i]) << 4;
ul += hex_to_int (hex[i+1]);
}
return ul;
}
/*
* nybble_to_char
*/
static char
nybble_to_char (int nybble)
{
if (nybble < 10)
return '0' + nybble;
else
return 'a' + (nybble - 10);
}
/*
* ix86_unsigned_long_to_hex
*
* Convert host unsigned long to target-order ascii string.
* Return char buffer, static, use before calling again.
*/
static char *
ix86_unsigned_long_to_hex (unsigned long value)
{
int i;
static char buf[16];
char *p;
p = buf;
for (i = 0; i < 4; i++)
{
/* Intel byte order. */
*p++ = nybble_to_char ((value >> 4) & 0xf);
*p++ = nybble_to_char (value & 0xf);
value >>= 8;
}
*p = '\0';
return buf;
}
/*
* target_pc_from_T
*
* Extract the PC value from the gdb protocol 'T' packet.
* Returns PC as host unsigned long long.
*/
unsigned long long
target_pc_from_T (char *tpacket)
{
char *p;
if (tpacket[0] == '$' && tpacket[1] == 'T' &&
(p = strstr (tpacket, ";08:")) != NULL)
{
return (unsigned long long) ix86_hex_to_unsigned_long (p + 4);
}
/* Fail -- just assume no legitimate PC will ever be -1... */
return (unsigned long long) -1;
}
/*
* ix86_pc_from_registers
*
* Extract the PC value from a gdb protocol registers file.
* Returns PC as host unsigned long long.
*/
static unsigned long long
ix86_pc_from_registers (char *regs)
{
return (unsigned long long) ix86_hex_to_unsigned_long (regs + 64);
}
/*
* target_pc_from_G
*
* Extract the PC value from the gdb protocol 'G' packet.
* Returns PC as host unsigned long long.
*/
unsigned long long
target_pc_from_G (char *gpacket)
{
if (gpacket[0] == '$' && gpacket[1] == 'G')
{
return (unsigned long long) ix86_pc_from_registers (gpacket + 2);
}
/* Fail -- just assume no legitimate PC will ever be -1... */
return (unsigned long long) -1;
}
/*
* expand_rle
*
* Stubs sometimes send us data with RLE compression.
* FIXME this code is generic, move into gdbfreeplay-back.c
* The code for this function lifted from gdb.
*/
static char *
expand_rle (char *input)
{
static char *buf = NULL;
static int sizeof_buf;
long bc = 0;
int c, repeat;
/* Allocate buf on first time thru. */
if (buf == NULL)
{
sizeof_buf = 4096;
buf = realloc (buf, sizeof_buf);
}
while (1)
{
c = *input++;
switch (c) {
case '#': /* End of interesting data. We don't care about checksum. */
case '\0':/* End of string data. */
buf[bc] = '\0';
return buf;
break;
case '$':
/* Shouldn't happen, but... */
fprintf (stderr, "Warning: expand_rle saw '$' in middle of packet.\n");
default:
if (bc >= sizeof_buf - 1)
{
/* Make some more room in the buffer. */
sizeof_buf *= 2;
buf = realloc (buf, sizeof_buf);
}
buf[bc++] = c;
continue;
break;
case '*': /* Run length encoding. */
c = *input++;
repeat = c - ' ' + 3; /* Compute repeat count. */
/* The character before '*' is repeated. */
if (bc + repeat - 1 >= sizeof_buf - 1)
{
/* Make some more room in the buffer. */
sizeof_buf *= 2;
buf = realloc (buf, sizeof_buf);
}
memset (&buf[bc], buf[bc - 1], repeat);
bc += repeat;
continue;
break;
}
}
}
/*
* target_pc_from_g
*
* Extract the PC value from the gdb protocol 'g' packet reply.
*
* Unlike the two above, this function accepts a FILE pointer
* rather than a char pointer, and must read data from the file.
*
* Returns PC as host unsigned long long.
*/
unsigned long long
target_pc_from_g (char *gpacket)
{
if (gpacket[0] == 'r' && gpacket[1] == ' ')
{
gpacket += 2;
if (gpacket[0] == '+')
gpacket++;
if (gpacket[0] == '$')
gpacket++;
}
return (unsigned long long) ix86_pc_from_registers (expand_rle (gpacket));
}
/*
* target_compose_T_packet
*
* On targets where DECR_PC_AFTER_BREAK is zero, this is a no-op.
* We just send back the T packet that was sent to us.
*
* On targets like i386, where DECR_PC_AFTER_BREAK is non-zero,
* it gets complicated. We have two pieces of information:
*
* 1) The address of the current instruction, and
* 2) Whether we arrived at the current instruction
* by virtue of a breakpoint -- ie. whether gdb is
* expecting the PC to be off-by-one.
*
* Based on that info, we decide whether the T packet that was
* sent to us is suitable as it is, or else we compose one and
* send it back.
*
* Returns a string T packet, possibly WITHOUT CHECKSUM.
* We leave it to the caller to handle that, if necessary,
* because it is target independent and therefore inappropriate
* to do here. However the caller has to check for it, because
* if we simply return the T packet that we received, it will
* already have a checksum.
*/
char *
target_compose_T_packet (char *origTpacket,
unsigned long long instruction_pc,
int breakpoint_p)
{
unsigned long long origTpacket_pc = target_pc_from_T (origTpacket);
static char reply_buf[128];
char *p;
/* There are four possibilities.
1) We got here by stepping, and instruction_pc == origTpacket_pc.
2) We got here by stepping, and instruction_pc != origTpacket_pc.
3) We got here by breakpoint, and instruction_pc == origTpacket_pc.
4) We got here by breakpoint, and instruction_pc != origTpacket_pc.
Actually, there's one more (not well understood):
5) instruction_pc and origTpacket_pc bear no relationship to each other.
In that case, we should bail and let gdb get the original Tpacket. */
/* Case #5. */
if (instruction_pc != origTpacket_pc &&
instruction_pc != origTpacket_pc - 1)
return origTpacket;
/* For #1 and #4, we don't have to do anything, and can return
the original T packet. */
/* Case #1. */
if (!breakpoint_p && (instruction_pc == origTpacket_pc))
return origTpacket;
/* Case #4. */
if (breakpoint_p && (instruction_pc == origTpacket_pc - 1))
return origTpacket;
/* That's it for the easy cases. For the other two, we have work to do.
Start by making a copy of the original T packet. */
strcpy (reply_buf, origTpacket);
if ((p = strstr (reply_buf, ";08:")) != NULL)
{
#if 0
/* Snip off the PC value from the original T packet. */
p[4] = '\0';
/* Case #2. */
if (breakpoint_p)
{
/* Compose a T packet using instruction_pc + 1. */
strcat (p, ix86_unsigned_long_to_hex (instruction_pc + 1));
}
else
{
/* Compose a T packet using instruction_pc. */
strcat (p, ix86_unsigned_long_to_hex (instruction_pc));
}
#else
/* Insert the new PC value in place
(without disturbing whatever follows it). */
/* Case #2. */
if (breakpoint_p)
{
/* Compose a T packet using instruction_pc + 1. */
memcpy (p + 4,
ix86_unsigned_long_to_hex (instruction_pc + 1),
8);
}
else
{
/* Compose a T packet using instruction_pc. */
memcpy (p + 4,
ix86_unsigned_long_to_hex (instruction_pc),
8);
}
#endif
/* Caller has to recompute checksum. */
return reply_buf;
}
/* Bail... */
return origTpacket;
}
/*
* target_compose_g_packet
*
* Take the registers from the 'T' packet, and compose them into a
* 'g' packet response. Registers for which we have no values will
* be filled in with 'xxxx', in the manner of tracepoints.
*
* Returns: string, g packet reply.
*/
#define IX86_TARGET_GBYTES 624
static char gbuffer[IX86_TARGET_GBYTES + 1];
#define IX86_BOGUS_NUMREGS (IX86_TARGET_GBYTES / 8)
static unsigned long regval[IX86_BOGUS_NUMREGS];
static int gotreg[IX86_BOGUS_NUMREGS];
char *
target_compose_g_packet (char *tpac)
{
int i;
int regnum;
int signum;
/* See which regs we can get from the T packet. Assume none... */
for (i = 0; i < IX86_BOGUS_NUMREGS; i++)
gotreg[i] = 0;
/* OK, scan off the prefix -- $T plus signal number. */
if (*tpac++ == '$' && *tpac++ == 'T')
{
/* We won't actually use signum. */
signum = (hex_to_int (*tpac++) << 4);
signum += hex_to_int (*tpac++);
while (*tpac)
{
regnum = (hex_to_int (*tpac++) << 4);
regnum += hex_to_int (*tpac++);
if (*tpac++ == ':')
{
gotreg[regnum] = 1;
regval[regnum] = ix86_hex_to_unsigned_long (expand_rle (tpac));
tpac = strchr (tpac, ';');
}
else goto gpacket_fail;
if (tpac && *tpac == ';')
tpac++;
else goto gpacket_fail;
if (strncmp (tpac, "thread", 6) == 0)
break;
}
/* Got values, now get to composin'. */
strcpy (gbuffer, "$");
for (i = 0; i < IX86_BOGUS_NUMREGS; i++)
if (gotreg[i])
strcat (gbuffer, ix86_unsigned_long_to_hex (regval[i]));
else
strcat (gbuffer, "xxxxxxxx");
/* Return composed g packet reply.
Caller has responsibility of appending checksum. */
return gbuffer;
}
gpacket_fail:
/* Fail. */
return NULL;
}