Rewrite the type_space processing.

Now each encoding is copied across def by def using memcpy, with the
expectation that any references to other types will be handled via the
reloc system. Unfortunately, it seems there's an off-by-4 (hmm, suspicious
number...) in the reloc offsets, but I'll look into that after I get some
sleep.
This commit is contained in:
Bill Currie 2012-11-13 21:55:54 +09:00
parent 8625964634
commit 31739f4dbf

View file

@ -159,13 +159,17 @@ static defspace_t **work_spaces[qfo_num_spaces] = {
&work_near_data,
&work_far_data,
&work_entity_data,
0,
&work_type_data,
};
static dstring_t *linker_current_file;
#define QFOSTR(q,s) QFO_GETSTR (q, s)
#define WORKSTR(s) QFOSTR (work, s)
#define QFOTYPE(t) ((qfot_type_t *) (char *) \
(qfo_type_defs->d.data + (t)))
#define WORKTYPE(t) ((qfot_type_t *) (char *) \
(work_type_data->data + (t)))
/** Produce an error message for defs with mismatched types.
@ -301,7 +305,6 @@ define_def (defref_t *ref, hashtab_t *extern_tab, hashtab_t *defined_tab)
// treat the new def as external
REF (ref)->flags |= QFOD_EXTERNAL;
resolve_external_def (ref, r);
//FIXME copy stuff from new def to existing def???
} else {
def_error (REF (ref), "%s redefined", WORKSTR (REF (ref)->name));
def_error (REF (r), "previous definition");
@ -345,17 +348,59 @@ process_def (defref_t *ref, qfo_mspace_t *space,
}
static void
process_data_def (defref_t *ref, qfo_mspace_t *space)
process_data_def (defref_t *ref, qfo_mspace_t *space, qfo_def_t *old)
{
process_def (ref, space, extern_field_defs, defined_field_defs);
}
static void
process_field_def (defref_t *ref, qfo_mspace_t *space)
process_field_def (defref_t *ref, qfo_mspace_t *space, qfo_def_t *old)
{
process_def (ref, space, extern_field_defs, defined_field_defs);
}
static void
process_type_def (defref_t *ref, qfo_mspace_t *space, qfo_def_t *old)
{
if (REF (ref)->flags & QFOD_EXTERNAL) {
// The most common (only?) time external type encodings show up is
// in pointers to incomplete structs. eg, struct foo *bar; with no
// struct foo {...};
extern_def (ref, extern_type_defs, defined_type_defs);
} else {
const char *name;
defref_t *r;
qfot_type_t *old_type;
qfot_type_t *new_type;
old_type = QFOTYPE(old->offset);
name = WORKSTR (REF (ref)->name);
if ((r = Hash_Find (defined_type_defs, name))) {
// The type has been defined already, so treat this def as if it
// were external
REF (ref)->flags |= QFOD_EXTERNAL;
resolve_external_def (ref, r);
} else {
// The type is new to the program, so we need to allocate space
// for it and copy the type encoding. Relocation records will
// take care of any cross-references.
REF (ref)->offset = alloc_data (qfo_type_space, old_type->size);
new_type = WORKTYPE(REF (ref)->offset);
memcpy (new_type, old_type, old_type->size * sizeof (pr_type_t));
define_def (ref, extern_type_defs, defined_type_defs);
}
// save the new address in the old def's type field so relocation
// records can be updated.
old->type = REF (ref)->offset;
// mark the old type encoding as having been transfered, and save the
// new address in the encoding's class field so def and function types
// can be updated easily.
old_type->ty = -1;
old_type->t.class = REF (ref)->offset;
}
}
static int
add_relocs (qfo_t *qfo, int start, int count, int target)
{
@ -388,7 +433,7 @@ add_relocs (qfo_t *qfo, int start, int count, int target)
static int
add_defs (qfo_t *qfo, qfo_mspace_t *space, qfo_mspace_t *dest_space,
void (*process) (defref_t *ref, qfo_mspace_t *space))
void (*process) (defref_t *ref, qfo_mspace_t *space, qfo_def_t *old))
{
int count = space->num_defs;
int size;
@ -406,16 +451,15 @@ add_defs (qfo_t *qfo, qfo_mspace_t *space, qfo_mspace_t *dest_space,
odef = dest_space->defs + dest_space->num_defs;
for (i = 0; i < count; i++, idef++, odef++) {
*odef = *idef; // copy the def data
idef->offset = num_work_defrefs; // so def can be found
odef->name = linker_add_string (QFOSTR (qfo, idef->name));
odef->file = linker_add_string (QFOSTR (qfo, idef->file));
type = (qfot_type_t *) (char *) (qfo_type_defs->d.data + idef->type);
type = QFOTYPE(idef->type);
odef->type = type->t.class; // pointer to type in work
if (odef->flags & QFOD_EXTERNAL && !odef->num_relocs)
continue;
ref = get_defref (odef, dest_space);
work_defrefs[num_work_defrefs++] = ref;
process (ref, dest_space);
process (ref, dest_space, idef);
odef->relocs = add_relocs (qfo, odef->relocs, odef->num_relocs,
odef - dest_space->defs);
}
@ -497,70 +541,6 @@ add_data_space (qfo_t *qfo, qfo_mspace_t *space)
ws->id = space->id;
}
static pointer_t
transfer_type (qfo_t *qfo, qfo_mspace_t *space, pointer_t type_offset)
{
qfot_type_t *type_buffer;
qfot_type_t *type;
int i;
int type_size;
const char *str;
type = (qfot_type_t *) (char *) &space->d.data[type_offset];
if (type->ty < 0)
return type->t.class;
type_size = type->size * sizeof (pr_type_t);
type_buffer = alloca (type_size);
memcpy (type_buffer, type, type_size);
type_offset = defspace_alloc_loc (work_type_data, type->size);
type->t.class = type_offset;
type->ty = -1;
type = type_buffer;
type->encoding = linker_add_string (QFOSTR (qfo, type->encoding));
switch ((ty_meta_e) type->ty) {
case ty_none:
if (type->t.type == ev_func) {
int count;
qfot_func_t *f = &type->t.func;
pointer_t *p;
count = f->num_params;
if (count < 0)
count = ~count; //ones complement
f->return_type = transfer_type (qfo, space, f->return_type);
for (i = 0, p = f->param_types; i < count; i++, p++)
*p = transfer_type (qfo, space, *p);
} else if (type->t.type == ev_pointer
|| type->t.type == ev_field) {
qfot_fldptr_t *fp = &type->t.fldptr;
fp->aux_type = transfer_type (qfo, space, fp->aux_type);
}
break;
case ty_struct:
case ty_union:
case ty_enum:
str = QFOSTR (qfo, type->t.strct.tag);
type->t.strct.tag = linker_add_string (str);
for (i = 0; i < type->t.strct.num_fields; i++) {
qfot_var_t *field = &type->t.strct.fields[i];
field->type = transfer_type (qfo, space, field->type);
field->name = linker_add_string (QFOSTR (qfo, field->name));
}
break;
case ty_array:
type->t.array.type = transfer_type (qfo, space,
type->t.array.type);
break;
case ty_class:
// There's nothing to do here as it will be take care of by a
// relocation record.
break;
}
memcpy (work_type_data->data + type_offset, type, type_size);
return type_offset;
}
static defref_t *
make_def (int s, const char *name, type_t *type, unsigned flags, int v)
{
@ -754,18 +734,25 @@ process_entity_space (qfo_t *qfo, qfo_mspace_t *space, int pass)
return 0;
}
// NOTE this will likely give weird results for qsort if the defs overlapp.
// However, that should not happen.
static int
type_def_compare (const void *a, const void *b)
{
qfo_def_t *ta = (qfo_def_t *) a;
qfo_def_t *tb = (qfo_def_t *) b;
if (ta->offset < tb->offset)
return -1;
if (ta->offset >= tb->offset + QFOTYPE (tb->offset)->size)
return 1;
return 0;
}
static int
process_type_space (qfo_t *qfo, qfo_mspace_t *space, int pass)
{
int i;
int size;
qfo_def_t *def;
qfo_def_t *type_def;
qfo_mspace_t *type_space;
qfot_type_t *type;
const char *name;
defref_t *ref;
pointer_t offset;
if (pass != 0)
return 0;
@ -774,54 +761,30 @@ process_type_space (qfo_t *qfo, qfo_mspace_t *space, int pass)
return 1;
}
qfo_type_defs = space;
type_space = &work->spaces[qfo_type_space];
size = (type_space->num_defs + space->num_defs) * sizeof (qfo_def_t);
type_space->defs = realloc (type_space->defs, size);
// first pass: check for already defined types
for (i = 0, def = space->defs; i < space->num_defs; i++, def++) {
name = QFOSTR (qfo, def->name);
type = (qfot_type_t *) (char *) &space->d.data[def->offset];
if ((ref = Hash_Find (defined_type_defs, name))) {
type->ty = -1;
type->t.class = REF (ref)->offset;
continue;
}
}
// second pass: transfer any new types
for (i = 0, def = space->defs; i < space->num_defs; i++, def++) {
name = QFOSTR (qfo, def->name);
type = (qfot_type_t *) (char *) &space->d.data[def->offset];
if (type->ty < 0)
continue;
offset = transfer_type (qfo, space, def->offset);
type_def = type_space->defs + type_space->num_defs++;
memset (type_def, 0, sizeof (*type_def));
type_def->name = linker_add_string (name);
type_def->offset = offset;
ref = get_defref (type_def, type_space);
Hash_Add (defined_type_defs, ref);
while ((ref = Hash_Del (extern_type_defs, name))) {
REF (ref)->flags = 0;
REF (ref)->offset = type_def->offset;
}
}
size = type_space->num_defs * sizeof (qfo_def_t);
type_space->defs = realloc (type_space->defs, size);
add_defs (qfo, space, work->spaces + qfo_type_space, process_type_def);
// the defs in qfo are no longer needed by the rest of the linker, so
// we're free to mess around with them
type_space->d.data = work_type_data->data;
type_space->data_size = work_type_data->size;
// sort the defs by addres. Unfortunately, they will usually be in order,
// so qsort will likely be pessimistic, but oh well.
qsort (space->defs, space->num_defs, sizeof (qfo_def_t), type_def_compare);
for (i = 0; i < num_builtins; i++) {
builtin_sym_t *bi = builtin_symbols + i;
if (!bi->defref)
// update the offsets of all relocation records that point into the type
// encoding space
for (i = 0; i < qfo->num_relocs; i++) {
qfo_reloc_t *reloc = qfo->relocs + i;
qfo_def_t dummy;
qfo_def_t *def;
if (reloc->space != space->id)
continue;
def = REF (bi->defref);
if (def->type >= 0)
continue;
ref = Hash_Find (defined_type_defs, WORKSTR (-def->type));
if (!ref)
continue;
def->type = REF (ref)->offset;
dummy.offset = reloc->offset;
def = (qfo_def_t *) bsearch (&dummy, space->defs, space->num_defs,
sizeof (qfo_def_t), type_def_compare);
if (!def)
linker_internal_error ("relocation record with invalid address. "
"corrupt object file?");
reloc->offset += def->type - def->offset;
}
return 0;
}
@ -839,7 +802,7 @@ process_funcs (qfo_t *qfo)
qfo->num_funcs * sizeof (qfo_func_t));
while (work->num_funcs < size) {
func = work->funcs + work->num_funcs++;
type = (qfot_type_t *) (char *) (qfo_type_defs->d.data + func->type);
type = QFOTYPE(func->type);
func->type = type->t.class;
func->name = linker_add_string (QFOSTR (qfo, func->name));
func->file = linker_add_string (QFOSTR (qfo, func->file));
@ -894,13 +857,6 @@ process_loose_relocs (qfo_t *qfo)
reloc->type = rel_none;
continue;
}
if (reloc->space == qfo_type_defs - qfo->spaces) {
// loose relocs in the type space become invalid
if (reloc->type != rel_def_string)
printf ("%d\n", reloc->type);
reloc->type = rel_none;
continue;
}
reloc->space = qfo->spaces[reloc->space].id;
if (reloc->type == rel_def_string) {
const char *str;