quakeforge/tools/qfcc/source/linker.c
2002-07-11 20:17:26 +00:00

364 lines
8.9 KiB
C

/*
link.c
qc object file linking
Copyright (C) 2002 Bill Currie <bill@taniwha.org>
Author: Bill Currie <bill@taniwha.org>
Date: 2002/7/3
This program 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 2
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, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
static const char rcsid[] =
"$Id$";
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include <stdlib.h>
#include "QF/hash.h"
#include "def.h"
#include "emit.h"
#include "expr.h"
#include "immediate.h"
#include "obj_file.h"
#include "qfcc.h"
#include "reloc.h"
#include "strpool.h"
static hashtab_t *extern_defs;
static hashtab_t *defined_defs;
static codespace_t *code;
static defspace_t *data;
static defspace_t *far_data;
static strpool_t *strings;
static strpool_t *type_strings;
static struct {
qfo_reloc_t *relocs;
int num_relocs;
int max_relocs;
} relocs;
static struct {
qfo_def_t *defs;
int num_defs;
int max_defs;
} defs;
static struct {
qfo_function_t *funcs;
int num_funcs;
int max_funcs;
} funcs;
static struct {
pr_lineno_t *lines;
int num_lines;
int max_lines;
} lines;
static int code_base;
static int data_base;
static int far_data_base;
static int reloc_base;
static int def_base;
static int func_base;
static int line_base;
static const char *
defs_get_key (void *_def, void *unused)
{
qfo_def_t *def = (qfo_def_t *) _def;
return G_GETSTR (def->name);
}
static void
add_strings (qfo_t *qfo)
{
int i;
for (i = 0; i < qfo->strings_size; i += strlen (qfo->strings + i))
strpool_addstr (strings, qfo->strings + i);
}
static void
add_relocs (qfo_t *qfo)
{
int i;
if (relocs.num_relocs + qfo->num_relocs > relocs.max_relocs) {
relocs.max_relocs = RUP (relocs.num_relocs + qfo->num_relocs, 16384);
relocs.relocs = realloc (relocs.relocs,
relocs.max_relocs * sizeof (qfo_reloc_t));
}
relocs.num_relocs += qfo->num_relocs;
memcpy (relocs.relocs + reloc_base, qfo->relocs,
qfo->num_relocs * sizeof (qfo_reloc_t));
for (i = reloc_base; i < relocs.num_relocs; i++) {
qfo_reloc_t *reloc = relocs.relocs + i;
switch ((reloc_type)reloc->type) {
case rel_none:
break;
case rel_op_a_def:
case rel_op_b_def:
case rel_op_c_def:
case rel_op_a_op:
case rel_op_b_op:
case rel_op_c_op:
reloc->ofs += code_base;
break;
case rel_def_op:
case rel_def_def:
case rel_def_func:
case rel_def_string:
reloc->ofs += data_base;
break;
}
}
}
static void
add_defs (qfo_t *qfo)
{
int i;
if (defs.num_defs + qfo->num_defs > defs.max_defs) {
defs.max_defs = RUP (defs.num_defs + qfo->num_defs, 16384);
defs.defs = realloc (defs.defs, defs.max_defs * sizeof (qfo_def_t));
}
defs.num_defs += qfo->num_defs;
memcpy (defs.defs + def_base, qfo->defs,
qfo->num_defs * sizeof (qfo_def_t));
for (i = def_base; i < defs.num_defs; i++) {
qfo_def_t *def = defs.defs + i;
def->full_type = strpool_addstr (type_strings,
qfo->types + def->full_type);
def->name = strpool_addstr (strings, qfo->strings + def->name);
if (!(def->flags & (QFOD_LOCAL | QFOD_ABSOLUTE))) {
def->ofs += data_base;
}
def->relocs += reloc_base;
def->file = strpool_addstr (strings, qfo->strings + def->file);
if (def->flags & QFOD_EXTERNAL) {
Hash_Add (extern_defs, def);
} else {
qfo_def_t *d;
if (def->flags & QFOD_GLOBAL) {
if ((d = Hash_Find (defined_defs, G_GETSTR (def->name)))) {
pr.source_file = def->file;
pr.source_line = def->line;
error (0, "%s redefined", G_GETSTR (def->name));
}
}
if (def->basic_type == ev_string && def->ofs
&& QFO_var (qfo, string, def->ofs)) {
string_t s;
s = strpool_addstr (strings, QFO_STRING (qfo, def->ofs));
QFO_var (qfo, string, def->ofs) = s;
}
if (def->ofs)
def->ofs += data_base;
if (def->flags & QFOD_GLOBAL) {
while ((d = Hash_Find (extern_defs, G_GETSTR (def->name)))) {
Hash_Del (extern_defs, G_GETSTR (d->name));
if (d->full_type != def->full_type) {
pr.source_file = def->file;
pr.source_line = def->line;
error (0, "type mismatch %s %s",
G_GETSTR (def->full_type),
G_GETSTR (d->full_type));
}
}
Hash_Add (defined_defs, def);
}
}
}
}
static void
add_funcs (qfo_t *qfo)
{
int i;
if (funcs.num_funcs + qfo->num_functions > funcs.max_funcs) {
funcs.max_funcs = RUP (funcs.num_funcs + qfo->num_functions, 16384);
funcs.funcs = realloc (funcs.funcs,
funcs.max_funcs * sizeof (qfo_function_t));
}
funcs.num_funcs += qfo->num_functions;
memcpy (funcs.funcs + func_base, qfo->functions,
qfo->num_functions * sizeof (qfo_function_t));
for (i = func_base; i < funcs.num_funcs; i++) {
qfo_function_t *func = funcs.funcs + i;
func->name = strpool_addstr (strings, qfo->strings + func->name);
func->file = strpool_addstr (strings, qfo->strings + func->file);
if (func->code)
func->code += code_base;
if (func->def)
func->def += def_base;
if (func->local_defs)
func->local_defs += def_base;
if (func->line_info)
func->line_info += line_base;
func->relocs += reloc_base;
}
}
static void
add_lines (qfo_t *qfo)
{
int i;
if (lines.num_lines + qfo->num_lines > lines.max_lines) {
lines.max_lines = RUP (lines.num_lines + qfo->num_lines, 16384);
lines.lines = realloc (lines.lines,
lines.max_lines * sizeof (pr_lineno_t));
}
lines.num_lines += qfo->num_lines;
memcpy (lines.lines + line_base, qfo->lines,
qfo->num_lines * sizeof (pr_lineno_t));
for (i = line_base; i < lines.num_lines; i++) {
pr_lineno_t *line = lines.lines + i;
if (line->line)
line->fa.addr += code_base;
else
line->fa.func += func_base;
}
}
static void
fixup_relocs (qfo_t *qfo)
{
qfo_reloc_t *reloc;
for (reloc = relocs.relocs + reloc_base;
reloc - relocs.relocs < relocs.num_relocs;
reloc++) {
switch ((reloc_type)reloc->type) {
case rel_none:
break;
case rel_op_a_def:
code->code[reloc->ofs].a += data_base;
break;
case rel_op_b_def:
code->code[reloc->ofs].b += data_base;
break;
case rel_op_c_def:
code->code[reloc->ofs].c += data_base;
break;
case rel_op_a_op:
case rel_op_b_op:
case rel_op_c_op:
break;
case rel_def_op:
data->data[reloc->ofs].integer_var += code_base;
break;
case rel_def_def:
data->data[reloc->ofs].integer_var += data_base;
break;
case rel_def_func:
data->data[reloc->ofs].integer_var += func_base;
break;
case rel_def_string:
data->data[reloc->ofs].string_var =
strpool_addstr (strings,
qfo->strings + data->data[reloc->ofs].string_var);
reloc->ofs += data_base;
break;
}
}
}
void
linker_begin (void)
{
extern_defs = Hash_NewTable (16381, defs_get_key, 0, 0);
defined_defs = Hash_NewTable (16381, defs_get_key, 0, 0);
code = codespace_new ();
data = new_defspace ();
far_data = new_defspace ();
strings = strpool_new ();
type_strings = strpool_new ();
}
void
linker_add_object_file (const char *filename)
{
qfo_t *qfo;
qfo = qfo_read (filename);
if (!qfo)
return;
puts(filename);
code_base = code->size;
data_base = data->size;
far_data_base = far_data->size;
reloc_base = relocs.num_relocs;
def_base = defs.num_defs;
func_base = funcs.num_funcs;
line_base = lines.num_lines;
codespace_addcode (code, qfo->code, qfo->code_size);
defspace_adddata (data, qfo->data, qfo->data_size);
defspace_adddata (far_data, qfo->far_data, qfo->far_data_size);
add_strings (qfo);
add_relocs (qfo);
add_defs (qfo);
add_funcs (qfo);
add_lines (qfo);
fixup_relocs (qfo);
qfo_delete (qfo);
qfo = qfo_new ();
qfo_add_code (qfo, code->code, code->size);
qfo_add_data (qfo, data->data, data->size);
qfo_add_far_data (qfo, far_data->data, far_data->size);
qfo_add_strings (qfo, strings->strings, strings->size);
qfo_add_relocs (qfo, relocs.relocs, relocs.num_relocs);
qfo_add_defs (qfo, defs.defs, defs.num_defs);
qfo_add_functions (qfo, funcs.funcs, funcs.num_funcs);
qfo_add_types (qfo, type_strings->strings, type_strings->size);
}
void
linker_finish (void)
{
qfo_def_t **undef_defs, **def;
undef_defs = (qfo_def_t **) Hash_GetList (extern_defs);
for (def = undef_defs; *def; def++) {
pr.source_file = (*def)->file;
pr.source_line = (*def)->line;
error (0, "undefined symbol %s", G_GETSTR ((*def)->name));
}
}