Mercurial > hg-old > index.cgi
view old-trunk/lwasm/old/pseudo.c @ 348:11a95c6414b4
Added third func to instab to split resolve and emit logic
author | lost@starbug |
---|---|
date | Sat, 27 Mar 2010 22:15:07 -0600 |
parents | eb230fa7d28e |
children |
line wrap: on
line source
/* pseudo.c Copyright © 2009 William Astle This file is part of LWASM. LWASM 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/>. This file implements the various pseudo operations. */ #include <config.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "lwasm.h" #include "instab.h" #include "expr.h" #include "util.h" extern int lwasm_read_file(asmstate_t *as, const char *filename); OPFUNC(pseudo_noop) { skip_operand(p); } OPFUNC(pseudo_org) { int v, r; if (as -> csect) { register_error(as, l, 1, "ORG not allowed within sections"); return; } if (as -> inmod) { register_error(as, l, 1, "ORG not allowed within modules"); return; } if (as -> passnum != 1) { // org is not needed to be processed on pass 2 // this will prevent phasing errors for forward references that // resolve on the second pass // we saved the org address in l -> codeaddr on pass 1 as -> addr = l -> codeaddr; skip_operand(p); return; } if (l -> sym) { register_error(as, l, 1, "No symbol allowed with ORG"); } r = lwasm_expr_result2(as, l, p, EXPR_PASS1CONST, &v, 0); if (r != 0) return; l -> codeaddr = v; l -> addrset = 1; as -> addr = v; } /* The operand for include is a string optionally enclosed in " */ OPFUNC(pseudo_include) { int v1; char *fn; // only include files on pass 1 // but make sure local include context is right // for the next line... if (as -> passnum != 1) { as -> context = lwasm_next_context(as); skip_operand(p); return; } while (**p && isspace(**p)) (*p)++; if (!**p) { register_error(as, l, 1, "Bad file name"); return; } if (**p == '"') { // search for ending " (*p)++; for (v1 = 0; *((*p)+v1) && *((*p)+v1) != '"'; v1++) /* do nothing */ ; if (*((*p)+v1) != '"') { register_error(as, l, 1, "Bad file name"); return; } } else { // search for a space type character for (v1 = 0; *((*p)+v1) && !isspace(*((*p)+v1)); v1++) ; } fn = lwasm_alloc(v1 + 1); memcpy(fn, *p, v1); fn[v1] = '\0'; (*p) += v1; if (**p == '"') (*p)++; // end local label context on include as -> context = lwasm_next_context(as); if (lwasm_read_file(as, fn) < 0) { register_error(as, l, 1, "File include error (%s)", fn); } lwasm_free(fn); } /* The operand for includebin is a string optionally enclosed in " */ OPFUNC(pseudo_includebin) { int v1; char *fn; FILE *f; // only include files on pass 1 while (**p && isspace(**p)) (*p)++; if (!**p) { register_error(as, l, 1, "Bad file name"); return; } if (**p == '"') { // search for ending " (*p)++; for (v1 = 0; *((*p)+v1) && *((*p)+v1) != '"'; v1++) /* do nothing */ ; if (*((*p)+v1) != '"') { register_error(as, l, 1, "Bad file name"); return; } } else { // search for a space type character for (v1 = 0; *((*p)+v1) && !isspace(*((*p)+v1)); v1++) ; } fn = lwasm_alloc(v1 + 1); memcpy(fn, *p, v1); fn[v1] = '\0'; (*p) += v1; if (**p == '"') (*p)++; // open the file f = fopen(fn, "rb"); if (!f) { register_error(as, l, 1, "Cannot open file: %s", strerror(errno)); register_error(as, l, 2, "Cannot open file: %s", strerror(errno)); } // don't need fn any more lwasm_free(fn); // read the contents of the file and "emit()" it while (!feof(f) && !ferror(f)) { v1 = fgetc(f); if (v1 == EOF) break; lwasm_emit(as, l, v1); } // close file fclose(f); } OPFUNC(pseudo_rmb) { int r, v; if (as -> passnum == 2) { as -> addr += l -> nocodelen; skip_operand(p); return; } r = lwasm_expr_result2(as, l, p, EXPR_SECTCONST | EXPR_PASS1CONST, &v, -1); if (r != 0) return; l -> nocodelen = v; as -> addr += v; } OPFUNC(pseudo_rmd) { int r, v; if (as -> passnum == 2) { as -> addr += l -> nocodelen; skip_operand(p); return; } r = lwasm_expr_result2(as, l, p, EXPR_SECTCONST | EXPR_PASS1CONST, &v, 0); if (r != 0) return; v *= 2; l -> nocodelen = v; as -> addr += v; } OPFUNC(pseudo_rmq) { int r, v; if (as -> passnum == 2) { as -> addr += l -> nocodelen; skip_operand(p); return; } r = lwasm_expr_result2(as, l, p, EXPR_SECTCONST | EXPR_PASS1CONST, &v, 0); if (r != 0) return; v *= 4; l -> nocodelen = v; as -> addr += v; } OPFUNC(pseudo_zmb) { int r, v; r = lwasm_expr_result2(as, l, p, EXPR_SECTCONST | EXPR_PASS1CONST, &v, 0); if (r != 0) return; while (v--) lwasm_emit(as, l, 0); } OPFUNC(pseudo_zmd) { int r, v; r = lwasm_expr_result2(as, l, p, EXPR_SECTCONST | EXPR_PASS1CONST, &v, 0); if (r != 0) return; v *= 2; while (v--) lwasm_emit(as, l, 0); } OPFUNC(pseudo_zmq) { int r, v; r = lwasm_expr_result2(as, l, p, EXPR_SECTCONST | EXPR_PASS1CONST, &v, 0); if (r != 0) return; v *= 4; while (v--) lwasm_emit(as, l, 0); } OPFUNC(pseudo_end) { int r, v; lwasm_expr_stack_t *s; as -> endseen = 1; // address only matters for DECB output if (as -> outformat != OUTPUT_DECB) { skip_operand(p); return; } r = lwasm_expr_result2(as, l, p, 0, &v, 0); if (r != 0) { register_error(as, l, 2, "Bad operand"); } v = v & 0xffff; if (as -> passnum == 2) { as -> execaddr = v; l -> symaddr = v; l -> addrset = 2; } } OPFUNC(pseudo_align) { int cn; int r, v; int pad = 0; // we have to parse this on pass 2 so that we get the pad value // if (as -> passnum == 2) // { // skip_operand(p); // while (as -> addr < l -> symaddr) // lwasm_emit(as, l, 0); // return; // } r = lwasm_expr_result2(as, l, p, EXPR_SECTCONST | EXPR_PASS1CONST, &v, 0); if (r != 0) { l -> symaddr = as -> addr; return; } if (v < 1) { register_error(as, l, 1, "Illegal alignment %d", v); return; } cn = l -> codeaddr % v; if (cn) cn = v - cn; if (**p == ',') { // we have a padding value specified (*p)++; r = lwasm_expr_result2(as, l, p, EXPR_SECTCONST, &pad, 1); if (r != 0 && as -> passnum == 2) { register_error(as, l, 2, "Illegal padding value - must be constant on pass 2"); return; } } while (cn--) { lwasm_emit(as, l, pad); } l -> symaddr = as -> addr; } OPFUNC(pseudo_equ) { int r, v; if (l -> sym == NULL) { register_error(as, l, 1, "No symbol specified"); return; } r = lwasm_expr_result2(as, l, p, EXPR_SECTCONST | EXPR_PASS1CONST, &v, 0); if (r < 0) v = 0; l -> symaddr = v & 0xFFFF; l -> addrset = 2; // note: we need to do this because the symbol might have resolved // to a constant! lwasm_register_symbol(as, l, l -> sym, v, (r > 0 ? SYMBOL_COMPLEX: SYMBOL_NORM) | SYMBOL_FORCE | (l -> forceglobal ? SYMBOL_GLOBAL : SYMBOL_NORM)); } OPFUNC(pseudo_set) { int r, v; // set MUST run on both passes as the symbol value changes! if (l -> sym == NULL) { register_error(as, l, 1, "No symbol specified"); return; } r = lwasm_expr_result2(as, l, p, EXPR_SECTCONST | EXPR_PASS1CONST, &v, 0); if (r < 0) v = 0; l -> symaddr = v & 0xFFFF; l -> addrset = 2; lwasm_register_symbol(as, l, l -> sym, v, (r > 0 ? SYMBOL_COMPLEX: SYMBOL_NORM) | SYMBOL_SET); } OPFUNC(pseudo_setdp) { int r, v; if (as -> outformat == OUTPUT_OBJ) { register_error(as, l, 1, "SETDP not permitted with OBJ target"); return; } // setdp is needed on both passes; must resolve to a constant on pass 1 r = lwasm_expr_result2(as, l, p, EXPR_SECTCONST | EXPR_PASS1CONST, &v, 0); if (r != 0) return; if (v < -127 || v > 255) { register_error(as, l, 1, "Byte overflow"); return; } l -> symaddr = v & 0xFF; l -> addrset = 2; as -> dpval = v & 0xFF; } // used to get a byte from a string // -1 is end of line int pseudo_fcc_fetchchar(asmstate_t *as, char **p) { int c; // - if (!**p) return -1; c = (unsigned char)(**p); (*p)++; if (as -> pragmas & PRAGMA_CESCAPES && c == '\\') { // decode escapes if needed if (!**p) return c; c = **p; (*p)++; switch (c) { // octal value // 1, 2, or 3 digits // NOTE: \0 for NUL is included in this... case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': c -= '0'; if (**p < '0' || **p > '9') return c; c = c << 3; c |= **p - '0'; (*p)++; if (**p < '0' || **p > '9') return c; c = c << 3; c |= **p - '0'; (*p)++; return c; // LF case 'n': return 10; // CR case 'r': return 13; // TAB case 't': return 9; // VT case 'v': return 11; // BS case 'b': return 8; // FF case 'f': return 12; // BEL case 'a': return 7; // hex char code (2 chars) case 'x': { int c2; if (!**p) return 'x'; c = toupper(**p); (*p)++; if (c < '0' || (c > '9' && c < 'A') || c > 'F') return 0; c -= '0'; if (c > 9) c -= 7; c2 = c << 4; if (!**p) return 0; c = toupper(**p); (*p)++; if (c < '0' || (c > '9' && c < 'A') || c > 'F') return 0; c -= '0'; if (c > 9) c -= 7; c2 |= c; return c2; } // everything else stands for itself as a fall back or legit default: return c; } } return c; } OPFUNC(pseudo_fcc) { int delim = 0; int c; delim = **p; if (!delim) { register_error(as, l, 1, "Bad operand"); return; } *p += 1; for (;;) { c = pseudo_fcc_fetchchar(as, p); if (c == delim || c < 0) break; lwasm_emit(as, l, c); } } OPFUNC(pseudo_fcs) { int delim = 0; int c, lc = -1; delim = **p; if (!delim) { register_error(as, l, 1, "Bad operand"); return; } *p += 1; for (;;) { c = pseudo_fcc_fetchchar(as, p); if (c == delim || c < 0) { if (lc >= 0) lwasm_emit(as, l, lc | 0x80); break; } if (lc >= 0) lwasm_emit(as, l, lc); lc = c; } } OPFUNC(pseudo_fcn) { int delim = 0; int c; delim = **p; if (!delim) { register_error(as, l, 1, "Bad operand"); return; } *p += 1; for (;;) { c = pseudo_fcc_fetchchar(as, p); if (c == delim || c < 0) break; lwasm_emit(as, l, c); } lwasm_emit(as, l, 0); } OPFUNC(pseudo_fcb) { int r, v; fcb_again: r = lwasm_expr_result2(as, l, p, 0, &v, -1); if (r < 0) return; if (r > 0) { register_error(as, l, 2, "Illegal external or inter-segment reference"); v = 0; } if (v < -127 || v > 255) { register_error(as, l, 1, "Byte overflow"); } lwasm_emit(as, l, v); if (**p == ',') { (*p)++; goto fcb_again; } } // FIXME: handle external references in an intelligent way OPFUNC(pseudo_fdb) { int r, v; int extseen = 0; char *p1; fdb_again: p1 = *p; r = lwasm_expr_result2(as, l, p, 0, &v, -1); if (r < 0) return; if (r > 0 && extseen == 1) { register_error(as, l, 2, "Illegal external or inter-segment reference (only 1 per FDB line)"); v = 0; } else if (r > 0) { l -> relocoff = as -> addr - l -> codeaddr; *p = p1; r = lwasm_expr_result2(as, l, p, 0, &v, 0); } lwasm_emit(as, l, v >> 8); lwasm_emit(as, l, v & 0xff); if (**p == ',') { (*p)++; goto fdb_again; } } // FIXME: handle external references in a sensible way OPFUNC(pseudo_fqb) { int r, v; fqb_again: r = lwasm_expr_result2(as, l, p, 0, &v, -1); if (r < 0) return; if (r > 0) { register_error(as, l, 2, "Illegal external or inter-segment reference"); v = 0; } lwasm_emit(as, l, v >> 24); lwasm_emit(as, l, v >> 16); lwasm_emit(as, l, v >> 8); lwasm_emit(as, l, v & 0xff); if (**p == ',') { (*p)++; goto fqb_again; } } // don't need to do anything if we are executing one of these OPFUNC(pseudo_endc) { if (as -> skipcond && !(as -> skipmacro)) { as -> skipcount -= 1; if (as -> skipcount <= 0) { as -> skipcond = 0; } } return; } // if "else" executes, we must be going into an "ignore" state OPFUNC(pseudo_else) { if (as -> skipmacro) return; if (as -> skipcond) { if (as -> skipcount == 1) { as -> skipcount = 0; as -> skipcond = 0; } return; } as -> skipcond = 1; as -> skipcount = 1; } OPFUNC(pseudo_ifne) { int v1; int rval; if (as -> skipcond && !(as -> skipmacro)) { as -> skipcount++; skip_operand(p); return; } rval = lwasm_expr_result2(as, l, p, EXPR_SECTCONST | EXPR_PASS1CONST, &v1, 0); if (rval != 0) return; if (!v1) { as -> skipcond = 1; as -> skipcount = 1; } } OPFUNC(pseudo_ifdef) { lwasm_symbol_ent_t *se; char *sym; char *p2; if (as -> skipcond && !(as -> skipmacro)) { as -> skipcount++; skip_operand(p); return; } if (as -> passnum != 1) { skip_operand(p); if (!(l -> fsize)) { as -> skipcond = 1; as -> skipcount = 1; } return; } if (!**p) { register_error(as, l, 1, "Need symbol name"); return; } for (p2 = *p; **p && !isspace(**p); (*p)++) /* do nothing */ ; sym = lwasm_alloc(*p - p2 + 1); memcpy(sym, p2, *p - p2); sym[*p - p2] = '\0'; // fprintf(stderr, "STUFF: %s; '%s'; '%s' (%d)\n", p2, *p, sym, as -> passnum); se = lwasm_find_symbol(as, sym, l -> context); if (!se) se = lwasm_find_symbol(as, sym, -1); lwasm_free(sym); if (!se) { as -> skipcond = 1; as -> skipcount = 1; l -> fsize = 0; } else { l -> fsize = 1; } } OPFUNC(pseudo_ifndef) { lwasm_symbol_ent_t *se; char *sym; char *p2; if (as -> skipcond && !(as -> skipmacro)) { as -> skipcount++; skip_operand(p); return; } if (as -> passnum != 1) { skip_operand(p); if (l -> fsize) { as -> skipcond = 1; as -> skipcount = 1; } return; } if (!**p) { register_error(as, l, 1, "Need symbol name"); return; } for (p2 = *p; *p2 && !isspace(*p2); p2++) /* do nothing */ ; sym = lwasm_alloc(p2 - *p + 1); memcpy(sym, *p, p2 - *p); sym[p2 - *p] = '\0'; *p = p2; // fprintf(stderr, "STUFF2: %s; '%s'; '%s' (%d)\n", *p, p2, sym, as -> passnum); se = lwasm_find_symbol(as, sym, l -> context); if (!se) se = lwasm_find_symbol(as, sym, -1); lwasm_free(sym); if (se) { as -> skipcond = 1; as -> skipcount = 1; l -> fsize = 0; } else { l -> fsize = 1; } } OPFUNC(pseudo_ifp1) { if (as -> skipcond && !(as -> skipmacro)) { as -> skipcount++; skip_operand(p); return; } if (as -> passnum != 1) { as -> skipcond = 1; as -> skipcount = 1; } } OPFUNC(pseudo_ifp2) { if (as -> skipcond && !(as -> skipmacro)) { as -> skipcount++; skip_operand(p); return; } if (as -> passnum != 2) { as -> skipcond = 1; as -> skipcount = 1; } } OPFUNC(pseudo_ifeq) { int v1; int rval; if (as -> skipcond && !(as -> skipmacro)) { as -> skipcount++; skip_operand(p); return; } rval = lwasm_expr_result2(as, l, p, EXPR_SECTCONST | EXPR_PASS1CONST, &v1, 0); if (rval != 0) return; if (v1) { as -> skipcond = 1; as -> skipcount = 1; } } OPFUNC(pseudo_iflt) { int v1; int rval; if (as -> skipcond && !(as -> skipmacro)) { as -> skipcount++; skip_operand(p); return; } rval = lwasm_expr_result2(as, l, p, EXPR_SECTCONST | EXPR_PASS1CONST, &v1, 0); if (rval != 0) return; if (v1 >= 0) { as -> skipcond = 1; as -> skipcount = 1; } } OPFUNC(pseudo_ifle) { int v1; int rval; if (as -> skipcond && !(as -> skipmacro)) { as -> skipcount++; skip_operand(p); return; } rval = lwasm_expr_result2(as, l, p, EXPR_SECTCONST | EXPR_PASS1CONST, &v1, 0); if (rval != 0) return; if (v1 > 0) { as -> skipcond = 1; as -> skipcount = 1; } } OPFUNC(pseudo_ifgt) { int v1; int rval; if (as -> skipcond && !(as -> skipmacro)) { as -> skipcount++; skip_operand(p); return; } rval = lwasm_expr_result2(as, l, p, EXPR_SECTCONST | EXPR_PASS1CONST, &v1, 0); if (rval != 0) return; if (v1 <= 0) { as -> skipcond = 1; as -> skipcount = 1; } } OPFUNC(pseudo_ifge) { int v1; int rval; if (as -> skipcond && !(as -> skipmacro)) { as -> skipcount++; skip_operand(p); return; } rval = lwasm_expr_result2(as, l, p, EXPR_SECTCONST | EXPR_PASS1CONST, &v1, 0); if (rval != 0) return; if (v1 < 0) { as -> skipcond = 1; as -> skipcount = 1; } } OPFUNC(pseudo_error) { register_error(as, l, 1, "User error: %s", *p); } OPFUNC(pseudo_section) { sectiontab_t *s; char *p2; char *sn; char *opts; if (as -> outformat != OUTPUT_OBJ) { register_error(as, l, 1, "Sections only supported for obj target"); return; } if (as -> csect) { as -> csect -> offset = as -> addr; as -> csect = NULL; } if (!**p) { register_error(as, l, 1, "Need section name"); return; } for (p2 = *p; *p2 && !isspace(*p2); p2++) /* do nothing */ ; sn = lwasm_alloc(p2 - *p + 1); memcpy(sn, *p, p2 - *p); sn[p2 - *p] = '\0'; *p = p2; opts = strchr(sn, ','); if (opts) { *opts++ = '\0'; } // have we seen the section name already? for (s = as -> sections; s; s = s -> next) { if (!strcmp(s -> name, sn)) break; } if (s && as -> passnum == 1) { lwasm_free(sn); if (opts) { register_error(as, l, 1, "Section options can only be specified the first time"); return; } } else if (!s) { s = lwasm_alloc(sizeof(sectiontab_t)); s -> name = sn; s -> offset = 0; s -> flags = 0; s -> obytes = NULL; s -> oblen = 0; s -> obsize = 0; s -> rl = NULL; s -> exports = NULL; // if name of section is "bss" or ".bss", assume bss flag // which can be overridden with !bss flag if (!strcasecmp(sn, "bss") || !strcasecmp(sn, ".bss")) s -> flags = SECTION_BSS; // parse options; only one "bss" if (opts && as -> passnum == 1) { if (!strcasecmp(opts, "bss")) { s -> flags |= SECTION_BSS; } else if (!strcasecmp(opts, "!bss")) { s -> flags &= ~SECTION_BSS; } else { register_error(as, l, 1, "Unrecognized section option '%s'", opts); lwasm_free(s -> name); lwasm_free(s); return; } } s -> next = as -> sections; as -> sections = s; } as -> addr = s -> offset; as -> csect = s; as -> context = lwasm_next_context(as); } OPFUNC(pseudo_endsection) { if (as -> outformat != OUTPUT_OBJ) { register_error(as, l, 1, "Sections only supported for obj target"); return; } if (!(as -> csect)) { register_error(as, l, 1, "ENDSECTION when not in a section"); return; } as -> csect -> offset = as -> addr; as -> addr = 0; as -> csect = 0; as -> context = lwasm_next_context(as); skip_operand(p); } OPFUNC(pseudo_extern) { if (as -> passnum != 1) return; if (as -> outformat != OUTPUT_OBJ) { register_error(as, l, 1, "External references only supported for obj target"); return; } if (as -> csect) { register_error(as, l, 1, "Cannot declare external symbols within a section"); return; } if (l -> sym) { lwasm_register_symbol(as, l, l -> sym, 0, SYMBOL_EXTERN); for ( ; **p; (*p)++) ; return; } while (**p) { char *sym2, *sym3; for (sym2 = *p; **p && !isspace(**p) && **p != ','; (*p)++) /* do nothing */ ; if (l -> sym) lwasm_free(l -> sym); sym3 = lwasm_alloc(*p - sym2 + 1); memcpy(sym3, sym2, *p - sym2); sym3[*p - sym2] = '\0'; l -> sym = sym3; debug_message(2, "import symbol: '%s'", sym3); lwasm_register_symbol(as, l, l -> sym, 0, SYMBOL_EXTERN); if (**p && (**p != ',')) { register_error(as, l, 1, "Bad import list"); return; } if (**p == ',') (*p)++; } if (!(l -> sym)) register_error(as, l, 1, "Bad import list"); } OPFUNC(pseudo_export) { lwasm_symbol_ent_t *se; export_list_t *ex; char *sym2, *sym3; int after = 0; if (as -> outformat != OUTPUT_OBJ) { register_error(as, l, 1, "Symbol exports only supported for obj target"); return; } if (as -> passnum == 1) { skip_operand(p); return; } if (l -> sym) { for ( ; **p; (*p)++) ; } again: if (!(l -> sym) || after == 1) { after = 1; // look for symbol after op if (**p) { for (sym2 = *p; **p && !isspace(**p) && **p != ','; (*p)++) /* do nothing */ ; debug_message(2, "export expression: %s", sym2); if (l -> sym) lwasm_free(l -> sym); sym3 = lwasm_alloc(*p - sym2 + 1); memcpy(sym3, sym2, *p - sym2); sym3[*p - sym2] = '\0'; l -> sym = sym3; debug_message(2, "export symbol: '%s'", sym3); } } if (!(l -> sym)) { register_error(as, l, 2, "No symbol"); return; } // the symbol better be defined at this point (pass 2) // local symbols cannot be exported nor can "global" symbols se = lwasm_find_symbol(as, l -> sym, -1); if ((!se || (se -> flags & SYMBOL_EXTERN)) && (as -> pragmas & PRAGMA_IMPORTUNDEFEXPORT)) { void *p; p = as -> csect; as -> csect = NULL; lwasm_register_symbol(as, l, l -> sym, 0, SYMBOL_EXTERN); as -> csect = p; } else { if (!se) { register_error(as, l, 2, "Exported symbols must be fully defined within a section"); return; } if (se -> sect == NULL) { register_error(as, l, 2, "Only non-local symbols within a section can be exported"); return; } if (se -> flags & SYMBOL_SET) { register_error(as, l, 2, "You cannot export symbols defined with SET"); return; } // if the symbol is not already a simple value, re-evaluate it // and see if it becomes simple if (se -> flags & SYMBOL_COMPLEX) { register_error(as, l, 2, "Exported symbols must be fully resolved on pass 2"); return; } // search for existing export for (ex = se -> sect -> exports; ex; ex = ex -> next) if (!strcmp(l -> sym, ex -> sym)) break; if (ex) { register_error(as, l, 2, "Symbol %s already exported", l -> sym); return; } // add an external reference ex = lwasm_alloc(sizeof(export_list_t)); ex -> next = se -> sect -> exports; se -> sect -> exports = ex; ex -> offset = se -> value; ex -> sym = lwasm_strdup(se -> sym); } next: if (after == 1) { if (**p == ',') { debug_message(2, "Export another symbol: %s", *p); (*p)++; for ( ; **p && isspace(**p); (*p)++) /* do nothing */ ; goto again; } } }