changeset 171:d610b8aef91b

Added LWAR skeleton
author lost
date Sun, 01 Mar 2009 19:37:03 +0000
parents bf69160da467
children 47427342e41d
files ChangeLog Makefile.am configure.ac lwar/Makefile.am lwar/lwar.h lwar/main.c lwar/util.c lwar/util.h lwlink/link.c lwlink/lwlink.c lwlink/lwlink.h lwlink/readfiles.c
diffstat 12 files changed, 476 insertions(+), 58 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sun Mar 01 00:53:21 2009 +0000
+++ b/ChangeLog	Sun Mar 01 19:37:03 2009 +0000
@@ -12,6 +12,7 @@
 
 Version 2.2
 
+[*] created LWAR to manage library/archive files
 [+] cescapes pragma to allow C-style string escapes in FCC, FCS, and FCN
 [+] .area alias for SECTION
 [+] .globl alias for EXPORT; also accept name of symbol as operand
@@ -23,6 +24,7 @@
 [+] ignore lines starting with # to permit C pre-processor output to be used
     as input to lwasm
 [+] allow "0x" and "0X" as prefixes to identify hexadecimal numbers
+[+] added support for a simple library/archive file format to LWLINK
 [b] actually show assembly errors when no list requested
 [b] pragma and --pragma now actually take multiple pragmas as documented
 
--- a/Makefile.am	Sun Mar 01 00:53:21 2009 +0000
+++ b/Makefile.am	Sun Mar 01 19:37:03 2009 +0000
@@ -1,2 +1,2 @@
-SUBDIRS = lwasm lwlink
-DIST_SUBDIRS = doc lwasm lwlink
+SUBDIRS = lwasm lwlink lwar
+DIST_SUBDIRS = doc lwasm lwlink lwar
--- a/configure.ac	Sun Mar 01 00:53:21 2009 +0000
+++ b/configure.ac	Sun Mar 01 19:37:03 2009 +0000
@@ -7,5 +7,6 @@
 	lwasm/Makefile
 	doc/Makefile
 	lwlink/Makefile
