shithub: scc

ref: 5b919477354727041f8a522f0f91273e623fa58d
dir: /src/cmd/ld/pass1.c/

View raw version
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <scc/mach.h>
#include <scc/scc.h>
#include <scc/ar.h>

#include "ld.h"

enum {
	OUTLIB,
	INLIB,
};

int bintype = -1;
static Objops *binops;
static Symbol refhead = {
	.next = &refhead,
	.prev = &refhead,
};

Symbol defhead = {
	.next = &defhead,
	.prev = &defhead,
};
static Objlst *objlast;

Objlst *objhead;

static Symbol *
linksym(Symbol *list, Symbol *sym)
{
	sym->next = list;
	sym->prev = list->prev;
	list->prev->next = sym;
	list->prev = sym;
	return sym;
}

static Symbol *
unlinksym(Symbol *sym)
{
	sym->next->prev = sym->prev;
	sym->prev->next = sym->next;
	return sym;
}

static Symbol *
undef(char *name)
{
	return linksym(&refhead, install(name));
}

static Symbol *
define(Objsym *osym, Obj *obj)
{
	Symbol *sym = lookup(osym->name);

	if (!sym) {
		sym = undef(osym->name);
	} else if (sym->def && sym->def->type != 'C') {
		error("%s: symbol redefined", osym->name);
		return NULL;
	}

	/* TODO: deal with C symbols */

	sym->obj = obj;
	sym->def = osym;
	sym->size = osym->size;
	sym->value = osym->value;

	unlinksym(sym);
	linksym(&defhead, sym);

	return sym;
}

static int
moreundef(void)
{
	return refhead.next != &refhead;
}

static void
listundef(void)
{
	Symbol *sym;

	for (sym = refhead.next; sym != &refhead; sym = sym->next) {
		fprintf(stderr,
		        "ld: symbol '%s' not defined\n",
		        sym->name);
	}
}

static int
is_needed(Obj *obj)
{
	Symbol *sym;

	for (sym = refhead.next; sym != &refhead; sym = sym->next) {
		if (objlookup(obj, sym->name, 0))
			return 1;
	}

	return 0;
}


static int
newsym(Objsym *osym, Obj *obj)
{
	Symbol *sym;

	switch (osym->type) {
	case 'U':
		if ((sym = lookup(osym->name)) == NULL)
			sym = undef(osym->name);
		break;
	case '?':
	case 'N':
		break;
	case 'C':
		sym = lookup(osym->name);
		if (!sym || !sym->def) {
			sym = define(osym, obj);
			break;
		}
		if (sym->def->type != 'C')
			break;
		if (sym->size < osym->size)
			sym->size = osym->size;
		break;
	default:
		if (isupper(osym->type))
			define(osym, obj);
		break;
	}

	return 1;
}

static void
addobj(Obj *obj, FILE *fp)
{
	int n;
	Objlst *lst;
	Objsym *sym;
	Objsec *secp;

	if ((lst = malloc(sizeof(*lst))) == NULL) {
		error("out of memory");
		return;
	}

	lst->obj = obj;
	lst->next = NULL;

	if (!objlast)
		objlast = objhead = lst;
	else
		objlast = objlast->next = lst;

	for (sym = obj->syms; sym; sym = sym->next)
		newsym(sym, obj);
}

static void
newobject(FILE *fp, int type, int inlib)
{
	Obj *obj;
 
	if (bintype != -1 && bintype != type) {
		error("not compatible object file");
		return;
	}
	bintype = type;
	binops = obj->ops;

	if ((obj = objnew(type)) == NULL) {
		error("out of memory");
		return;
	}

	if ((*binops->read)(obj, fp) < 0) {
		error("object file corrupted");
		goto delete;
	}

	if (inlib && !is_needed(obj))
		goto delete;

	addobj(obj, fp);
	return;

 delete:
	(*binops->del)(obj);
	return;
}

