changeset 116:817ab0e124fa

reading files implemented
author lost
date Wed, 21 Jan 2009 03:15:49 +0000
parents 776d8bea5b46
children bb3cc989e84b
files lwlink/trunk/src/Makefile.am lwlink/trunk/src/expr.c lwlink/trunk/src/expr.h lwlink/trunk/src/lwlink.h lwlink/trunk/src/readfiles.c lwlink/trunk/src/util.c
diffstat 6 files changed, 798 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/lwlink/trunk/src/Makefile.am	Sun Jan 18 04:53:57 2009 +0000
+++ b/lwlink/trunk/src/Makefile.am	Wed Jan 21 03:15:49 2009 +0000
@@ -1,3 +1,3 @@
 bin_PROGRAMS = lwlink
-lwlink_SOURCES = main.c lwlink.c util.c readfiles.c
-EXTRA_DIST = lwlink.h util.h
+lwlink_SOURCES = main.c lwlink.c util.c readfiles.c expr.c
+EXTRA_DIST = lwlink.h util.h expr.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlink/trunk/src/expr.c	Wed Jan 21 03:15:49 2009 +0000
@@ -0,0 +1,356 @@
+/*
+expr.c
+Copyright © 2008 William Astle
+
+This file is part of LWLINK.
+
+LWLINK 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 contains the actual expression evaluator
+*/
+
+#define __expr_c_seen__
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "expr.h"
+#include "util.h"
+
+lw_expr_stack_t *lw_expr_stack_create(void)
+{
+	lw_expr_stack_t *s;
+	
+	s = lw_malloc(sizeof(lw_expr_stack_t));
+	s -> head = NULL;
+	s -> tail = NULL;
+	return s;
+}
+
+void lw_expr_stack_free(lw_expr_stack_t *s)
+{
+	while (s -> head)
+	{
+		s -> tail = s -> head;
+		s -> head = s -> head -> next;
+		lw_expr_term_free(s -> tail -> term);
+		lw_free(s -> tail);
+	}
+	lw_free(s);
+}
+
+void lw_expr_term_free(lw_expr_term_t *t)
+{
+	if (t)
+	{
+		if (t -> term_type == LW_TERM_SYM)
+			lw_free(t -> symbol);
+		lw_free(t);
+	}
+}
+
+lw_expr_term_t *lw_expr_term_create_oper(int oper)
+{
+	lw_expr_term_t *t;
+
+	t = lw_malloc(sizeof(lw_expr_term_t));
+	t -> term_type = LW_TERM_OPER;
+	t -> value = oper;
+	return t;
+}
+
+lw_expr_term_t *lw_expr_term_create_int(int val)
+{
+	lw_expr_term_t *t;
+	
+	t = lw_malloc(sizeof(lw_expr_term_t));
+	t -> term_type = LW_TERM_INT;
+	t -> value = val;
+	return t;
+}
+
+lw_expr_term_t *lw_expr_term_create_sym(char *sym, int symtype)
+{
+	lw_expr_term_t *t;
+	
+	t = lw_malloc(sizeof(lw_expr_term_t));
+	t -> term_type = LW_TERM_SYM;
+	t -> symbol = lw_strdup(sym);
+	t -> value = symtype;
+	return t;
+}
+
+lw_expr_term_t *lw_expr_term_dup(lw_expr_term_t *t)
+{
+	switch (t -> term_type)
+	{
+	case LW_TERM_INT:
+		return lw_expr_term_create_int(t -> value);
+		
+	case LW_TERM_OPER:
+		return lw_expr_term_create_oper(t -> value);
+		
+	case LW_TERM_SYM:
+		return lw_expr_term_create_sym(t -> symbol, t -> value);
+	
+	default:
+		exit(1);
+	}
+// can't get here
+}
+
+void lw_expr_stack_push(lw_expr_stack_t *s, lw_expr_term_t *t)
+{
+	lw_expr_stack_node_t *n;
+
+	if (!s)
+	{
+		exit(1);
+	}
+	
+	n = lw_malloc(sizeof(lw_expr_stack_node_t));
+	n -> next = NULL;
+	n -> prev = s -> tail;
+	n -> term = lw_expr_term_dup(t);
+	
+	if (s -> head)
+	{
+		s -> tail -> next = n;
+		s -> tail = n;
+	}
+	else
+	{
+		s -> head = n;
+		s -> tail = n;
+	}
+}
+
+lw_expr_term_t *lw_expr_stack_pop(lw_expr_stack_t *s)
+{
+	lw_expr_term_t *t;
+	lw_expr_stack_node_t *n;
+	
+	if (!(s -> tail))
+		return NULL;
+	
+	n = s -> tail;
+	s -> tail = n -> prev;
+	if (!(n -> prev))
+	{
+		s -> head = NULL;
+	}
+	
+	t = n -> term;
+	n -> term = NULL;
+	
+	lw_free(n);
+	
+	return t;
+}
+
+/*
+take an expression stack s and scan for operations that can be completed
+
+return -1 on error, 0 on no error
+
+possible errors are: division by zero or unknown operator
+
+theory of operation:
+
+scan the stack for an operator which has two constants preceding it (binary)
+or 1 constant preceding it (unary) and if found, perform the calculation
+and replace the operator and its operands with the result
+
+repeat the scan until no futher simplications are found or if there are no
+further operators or only a single term remains
+
+*/
+int lw_expr_reval(lw_expr_stack_t *s, lw_expr_stack_t *(*sfunc)(char *sym, int stype, void *state), void *state)
+{
+	lw_expr_stack_node_t *n, *n2;
+	lw_expr_stack_t *ss;
+	int c;
+	
+next_iter_sym:
+	// resolve symbols
+	// symbols that do not resolve to an expression are left alone
+	for (c = 0, n = s -> head; n; n = n -> next)
+	{
+		if (n -> term -> term_type == LW_TERM_SYM)
+		{
+			ss = sfunc(n -> term -> symbol, n -> term -> value, state);
+			if (ss)
+			{
+				c++;
+				// splice in the result stack
+				if (n -> prev)
+				{
+					n -> prev -> next = ss -> head;
+				}
+				else
+				{
+					s -> head = ss -> head;
+				}
+				ss -> head -> prev = n -> prev;
+				ss -> tail -> next = n -> next;
+				if (n -> next)
+				{
+					n -> next -> prev = ss -> tail;
+				}
+				else
+				{
+					s -> tail = ss -> tail;
+				}
+				lw_expr_term_free(n -> term);
+				lw_free(n);
+				n = ss -> tail;
+				
+				ss -> head = NULL;
+				ss -> tail = NULL;
+				lw_expr_stack_free(ss);
+			}
+		}
+	}
+	if (c)
+		goto next_iter_sym;
+
+next_iter:	
+	// a single term
+	if (s -> head == s -> tail)
+		return 0;
+	
+	// search for an operator
+	for (n = s -> head; n; n = n -> next)
+	{
+		if (n -> term -> term_type == LW_TERM_OPER)
+		{
+			if (n -> term -> value == LW_OPER_NEG
+				|| n -> term -> value == LW_OPER_COM
+				)
+			{
+				// unary operator
+				if (n -> prev && n -> prev -> term -> term_type == LW_TERM_INT)
+				{
+					// a unary operator we can resolve
+					// we do the op then remove the term "n" is pointing at
+					if (n -> term -> value == LW_OPER_NEG)
+					{
+						n -> prev -> term -> value = -(n -> prev -> term -> value);
+					}
+					else if (n -> term -> value == LW_OPER_COM)
+					{
+						n -> prev -> term -> value = ~(n -> prev -> term -> value);
+					}
+					n -> prev -> next = n -> next;
+					if (n -> next)
+						n -> next -> prev = n -> prev;
+					else
+						s -> tail = n -> prev;	
+					
+					lw_expr_term_free(n -> term);
+					lw_free(n);
+					break;
+				}
+			}
+			else
+			{
+				// binary operator
+				if (n -> prev && n -> prev -> prev && n -> prev -> term -> term_type == LW_TERM_INT && n -> prev -> prev -> term -> term_type == LW_TERM_INT)
+				{
+					// a binary operator we can resolve
+					switch (n -> term -> value)
+					{
+					case LW_OPER_PLUS:
+						n -> prev -> prev -> term -> value += n -> prev -> term -> value;
+						break;
+
+					case LW_OPER_MINUS:
+						n -> prev -> prev -> term -> value -= n -> prev -> term -> value;
+						break;
+
+					case LW_OPER_TIMES:
+						n -> prev -> prev -> term -> value *= n -> prev -> term -> value;
+						break;
+
+					case LW_OPER_DIVIDE:
+						if (n -> prev -> term -> value == 0)
+							return -1;
+						n -> prev -> prev -> term -> value /= n -> prev -> term -> value;
+						break;
+
+					case LW_OPER_MOD:
+						if (n -> prev -> term -> value == 0)
+							return -1;
+						n -> prev -> prev -> term -> value %= n -> prev -> term -> value;
+						break;
+
+					case LW_OPER_INTDIV:
+						if (n -> prev -> term -> value == 0)
+							return -1;
+						n -> prev -> prev -> term -> value /= n -> prev -> term -> value;
+						break;
+
+					case LW_OPER_BWAND:
+						n -> prev -> prev -> term -> value &= n -> prev -> term -> value;
+						break;
+
+					case LW_OPER_BWOR:
+						n -> prev -> prev -> term -> value |= n -> prev -> term -> value;
+						break;
+
+					case LW_OPER_BWXOR:
+						n -> prev -> prev -> term -> value ^= n -> prev -> term -> value;
+						break;
+
+					case LW_OPER_AND:
+						n -> prev -> prev -> term -> value = (n -> prev -> term -> value && n -> prev -> prev -> term -> value) ? 1 : 0;
+						break;
+
+					case LW_OPER_OR:
+						n -> prev -> prev -> term -> value = (n -> prev -> term -> value || n -> prev -> prev -> term -> value) ? 1 : 0;
+						break;
+
+					default:
+						// return error if unknown operator!
+						return -1;
+					}
+
+					// now remove the two unneeded entries from the stack
+					n -> prev -> prev -> next = n -> next;
+					if (n -> next)
+						n -> next -> prev = n -> prev -> prev;
+					else
+						s -> tail = n -> prev -> prev;	
+					
+					lw_expr_term_free(n -> term);
+					lw_expr_term_free(n -> prev -> term);
+					lw_free(n -> prev);
+					lw_free(n);
+					break;
+				}
+			}
+		}
+	}
+	// note for the terminally confused about dynamic memory and pointers:
+	// n will not be NULL even after the lw_free calls above so
+	// this test will still work (n will be a dangling pointer)
+	// (n will only be NULL if we didn't find any operators to simplify)
+	if (n)
+		goto next_iter;
+	
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlink/trunk/src/expr.h	Wed Jan 21 03:15:49 2009 +0000
@@ -0,0 +1,107 @@
+/*
+expr.h
+Copyright © 2008 William Astle
+
+This file is part of LWLINK.
+
+LWLINK 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/>.
+*/
+
+/*
+Definitions for expression evaluator
+*/
+
+#ifndef __expr_h_seen__
+#define __expr_h_seen__
+
+#ifndef __expr_c_seen__
+#define __expr_E__ extern
+#else
+#define __expr_E__
+#endif
+
+// term types
+#define LW_TERM_NONE		0
+#define LW_TERM_OPER		1	// an operator
+#define LW_TERM_INT		2	// 32 bit signed integer
+#define LW_TERM_SYM		3	// symbol reference
+
+// operator types
+#define LW_OPER_NONE		0
+#define LW_OPER_PLUS		1	// +
+#define LW_OPER_MINUS	2	// -
+#define LW_OPER_TIMES	3	// *
+#define LW_OPER_DIVIDE	4	// /
+#define LW_OPER_MOD		5	// %
+#define LW_OPER_INTDIV	6	// \ (don't end line with \)
+#define LW_OPER_BWAND	7	// bitwise AND
+#define LW_OPER_BWOR		8	// bitwise OR
+#define LW_OPER_BWXOR	9	// bitwise XOR
+#define LW_OPER_AND		10	// boolean AND
+#define LW_OPER_OR		11	// boolean OR
+#define LW_OPER_NEG		12	// - unary negation (2's complement)
+#define LW_OPER_COM		13	// ^ unary 1's complement
+
+
+// term structure
+typedef struct lw_expr_term_s
+{
+	int term_type;		// type of term (see above)
+	char *symbol;		// name of a symbol
+	int value;			// value of the term (int) or operator number (OPER)
+} lw_expr_term_t;
+
+// type for an expression evaluation stack
+typedef struct lw_expr_stack_node_s lw_expr_stack_node_t;
+struct lw_expr_stack_node_s
+{
+	lw_expr_term_t		*term;
+	lw_expr_stack_node_t	*prev;
+	lw_expr_stack_node_t	*next;	
+};
+
+typedef struct lw_expr_stack_s
+{
+	lw_expr_stack_node_t *head;
+	lw_expr_stack_node_t *tail;
+} lw_expr_stack_t;
+
+__expr_E__ void lw_expr_term_free(lw_expr_term_t *t);
+__expr_E__ lw_expr_term_t *lw_expr_term_create_oper(int oper);
+__expr_E__ lw_expr_term_t *lw_expr_term_create_sym(char *sym, int symtype);
+__expr_E__ lw_expr_term_t *lw_expr_term_create_int(int val);
+__expr_E__ lw_expr_term_t *lw_expr_term_dup(lw_expr_term_t *t);
+
+__expr_E__ void lw_expr_stack_free(lw_expr_stack_t *s);
+__expr_E__ lw_expr_stack_t *lw_expr_stack_create(void);
+
+__expr_E__ void lw_expr_stack_push(lw_expr_stack_t *s, lw_expr_term_t *t);
+__expr_E__ lw_expr_term_t *lw_expr_stack_pop(lw_expr_stack_t *s);
+
+// simplify expression
+__expr_E__ int lw_expr_reval(lw_expr_stack_t *s, lw_expr_stack_t *(*sfunc)(char *sym, int symtype, void *state), void *state);
+
+// useful macros
+// is the expression "simple" (one term)?
+#define lw_expr_is_simple(s) ((s) -> head == (s) -> tail)
+
+// is the expression constant?
+#define lw_expr_is_constant(s) (lw_expr_is_simple(s) && (!((s) -> head) || (s) -> head -> term -> term_type == LW_TERM_INT))
+
+// get the constant value of an expression or 0 if not constant
+#define lw_expr_get_value(s) (lw_expr_is_constant(s) ? ((s) -> head ? (s) -> head -> term -> value : 0) : 0)
+
+#undef __expr_E__
+
+#endif // __expr_h_seen__
--- a/lwlink/trunk/src/lwlink.h	Sun Jan 18 04:53:57 2009 +0000
+++ b/lwlink/trunk/src/lwlink.h	Wed Jan 21 03:15:49 2009 +0000
@@ -24,17 +24,55 @@
 #ifndef __lwlink_h_seen__
 #define __lwlink_h_seen__
 
+#include "expr.h"
+
 #define OUTPUT_DECB		0	// DECB multirecord format
 #define OUTPUT_RAW		1	// raw sequence of bytes
 
+typedef struct symtab_s symtab_t;
+struct symtab_s
+{
+	unsigned char *sym;		// symbol name
+	int offset;				// local offset
+	int realval;			// resolved value
+	symtab_t *next;			// next symbol
+};
+
+typedef struct reloc_s reloc_t;
+struct reloc_s
+{
+	int offset;				// where in the section
+	lw_expr_stack_t *expr;	// the expression to calculate it
+	reloc_t *next;			// ptr to next relocation
+};
+
+#define SECTION_BSS		1
+typedef struct
+{
+	unsigned char *name;	// name of the section
+	int flags;				// section flags
+	int codesize;			// size of the code
+	unsigned char *code;	// pointer to the code
+	int loadaddress;		// the actual load address of the section
+	
+	symtab_t *localsyms;	// local symbol table
+	symtab_t *exportedsyms;	// exported symbols table
+	
+	reloc_t *incompletes;	// table of incomplete references
+} section_t;
 
 typedef struct
 {
 	char *filename;
 	unsigned char *filedata;
 	long filesize;
+	section_t *sections;
+	int nsections;
+	
 } fileinfo_t;
 
+
+
 #ifndef __lwlink_c_seen__
 
 extern int debug_level;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlink/trunk/src/readfiles.c	Wed Jan 21 03:15:49 2009 +0000
@@ -0,0 +1,292 @@
+/*
+readfiles.c
+Copyright © 2008 William Astle
+
+This file is part of LWLINK.
+
+LWLINK 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/>.
+
+
+Reads input files
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <argp.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "lwlink.h"
+#include "util.h"
+
+void read_lwobj16v0(fileinfo_t *fn);
+
+/*
+The logic of reading the entire file into memory is simple. All the symbol
+names in the file are NUL terminated strings and can be used directly without
+making additional copies.
+*/
+void read_files(void)
+{
+	int i;
+	long size;
+	FILE *f;
+	long bread;
+	for (i = 0; i < ninputfiles; i++)
+	{
+		f = fopen(inputfiles[i] -> filename, "rb");
+		if (!f)
+		{
+			fprintf(stderr, "Can't open file %s:", inputfiles[i] -> filename);
+			perror("");
+			exit(1);
+		}
+		fseek(f, 0, SEEK_END);
+		size = ftell(f);
+		rewind(f);
+		
+		inputfiles[i] -> filedata = lw_malloc(size);
+		inputfiles[i] -> filesize = size;
+		
+		bread = fread(inputfiles[i] -> filedata, 1, size, f);
+		if (bread < size)
+		{
+			fprintf(stderr, "Short read on file %s (%ld/%ld):", inputfiles[i] -> filename, bread, size);
+			perror("");
+			exit(1);
+		}
+			
+		fclose(f);
+		
+		if (!memcmp(inputfiles[i] -> filedata, "LWOBJ16", 8))
+		{
+			// read v0 LWOBJ16 file
+			read_lwobj16v0(inputfiles[i]);
+		}
+		else
+		{
+			fprintf(stderr, "%s: unknown file format\n", inputfiles[i] -> filename);
+			exit(1);
+		}
+	}
+}
+
+// this macro is used to bail out if we run off the end of the file data
+// while parsing - it keeps the code below cleaner
+#define NEXTBYTE()	do { cc++; if (cc >= fn -> filesize) { fprintf(stderr, "%s: invalid file format\n", fn -> filename); exit(1); } } while (0)
+// this macro is used to refer to the current byte in the stream
+#define CURBYTE()	(fn -> filedata[cc])
+// this one will leave the input pointer past the trailing NUL
+#define CURSTR()	read_lwobj16v0_str(&cc, fn)
+unsigned char *read_lwobj16v0_str(long *cc1, fileinfo_t *fn)
+{
+	int cc = *cc1;
+	unsigned char *fp;
+	fp = &CURBYTE();
+	while (CURBYTE())
+		NEXTBYTE();
+	NEXTBYTE();
+	*cc1 = cc;
+	return fp;
+}
+// the function below can be switched to dealing with data coming from a
+// source other than an in-memory byte pool by adjusting the input data
+// in "fn" and the above two macros
+void read_lwobj16v0(fileinfo_t *fn)
+{
+	unsigned char *fp;
+	long cc;
+	section_t *s;
+	int val;
+	symtab_t *se;
+	
+	// start reading *after* the magic number
+	cc = 7;
+	
+	// init data
+	fn -> sections = NULL;
+	fn -> nsections = 0;
+
+	while (1)
+	{
+		NEXTBYTE();
+		// bail out if no more sections
+		if (!(CURBYTE()))
+			break;
+		
+		fp = CURSTR();
+		
+		// we now have a section name in fp
+		// create new section entry
+		fn -> sections = lw_realloc(fn -> sections, sizeof(section_t) * (fn -> nsections + 1));
+		s = &(fn -> sections[fn -> nsections]);
+		fn -> nsections += 1;
+		
+		s -> localsyms = NULL;
+		s -> flags = 0;
+		s -> codesize = 0;
+		s -> name = fp;
+		s -> loadaddress = 0;
+		s -> localsyms = NULL;
+		s -> exportedsyms = NULL;
+		s -> incompletes = NULL;
+
+		// read flags
+		while (CURBYTE())
+		{
+			switch (CURBYTE())
+			{
+			case 0x01:
+				s -> flags |= SECTION_BSS;
+				break;
+
+			default:
+				fprintf(stderr, "%s (%s): unrecognized section flag %02X\n", fn -> filename, s -> name, (int)(CURBYTE()));
+				exit(1);
+			}
+			NEXTBYTE();
+		}
+		// skip NUL terminating flags
+		NEXTBYTE();
+		
+		// now parse the local symbol table
+		while (CURBYTE())
+		{
+			fp = CURSTR();
+
+			// fp is the symbol name
+			val = (CURBYTE()) << 8;
+			NEXTBYTE();
+			val |= (CURBYTE());
+			NEXTBYTE();
+			// val is now the symbol value
+			
+			// create symbol table entry
+			se = lw_malloc(sizeof(symtab_t));
+			se -> next = s -> localsyms;
+			s -> localsyms = se;
+			se -> sym = fp;
+			se -> offset = val;
+			se -> realval = val;
+		}
+
+		// now parse the exported symbol table
+		while (CURBYTE())
+		{
+			fp = CURSTR();
+
+			// fp is the symbol name
+			val = (CURBYTE()) << 8;
+			NEXTBYTE();
+			val |= (CURBYTE());
+			NEXTBYTE();
+			// val is now the symbol value
+			
+			// create symbol table entry
+			se = lw_malloc(sizeof(symtab_t));
+			se -> next = s -> exportedsyms;
+			s -> localsyms = se;
+			se -> sym = fp;
+			se -> offset = val;
+			se -> realval = val;
+		}
+		
+		// now parse the incomplete references and make a list of
+		// external references that need resolution
+		while (CURBYTE())
+		{
+			reloc_t *rp;
+			lw_expr_term_t *term;
+			
+			// we have a reference
+			rp = lw_malloc(sizeof(reloc_t));
+			rp -> next = s -> incompletes;
+			s -> incompletes = rp;
+			rp -> offset = 0;
+			rp -> expr = lw_expr_stack_create();
+			
+			// parse the expression
+			while (CURBYTE())
+			{
+				int tt = CURBYTE();
+				NEXTBYTE();
+				switch (tt)
+				{
+				case 0x01:
+					// 16 bit integer
+					tt = CURBYTE() << 8;
+					NEXTBYTE();
+					tt |= CURBYTE();
+					NEXTBYTE();
+					// normalize for negatives...
+					if (tt > 0x7fff)
+						tt -= 0x10000;
+					term = lw_expr_term_create_int(tt);
+					break;
+				
+				case 0x02:
+					// external symbol reference
+					term = lw_expr_term_create_sym(CURSTR(), 0);
+					break;
+					
+				case 0x03:
+					// internal symbol reference
+					term = lw_expr_term_create_sym(CURSTR(), 1);
+					break;
+				
+				case 0x04:
+					// operator
+					term = lw_expr_term_create_oper(CURBYTE());
+					NEXTBYTE();
+					break;
+
+				case 0x05:
+					// section base reference (NULL internal reference is
+					// the section base address
+					term = lw_expr_term_create_sym(NULL, 0);
+					break;
+					
+				default:
+					fprintf(stderr, "%s (%s): bad relocation expression\n", fn -> filename, s -> name);
+					exit(1);
+				}
+				lw_expr_stack_push(rp -> expr, term);
+				lw_expr_term_free(term);
+			}
+		}
+		// skip the NUL terminatin
+		NEXTBYTE();
+				
+		// now set code location and size and verify that the file
+		// contains data going to the end of the code (if !SECTION_BSS)
+		s -> codesize = CURBYTE() << 8;
+		NEXTBYTE();
+		s -> codesize |= CURBYTE();
+		NEXTBYTE();
+		
+		s -> code = &(CURBYTE());
+		
+		// skip the code if we're not in a BSS section
+		if (!(s -> flags & SECTION_BSS))
+		{
+			int i;
+			for (i = 0; i < s -> codesize; i++)
+				NEXTBYTE();
+		}
+	}
+}
--- a/lwlink/trunk/src/util.c	Sun Jan 18 04:53:57 2009 +0000
+++ b/lwlink/trunk/src/util.c	Wed Jan 21 03:15:49 2009 +0000
@@ -73,6 +73,9 @@
 {
 	char *d;
 	
+	if (!s)
+		return NULL;
+
 	d = strdup(s);
 	if (!d)
 	{