view lwlink/script.c @ 258:ebda5c96665e

Improved stack handling for os9 target in lwlink Added "stack" as a valid symbol in the __os9 section. All instances of __os9 are now polled for "stack" symobls and the values added to the stack size set in the linker script. The stack size is then added to the final data size of the module. Also set a default minimum stack size of 32 bytes.
author William Astle <lost@l-w.ca>
date Thu, 31 Jan 2013 19:34:54 -0700
parents ce1fdc8d6568
children 7de7b14ebaee
line wrap: on
line source

/*
script.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/>.


Read and parse linker scripts
*/

#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <lw_alloc.h>
#include <lw_string.h>

#include "lwlink.h"

// the built-in OS9 script
// the 000D bit is to handle the module header!
static char *os9_script =
	"define basesympat s_%s\n"
	"define lensympat l_%s\n"
	"section code load 000D\n"
	"section .text\n"
	"section data\n"
	"section .data\n"
	"section bss,bss load 0000\n"
	"section .bss,bss\n"
	"stacksize 32\n"
	"entry __start\n"
	;

// the built-in DECB target linker script
static char *decb_script =
	"define basesympat s_%s\n"
	"define lensympat l_%s\n"
	"section init load 2000\n"
	"section code\n"
	"section *,!bss\n"
	"section *,bss\n"
	"entry 2000\n"
	;

// the built-in RAW target linker script
static char *raw_script = 
	"define basesympat s_%s\n"
	"define lensympat l_%s\n"
	"section init load 0000\n"
	"section code\n"
	"section *,!bss\n"
	"section *,bss\n"
	;

static char *lwex0_script =
	"define basesympat s_%s\n"
	"define lensympat l_%s\n"
	"sectopt .ctors padafter 00,00\n"
	"sectopt .dtors padafter 00,00\n"
	"section init load 0100\n"
	"section .text\n"
	"section .data\n"
	"section .ctors_start\n"
	"section .ctors\n"
	"section .ctors_end\n"
	"section .dtors_start\n"
	"section .dtors\n"
	"section .dtors_end\n"
	"section *,!bss\n"
	"section *,bss\n"
	"entry __start\n"
	"stacksize 0100\n"		// default 256 byte stack
	;

// the "simple" script
static char *simple_script = 
	"define basesympat s_%s\n"
	"define lensympat l_%s\n"
	"section *,!bss\n"
	"section *,bss\n"
	;

linkscript_t linkscript = { 0, NULL, -1 };

