view lwlink/link.c @ 140:8d65ef71fc01

repo reorg
author lost
date Wed, 28 Jan 2009 06:00:48 +0000
parents 106c2fe3c9d9
children d610b8aef91b
line wrap: on
line source

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


Resolve section and symbol addresses; handle incomplete references
*/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdio.h>
#include <stdlib.h>

#include "expr.h"
#include "lwlink.h"
#include "util.h"

struct section_list *sectlist = NULL;
int nsects = 0;

// 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)
{
	int laddr = 0;
	int ln, sn, fn;
	
	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].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
		{
			// wildcard section
			// look for all sections not yet processed that match flags

			int f = 0;
			int fn0, sn0;
			char *sname;
			
			// named section
			// look for all instances of a section by the specified name
			// and resolve base addresses and add to the list
			for (fn0 = 0; fn0 < ninputfiles; fn0++)
			{
				for (sn0 = 0; sn0 < inputfiles[fn0] -> nsections; sn0++)
				{
					// ignore if the "no flags" bit says to
					if (linkscript.lines[ln].noflags && (inputfiles[fn0] -> sections[sn0].flags & linkscript.lines[ln].noflags))
						continue;
					// ignore unless the yes flags tell us not to
					if (linkscript.lines[ln].yesflags && (inputfiles[fn0] -> sections[sn0].flags & linkscript.lines[ln].yesflags == 0))
						continue;
					if (inputfiles[fn0] -> sections[sn0].processed == 0)
					{
						sname = inputfiles[fn0] -> sections[sn0].name;
						for (fn = 0; fn < ninputfiles; fn++)
						{
							for (sn = 0; sn < inputfiles[fn] -> nsections; sn++)
							{
								if (!strcmp(sname, 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++;
								}
							}
						}
					}
				}
			}
		}
	}
	
	// theoretically, all the base addresses are set now
}

// 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
lw_expr_stack_t *resolve_sym(char *sym, int symtype, void *state)
{
	section_t *sect = state;
	lw_expr_term_t *term;
	int val = 0, i, fn;
	lw_expr_stack_t *s;
	symtab_t *se;
	
	if (symtype == 1)
	{
		// local symbol
		if (!sym)
		{
			val = sect -> loadaddress;
			goto out;
		}
		
		// start with this section
		for (se = sect -> localsyms; se; se = se -> next)
		{
			if (!strcmp(se -> sym, sym))
			{
				val = se -> offset + sect -> loadaddress;
				goto out;
			}
		}
		// not in this section - check all sections in this file
		for (i = 0; i < sect -> file -> nsections; i++)
		{
			for (se = sect -> file -> sections[i].localsyms; se; se = se -> next)
			{
				if (!strcmp(se -> sym, sym))
				{
					val = se -> offset + sect -> file -> sections[i].loadaddress;
					goto out;
				}
			}
		}
		// not found
		fprintf(stderr, "Local symbol %s not found in %s:%s\n", sym, sect -> file -> filename, sect -> name);
		exit(1);
	}
	else
	{
		// external symbol
		// read all files in order until found (or not found)
		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;
					}
				}
			}
		}
		if (sect)
		{
			fprintf(stderr, "External symbol %s not found in %s:%s\n", sym, sect -> file -> filename, sect -> name);
		}
		else
		{
			fprintf(stderr, "External symbol %s not found\n", sym);
		}
		exit(1);
	}
	fprintf(stderr, "Shouldn't ever get here!!!\n");
	exit(88);
out:
	s = lw_expr_stack_create();
	term = lw_expr_term_create_int(val & 0xffff);
	lw_expr_stack_push(s, term);
	lw_expr_term_free(term);
	return s;
}

void resolve_references(void)
{
	int sn;
	reloc_t *rl;
	int rval;

	// resolve entry point if required
	// this must resolve to an *exported* symbol and will resolve to the
	// first instance of that symbol
	if (linkscript.execsym)
	{
		lw_expr_stack_t *s;
		
		s = resolve_sym(linkscript.execsym, 0, NULL);
		linkscript.execaddr = lw_expr_get_value(s);
		lw_expr_stack_free(s);
	}
	
	for (sn = 0; sn < nsects; sn++)
	{
		for (rl = sectlist[sn].ptr -> incompletes; rl; rl = rl -> next)
		{
			// do a "simplify" on the expression
			rval = lw_expr_reval(rl -> expr, resolve_sym, sectlist[sn].ptr);

			// is it constant? error out if not
			if (rval != 0 || !lw_expr_is_constant(rl -> expr))
			{
				fprintf(stderr, "Incomplete reference at %s:%s:%02X\n", sectlist[sn].ptr -> file -> filename, sectlist[sn].ptr -> name, rl -> offset);
				exit(1);
			}
			
			// put the value into the relocation address
			rval = lw_expr_get_value(rl -> expr);
			sectlist[sn].ptr -> code[rl -> offset] = (rval >> 8) & 0xff;
			sectlist[sn].ptr -> code[rl -> offset + 1] = rval & 0xff;
		}
	}
}