+	lwar/Makefile
 ])
 AC_OUTPUT
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwar/Makefile.am	Sun Mar 01 19:37:03 2009 +0000
@@ -0,0 +1,3 @@
+bin_PROGRAMS = lwar
+lwar_SOURCES = main.c util.c lwar.c
+EXTRA_DIST = lwar.h util.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwar/lwar.h	Sun Mar 01 19:37:03 2009 +0000
@@ -0,0 +1,38 @@
+/*
+lwar.h
+Copyright © 2009 William Astle
+
+This file is part of LWAR.
+
+LWAR 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/>.
+
+Contains the main defs used by the linker
+*/
+
+
+#ifndef __lwar_h_seen__
+#define __lwar_h_seen__
+
+#ifndef __lwar_c_seen__
+
+extern int debug_level;
+
+#define __lwar_E__ extern
+#else
+#define __lwar_E__
+#endif // __lwar_c_seen__
+
+#undef __lwar_E__
+
+#endif //__lwar_h_seen__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwar/main.c	Sun Mar 01 19:37:03 2009 +0000
@@ -0,0 +1,81 @@
+/*
+main.c
+Copyright © 2009 William Astle
+
+This file is part of LWAR.
+
+LWAR 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/>.
+
+
+Implements the program startup code
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <argp.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "lwar.h"
+
+// command line option handling
+const char *argp_program_version = "LWAR from " PACKAGE_STRING;
+const char *argp_program_bug_address = PACKAGE_BUGREPORT;
+
+static error_t parse_opts(int key, char *arg, struct argp_state *state)
+{
+	switch (key)
+	{
+	case 'd':
+		// debug
+		debug_level++;
+		break;
+	
+//	case ARGP_KEY_ARG:
+//
+//		break;
+		
+	default:
+		return ARGP_ERR_UNKNOWN;
+	}
+	return 0;
+}
+
+static struct argp_option options[] =
+{
+	{ "debug",		'd',	0,		0,
+				"Set debug mode"},
+	{ 0 }
+};
+
+static struct argp argp =
+{
+	options,
+	parse_opts,
+	"<input file> ...",
+	"LWLINK, a HD6309 and MC6809 cross-linker"
+};
+
+// main function; parse command line, set up assembler state, and run the
+// assembler on the first file
+int main(int argc, char **argv)
+{
+	argp_parse(&argp, argc, argv, 0, 0, NULL);
+	
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwar/util.c	Sun Mar 01 19:37:03 2009 +0000
@@ -0,0 +1,87 @@
+/*
+util.c
+Copyright © 2009 William Astle
+
+This file is part of LWAR.
+
+LWAR 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/>.
+*/
+
+/*
+Utility functions
+*/
+
+#define __util_c_seen__
+
+#include <malloc.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "util.h"
+
+void *lw_malloc(int size)
+{
+	void *ptr;
+	
+	ptr = malloc(size);
+	if (!ptr)
+	{
+		// bail out; memory allocation error
+		fprintf(stderr, "lw_malloc(): Memory allocation error\n");
+		exit(1);
+	}
+	return ptr;
+}
+
+void *lw_realloc(void *optr, int size)
+{
+	void *ptr;
+	
+	if (size == 0)
+	{
+		lw_free(optr);
+		return;
+	}
+	
+	ptr = realloc(optr, size);
+	if (!ptr)
+	{
+		fprintf(stderr, "lw_realloc(): memory allocation error\n");
+		exit(1);
+	}
+}
+
+void lw_free(void *ptr)
+{
+	if (ptr)
+		free(ptr);
+}
+
+char *lw_strdup(const char *s)
+{
+	char *d;
+	
+	if (!s)
+		return NULL;
+
+	d = strdup(s);
+	if (!d)
+	{
+		fprintf(stderr, "lw_strdup(): memory allocation error\n");
+		exit(1);
+	}
+	
+	return d;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwar/util.h	Sun Mar 01 19:37:03 2009 +0000
@@ -0,0 +1,44 @@
+/*
+util.h
+Copyright © 2009 William Astle
+
+This file is part of LWAR.
+
+LWAR 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/>.
+*/
+
+/*
+Utility functions
+*/
+
+#ifndef __util_h_seen__
+#define __util_h_seen__
+
+#ifndef __util_c_seen__
+#define __util_E__ extern
+#else
+#define __util_E__
+#endif
+
+// allocate memory
+__util_E__ void *lw_malloc(int size);
+__util_E__ void lw_free(void *ptr);
+__util_E__ void *lw_realloc(void *optr, int size);
+
+// string stuff
+__util_E__ char *lw_strdup(const char *s);
+
+#undef __util_E__
+
+#endif // __util_h_seen__
--- a/lwlink/link.c	Sun Mar 01 00:53:21 2009 +0000
+++ b/lwlink/link.c	Sun Mar 01 19:37:03 2009 +0000
@@ -35,6 +35,84 @@
 struct section_list *sectlist = NULL;
 int nsects = 0;
 
+void check_section_name(char *name, int *base, fileinfo_t *fn)
+{
+	int sn;
+	for (sn = 0; sn < fn -> nsections; sn++)
+	{
+		if (!strcmp(name, fn -> sections[sn].name))
+		{
+			// we have a match
+			sectlist = lw_realloc(sectlist, sizeof(struct section_list) * (nsects + 1));
+			sectlist[nsects].ptr = &(fn -> sections[sn]);
+					
+			fn -> sections[sn].processed = 1;
+			fn -> sections[sn].loadaddress = *base;
+			*base += fn -> sections[sn].codesize;
+			nsects++;
+		}
+	}
+	for (sn = 0; sn < fn -> nsubs; fn++)
+	{
+		check_section_name(name, base, fn -> subs[sn]);
+	}
+}
+
+void add_matching_sections(char *name, int yesflags, int noflags, int *base);
+void check_section_flags(int yesflags, int noflags, int *base, fileinfo_t *fn)
+{
+	int sn;
+	for (sn = 0; sn < fn -> nsections; sn++)
+	{
+		// ignore if the noflags tell us to
+		if (noflags && (fn -> sections[sn].flags & noflags))
+			continue;
+		// ignore unless the yesflags tell us not to
+		if (yesflags && (fn -> sections[sn].flags & yesflags == 0))
+			continue;
+		// ignore it if already processed
+		if (fn -> sections[sn].processed)
+			continue;
+
+		// we have a match - now collect *all* sections of the same name!
+		add_matching_sections(fn -> sections[sn].name, 0, 0, base);
+		
+		// and then continue looking for sections
+	}
+	for (sn = 0; sn < fn -> nsubs; fn++)
+	{
+		check_section_flags(yesflags, noflags, base, fn -> subs[sn]);
+	}
+}
+
+
+
+void add_matching_sections(char *name, int yesflags, int noflags, int *base)
+{
+	int fn;
+	if (name)
+	{
+		// named section
+		// look for all instances of a section by the specified name
+		// and resolve base addresses and add to the list
+		for (fn = 0; fn < ninputfiles; fn++)
+		{
+			check_section_name(name, base, inputfiles[fn]);
+		}
+	}
+	else
+	{
+		// wildcard section
+		// named section
+		// look for all instances of a section by the specified name
+		// and resolve base addresses and add to the list
+		for (fn = 0; fn < ninputfiles; fn++)
+		{
+			check_section_flags(yesflags, noflags, base, inputfiles[fn]);
+		}
+	}
+}
+
 // work out section load order and resolve base addresses for each section
 // make a list of sections to load in order
 void resolve_sections(void)
@@ -44,41 +122,12 @@
 	
 	for (ln = 0; ln < linkscript.nlines; ln++)
 	{
-//	printf("Linker script line %d: '%s', %04X, %d, %d\n", ln, linkscript.lines[ln].sectname, linkscript.lines[ln].loadat, linkscript.lines[ln].yesflags, linkscript.lines[ln].noflags);
+		if (linkscript.lines[ln].loadat >= 0)
+			laddr = linkscript.lines[ln].loadat;
+		add_matching_sections(linkscript.lines[ln].sectname, linkscript.lines[ln].yesflags, linkscript.lines[ln].noflags, &laddr);
+		
 		if (linkscript.lines[ln].sectname)
 		{
-			int f = 0;
-			// named section
-			// look for all instances of a section by the specified name
-			// and resolve base addresses and add to the list
-			for (fn = 0; fn < ninputfiles; fn++)
-			{
-				for (sn = 0; sn < inputfiles[fn] -> nsections; sn++)
-				{
-//				printf("  Considering %s:%s\n", inputfiles[fn]->filename, inputfiles[fn]->sections[sn].name);
-					if (!strcmp(linkscript.lines[ln].sectname, inputfiles[fn] -> sections[sn].name))
-					{
-						// we have a match
-						sectlist = lw_realloc(sectlist, sizeof(struct section_list) * (nsects + 1));
-						sectlist[nsects].ptr = &(inputfiles[fn] -> sections[sn]);
-						
-						inputfiles[fn] -> sections[sn].processed = 1;
-						if (!f && linkscript.lines[ln].loadat >= 0)
-						{
-							f = 1;
-							sectlist[nsects].forceaddr = 1;
-							laddr = linkscript.lines[ln].loadat;
-						}
-						else
-						{
-							sectlist[nsects].forceaddr = 0;
-						}
-						inputfiles[fn] -> sections[sn].loadaddress = laddr;
-						laddr += inputfiles[fn] -> sections[sn].codesize;
-						nsects++;
-					}
-				}
-			}
 		}
 		else
 		{
@@ -141,6 +190,39 @@
 	// theoretically, all the base addresses are set now
 }
 
+lw_expr_stack_t *find_external_sym_recurse(char *sym, fileinfo_t *fn)
+{
+	int sn;
+	lw_expr_stack_t *r;
+	lw_expr_term_t *term;
+	symtab_t *se;
+	int val;
+	
+	for (sn = 0; sn < fn -> nsections; sn++)
+	{
+		for (se = fn -> sections[sn].exportedsyms; se; se = se -> next)
+		{
+			if (!strcmp(sym, se -> sym))
+			{
+				val = se -> offset + fn -> sections[sn].loadaddress;
+				r = lw_expr_stack_create();
+				term = lw_expr_term_create_int(val & 0xffff);
+				lw_expr_stack_push(r, term);
+				lw_expr_term_free(term);
+				return r;
+			}
+		}
+	}
+		
+	for (sn = 0; sn < fn -> nsubs; sn++)
+	{
+		r = find_external_sym_recurse(sym, fn -> subs[sn]);
+		if (r)
+			return r;
+	}
+	return NULL;
+}
+
 // resolve all incomplete references now
 // anything that is unresolvable at this stage will throw an error
 // because we know the load address of every section now
@@ -151,7 +233,8 @@
 	int val = 0, i, fn;
 	lw_expr_stack_t *s;
 	symtab_t *se;
-	
+	fileinfo_t *fp;
+
 	if (symtype == 1)
 	{
 		// local symbol
@@ -190,19 +273,18 @@
 	{
 		// external symbol
 		// read all files in order until found (or not found)
+		for (fp = sect -> file; fp; fp = fp -> parent)
+		{
+			s = find_external_sym_recurse(sym, fp);
+			if (s)
+				return s;
+		}
+
 		for (fn = 0; fn < ninputfiles; fn++)
 		{
-			for (i = 0; i < inputfiles[fn] -> nsections; i++)
-			{
-				for (se = inputfiles[fn] -> sections[i].exportedsyms; se; se = se -> next)
-				{
-					if (!strcmp(sym, se -> sym))
-					{
-						val = se -> offset + inputfiles[fn] -> sections[i].loadaddress;
-						goto out;
-					}
-				}
-			}
+			s = find_external_sym_recurse(sym, inputfiles[fn]);
+			if (s)
+				return s;
 		}
 		if (sect)
 		{
--- a/lwlink/lwlink.c	Sun Mar 01 00:53:21 2009 +0000
+++ b/lwlink/lwlink.c	Sun Mar 01 19:37:03 2009 +0000
@@ -31,6 +31,7 @@
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 
 #include "lwlink.h"
 #include "util.h"
@@ -47,6 +48,7 @@
 {
 	inputfiles = lw_realloc(inputfiles, sizeof(fileinfo_t *) * (ninputfiles + 1));
 	inputfiles[ninputfiles] = lw_malloc(sizeof(fileinfo_t));
+	memset(inputfiles[ninputfiles], 0, sizeof(fileinfo_t));
 	inputfiles[ninputfiles++] -> filename = lw_strdup(fn);
 }
 
--- a/lwlink/lwlink.h	Sun Mar 01 00:53:21 2009 +0000
+++ b/lwlink/lwlink.h	Sun Mar 01 19:37:03 2009 +0000
@@ -73,7 +73,11 @@
 	long filesize;
 	section_t *sections;
 	int nsections;
-	
+
+	// "sub" files (like in archives or libraries)
+	int nsubs;
+	fileinfo_t **subs;
+	fileinfo_t *parent;
 };
 
 struct section_list
--- a/lwlink/readfiles.c	Sun Mar 01 00:53:21 2009 +0000
+++ b/lwlink/readfiles.c	Sun Mar 01 19:37:03 2009 +0000
@@ -30,17 +30,38 @@
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 
 #include "lwlink.h"
 #include "util.h"
 
 void read_lwobj16v0(fileinfo_t *fn);
+void read_lwar1v(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_file(fileinfo_t *fn)
+{
+	if (!memcmp(fn -> filedata, "LWOBJ16", 8))
+		{
+			// read v0 LWOBJ16 file
+			read_lwobj16v0(fn);
+		}
+		else if (!memcmp(fn -> filedata, "LWAR1V", 6))
+		{
+			// archive file
+			read_lwar1v(fn);
+		}
+		else
+		{
+			fprintf(stderr, "%s: unknown file format\n", fn -> filename);
+			exit(1);
+		}
+}
+
 void read_files(void)
 {
 	int i;
@@ -73,16 +94,7 @@
 			
 		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);
-		}
+		read_file(inputfiles[i]);
 	}
 }
 
@@ -107,6 +119,7 @@
 // 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;
@@ -302,3 +315,64 @@
 		}
 	}
 }
