前回、GDBのパケット通信の確立方法について学んだ。次は、実際にどのようなパケットが送信されているのか、観察してみることにしよう。
https://github.com/msyksphinz/swimmer_iss/tree/feature/gdb_implgithub.com
GDBリモートサーバで、クライアントからのパケットを取得する
まずはor1ksimのgdbサーバを間借りして、GDBからのリモートターゲット接続、その際のプロトコルを取得してみよう。 or1ksimのgdbサーバをC++で簡単に移植して、パケットを解析できる環境を作った。
void GdbEnv::HandleClientRequest () { struct rsp_buf *buf = GetPacket (); /* Message sent to us */ // Null packet means we hit EOF or the link was closed for some other // reason. Close the client and return if (NULL == buf) { CloseRSPClient (); return; } std::cout << "Packet received " << buf->data << ": " << buf->len << "chars\n";
HandleClientRequest()は、GDBクライアントからのパケットを受信する関数だ。GetPacket()はここではとりあえず置いておいて、std::cout で何が出力されるか見てみよう。 GDBクライアントからリモートターゲット(つまりISS)へ接続すると、ISSが以下のような出力を出す。
- GDBクライアント側
(gdb) target remote :2000
- GDBサーバ側
$ ./swimmer_mips64 --gdb Swimmer-RISCV Version 20160118 Revision 5858f70 developed by msyksphinz <msyksphinz@gmail.com> Listening for RSP on port 2000 Remote debugging from host0.0.0.0 Packet received qSupported:multiprocess+;qRelocInsn+: 36chars
何やら、"qSupported"などというパケットが受信されてきたらしい。これは何だろう。
GetPacket()を読み解いてGDBのパケット通信を読み解く
GDBリモートサーバの資料によると、GDBのパケットは以下のような構造をしている。
なるほど、最初に$、最後に#、そしてチェックサムだ。 これはGDBサーバのパケット受信関数を見ても分かる。
while (1) { unsigned char checksum; /* The checksum we have computed */ int count; /* Index into the buffer */ int ch; /* Current character */ /* Wait around for the start character ('$'). Ignore all other characters */ ch = GetRSPChar (); while (ch != '$') { if (-1 == ch) { return NULL; /* Connection failed */ } ch = GetRSPChar (); } ... /* If we have a valid end of packet char, validate the checksum */ if ('#' == ch) { unsigned char xmitcsum; /* The checksum in the packet */ ch = GetRSPChar (); if (-1 == ch) { return NULL; /* Connection failed */ } xmitcsum = Hex (ch) << 4; ch = GetRSPChar (); if (-1 == ch) { return NULL; /* Connection failed */ } ...
このようにして最初と最後を除いたパケットを取得し、文字列として返している訳だ。 さらに先頭文字によって様々なパケットに分けられるらしい。
Howto: GDB Remote Serial Protocol
- Packets requiring no acknowledgment. These commands are: f, i, I, k, R, t and vFlashDone.
- Packets requiring a simple acknowledgment packet. The acknowledgment is either OK, Enn (where nn is an error number) or for some commands an empty packet (meaning "unsupported"). These commands are: !, A, D, G, H, M, P, Qxxxx, T, vFlashErase, vFlashWrite, X, z and Z.
- Packets that return result data or an error code.. These commands are: ?, c, C, g, m, p, qxxxx, s, S and most vxxxx.
- Deprecated packets which should no longer be used. These commands are b, B, d and r.
なるほどなあ。ここではqSupportedなので、クライアントからの要求に対してデータを返さなければならない。
qSupported. Report the features supported by the RSP server. As a minimum, just the packet size can be reported.
なるほど、ここではGDBサーバの受信バッファサイズを返せばいいのね。
else if (0 == strncmp ("qSupported", buf->data, strlen ("qSupported"))) { char reply[GDB_BUF_MAX]; sprintf (reply, "PacketSize=%x", GDB_BUF_MAX); put_str_packet (reply);