void setup_script()
{
	char *script, *oscript;
	long size;

	// read the file if needed
	if (scriptfile)
	{
		FILE *f;
		long bread;
		
		f = fopen(scriptfile, "rb");
		if (!f)
		{
			fprintf(stderr, "Can't open file %s:", scriptfile);
			perror("");
			exit(1);
		}
		fseek(f, 0, SEEK_END);
		size = ftell(f);
		rewind(f);
		
		script = lw_alloc(size + 2);
		
		bread = fread(script, 1, size, f);
		if (bread < size)
		{
			fprintf(stderr, "Short read on file %s (%ld/%ld):", scriptfile, bread, size);
			perror("");
			exit(1);
		}
		fclose(f);
		
		script[size] = '\n';
		script[size + 1] = '\0';
	}
	else
	{
		// fetch defaults based on output mode
		switch (outformat)
		{
		case OUTPUT_RAW:
			script = raw_script;
			break;
		
		case OUTPUT_DECB:
			script = decb_script;
			break;
		
		case OUTPUT_LWEX0:
			script = lwex0_script;
			break;
		
		case OUTPUT_OS9:
			script = os9_script;
			break;
			
		default:
			script = simple_script;
			break;
		}
		
		size = strlen(script);
		if (nscriptls)
		{
			char *rscript;
			int i;
			// prepend the "extra" script lines
			for (i = 0; i < nscriptls; i++)
				size += strlen(scriptls[i]) + 1;
			
			rscript = lw_alloc(size + 1);
			oscript = rscript;
			for (i = 0; i < nscriptls; i++)
			{
				oscript += sprintf(oscript, "%s\n", scriptls[i]);
			}
			strcpy(oscript, script);
			script = rscript;
		}
	}

	if (outformat == OUTPUT_LWEX0)
		linkscript.stacksize = 0x100;

	if (outformat == OUTPUT_OS9)
	{
		linkscript.modtype = 0x01;
		linkscript.modlang = 0x01;
		linkscript.modattr = 0x08;
		linkscript.modrev = 0x00;
		linkscript.name = NULL;
	}

	oscript = script;
	// now parse the script file
	while (*script)
	{
		char *ptr, *ptr2, *line;

		for (ptr = script; *ptr && *ptr != '\n' && *ptr != '\r'; ptr++)
			/* do nothing */ ;
		
		line = lw_alloc(ptr - script + 1);
		memcpy(line, script, ptr - script);
		line[ptr - script] = '\0';

		// skip line terms
		for (script = ptr + 1; *script == '\n' || *script == '\r'; script++)
			/* do nothing */ ;
		
		// ignore leading whitespace
		for (ptr = line; *ptr && isspace(*ptr); ptr++)
			/* do nothing */ ;
		
		// ignore blank lines
		if (!*ptr)
			continue;
		
		for (ptr = line; *ptr && !isspace(*ptr); ptr++)
			/* do nothing */ ;
		
		// now ptr points to the char past the first word
		// NUL it out
		if (*ptr)
			*ptr++ = '\0';
		
		// skip spaces after the first word
		for ( ; *ptr && isspace(*ptr); ptr++)
			/* do nothing */ ;
		
		if (!strcmp(line, "sectopt"))
		{
			char *sn;
			char *ptr3;
			sectopt_t *so;
			
			for (ptr2 = ptr; *ptr && !isspace(*ptr2); ptr2++)
				/* do nothing */ ;
			
			if (*ptr2)
				*ptr2++ = '\0';
			
			while (*ptr2 && isspace(*ptr2))
				ptr2++;
			
			// section name is at ptr
			// ptr2 is the option type
			sn = ptr;

			// now ptr2 points to the section option name
			for (ptr3 = ptr2; *ptr3 && !isspace(*ptr3); ptr3++)
				/* do nothing */ ;
			
			if (*ptr3)
				*ptr3++ = 0;
			
			while (*ptr3 && isspace(*ptr3))
				ptr3++;
			
			// now ptr3 points to option value
			for (so = section_opts; so; so = so -> next)
			{
				if (!strcmp(so -> name, sn))
					break;
			}
			
			if (!so)
			{
				so = lw_alloc(sizeof(sectopt_t));
				so -> name = lw_strdup(sn);
				so -> aftersize = 0;
				so -> afterbytes = NULL;
				so -> next = section_opts;
				section_opts = so;
			}
			
			if (!strcmp(ptr2, "padafter"))
			{
				if (so -> afterbytes)
					lw_free(so -> afterbytes);
				so -> aftersize = 0;
				
				for (;;)
				{
					int v;
					char *ptr4;

					while (*ptr3 && isspace(*ptr3))
						ptr3++;
					
					if (!ptr3)
						break;
					
					v = strtoul(ptr3, &ptr4, 16);
					if (ptr3 == ptr4)
						break;
					
					
					so -> afterbytes = lw_realloc(so -> afterbytes, so -> aftersize + 1);
					so -> afterbytes[so -> aftersize] = v;
					so -> aftersize++;
					ptr3 = ptr4;
					while (*ptr3 && isspace(*ptr3))
						ptr3++;

					if (*ptr3 != ',')
						break;

					ptr3++;
				}
			}
			else
			{
				fprintf(stderr, "%s: bad script line: %s %s\n", scriptfile, line, ptr2);
				exit(1);
			}
		}
		else if (!strcmp(line, "define"))
		{
			// parse out the definition type
			for (ptr2 = ptr; *ptr2 && !isspace(*ptr2); ptr2++)
				/* do nothing */ ;
			
			if (*ptr2)
				*ptr2++ = '\0';
			
			while (*ptr2 && isspace(*ptr2))
				ptr2++;
			
			// now ptr points to the define type
			if (!strcmp(ptr, "basesympat"))
			{
				/* section base symbol pattern */
				lw_free(linkscript.basesympat);
				linkscript.basesympat = lw_strdup(ptr2);
			}
			else if (!strcmp(ptr, "lensympat"))
			{
				/* section length symbol pattern */
				lw_free(linkscript.lensympat);
				linkscript.lensympat = lw_strdup(ptr2);
			}
			else
			{
			}
		}
		else if (!strcmp(line, "pad"))
		{
			// padding
			// parse the hex number and stow it
			linkscript.padsize = strtol(ptr, NULL, 16);
			if (linkscript.padsize < 0)
				linkscript.padsize = 0;
		}
		else if (!strcmp(line, "stacksize"))
		{
			// stack size for targets that support it
			// parse the hex number and stow it
			linkscript.padsize = strtol(ptr, NULL, 16);
			if (linkscript.stacksize < 0)
				linkscript.stacksize = 0x100;
		}
		else if (!strcmp(line, "entry"))
		{
			int eaddr;
			
			eaddr = strtol(ptr, &ptr2, 16);
			if (*ptr2)
			{
				linkscript.execaddr = -1;
				linkscript.execsym = lw_strdup(ptr);
			}
			else
			{
				linkscript.execaddr = eaddr;
				linkscript.execsym = NULL;
			}
		}
		else if (!strcmp(line, "section"))
		{
			// section
			// parse out the section name and flags
			for (ptr2 = ptr; *ptr2 && !isspace(*ptr2); ptr2++)
				/* do nothing */ ;
			
			if (*ptr2)
				*ptr2++ = '\0';
			
			while (*ptr2 && isspace(*ptr2))
				ptr2++;
				
			// ptr now points to the section name and flags and ptr2
			// to the first non-space character following
			
			// then look for "load <addr>" clause
			if (*ptr2)
			{
				if (!strncmp(ptr2, "load", 4))
				{
					ptr2 += 4;
					while (*ptr2 && isspace(*ptr2))
						ptr2++;
					
				}
				else
				{
					fprintf(stderr, "%s: bad script\n", scriptfile);
					exit(1);
				}
			}
			
			// now ptr2 points to the load address if there is one
			// or NUL if not
			linkscript.lines = lw_realloc(linkscript.lines, sizeof(struct scriptline_s) * (linkscript.nlines + 1));

			linkscript.lines[linkscript.nlines].noflags = 0;
			linkscript.lines[linkscript.nlines].yesflags = 0;
			if (*ptr2)
				linkscript.lines[linkscript.nlines].loadat = strtol(ptr2, NULL, 16);
			else
				linkscript.lines[linkscript.nlines].loadat = -1;
			for (ptr2 = ptr; *ptr2 && *ptr2 != ','; ptr2++)
				/* do nothing */ ;
			if (*ptr2)
			{
				*ptr2++ = '\0';
				if (!strcmp(ptr2, "!bss"))
				{
					linkscript.lines[linkscript.nlines].noflags = SECTION_BSS;
				}
				else if (!strcmp(ptr2, "bss"))
				{
					linkscript.lines[linkscript.nlines].yesflags = SECTION_BSS;
				}
				else
				{
					fprintf(stderr, "%s: bad script\n", scriptfile);
					exit(1);
				}
			}
			if (ptr[0] == '*' && ptr[1] == '\0')
				linkscript.lines[linkscript.nlines].sectname = NULL;
			else
				linkscript.lines[linkscript.nlines].sectname = lw_strdup(ptr);
			linkscript.nlines++;
		}
		else
		{
			fprintf(stderr, "%s: bad script line: %s\n", scriptfile, line);
			exit(1);
		}
		lw_free(line);
	}
	
	if (scriptfile || nscriptls)
		lw_free(oscript);
	
	if (entrysym)
	{
			int eaddr;
			char *ptr2;
			
			lw_free(linkscript.execsym);
			
			eaddr = strtol(entrysym, &ptr2, 0);
			if (*ptr2)
			{
				linkscript.execaddr = -1;
				linkscript.execsym = lw_strdup(entrysym);
			}
			else
			{
				linkscript.execaddr = eaddr;
				linkscript.execsym = NULL;
			}

	}
}