+
+/*
+Read an archive file - this will create a "sub" record and farm out the
+parsing of the sub files to the regular file parsers
+
+The archive file format consists of the 6 byte magic number followed by a
+series of records as follows:
+
+- NUL terminated file name
+- 32 bit file length in big endian order
+- the file data
+
+An empty file name indicates the end of the file.
+
+*/
+void read_lwar1v(fileinfo_t *fn)
+{
+	unsigned long cc = 6;
+	unsigned long flen;
+	unsigned long l;
+	for (;;)
+	{
+		if (cc >= fn -> filesize || !(fn -> filedata[cc]))
+			return;
+
+		for (l = cc; cc < fn -> filesize && fn -> filedata[cc]; l++)
+			/* do nothing */ ;
+
+		if (cc >= fn -> filesize)
+		{
+			fprintf(stderr, "Malformed archive file %s.\n", fn -> filename);
+			exit(1);
+		}
+
+		if (cc + 4 > fn -> filesize)
+			return;
+
+		flen = (fn -> filedata[cc++] << 24) | (fn -> filedata[cc++] << 16)
+			| (fn -> filedata[cc++] << 8) | (fn -> filedata[cc]);
+
+		if (flen == 0)
+			return;
+		
+		if (cc + flen > fn -> filesize)
+		{
+			fprintf(stderr, "Malformed archive file %s.\n", fn -> filename);
+			exit(1);
+		}
+		
+		// add the "sub" input file
+		fn -> subs = lw_realloc(fn -> subs, sizeof(fileinfo_t *) * (fn -> nsubs + 1));
+		fn -> subs[fn -> nsubs] = lw_malloc(sizeof(fileinfo_t));
+		memset(fn -> subs[fn -> nsubs], 0, sizeof(fileinfo_t));
+		fn -> subs[fn -> nsubs] -> filedata = fn -> filedata + cc;
+		fn -> subs[fn -> nsubs] -> filesize = flen;
+		fn -> subs[fn -> nsubs] -> filename = lw_strdup(fn -> filedata + l);
+		fn -> subs[fn -> nsubs] -> parent = fn;
+		read_file(fn -> subs[fn -> nsubs]);
+		fn -> nsubs++;
+	}
+}