diff lwasm/pseudo.c @ 151:427e268e876b

renamed src to lwasm to better reflect its purpose
author lost
date Fri, 30 Jan 2009 04:01:55 +0000
parents src/pseudo.c@2ba8f9ef1417
children f0527dc3804d
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/pseudo.c	Fri Jan 30 04:01:55 2009 +0000
@@ -0,0 +1,967 @@
+/*
+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 <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_org)
+{
+	int v, r;
+
+	if (as -> csect)
+	{
+		register_error(as, l, 1, "ORG not allowed within sections");
+		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;
+		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);
+		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';
+
+	// 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);
+}
+
+OPFUNC(pseudo_rmb)
+{
+	int r, v;
+	
+	if (as -> passnum == 2)
+	{
+		as -> addr += l -> nocodelen;
+		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;
+		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;
+		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)
+		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;
+	
+	if (as -> passnum == 2)
+	{
+		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; 
+
+	while (cn--)
+	{
+		lwasm_emit(as, l, 0);
+	}
+	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);
+}
+
+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;
+}
+
+OPFUNC(pseudo_fcc)
+{
+	int delim = 0;
+				
+	delim = **p;
+	if (!delim)
+	{
+		register_error(as, l, 1, "Bad operand");
+		return;
+	}
+	*p += 1;
+	while (**p && **p != delim)
+	{
+		lwasm_emit(as, l, **p);
+		(*p)++;
+	}
+	if (**p)
+		(*p)++;
+}
+		
+
+OPFUNC(pseudo_fcs)
+{
+	int delim = 0;
+
+	delim = **p;
+	if (!delim)
+	{
+		register_error(as, l, 1, "Bad operand");
+		return;
+	}
+	*p += 1;
+	while (**p && **p != delim)
+	{
+		if (!*((*p) + 1) || *((*p) + 1) == delim)
+			lwasm_emit(as, l, **p | 0x80);
+		else
+			lwasm_emit(as, l, **p);
+		(*p)++;
+	}
+	if (**p)
+		(*p)++;
+}
+
+OPFUNC(pseudo_fcn)
+{		
+	int delim = 0;
+				
+	delim = **p;
+	if (!delim)
+	{
+		register_error(as, l, 1, "Bad operand");
+		return;
+	}
+	*p += 1;
+	while (**p && **p != delim)
+	{
+		lwasm_emit(as, l, **p);
+		(*p)++;
+	}
+	if (**p)
+		(*p)++;
+	lwasm_emit(as, l, 0);
+}
+
+// FIXME: handle external, etc., references in a useful manner
+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++;
+		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++;
+		return;
+	}
+
+	if (as -> passnum != 1)
+	{
+		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;
+
+	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++;
+		return;
+	}
+
+	if (as -> passnum != 1)
+	{
+		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;
+
+	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_ifeq)
+{
+	int v1;
+	int rval;
+
+	if (as -> skipcond && !(as -> skipmacro))
+	{
+		as -> skipcount++;
+		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++;
+		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++;
+		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++;
+		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++;
+		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;
+		// parse options; only one "bss"
+		if (opts && as -> passnum == 1)
+		{
+			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);
+}
+
+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;
+	}
+	
+	lwasm_register_symbol(as, l, l -> sym, 0, SYMBOL_EXTERN);
+}
+
+OPFUNC(pseudo_export)
+{
+	lwasm_symbol_ent_t *se;
+	export_list_t *ex;
+	
+	if (as -> outformat != OUTPUT_OBJ)
+	{
+		register_error(as, l, 1, "Symbol exports only supported for obj target");
+		return;
+	}
+	
+	if (as -> passnum == 1)
+		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)
+	{
+		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);
+}