Mercurial > hg > index.cgi
view lwasm/pass1.c @ 216:398773d7e504
Fix crash bug on indexed expression handling
Fix NULL pointer deref when simplifying an expression in indexed mode. This
fix should eliminate all similar crash bugs.
author | William Astle <lost@l-w.ca> |
---|---|
date | Sun, 10 Jun 2012 14:10:04 -0600 |
parents | b0d53e2f3f53 |
children | afd50d6b4113 |
line wrap: on
line source
/* pass1.c Copyright © 2010 William Astle This file is part of LWTOOLS. LWTOOLS is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <stdio.h> #include <string.h> #include <ctype.h> #include <stdlib.h> #include <lw_alloc.h> #include <lw_string.h> #include "lwasm.h" #include "instab.h" #include "input.h" extern int expand_macro(asmstate_t *as, line_t *l, char **p, char *opc); extern int expand_struct(asmstate_t *as, line_t *l, char **p, char *opc); extern int add_macro_line(asmstate_t *as, char *optr); /* pass 1: parse the lines line format: [<symbol>] <opcode> <operand>[ <comment>] If <symbol> is followed by a :, whitespace may precede the symbol A line may optionally start with a number which must not be preceded by white space and must be followed by a single whitespace character. After that whitespace character, the line is parsed as if it had no line number. */ void do_pass1(asmstate_t *as) { char *line; line_t *cl; char *p1; int stspace; char *tok, *sym = NULL; int opnum; int lc = 1; int nomacro; for (;;) { nomacro = 0; if (sym) lw_free(sym); sym = NULL; line = input_readline(as); if (!line) break; if (line[0] == 1 && line[1] == 1) { // special internal directive // these DO NOT appear in the output anywhere // they are generated by the parser to pass information // forward for (p1 = line + 2; *p1 && !isspace(*p1); p1++) /* do nothing */ ; *p1++ = '\0'; if (!strcmp(line + 2, "SETCONTEXT")) { as -> context = strtol(p1, NULL, 10); } else if (!strcmp(line + 2, "SETLINENO")) { lc = strtol(p1, NULL, 10); } else if (!strcmp(line + 2, "SETNOEXPANDSTART")) { as -> line_tail -> noexpand_start = 1; } else if (!strcmp(line + 2, "SETNOEXPANDEND")) { as -> line_tail -> noexpand_end = 1; } lw_free(line); if (lc == 0) lc = 1; continue; } debug_message(as, 75, "Read line: %s", line); cl = lw_alloc(sizeof(line_t)); memset(cl, 0, sizeof(line_t)); cl -> outputl = -1; cl -> linespec = lw_strdup(input_curspec(as)); cl -> prev = as -> line_tail; cl -> insn = -1; cl -> as = as; cl -> inmod = as -> inmod; cl -> csect = as -> csect; cl -> pragmas = as -> pragmas; cl -> context = as -> context; cl -> ltext = lw_strdup(line); cl -> soff = -1; cl -> dshow = -1; cl -> dsize = 0; cl -> dptr = NULL; cl -> isbrpt = 0; cl -> dlen = 0; as -> cl = cl; if (!as -> line_tail) { as -> line_head = cl; cl -> addr = lw_expr_build(lw_expr_type_int, 0); cl -> daddr = lw_expr_build(lw_expr_type_int, 0); } else { lw_expr_t te; cl -> lineno = as -> line_tail -> lineno + 1; as -> line_tail -> next = cl; // set the line address te = lw_expr_build(lw_expr_type_special, lwasm_expr_linelen, cl -> prev); cl -> addr = lw_expr_build(lw_expr_type_oper, lw_expr_oper_plus, cl -> prev -> addr, te); lw_expr_destroy(te); lwasm_reduce_expr(as, cl -> addr); // lw_expr_simplify(cl -> addr, as); // set the data address if relevant if (as -> output_format == OUTPUT_OS9) { te = lw_expr_build(lw_expr_type_special, lwasm_expr_linedlen, cl -> prev); cl -> daddr = lw_expr_build(lw_expr_type_oper, lw_expr_oper_plus, cl -> prev -> daddr, te); lw_expr_destroy(te); lwasm_reduce_expr(as, cl -> daddr); } else { cl -> daddr = lw_expr_copy(cl -> addr); } // carry DP value forward cl -> dpval = cl -> prev -> dpval; } if (!lc && strcmp(cl -> linespec, cl -> prev -> linespec)) lc = 1; if (lc) { cl -> lineno = lc; lc = 0; } as -> line_tail = cl; // blank lines don't count for anything // except a local symbol context break if (!*line) { as -> context = lwasm_next_context(as); goto nextline; } // skip comments // commends do not create a context break if (*line == '*' || *line == ';' || *line == '#') goto nextline; p1 = line; if (isdigit(*p1)) { // skip line number while (*p1 && isdigit(*p1)) p1++; if (!*p1 && !isspace(*p1)) p1 = line; else if (*p1 && !isspace(*p1)) p1 = line; else if (*p1 && isspace(*p1)) p1++; } // blank line - context break if (!*p1) { as -> context = lwasm_next_context(as); goto nextline; } // comment - no context break if (*p1 == '*' || *p1 == ';' || *p1 == '#') goto nextline; if (isspace(*p1)) { for (; *p1 && isspace(*p1); p1++) /* do nothing */ ; stspace = 1; } else stspace = 0; // if (*p1 == '*' || *p1 == ';' || *p1 == '#') // goto nextline; if (!*p1) { // nothing but whitespace - context break as -> context = lwasm_next_context(as); goto nextline; } // find the end of the first token for (tok = p1; *p1 && !isspace(*p1) && *p1 != ':' && *p1 != '='; p1++) /* do nothing */ ; if (*p1 == ':' || *p1 == '=' || stspace == 0) { if (*tok == '*' || *tok == ';' || *tok == '#') goto nextline; // have a symbol here sym = lw_strndup(tok, p1 - tok); if (*p1 == ':') p1++; for (; *p1 && isspace(*p1); p1++) /* do nothing */ ; if (*p1 == '=') { tok = p1++; } else { for (tok = p1; *p1 && !isspace(*p1); p1++) /* do nothing */ ; } } if (sym && strcmp(sym, "!") == 0) cl -> isbrpt = 1; else if (sym) cl -> sym = lw_strdup(sym); cl -> symset = 0; // tok points to the opcode for the line or NUL if none // if the first two chars of the opcode are "??", that's // a flag to inhibit macro expansion if (*tok && tok[0] == '?' && tok[1] == '?') { nomacro = 1; tok += 2; } if (*tok) { // look up operation code lw_free(sym); sym = lw_strndup(tok, p1 - tok); for (; *p1 && isspace(*p1); p1++) /* do nothing */ ; for (opnum = 0; instab[opnum].opcode; opnum++) { if (!strcasecmp(instab[opnum].opcode, sym)) break; } if ((as -> target != TARGET_6309) && (instab[opnum].flags & lwasm_insn_is6309)) lwasm_register_error(as, cl, "Illegal use of 6309 instruction in 6809 mode (%s)", sym); // have to go to linedone here in case there was a symbol // to register on this line if (instab[opnum].opcode == NULL && (*tok == '*' || *tok == ';' || *tok == '#')) goto linedone; // p1 points to the start of the operand // if we're inside a macro definition and not at ENDM, // add the line to the macro definition and continue if (as -> inmacro && !(instab[opnum].flags & lwasm_insn_endm)) { add_macro_line(as, line); goto linedone; } // if skipping a condition and the operation code doesn't // operate within a condition (not a conditional) // do nothing if (as -> skipcond && !(instab[opnum].flags & lwasm_insn_cond)) goto linedone; if (!nomacro && (as -> pragmas & PRAGMA_SHADOW)) { // check for macros even if they shadow real operations // NOTE: "ENDM" cannot be shadowed if (expand_macro(as, cl, &p1, sym) == 0) { // a macro was expanded here goto linedone; } } if (instab[opnum].opcode == NULL) { cl -> insn = -1; if (*tok != ';' && *tok != '*') { // bad opcode; check for macro here // but don't expand it if "nomacro" is in effect if (nomacro || expand_macro(as, cl, &p1, sym) != 0) { // macro expansion failed if (expand_struct(as, cl, &p1, sym) != 0) { // structure expansion failed lwasm_register_error(as, cl, "Bad opcode"); } } } } else { cl -> insn = opnum; // no parse func means operand doesn't matter if (instab[opnum].parse) { if (as -> instruct == 0 || instab[opnum].flags & lwasm_insn_struct) { struct line_expr_s *le; cl -> len = -1; // call parse function debug_message(as, 100, "len = %d, dlen = %d", cl -> len, cl -> dlen); (instab[opnum].parse)(as, cl, &p1); if ((cl -> inmod == 0) && cl -> len >= 0 && cl -> dlen >= 0) { if (cl -> len == 0) cl -> len = cl -> dlen; else cl -> dlen = cl -> len; } if (*p1 && !isspace(*p1) && !(cl -> err)) { // flag bad operand error lwasm_register_error(as, cl, "Bad operand (%s)", p1); } /* do a reduction on the line expressions to avoid carrying excessive expression baggage if not needed */ as -> cl = cl; // simplify address lwasm_reduce_expr(as, cl -> addr); // simplify each expression for (le = cl -> exprs; le; le = le -> next) lwasm_reduce_expr(as, le -> expr); /* try resolving the instruction as well */ if (cl -> insn >= 0 && instab[cl -> insn].resolve) { (instab[cl -> insn].resolve)(as, cl, 0); if ((cl -> inmod == 0) && cl -> len >= 0 && cl -> dlen >= 0) { if (cl -> len == 0) cl -> len = cl -> dlen; else cl -> dlen = cl -> len; } } } else if (as -> instruct == 1) { lwasm_register_error(as, cl, "Bad operand (%s)", p1); } } } } linedone: if (!as -> skipcond && !as -> inmacro) { if (cl -> sym && cl -> symset == 0) { debug_message(as, 50, "Register symbol %s: %s", cl -> sym, lw_expr_print(cl -> addr)); // register symbol at line address if (instab[cl -> insn].flags & lwasm_insn_setdata) { if (!register_symbol(as, cl, cl -> sym, cl -> daddr, symbol_flag_none)) { // symbol error // lwasm_register_error(as, cl, "Bad symbol '%s'", cl -> sym); } } else { if (!register_symbol(as, cl, cl -> sym, cl -> addr, symbol_flag_none)) { // symbol error // lwasm_register_error(as, cl, "Bad symbol '%s'", cl -> sym); } } } debug_message(as, 40, "Line address: %s", lw_expr_print(cl -> addr)); } nextline: if (sym) lw_free(sym); sym = NULL; lw_free(line); // if we've hit the "end" bit, finish out if (as -> endseen) return; } }