diff lwlink/readfiles.c @ 0:2c24602be78f

Initial import from lwtools 3.0.1 version, with new hand built build system and file reorganization
author lost@l-w.ca
date Wed, 19 Jan 2011 22:27:17 -0700
parents
children 7317fbe024af
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlink/readfiles.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,423 @@
+/*
+readfiles.c
+Copyright © 2009 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
+
+*/
+
+#include <argp.h>
+#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;
+	long size;
+	FILE *f;
+	long bread;
+	for (i = 0; i < ninputfiles; i++)
+	{
+		if (inputfiles[i] -> islib)
+		{
+			char *tf;
+			int s;
+			int j;
+			
+			f = NULL;
+			
+			for (j = 0; j < nlibdirs; j++)
+			{
+				s = strlen(libdirs[j]) + 7 + strlen(inputfiles[i] -> filename);
+				tf = lw_malloc(s + 1);
+				sprintf(tf, "%s/lib%s.a", libdirs[j], inputfiles[i] -> filename);
+				f = fopen(tf, "rb");
+				if (!f)
+				{
+					free(tf);
+					continue;
+				}
+				free(tf);
+				break;
+			}
+			if (!f)
+			{
+				fprintf(stderr, "Can't open library: -l%s\n", inputfiles[i] -> filename);
+				exit(1);
+			}
+		}
+		else
+		{
+			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);
+		
+		read_file(inputfiles[i]);
+	}
+}
+
+// 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 < fn -> filesize ? cc : fn -> filesize - 1])
+// 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 = 8;
+	
+	// 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;
+		s -> processed = 0;
+		s -> file = fn;
+		
+		// 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;
+		}
+		// skip terminating NUL
+		NEXTBYTE();
+		
+		// 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 -> exportedsyms = se;
+			se -> sym = fp;
+			se -> offset = val;
+		}
+		// skip terminating NUL
+		NEXTBYTE();
+		
+		// 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();
+			rp -> flags = RELOC_NORM;
+			
+			// parse the expression
+			while (CURBYTE())
+			{
+				int tt = CURBYTE();
+				NEXTBYTE();
+				switch (tt)
+				{
+				case 0xFF:
+					// a flag specifier
+					tt = CURBYTE();
+					rp -> flags = tt;
+					NEXTBYTE();
+					term = NULL;
+					break;
+					
+				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, 1);
+					break;
+					
+				default:
+					fprintf(stderr, "%s (%s): bad relocation expression (%02X)\n", fn -> filename, s -> name, tt);
+					exit(1);
+				}
+				if (term)
+				{
+					lw_expr_stack_push(rp -> expr, term);
+					lw_expr_term_free(term);
+				}
+			}
+			// skip the NUL
+			NEXTBYTE();
+			
+			// fetch the offset
+			rp -> offset = CURBYTE() << 8;
+			NEXTBYTE();
+			rp -> offset |= CURBYTE() & 0xff;
+			NEXTBYTE();
+		}
+		// skip the NUL terminating the relocations
+		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();
+		}
+	}
+}
+
+/*
+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]; cc++)
+			/* do nothing */ ;
+
+		cc++;
+
+		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);
+		flen |= (fn -> filedata[cc++] << 16);
+		flen |= (fn -> filedata[cc++] << 8);
+		flen |= (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;
+		fn -> subs[fn -> nsubs] -> forced = fn -> forced;		
+		read_file(fn -> subs[fn -> nsubs]);
+		fn -> nsubs++;
+		cc += flen;
+	}
+}