Plan 9 from Bell Labs’s /usr/web/sources/contrib/stallion/src/gdbfs/gdb.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#include <u.h>
#include <libc.h>
#include <bio.h>

#include "gdb.h"

#define	BUFSIZE		(64*1024)

static char Echecksum[] = "bad checksum";
static char Eescape[] = "bad escape";
static char Enotsup[] = "not supported";
static char Epacket[] = "bad packet";
static char Erunlen[] = "bad run-length";
static char Etimeout[] = "timed out";

static int fd;
static Biobuf bin;
static int acked;
static int errored;
static int notified;

int chattygdb;

static int
checksum(char *s)
{
	int sum;

	sum = 0;
	while(*s != '\0')
		sum += *s++;
	return sum % 256;
}

int
gdbsend(char *s)
{
	long n;

	if(chattygdb)
		fprint(2, "<- %s\n", s);
	n = fprint(fd, s);
	if(n < 0)
		sysfatal("%r");
	return n;
}

int
gdbsendp(char *s)
{
	long n;

	if(chattygdb)
		fprint(2, "<- %s\n", s);
	n = fprint(fd, "$%s#%02ux", s, checksum(s));
	if(n < 0)
		sysfatal("%r");
	return n;
}

int
gdback(void)
{
	return gdbsend("+");
}

int
gdbresend(void)
{
	return gdbsend("-");
}

static int
recv(char *buf, int len)
{
	char *p, *e;
	int state, sum, c;
	static char cksum[2+1];
	enum { None, Data, Checksum };

	sum = 0;
	state = None;
	acked = errored = notified = 0;
	p = buf;
	e = buf + len;
	while(p < e)
		switch(state){
		case None:
			switch(Bgetc(&bin)){
			case -1:
				sysfatal("%r");
			case '%':
				notified++;
				/* fall through */
			case '$':
				state = Data;
				break;
			case '+':
				acked++;
				return 0;
			case '-':
				acked++;
				return -1;
			}
			break;
		case Data:
			switch(c = Bgetc(&bin)){
			case -1:
				sysfatal("%r");
			case '#':
				state = Checksum;
				break;
			default:
				*p++ = c;
				sum += c;
			}
			break;
		case Checksum:
			if(Bread(&bin, cksum, sizeof cksum - 1) < 0)
				sysfatal("%r");
			if(strtoul(cksum, nil, 16) != sum%256){
				werrstr(Echecksum);
				return -1;
			}
			return p - buf;
		}
	werrstr(Epacket);
	return -1;
}

static int
expand(char *s, char *buf, int len)
{
	char *p, *e, c;
	int rlen;

	p = buf;
	e = buf + len;
	while(*s != '\0')
		switch(c = *s++){
		case '*':
			if(*s == '\0' || p == buf){
				werrstr(Erunlen);
				return -1;
			}
			rlen = *s++ - 29;
			while(rlen--)
				p = seprint(p, e, "%c", s[-3]);
			break;
		case '}':
			if(*s == '\0'){
				werrstr(Eescape);
				return -1;
			}
			c = *s++ ^ 0x20;
			/* fall through */
		default:
			p = seprint(p, e, "%c", c);
		}
	return p - buf;
}

int
gdbrecv(char *buf, int len)
{
	int n;
	static char buf2[BUFSIZE];

	/*
	 * Unfortunately, some targets persist in use of run-length
	 * encoding.  We first read the packet to a holding buffer and
	 * expand to the user-specified buffer.
	 */
	n = recv(buf2, sizeof buf2);
	if(n < 0)
		return -1;
	if(acked)
		return 0;
	buf2[n] = '\0';
	if(chattygdb)
		fprint(2, "-> %s\n", buf2);
	n = expand(buf2, buf, len);
	if(n < 0)
		return -1;
	else if(n == 0){	/* $#00 */
		werrstr(Enotsup);
		errored++;
	}
	else if(*buf == 'E'){
		werrstr(buf);
		errored++;
	}
	return n;
}

int
wasack(void)
{
	return acked;
}

int
waserror(void)
{
	return errored;
}

int
wasnotify(void)
{
	return notified;
}

int
gdbcommand(char *s, char *buf, int len)
{
	int i;
	long n;
	enum { Nretries = 10 };

	i = 0;
again:
	for(; i < Nretries; ++i)
		if(gdbsendp(s) > 0)
		if(gdbrecv(buf, len) == 0)
		if(wasack())
			break;

	for(; i < Nretries; ++i){
		n = gdbrecv(buf, len);
		if(n < 0)
			gdbresend();
		else{
			gdback();
			if(wasnotify())
				goto again;
			return n;
		}
	}
	werrstr(Etimeout);
	return -1;
}

int
gdbopen(char *target)
{
	/*
	 * Remote targets support both serial and network access.  We
	 * assume serial will either be served by consolefs(4) or
	 * uart(3), hence we have a file to open.
	 */
	if(access(target, AEXIST) == 0)
		fd = open(target, ORDWR);
	else
		fd = dial(target, nil, nil, nil);
	Binit(&bin, fd, OREAD);
	return fd;
}

void
gdbclose(void)
{
	Bterm(&bin);
	close(fd);
}

void
gdbkill(void)
{
	/*
	 * The kill request (understandably) may not provide a
	 * response.  We short circuit gdbcommand to send the packet.
	 * It is not clear that waiting for an acknowlegement is
	 * worthwhile; a resend will fail miserably anyway.
	 */
	gdbsendp("k");
	gdbclose();
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.