Strengthen tagfile parsing

This commit is contained in:
Stefan Rueger
2024-07-27 16:50:04 +01:00
parent 95bc4db4b9
commit 7c92a355f9
2 changed files with 115 additions and 92 deletions

View File

@@ -130,108 +130,125 @@ static void add_symbol(int address, int type, int subtype, int count, const char
cx->dis_symbols[N].count = count;
cx->dis_symbols[N].used = 0;
cx->dis_symbols[N].printed = 0;
cx->dis_symbols[N].name = name? cleanup(mmt_strdup(name)): NULL;
cx->dis_symbols[N].comment = comment? mmt_strdup(comment): NULL;
cx->dis_symbols[N].name = name? cleanup(str_rtrim(mmt_strdup(str_ltrim(name)))): NULL;
cx->dis_symbols[N].comment = comment? str_rtrim(mmt_strdup(str_ltrim(comment))): NULL;
}
static int line_error(const char *token, const char *message, int lineno) {
if((token == NULL) || (strlen(token) == 0)) {
pmsg_error("%s in tagfile, line %d\n", message, lineno);
return 1;
/*
* Tokenising of a tagfile line returning (argc, argv); parsing ends when
* - A token starts with a comment character #
* - The comment field after the name of the symbol is encountered
* - The end of the string is encountered
*
* Argv is allocated once, so the caller only needs to mmt_free argv.
* On error NULL is returned (when input line was too long).
*
*/
static int tagfile_tokenize(char *s, int *argcp, const char ***argvp) {
size_t slen;
int n, nargs;
const char **argv;
char *buf, *q, *r;
// Upper estimate of the number of arguments
for(nargs=0, q=s; *q; nargs++) {
while(*q && !isspace((unsigned char) *q))
q++;
while(*q && isspace((unsigned char) *q))
q++;
}
return 0;
}
slen = q - s;
static int tagfile_readline(char *line, int lineno, const char * const *isrnames, int ni) {
char *token, type, subtype, *name;
int vn, address, count;
const char *errptr;
if(line[0] == '#' || strlen(line) <= 1)
// Limit input line to some 186 Megabytes as max nargs is (slen+1)/2
if(slen > 2*((INT_MAX - 2*sizeof(char *))/(sizeof(char *)+3)))
return 0;
token = strtok(line, " \t\n");
if(line_error(token, "nonempty line", lineno))
return -1;
address = str_int(token, STR_INT32, &errptr);
if(errptr) {
pmsg_error("address %s: %s\n", token, errptr);
return -1;
// Allocate once for pointers and contents, so caller only needs to mmt_free(argv)
argv = mmt_malloc((nargs+2)*sizeof(char *) + slen + nargs);
buf = (char *) (argv+nargs+1);
for(n=0, r=s; *r; ) {
q = str_nexttok(r, " \t\n\r\v\f", &r);
size_t len = strlen(q);
if(*q == '#') { // Inline comment: ignore rest of line
r = q+len;
break;
}
strcpy(buf, q);
if(*buf) // Don't record empty arguments
argv[n++] = buf;
if(n > 1 && n == (str_eq(argv[1], "L")? 3: 5)) { // Stop parsing after symbol name
if(*r)
argv[n] = r; // Comment, if any
break;
}
buf += len + 1;
}
token = strtok(NULL, " \t\n");
if(line_error(token, "no second argument", lineno))
return -1;
if(strlen(token) != 1) {
line_error(NULL, "second argument should be a type (L, P or M)", lineno);
return -1;
}
type = token[0];
*argcp = n;
*argvp = argv;
return 1;
}
token = strtok(NULL, " \t\n");
if(line_error(token, "no third argument", lineno))
return -1;
#define Return(fmt, ...) do { \
pmsg_error("tagfile line %d " fmt, lineno, __VA_ARGS__); msg_error("\n"); \
return -1; \
} while (0)
static int tagfile_readline(char *line, int lineno, const char * const *isrnames, int ni) {
int type, subtype, vn, address, count, argc = 0;
const char *errptr, **argv = NULL;
if(!tagfile_tokenize(line, &argc, &argv))
Return("%s", "is too long");
if(argc == 0)
return 0;
if(argc < 3)
Return("%s", "needs at least address, symbol type (L/P/M) and name");
address = str_int(argv[0], STR_INT32, &errptr);
if(errptr)
Return("address %s: %s", argv[0], errptr);
if(strlen(argv[1]) != 1 || !strchr("LPM", *argv[1]))
Return("%s", "2nd argument must be L, P or M");
type = *argv[1];
if(type == 'L') {
name = token; // Name, comment is optional
const char *name = argv[2];
if(str_starts(name, "__vector_") && looks_like_number(name + 9))
if((vn = strtol(name+9, NULL, 0)) > 0 && vn < ni) // Don't replace __vectors_0
name = str_lc((char *) str_ccprintf("__vector_%s", isrnames[vn]));
add_symbol(address, 'L', TYPE_BYTE, 1, name, strtok(NULL, "\t\n"));
add_symbol(address, 'L', TYPE_BYTE, 1, name, argv[3]);
return 0;
}
if(line_error(token, "no fourth argument", lineno))
return -1;
if(strlen(token) != 1) {
line_error(NULL, "fourth argument should be a subtype (B, W, A or S)", lineno);
return -1;
}
subtype = token[0];
if(argc < 5 || strlen(argv[2]) != 1 || !strchr("BWAS", *argv[2]))
Return("needs to be <address> %c [%s] <count> <name>", type, type == 'M'? "BW": "BWAS");
switch(subtype) {
case 'B':
subtype = TYPE_BYTE;
break;
case 'W':
subtype = TYPE_WORD;
break;
case 'A':
subtype = TYPE_ASTRING;
break;
case 'S':
subtype = TYPE_STRING;
break;
default:
line_error(NULL, "invalid subtype (expected one of B, W, A or S)", lineno);
switch(*argv[2]) {
default: subtype = TYPE_BYTE; break;
case 'W': subtype = TYPE_WORD; break;
case 'A': subtype = TYPE_ASTRING; break;
case 'S': subtype = TYPE_STRING;
}
if(type == 'M' && subtype != TYPE_BYTE && subtype != TYPE_WORD) {
pmsg_error("memory label type can only be B(yte) or W(ord)");
return -1;
}
if((type == 'M') && ((subtype != TYPE_BYTE) && (subtype != TYPE_WORD))) {
line_error(NULL, "memory labels can only be of type B or W", lineno);
return -1;
}
count = str_int(argv[3], STR_INT32, &errptr);
if(errptr)
Return("count %s: %s\n", argv[3], errptr);
if(count < 1)
Return("tagfile line %d has invalid count %d", lineno, count);
token = strtok(NULL, " \t\n");
count = str_int(token, STR_INT32, &errptr);
if(errptr) {
pmsg_error("count %s: %s\n", token, errptr);
return -1;
}
if(count < 1) {
line_error(NULL, str_ccprintf("invalid count %d given", count), lineno);
return -1;
}
name = strtok(NULL, " \t\n");
if(type != 'P' && type != 'M') {
pmsg_error("invalid tag type %c (must be L, P or M)\n", type);
return -1;
}
add_symbol(address, type, subtype, count, name, NULL);
add_symbol(address, type, subtype, count, argv[4], argv[5]);
mmt_free(argv);
return 0;
}
@@ -386,8 +403,8 @@ typedef struct {
char label[LINE_N], code[LINE_N], comment[LINE_N];
} Disasm_line;
// Column where opcode starts
static int codecol() {
// Comments start in commentcol() + 1
static int commentcol() {
int ret = 0;
if(cx->dis_opts.addresses)
ret += 3 + cx->dis_addrwidth;
@@ -398,7 +415,7 @@ static int codecol() {
if(cx->dis_opts.opcode_bytes)
ret += 12;
return ret? ret+1: 2;
return (ret? ret+1: 2) + cx->dis_codewidth;
}
static int is_jumpable(int address) {
@@ -413,7 +430,7 @@ static int is_jumpable(int address) {
static void lineout(const char *code, const char *comment,
int mnemo, int oplen, const char *buf, int pos, int addr, int showlabel) {
int here = disasm_wrap(pos + addr), codewidth = 27;
int here = disasm_wrap(pos + addr);
if(cx->dis_opts.labels && showlabel) {
int match = 0;
@@ -430,7 +447,7 @@ static void lineout(const char *code, const char *comment,
if(match && (name = get_label_name(here, &comment))) {
if(comment)
disasm_out("%-*s ; %s\n", codecol() + codewidth, str_ccprintf("%s:", name), comment);
disasm_out("%-*s ; %s\n", commentcol(), str_ccprintf("%s:", name), comment);
else
disasm_out("%s:\n", name);
}
@@ -445,11 +462,11 @@ static void lineout(const char *code, const char *comment,
if(cx->dis_opts.opcode_bytes)
for(int i = 0; i < 4; i++)
disasm_out(i < oplen? "%02x ": " ", buf[pos + i] & 0xff);
disasm_out(codecol() > 2? " ": " ");
disasm_out(commentcol() > 2 + cx->dis_codewidth? " ": " ");
if(!comment || !*comment || !cx->dis_opts.comments)
disasm_out("%s\n", code);
else
disasm_out("%-*s ; %s\n", codewidth, code, comment);
disasm_out("%-*s ; %s\n", cx->dis_codewidth, code, comment);
if(mnemo == MNEMO_ret || mnemo == MNEMO_u_ret || mnemo == MNEMO_reti || mnemo == MNEMO_u_reti)
cx->dis_para = 1;
}
@@ -559,14 +576,19 @@ static void emit_used_symbols() {
int len, maxlen = 0;
for(int i = 0; i < cx->dis_symbolN; i++)
if(s[i].used && !s[i].printed)
if(s[i].used && !s[i].printed && s[i].name)
if((len = strlen(s[i].name)) > maxlen)
maxlen = len;
for(int i = 0; i < cx->dis_symbolN; i++)
if(s[i].used && !s[i].printed)
disasm_out(".equ %s,%*s 0x%02x\n", s[i].name,
(int) (maxlen-strlen(s[i].name)), "", s[i].address);
if(s[i].used && !s[i].printed && s[i].name) {
const char *equ = str_ccprintf(".equ %s,%*s 0x%02x", s[i].name,
(int) (maxlen-strlen(s[i].name)), "", s[i].address);
if(s[i].comment)
disasm_out("%-*s ; %s\n", commentcol(), equ, s[i].comment);
else
disasm_out("%s\n", equ);
}
}
void disasm_zap_jumpcalls() {
@@ -1085,6 +1107,7 @@ int disasm_init(const AVRPART *p) {
cx->dis_flashsz2 = 0; // Flash size rounded up to next power of two
cx->dis_addrwidth = 4; // Number of hex digits needed for flash addresses
cx->dis_sramwidth = 4; // Number of hex digits needed for sram addresses
cx->dis_codewidth = 27; // Width of the code column (ldi r17, 0x32)
if((mem = avr_locate_flash(p)) && mem->size > 1) {
int nbits = intlog2(mem->size - 1) + 1;

View File

@@ -1824,7 +1824,7 @@ typedef struct {
// Static variables from disasm.c
int dis_initopts, dis_flashsz, dis_flashsz2, dis_addrwidth, dis_sramwidth;
int dis_pass, dis_para, dis_cycle_index, dis_io_offset;
int dis_pass, dis_para, dis_cycle_index, dis_io_offset, dis_codewidth;
Disasm_options dis_opts;
int dis_jumpcallN, dis_symbolN, *dis_jumpable, dis_start, dis_end;
Disasm_jumpcall *dis_jumpcalls;