static void
addlib(FILE *fp)
{
	int t, added;
	long n;
	Objsymdef *def, *dp;
	Symbol *sym;

	if ((*binops->getidx)(&n, &def, fp) < 0) {
		error("corrupted index");
		return;
	}

	for (added = 0; moreundef(); added = 0) {
		for (dp = def; dp; dp = dp->next) {
			sym = lookup(dp->name);
			if (!sym || sym->def)
				continue;

			if (fseek(fp, dp->offset, SEEK_SET) == EOF) {
				error(errstr());
				goto clean;
			}

			if ((t = objtype(fp, NULL)) == -1) {
				error("library file corrupted");
				goto clean;
			}

			if (t != bintype) {
				error("incompatible library");
				goto clean;
			}

			newobject(fp, t, OUTLIB);
			added = 1;
		}

		if (!added)
			break;
	}
clean:
	free(def);
}

static int
newmember(FILE *fp, char *name, void *data)
{
	int t;
	int *nmemb = data;

	if (bintype == -1) {
		error("an object file is needed before any library");
		return 0;
	}

	if (*nmemb++ == 0) {
		if (!strncmp(name, "/", SARNAM) ||
		    !strncmp(name, "__.SYMDEF", SARNAM)) {
			addlib(fp);
			return 0;
		}
	}

	membname = name;
	if ((t = objtype(fp, NULL)) == -1)
		return 1;

	if (bintype != t) {
		error("wrong object file format");
		return 1;
	}

	newobject(fp, t, INLIB);
	membname = NULL;

	return 1;
}

static int
newlibrary(FILE *fp)
{
	int nmemb = 0;

	return formember(fp, newmember, &nmemb);
}

static FILE *
openfile(char *name, char *buffer)
{
	size_t pathlen, len;
	FILE *fp;
	char **bp;
	char libname[FILENAME_MAX];
	extern char *syslibs[];

	filename = name;
	membname = NULL;
	if (name[0] != '-' || name[1] != 'l') {
		if ((fp = fopen(name, "rb")) == NULL)
			error(errstr());
		return fp;
	}

	len = strlen(name+2) + 3;
	if (len > FILENAME_MAX-1) {
		error("library name too long");
		return NULL;
	}
	strcat(strcpy(buffer, "lib"), name+2);

	filename = buffer;
	if ((fp = fopen(libname, "rb")) != NULL)
		return fp;

	for (bp = syslibs; *bp; ++bp) {
		pathlen = strlen(*bp);
		if (pathlen + len > FILENAME_MAX-1)
			continue;
		memcpy(libname, *bp, pathlen);
		memcpy(libname+pathlen+1, buffer, len);
		buffer[pathlen] = '/';

		if ((fp = fopen(buffer, "rb")) != NULL)
			return fp;
	}

	error("not found");
	return NULL;
}

static void
load(char *name)
{
	int t;
	FILE *fp;
	char buff[FILENAME_MAX];

	if ((fp = openfile(name, buff)) == NULL)
		return;

	if ((t = objtype(fp, NULL)) != -1)
		newobject(fp, t, OUTLIB);
	else if (archive(fp))
		newlibrary(fp);
	else
		error("bad format");
}

/*
 * Get the list of object files that are going to be linked
 */
void
pass1(int argc, char *argv[])
{
	char **ap, *cp, *arg;

	for (ap = argv+1; *ap; ++ap) {
		if (ap[0][0] != '-') {
			load(*ap);
			continue;
		}
		for (cp = &ap[0][1]; *cp; ++cp) {
			switch (*cp) {
			case 'l':
				arg = (cp[1]) ? cp+1 : *++ap;
				load(arg);
				continue;
			case 'u':
				arg = (cp[1]) ? cp+1 : *++ap;
				undef(arg);
				continue;
			}
		}
	}

	if (moreundef()) {
		listundef();
		exit(EXIT_FAILURE);
	}
}