mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-10 23:32:09 +00:00
7a504f8983
The values are (or should be!) written as floats, so don't give any debuggers hernias when displaying enums in v6 progs.
1048 lines
30 KiB
C
1048 lines
30 KiB
C
/*
|
|
obj_file.c
|
|
|
|
qfcc object file support
|
|
|
|
Copyright (C) 2002 Bill Currie <bill@taniwha.org>
|
|
|
|
Author: Bill Currie <bill@taniwha.org>
|
|
Date: 2002/6/21
|
|
|
|
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
|
|
|
|
*/
|
|
#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/dstring.h"
|
|
#include "QF/qendian.h"
|
|
#include "QF/quakeio.h"
|
|
#include "QF/va.h"
|
|
|
|
#include "codespace.h"
|
|
#include "debug.h"
|
|
#include "def.h"
|
|
#include "defspace.h"
|
|
#include "emit.h"
|
|
#include "expr.h"
|
|
#include "function.h"
|
|
#include "obj_file.h"
|
|
#include "obj_type.h"
|
|
#include "options.h"
|
|
#include "qfcc.h"
|
|
#include "reloc.h"
|
|
#include "statements.h"
|
|
#include "strpool.h"
|
|
#include "symtab.h"
|
|
#include "type.h"
|
|
#include "value.h"
|
|
|
|
static int
|
|
count_relocs (reloc_t *r)
|
|
{
|
|
int count;
|
|
|
|
for (count = 0; r; r = r->next)
|
|
count++;
|
|
return count;
|
|
}
|
|
|
|
static unsigned
|
|
qfo_def_flags (def_t *def)
|
|
{
|
|
unsigned flags = 0;
|
|
if (def->initialized)
|
|
flags |= QFOD_INITIALIZED;
|
|
if (def->constant)
|
|
flags |= QFOD_CONSTANT;
|
|
if (def->global)
|
|
flags |= QFOD_GLOBAL;
|
|
if (def->external)
|
|
flags |= QFOD_EXTERNAL;
|
|
if (def->local)
|
|
flags |= QFOD_LOCAL;
|
|
if (def->system)
|
|
flags |= QFOD_SYSTEM;
|
|
if (def->nosave)
|
|
flags |= QFOD_NOSAVE;
|
|
if (def->param)
|
|
flags |= QFOD_PARAM;
|
|
return flags;
|
|
}
|
|
|
|
static void
|
|
qfo_encode_one_reloc (reloc_t *reloc, qfo_reloc_t **qfo_reloc, pr_int_t target)
|
|
{
|
|
qfo_reloc_t *q;
|
|
|
|
q = (*qfo_reloc)++;
|
|
if (reloc->space) // op_* relocs do not have a space (code is implied)
|
|
q->space = reloc->space->qfo_space;
|
|
q->offset = reloc->offset;
|
|
q->type = reloc->type;
|
|
q->target = target;
|
|
}
|
|
|
|
static int
|
|
qfo_encode_relocs (reloc_t *relocs, qfo_reloc_t **qfo_relocs, pr_int_t target)
|
|
{
|
|
int count;
|
|
reloc_t *r;
|
|
|
|
for (count = 0, r = relocs; r; r = r->next) {
|
|
count++;
|
|
qfo_encode_one_reloc (r, qfo_relocs, target);
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static int
|
|
qfo_encode_defs (qfo_t *qfo, def_t *defs, qfo_def_t **qfo_defs,
|
|
qfo_reloc_t **qfo_relocs)
|
|
{
|
|
int count;
|
|
def_t *d;
|
|
qfo_def_t *q;
|
|
|
|
for (count = 0, d = defs; d; d = d->next) {
|
|
count++;
|
|
q = (*qfo_defs)++;
|
|
d->qfo_def = q - qfo->defs;
|
|
// defs in the type data space do not have types
|
|
if (d->type)
|
|
q->type = d->type->type_def->offset;
|
|
q->name = ReuseString (d->name);
|
|
q->offset = d->offset;
|
|
q->relocs = *qfo_relocs - qfo->relocs;
|
|
q->num_relocs = qfo_encode_relocs (d->relocs, qfo_relocs,
|
|
q - qfo->defs);
|
|
q->flags = qfo_def_flags (d);
|
|
q->file = d->file;
|
|
q->line = d->line;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static void
|
|
qfo_count_space_stuff (qfo_t *qfo, defspace_t *space)
|
|
{
|
|
def_t *d;
|
|
|
|
for (d = space->defs; d; d = d->next) {
|
|
qfo->num_defs++;
|
|
qfo->num_relocs += count_relocs (d->relocs);
|
|
}
|
|
}
|
|
|
|
static void
|
|
qfo_count_function_stuff (qfo_t *qfo, function_t *functions)
|
|
{
|
|
function_t *func;
|
|
|
|
for (func = functions; func; func = func->next) {
|
|
qfo->num_funcs++;
|
|
qfo->num_relocs += count_relocs (func->refs);
|
|
if (func->symtab && func->symtab->space) {
|
|
qfo->num_spaces++;
|
|
qfo_count_space_stuff (qfo, func->symtab->space);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
qfo_count_stuff (qfo_t *qfo, pr_info_t *pr)
|
|
{
|
|
qfo_count_space_stuff (qfo, pr->near_data);
|
|
qfo_count_space_stuff (qfo, pr->far_data);
|
|
qfo_count_space_stuff (qfo, pr->entity_data);
|
|
qfo_count_space_stuff (qfo, pr->type_data);
|
|
qfo_count_function_stuff (qfo, pr->func_head);
|
|
qfo->num_relocs += count_relocs (pr->relocs);
|
|
}
|
|
|
|
static void
|
|
qfo_init_string_space (qfo_t *qfo, qfo_mspace_t *space, strpool_t *strings)
|
|
{
|
|
strings->qfo_space = space - qfo->spaces;
|
|
space->type = qfos_string;
|
|
space->num_defs = 0;
|
|
space->defs = 0;
|
|
space->d.strings = strings->strings;
|
|
space->data_size = strings->size;
|
|
space->id = qfo_strings_space;
|
|
}
|
|
|
|
static void
|
|
qfo_init_code_space (qfo_t *qfo, qfo_mspace_t *space, codespace_t *code)
|
|
{
|
|
code->qfo_space = space - qfo->spaces;
|
|
space->type = qfos_code;
|
|
space->num_defs = 0;
|
|
space->defs = 0;
|
|
space->d.code = code->code;
|
|
space->data_size = code->size;
|
|
space->id = qfo_code_space;
|
|
}
|
|
|
|
static void
|
|
qfo_init_data_space (qfo_t *qfo, qfo_def_t **defs, qfo_reloc_t **relocs,
|
|
qfo_mspace_t *space, defspace_t *data)
|
|
{
|
|
data->qfo_space = space - qfo->spaces;
|
|
space->type = qfos_data;
|
|
space->defs = *defs;
|
|
space->num_defs = qfo_encode_defs (qfo, data->defs, defs, relocs);
|
|
space->d.data = data->data;
|
|
space->data_size = data->size;
|
|
}
|
|
|
|
static void
|
|
qfo_init_entity_space (qfo_t *qfo, qfo_def_t **defs, qfo_reloc_t **relocs,
|
|
qfo_mspace_t *space, defspace_t *data)
|
|
{
|
|
data->qfo_space = space - qfo->spaces;
|
|
space->type = qfos_entity;
|
|
space->defs = *defs;
|
|
space->num_defs = qfo_encode_defs (qfo, data->defs, defs, relocs);
|
|
space->d.data = 0;
|
|
space->data_size = data->size;
|
|
space->id = qfo_entity_space;
|
|
}
|
|
|
|
static void
|
|
qfo_init_type_space (qfo_t *qfo, qfo_def_t **defs, qfo_reloc_t **relocs,
|
|
qfo_mspace_t *space, defspace_t *data)
|
|
{
|
|
data->qfo_space = space - qfo->spaces;
|
|
space->type = qfos_type;
|
|
space->defs = *defs;
|
|
space->num_defs = qfo_encode_defs (qfo, data->defs, defs, relocs);
|
|
space->d.data = data->data;
|
|
space->data_size = data->size;
|
|
space->id = qfo_type_space;
|
|
}
|
|
|
|
static void
|
|
qfo_encode_functions (qfo_t *qfo, qfo_def_t **defs, qfo_reloc_t **relocs,
|
|
qfo_mspace_t *space, function_t *functions)
|
|
{
|
|
function_t *f;
|
|
qfo_func_t *q;
|
|
|
|
for (f = functions, q = qfo->funcs; f; f = f->next, q++) {
|
|
q->name = f->s_name;
|
|
q->type = f->def->type->type_def->offset;
|
|
q->file = f->s_file;
|
|
q->line = f->def->line;
|
|
q->code = f->code;
|
|
if (f->builtin) // FIXME redundant
|
|
q->code = -f->builtin;
|
|
q->def = f->def->qfo_def;
|
|
if (f->symtab && f->symtab->space) {
|
|
space->id = f->symtab->space->qfo_space;
|
|
qfo_init_data_space (qfo, defs, relocs, space++, f->symtab->space);
|
|
q->locals_space = f->symtab->space->qfo_space;
|
|
}
|
|
q->line_info = f->line_info;
|
|
q->relocs = *relocs - qfo->relocs;
|
|
q->num_relocs = qfo_encode_relocs (f->refs, relocs, q - qfo->funcs);
|
|
}
|
|
}
|
|
|
|
qfo_t *
|
|
qfo_from_progs (pr_info_t *pr)
|
|
{
|
|
qfo_t *qfo;
|
|
qfo_def_t *def;
|
|
qfo_reloc_t *reloc;
|
|
reloc_t *r;
|
|
|
|
qfo = calloc (1, sizeof (qfo_t));
|
|
qfo->num_spaces = qfo_num_spaces; // certain spaces are always present
|
|
qfo_count_stuff (qfo, pr);
|
|
qfo->spaces = calloc (qfo->num_spaces, sizeof (qfo_mspace_t));
|
|
qfo->relocs = calloc (qfo->num_relocs, sizeof (qfo_reloc_t));
|
|
qfo->defs = calloc (qfo->num_defs, sizeof (qfo_def_t));
|
|
qfo->funcs = calloc (qfo->num_funcs, sizeof (qfo_func_t));
|
|
|
|
def = qfo->defs;
|
|
reloc = qfo->relocs;
|
|
|
|
pr->code->qfo_space = qfo_code_space;
|
|
pr->near_data->qfo_space = qfo_near_data_space;
|
|
pr->far_data->qfo_space = qfo_far_data_space;
|
|
pr->entity_data->qfo_space = qfo_entity_space;
|
|
pr->type_data->qfo_space = qfo_type_space;
|
|
pr->strings->qfo_space = qfo_strings_space;
|
|
|
|
qfo_init_code_space (qfo, &qfo->spaces[qfo_code_space], pr->code);
|
|
qfo_init_data_space (qfo, &def, &reloc, &qfo->spaces[qfo_near_data_space],
|
|
pr->near_data);
|
|
qfo->spaces[qfo_near_data_space].id = qfo_near_data_space;
|
|
qfo_init_data_space (qfo, &def, &reloc, &qfo->spaces[qfo_far_data_space],
|
|
pr->far_data);
|
|
qfo->spaces[qfo_far_data_space].id = qfo_far_data_space;
|
|
qfo_init_entity_space (qfo, &def, &reloc, &qfo->spaces[qfo_entity_space],
|
|
pr->entity_data);
|
|
qfo_init_type_space (qfo, &def, &reloc, &qfo->spaces[qfo_type_space],
|
|
pr->type_data);
|
|
|
|
qfo_encode_functions (qfo, &def, &reloc, qfo->spaces + qfo_num_spaces,
|
|
pr->func_head);
|
|
if (pr->num_linenos) {
|
|
qfo->lines = malloc (pr->num_linenos * sizeof (pr_lineno_t));
|
|
memcpy (qfo->lines, pr->linenos,
|
|
pr->num_linenos * sizeof (pr_lineno_t));
|
|
qfo->num_lines = pr->num_linenos;
|
|
}
|
|
// strings must be done last because encoding defs and functions adds the
|
|
// names
|
|
qfo_init_string_space (qfo, &qfo->spaces[qfo_strings_space], pr->strings);
|
|
|
|
qfo->num_loose_relocs = qfo->num_relocs - (reloc - qfo->relocs);
|
|
for (r = pr->relocs; r; r = r->next) {
|
|
if (r->type == rel_def_op) {
|
|
qfo_encode_one_reloc (r, &reloc, r->label->dest->offset);
|
|
} else if (r->type == rel_def_string) {
|
|
def_t d;
|
|
d.space = r->space;
|
|
d.offset = r->offset;
|
|
qfo_encode_one_reloc (r, &reloc, D_STRING (&d));
|
|
} else {
|
|
qfo_encode_one_reloc (r, &reloc, 0);
|
|
}
|
|
}
|
|
|
|
return qfo;
|
|
}
|
|
|
|
static int
|
|
qfo_space_size (qfo_mspace_t *space)
|
|
{
|
|
switch (space->type) {
|
|
case qfos_null:
|
|
return 0;
|
|
case qfos_code:
|
|
return space->data_size * sizeof (*space->d.code);
|
|
case qfos_data:
|
|
// data size > 0 but d.data == null -> all data is zero
|
|
if (!space->d.data)
|
|
return 0;
|
|
return space->data_size * sizeof (*space->d.data);
|
|
case qfos_string:
|
|
return space->data_size * sizeof (*space->d.strings);
|
|
case qfos_entity:
|
|
return 0;
|
|
case qfos_type:
|
|
return space->data_size * sizeof (*space->d.data);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
qfo_byteswap_space (void *space, int size, qfos_type_t type)
|
|
{
|
|
int c;
|
|
pr_type_t *val;
|
|
dstatement_t *s;
|
|
switch (type) {
|
|
case qfos_null:
|
|
case qfos_string:
|
|
break;
|
|
case qfos_code:
|
|
for (s = (dstatement_t *) space, c = 0; c < size; c++, s++) {
|
|
s->op = LittleShort (s->op);
|
|
s->a = LittleShort (s->a);
|
|
s->b = LittleShort (s->b);
|
|
s->c = LittleShort (s->c);
|
|
}
|
|
break;
|
|
case qfos_data:
|
|
case qfos_entity:
|
|
case qfos_type:
|
|
for (val = (pr_type_t *) space, c = 0; c < size; c++, val++)
|
|
val->integer_var = LittleLong (val->integer_var);
|
|
break;
|
|
}
|
|
}
|
|
|
|
int
|
|
qfo_write (qfo_t *qfo, const char *filename)
|
|
{
|
|
int size;
|
|
int space_offset;
|
|
int i;
|
|
byte *data;
|
|
qfo_header_t *header;
|
|
qfo_space_t *spaces;
|
|
qfo_reloc_t *relocs;
|
|
qfo_def_t *defs;
|
|
qfo_func_t *funcs;
|
|
pr_lineno_t *lines;
|
|
byte *space_data;
|
|
QFile *file;
|
|
|
|
file = Qopen (filename, options.gzip ? "wbz9" : "wb");
|
|
if (!file)
|
|
return -1;
|
|
|
|
size = sizeof (qfo_header_t);
|
|
size += sizeof (qfo_space_t) * qfo->num_spaces;
|
|
size += sizeof (qfo_reloc_t) * qfo->num_relocs;
|
|
size += sizeof (qfo_def_t) * qfo->num_defs;
|
|
size += sizeof (qfo_func_t) * qfo->num_funcs;
|
|
size += sizeof (pr_lineno_t) * qfo->num_lines;
|
|
space_offset = size;
|
|
for (i = 0; i < qfo->num_spaces; i++)
|
|
size += RUP (qfo_space_size (qfo->spaces + i), 16);
|
|
data = calloc (1, size);
|
|
header = (qfo_header_t *) data;
|
|
memcpy (header->qfo, QFO, 4);
|
|
header->version = LittleLong (QFO_VERSION);
|
|
header->num_spaces = LittleLong (qfo->num_spaces);
|
|
header->num_relocs = LittleLong (qfo->num_relocs);
|
|
header->num_defs = LittleLong (qfo->num_defs);
|
|
header->num_funcs = LittleLong (qfo->num_funcs);
|
|
header->num_lines = LittleLong (qfo->num_lines);
|
|
header->num_loose_relocs = LittleLong (qfo->num_loose_relocs);
|
|
spaces = (qfo_space_t *) &header[1];
|
|
relocs = (qfo_reloc_t *) &spaces[qfo->num_spaces];
|
|
defs = (qfo_def_t *) &relocs[qfo->num_relocs];
|
|
funcs = (qfo_func_t *) &defs[qfo->num_defs];
|
|
lines = (pr_lineno_t *) &funcs[qfo->num_funcs];
|
|
space_data = data + space_offset;
|
|
for (i = 0; i < qfo->num_spaces; i++) {
|
|
spaces[i].type = LittleLong (qfo->spaces[i].type);
|
|
if (qfo->spaces[i].defs)
|
|
spaces[i].defs = LittleLong (qfo->spaces[i].defs - qfo->defs);
|
|
spaces[i].num_defs = LittleLong (qfo->spaces[i].num_defs);
|
|
if (qfo->spaces[i].d.data) {
|
|
int space_size = qfo_space_size (qfo->spaces + i);
|
|
spaces[i].data = LittleLong (space_data - data);
|
|
memcpy (space_data, qfo->spaces[i].d.data, space_size);
|
|
qfo_byteswap_space (space_data, qfo->spaces[i].data_size,
|
|
qfo->spaces[i].type);
|
|
space_data += RUP (space_size, 16);
|
|
}
|
|
spaces[i].data_size = LittleLong (qfo->spaces[i].data_size);
|
|
spaces[i].id = LittleLong (qfo->spaces[i].id);
|
|
}
|
|
for (i = 0; i < qfo->num_relocs; i++) {
|
|
relocs[i].space = LittleLong (qfo->relocs[i].space);
|
|
relocs[i].offset = LittleLong (qfo->relocs[i].offset);
|
|
relocs[i].type = LittleLong (qfo->relocs[i].type);
|
|
relocs[i].target = LittleLong (qfo->relocs[i].target);
|
|
}
|
|
for (i = 0; i < qfo->num_defs; i++) {
|
|
defs[i].type = LittleLong (qfo->defs[i].type);
|
|
defs[i].name = LittleLong (qfo->defs[i].name);
|
|
defs[i].offset = LittleLong (qfo->defs[i].offset);
|
|
defs[i].relocs = LittleLong (qfo->defs[i].relocs);
|
|
defs[i].num_relocs = LittleLong (qfo->defs[i].num_relocs);
|
|
defs[i].flags = LittleLong (qfo->defs[i].flags);
|
|
defs[i].file = LittleLong (qfo->defs[i].file);
|
|
defs[i].line = LittleLong (qfo->defs[i].line);
|
|
}
|
|
for (i = 0; i < qfo->num_funcs; i++) {
|
|
funcs[i].name = LittleLong (qfo->funcs[i].name);
|
|
funcs[i].type = LittleLong (qfo->funcs[i].type);
|
|
funcs[i].file = LittleLong (qfo->funcs[i].file);
|
|
funcs[i].line = LittleLong (qfo->funcs[i].line);
|
|
funcs[i].code = LittleLong (qfo->funcs[i].code);
|
|
funcs[i].def = LittleLong (qfo->funcs[i].def);
|
|
funcs[i].locals_space = LittleLong (qfo->funcs[i].locals_space);
|
|
funcs[i].line_info = LittleLong (qfo->funcs[i].line_info);
|
|
funcs[i].relocs = LittleLong (qfo->funcs[i].relocs);
|
|
funcs[i].num_relocs = LittleLong (qfo->funcs[i].num_relocs);
|
|
}
|
|
for (i = 0; i < qfo->num_lines; i++) {
|
|
lines[i].fa.addr = LittleLong (qfo->lines[i].fa.addr);
|
|
lines[i].line = LittleLong (qfo->lines[i].line);
|
|
}
|
|
|
|
Qwrite (file, data, size);
|
|
free (data);
|
|
Qclose (file);
|
|
return 0;
|
|
}
|
|
|
|
qfo_t *
|
|
qfo_read (QFile *file)
|
|
{
|
|
char *data;
|
|
int size;
|
|
qfo_header_t *header;
|
|
qfo_space_t *spaces;
|
|
qfo_t *qfo;
|
|
int i;
|
|
|
|
size = Qfilesize (file);
|
|
data = malloc (size);
|
|
Qread (file, data, size);
|
|
|
|
header = (qfo_header_t *) data;
|
|
header->version = LittleLong (header->version);
|
|
if (memcmp (header->qfo, QFO, 4) || header->version != QFO_VERSION) {
|
|
fprintf (stderr, "not a valid qfo file\n");
|
|
free (data);
|
|
return 0;
|
|
}
|
|
qfo = calloc (1, sizeof (qfo_t));
|
|
|
|
qfo->num_spaces = LittleLong (header->num_spaces);
|
|
qfo->num_relocs = LittleLong (header->num_relocs);
|
|
qfo->num_defs = LittleLong (header->num_defs);
|
|
qfo->num_funcs = LittleLong (header->num_funcs);
|
|
qfo->num_lines = LittleLong (header->num_lines);
|
|
qfo->num_loose_relocs = LittleLong (header->num_loose_relocs);
|
|
|
|
spaces = (qfo_space_t *) &header[1];
|
|
qfo->data = data;
|
|
qfo->spaces = calloc (sizeof (qfo_mspace_t), qfo->num_spaces);
|
|
qfo->relocs = (qfo_reloc_t *) &spaces[qfo->num_spaces];
|
|
qfo->defs = (qfo_def_t *) &qfo->relocs[qfo->num_relocs];
|
|
qfo->funcs = (qfo_func_t *) &qfo->defs[qfo->num_defs];
|
|
qfo->lines = (pr_lineno_t *) &qfo->funcs[qfo->num_funcs];
|
|
|
|
for (i = 0; i < qfo->num_spaces; i++) {
|
|
qfo->spaces[i].type = LittleLong (spaces[i].type);
|
|
qfo->spaces[i].num_defs = LittleLong (spaces[i].num_defs);
|
|
if (qfo->spaces[i].num_defs)
|
|
qfo->spaces[i].defs = qfo->defs + LittleLong (spaces[i].defs);
|
|
qfo->spaces[i].data_size = LittleLong (spaces[i].data_size);
|
|
if (spaces[i].data) {
|
|
qfo->spaces[i].d.strings = data + LittleLong (spaces[i].data);
|
|
qfo_byteswap_space (qfo->spaces[i].d.data,
|
|
qfo->spaces[i].data_size, qfo->spaces[i].type);
|
|
}
|
|
qfo->spaces[i].id = LittleLong (spaces[i].id);
|
|
}
|
|
for (i = 0; i < qfo->num_relocs; i++) {
|
|
qfo->relocs[i].space = LittleLong (qfo->relocs[i].space);
|
|
qfo->relocs[i].offset = LittleLong (qfo->relocs[i].offset);
|
|
qfo->relocs[i].type = LittleLong (qfo->relocs[i].type);
|
|
qfo->relocs[i].target = LittleLong (qfo->relocs[i].target);
|
|
}
|
|
for (i = 0; i < qfo->num_defs; i++) {
|
|
qfo->defs[i].type = LittleLong (qfo->defs[i].type);
|
|
qfo->defs[i].name = LittleLong (qfo->defs[i].name);
|
|
qfo->defs[i].offset = LittleLong (qfo->defs[i].offset);
|
|
qfo->defs[i].relocs = LittleLong (qfo->defs[i].relocs);
|
|
qfo->defs[i].num_relocs = LittleLong (qfo->defs[i].num_relocs);
|
|
qfo->defs[i].flags = LittleLong (qfo->defs[i].flags);
|
|
qfo->defs[i].file = LittleLong (qfo->defs[i].file);
|
|
qfo->defs[i].line = LittleLong (qfo->defs[i].line);
|
|
}
|
|
for (i = 0; i < qfo->num_funcs; i++) {
|
|
qfo->funcs[i].name = LittleLong (qfo->funcs[i].name);
|
|
qfo->funcs[i].type = LittleLong (qfo->funcs[i].type);
|
|
qfo->funcs[i].file = LittleLong (qfo->funcs[i].file);
|
|
qfo->funcs[i].line = LittleLong (qfo->funcs[i].line);
|
|
qfo->funcs[i].code = LittleLong (qfo->funcs[i].code);
|
|
qfo->funcs[i].def = LittleLong (qfo->funcs[i].def);
|
|
qfo->funcs[i].locals_space = LittleLong (qfo->funcs[i].locals_space);
|
|
qfo->funcs[i].line_info = LittleLong (qfo->funcs[i].line_info);
|
|
qfo->funcs[i].relocs = LittleLong (qfo->funcs[i].relocs);
|
|
qfo->funcs[i].num_relocs = LittleLong (qfo->funcs[i].num_relocs);
|
|
}
|
|
for (i = 0; i < qfo->num_lines; i++) {
|
|
qfo->lines[i].fa.addr = LittleLong (qfo->lines[i].fa.addr);
|
|
qfo->lines[i].line = LittleLong (qfo->lines[i].line);
|
|
}
|
|
return qfo;
|
|
}
|
|
|
|
qfo_t *
|
|
qfo_open (const char *filename)
|
|
{
|
|
qfo_t *qfo;
|
|
QFile *file;
|
|
|
|
file = Qopen (filename, "rbz");
|
|
if (!file) {
|
|
perror (filename);
|
|
return 0;
|
|
}
|
|
qfo = qfo_read (file);
|
|
Qclose (file);
|
|
return qfo;
|
|
}
|
|
|
|
qfo_t *
|
|
qfo_new (void)
|
|
{
|
|
return calloc (1, sizeof (qfo_t));
|
|
}
|
|
|
|
void
|
|
qfo_delete (qfo_t *qfo)
|
|
{
|
|
if (qfo->data) {
|
|
free (qfo->data);
|
|
} else {
|
|
int i;
|
|
for (i = 0; i < qfo->num_spaces; i++)
|
|
free (qfo->spaces->d.data);
|
|
free (qfo->relocs);
|
|
free (qfo->defs);
|
|
free (qfo->funcs);
|
|
free (qfo->lines);
|
|
}
|
|
free (qfo->spaces);
|
|
free (qfo);
|
|
}
|
|
|
|
static etype_t
|
|
get_def_type (qfo_t *qfo, pointer_t type)
|
|
{
|
|
qfot_type_t *type_def;
|
|
if (type >= qfo->spaces[qfo_type_space].data_size)
|
|
return ev_void;
|
|
type_def = QFO_POINTER (qfo, qfo_type_space, qfot_type_t, type);
|
|
switch ((ty_meta_e)type_def->ty) {
|
|
case ty_none:
|
|
// field, pointer and function types store their basic type in
|
|
// the same location.
|
|
return type_def->t.type;
|
|
case ty_struct:
|
|
case ty_union:
|
|
return ev_invalid;
|
|
case ty_enum:
|
|
if (options.code.progsversion == PROG_ID_VERSION)
|
|
return ev_float;
|
|
return ev_integer;
|
|
case ty_array:
|
|
case ty_class:
|
|
return ev_invalid;
|
|
}
|
|
return ev_invalid;
|
|
}
|
|
|
|
static etype_t
|
|
get_type_size (qfo_t *qfo, pointer_t type)
|
|
{
|
|
qfot_type_t *type_def;
|
|
int i, size;
|
|
if (type >= qfo->spaces[qfo_type_space].data_size)
|
|
return 1;
|
|
type_def = QFO_POINTER (qfo, qfo_type_space, qfot_type_t, type);
|
|
switch ((ty_meta_e)type_def->ty) {
|
|
case ty_none:
|
|
// field, pointer and function types store their basic type in
|
|
// the same location.
|
|
return pr_type_size[type_def->t.type];
|
|
case ty_struct:
|
|
for (i = size = 0; i < type_def->t.strct.num_fields; i++)
|
|
size += get_type_size (qfo, type_def->t.strct.fields[i].type);
|
|
return size;
|
|
case ty_union:
|
|
for (i = size = 0; i < type_def->t.strct.num_fields; i++) {
|
|
int s;
|
|
s = get_type_size (qfo, type_def->t.strct.fields[i].type);
|
|
if (s > size)
|
|
size = s;
|
|
}
|
|
return size;
|
|
case ty_enum:
|
|
return pr_type_size[ev_integer];
|
|
case ty_array:
|
|
return type_def->t.array.size
|
|
* get_type_size (qfo, type_def->t.array.type);
|
|
case ty_class:
|
|
return 0; // FIXME
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
function_params (qfo_t *qfo, qfo_func_t *func, dfunction_t *df)
|
|
{
|
|
qfot_type_t *type;
|
|
int num_params;
|
|
int i;
|
|
|
|
if (func->type >= qfo->spaces[qfo_type_space].data_size)
|
|
return;
|
|
type = QFO_POINTER (qfo, qfo_type_space, qfot_type_t, func->type);
|
|
if (type->ty != ty_none && type->t.type != ev_func)
|
|
return;
|
|
df->numparms = num_params = type->t.func.num_params;
|
|
if (num_params < 0)
|
|
num_params = ~num_params;
|
|
for (i = 0; i < num_params; i++)
|
|
df->parm_size[i] = get_type_size (qfo, type->t.func.param_types[i]);
|
|
}
|
|
|
|
static void
|
|
convert_def (qfo_t *qfo, const qfo_def_t *def, ddef_t *ddef)
|
|
{
|
|
ddef->type = get_def_type (qfo, def->type);
|
|
ddef->ofs = def->offset;
|
|
ddef->s_name = def->name;
|
|
if (!(def->flags & QFOD_NOSAVE)
|
|
&& !(def->flags & QFOD_CONSTANT)
|
|
&& (def->flags & QFOD_GLOBAL)
|
|
&& ddef->type != ev_func
|
|
&& ddef->type != ev_field)
|
|
ddef->type |= DEF_SAVEGLOBAL;
|
|
}
|
|
|
|
static void
|
|
qfo_relocate_refs (qfo_t *qfo)
|
|
{
|
|
int i;
|
|
qfo_reloc_t *reloc;
|
|
|
|
for (i = 0, reloc = qfo->relocs; i < qfo->num_relocs; i++, reloc++) {
|
|
// this will be valid only for *_def[_ofs] and *_field[_ofs] relocs
|
|
qfo_def_t *def = qfo->defs + reloc->target;
|
|
|
|
switch (reloc->type) {
|
|
case rel_none:
|
|
break;
|
|
case rel_op_a_def:
|
|
QFO_STATEMENT (qfo, reloc->offset)->a = def->offset;
|
|
break;
|
|
case rel_op_b_def:
|
|
QFO_STATEMENT (qfo, reloc->offset)->b = def->offset;
|
|
break;
|
|
case rel_op_c_def:
|
|
QFO_STATEMENT (qfo, reloc->offset)->c = def->offset;
|
|
break;
|
|
case rel_op_a_op:
|
|
case rel_op_b_op:
|
|
case rel_op_c_op:
|
|
// these should never appear in a qfo file
|
|
break;
|
|
case rel_def_op:
|
|
QFO_INT (qfo, reloc->space, reloc->offset) = reloc->target;
|
|
break;
|
|
case rel_def_def:
|
|
QFO_INT (qfo, reloc->space, reloc->offset) = def->offset;
|
|
break;
|
|
case rel_def_func:
|
|
QFO_INT (qfo, reloc->space, reloc->offset) = reloc->target + 1;
|
|
break;
|
|
case rel_def_string:
|
|
QFO_INT (qfo, reloc->space, reloc->offset) = reloc->target;
|
|
break;
|
|
case rel_def_field:
|
|
QFO_INT (qfo, reloc->space, reloc->offset) = def->offset;
|
|
break;
|
|
case rel_op_a_def_ofs:
|
|
QFO_STATEMENT (qfo, reloc->offset)->a += def->offset;
|
|
break;
|
|
case rel_op_b_def_ofs:
|
|
QFO_STATEMENT (qfo, reloc->offset)->b += def->offset;
|
|
break;
|
|
case rel_op_c_def_ofs:
|
|
QFO_STATEMENT (qfo, reloc->offset)->c += def->offset;
|
|
break;
|
|
case rel_def_def_ofs:
|
|
QFO_INT (qfo, reloc->space, reloc->offset) += def->offset;
|
|
break;
|
|
case rel_def_field_ofs:
|
|
QFO_INT (qfo, reloc->space, reloc->offset) += def->offset;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
dprograms_t *
|
|
qfo_to_progs (qfo_t *qfo, int *size)
|
|
{
|
|
byte *data;
|
|
char *strings;
|
|
dstatement_t *statements;
|
|
dfunction_t *functions;
|
|
ddef_t *globaldefs;
|
|
ddef_t *fielddefs;
|
|
pr_type_t *globals;
|
|
pr_type_t *locals;
|
|
pr_type_t *far_data;
|
|
pr_type_t *type_data;
|
|
dprograms_t *progs;
|
|
qfo_def_t *types_def = 0;
|
|
int i, j;
|
|
unsigned locals_size = 0;
|
|
int locals_start;
|
|
int big_locals = 0;
|
|
int big_func = 0;
|
|
|
|
*size = RUP (sizeof (dprograms_t), 16);
|
|
progs = calloc (1, *size);
|
|
progs->version = options.code.progsversion;
|
|
progs->numstatements = qfo->spaces[qfo_code_space].data_size;
|
|
progs->numglobaldefs = qfo->spaces[qfo_near_data_space].num_defs;
|
|
//FIXME ddef offsets are 16 bits
|
|
progs->numglobaldefs += qfo->spaces[qfo_far_data_space].num_defs;
|
|
progs->numfielddefs = qfo->spaces[qfo_entity_space].num_defs;
|
|
progs->numfunctions = qfo->num_funcs + 1;
|
|
progs->numstrings = qfo->spaces[qfo_strings_space].data_size;
|
|
progs->numglobals = qfo->spaces[qfo_near_data_space].data_size;
|
|
progs->numglobals += qfo->spaces[qfo_far_data_space].data_size;
|
|
progs->numglobals += qfo->spaces[qfo_type_space].data_size;
|
|
locals_start = qfo->spaces[qfo_near_data_space].data_size;
|
|
for (i = qfo_num_spaces; i < qfo->num_spaces; i++) {
|
|
if (options.code.local_merging) {
|
|
if (locals_size < qfo->spaces[i].data_size) {
|
|
locals_size = qfo->spaces[i].data_size;
|
|
big_locals = i;
|
|
}
|
|
} else {
|
|
locals_size += qfo->spaces[i].data_size;
|
|
}
|
|
}
|
|
progs->numglobals += locals_size;
|
|
progs->numglobals = RUP (progs->numglobals, 16 / sizeof (pr_type_t));
|
|
progs->entityfields = qfo->spaces[qfo_entity_space].data_size;
|
|
*size += progs->numstatements * sizeof (dstatement_t);
|
|
*size += progs->numglobaldefs * sizeof (ddef_t);
|
|
*size += progs->numfielddefs * sizeof (ddef_t);
|
|
*size += progs->numfunctions * sizeof (dfunction_t);
|
|
*size += RUP (progs->numstrings * sizeof (char), 16);
|
|
*size += progs->numglobals * sizeof (pr_type_t);
|
|
|
|
progs = realloc (progs, *size);
|
|
data = (byte *) progs;
|
|
memset (progs + 1, 0, *size - sizeof (dprograms_t));
|
|
data += RUP (sizeof (dprograms_t), 16);
|
|
|
|
progs->ofs_strings = data - (byte *) progs;
|
|
strings = (char *) data;
|
|
data += RUP (progs->numstrings * sizeof (char), 16);
|
|
|
|
progs->ofs_statements = data - (byte *) progs;
|
|
statements = (dstatement_t *) data;
|
|
data += progs->numstatements * sizeof (dstatement_t);
|
|
|
|
progs->ofs_functions = data - (byte *) progs;
|
|
functions = (dfunction_t *) data;
|
|
functions++; // skip over null function
|
|
data += progs->numfunctions * sizeof (dfunction_t);
|
|
|
|
progs->ofs_globaldefs = data - (byte *) progs;
|
|
globaldefs = (ddef_t *) data;
|
|
data += progs->numglobaldefs * sizeof (ddef_t);
|
|
|
|
progs->ofs_fielddefs = data - (byte *) progs;
|
|
fielddefs = (ddef_t *) (globaldefs + progs->numglobaldefs);
|
|
data += progs->numfielddefs * sizeof (ddef_t);
|
|
|
|
progs->ofs_globals = data - (byte *) progs;
|
|
globals = (pr_type_t*) data;
|
|
locals = globals + qfo->spaces[qfo_near_data_space].data_size;
|
|
far_data = locals + locals_size;
|
|
type_data = far_data + qfo->spaces[qfo_far_data_space].data_size;
|
|
|
|
memcpy (strings, qfo->spaces[qfo_strings_space].d.strings,
|
|
qfo->spaces[qfo_strings_space].data_size * sizeof (char));
|
|
qfo->spaces[qfo_strings_space].d.strings = strings;
|
|
|
|
memcpy (statements, qfo->spaces[qfo_code_space].d.code,
|
|
qfo->spaces[qfo_code_space].data_size * sizeof (dstatement_t));
|
|
qfo->spaces[qfo_code_space].d.code = statements;
|
|
|
|
for (i = 0; i < qfo->num_funcs; i++) {
|
|
dfunction_t *df = functions + i;
|
|
qfo_func_t *qf = qfo->funcs + i;
|
|
qfo_mspace_t *space = qfo->spaces + qf->locals_space;
|
|
|
|
df->first_statement = qf->code;
|
|
df->parm_start = locals_start;
|
|
df->locals = space->data_size;
|
|
// finalize the offsets of the local defs
|
|
for (j = 0; j < space->num_defs; j++)
|
|
space->defs[j].offset += locals_start;
|
|
if (!options.code.local_merging)
|
|
locals_start += df->locals;
|
|
df->profile = 0;
|
|
df->s_name = qf->name;
|
|
df->s_file = qf->file;
|
|
function_params (qfo, qf, df);
|
|
}
|
|
|
|
for (i = 0; i < qfo->spaces[qfo_near_data_space].num_defs; i++) {
|
|
qfo_def_t *def = qfo->spaces[qfo_near_data_space].defs + i;
|
|
if (!strcmp (QFO_GETSTR (qfo, def->name), ".type_encodings"))
|
|
types_def = def;
|
|
convert_def (qfo, def, globaldefs++);
|
|
}
|
|
|
|
//FIXME ddef offsets are 16 bits
|
|
for (i = 0; i < qfo->spaces[qfo_far_data_space].num_defs; i++) {
|
|
qfo_def_t *def = qfo->spaces[qfo_far_data_space].defs + i;
|
|
def->offset += far_data - globals;
|
|
convert_def (qfo, def, globaldefs++);
|
|
}
|
|
|
|
for (i = 0; i < qfo->spaces[qfo_type_space].num_defs; i++) {
|
|
qfo->spaces[qfo_type_space].defs[i].offset += type_data - globals;
|
|
}
|
|
|
|
for (i = 0; i < qfo->spaces[qfo_entity_space].num_defs; i++) {
|
|
convert_def (qfo, qfo->spaces[qfo_entity_space].defs + i,
|
|
fielddefs + i);
|
|
}
|
|
|
|
// copy near data
|
|
memcpy (globals, qfo->spaces[qfo_near_data_space].d.data,
|
|
qfo->spaces[qfo_near_data_space].data_size * sizeof (pr_type_t));
|
|
qfo->spaces[qfo_near_data_space].d.data = globals;
|
|
// lcear locals data
|
|
memset (locals, 0, locals_size * sizeof (pr_type_t));
|
|
// copy far data
|
|
memcpy (far_data, qfo->spaces[qfo_far_data_space].d.data,
|
|
qfo->spaces[qfo_far_data_space].data_size * sizeof (pr_type_t));
|
|
qfo->spaces[qfo_far_data_space].d.data = far_data;
|
|
// copy type data
|
|
memcpy (type_data, qfo->spaces[qfo_type_space].d.data,
|
|
qfo->spaces[qfo_type_space].data_size * sizeof (pr_type_t));
|
|
qfo->spaces[qfo_type_space].d.data = type_data;
|
|
|
|
qfo_relocate_refs (qfo);
|
|
if (types_def) {
|
|
qfot_type_encodings_t *encodings;
|
|
encodings = (qfot_type_encodings_t *) &globals[types_def->offset];
|
|
encodings->types = type_data - globals;
|
|
encodings->size = qfo->spaces[qfo_type_space].data_size;
|
|
}
|
|
|
|
// undo the relocation of the offsets of local defs so the local defs have
|
|
// the correct offset in the debug info
|
|
for (i = 0; i < qfo->num_funcs; i++) {
|
|
dfunction_t *df = functions + i;
|
|
qfo_func_t *qf = qfo->funcs + i;
|
|
qfo_mspace_t *space = qfo->spaces + qf->locals_space;
|
|
|
|
if (qf->locals_space == big_locals)
|
|
big_func = i;
|
|
for (j = 0; j < space->num_defs; j++)
|
|
space->defs[j].offset -= df->parm_start;
|
|
}
|
|
|
|
if (options.verbosity >= 0) {
|
|
const char *big_function = "";
|
|
if (big_func)
|
|
big_function = va (" (%s)", strings + qfo->funcs[big_func].name);
|
|
printf ("%6i strofs\n", progs->numstrings);
|
|
printf ("%6i statements\n", progs->numstatements);
|
|
printf ("%6i functions\n", progs->numfunctions);
|
|
printf ("%6i global defs\n", progs->numglobaldefs);
|
|
printf ("%6i fielddefs\n", progs->numfielddefs);
|
|
printf ("%6i globals\n", progs->numglobals);
|
|
printf (" %6i near globals\n",
|
|
qfo->spaces[qfo_near_data_space].data_size + locals_size);
|
|
printf (" %6i locals size%s\n", locals_size, big_function);
|
|
printf (" %6i far globals\n",
|
|
qfo->spaces[qfo_far_data_space].data_size);
|
|
printf (" %6i type globals\n",
|
|
qfo->spaces[qfo_type_space].data_size);
|
|
printf ("%6i entity fields\n", progs->entityfields);
|
|
}
|
|
|
|
if (options.verbosity >= -1)
|
|
printf ("%6i TOTAL SIZE\n", *size);
|
|
|
|
return progs;
|
|
}
|
|
|
|
pr_debug_header_t *
|
|
qfo_to_sym (qfo_t *qfo, int *size)
|
|
{
|
|
pr_debug_header_t *sym;
|
|
int i, j;
|
|
pr_auxfunction_t *auxfuncs;
|
|
pr_auxfunction_t *aux;
|
|
pr_lineno_t *linenos;
|
|
ddef_t *locals, *ld;
|
|
|
|
*size = sizeof (pr_debug_header_t);
|
|
sym = calloc (1, *size);
|
|
|
|
sym->version = PROG_DEBUG_VERSION;
|
|
for (i = 0; i < qfo->num_funcs; i++) {
|
|
qfo_func_t *func = qfo->funcs + i;
|
|
int num_locals = 0;
|
|
|
|
if (func->locals_space)
|
|
num_locals = qfo->spaces[func->locals_space].num_defs;
|
|
if (!func->line_info && !num_locals)
|
|
continue;
|
|
sym->num_auxfunctions++;
|
|
sym->num_locals += num_locals;
|
|
}
|
|
sym->num_linenos = qfo->num_lines;
|
|
|
|
*size += sym->num_auxfunctions * sizeof (pr_auxfunction_t);
|
|
*size += sym->num_linenos * sizeof (pr_lineno_t);
|
|
*size += sym->num_locals * sizeof (ddef_t);
|
|
sym = realloc (sym, *size);
|
|
|
|
auxfuncs = (pr_auxfunction_t *)(sym + 1);
|
|
linenos = (pr_lineno_t *)(auxfuncs + sym->num_auxfunctions);
|
|
locals = (ddef_t *)(linenos + sym->num_linenos);
|
|
|
|
sym->auxfunctions = (char *) auxfuncs - (char *) sym;
|
|
sym->linenos = (char *) linenos - (char *) sym;
|
|
sym->locals = (char *) locals - (char *) sym;
|
|
|
|
ld = locals;
|
|
|
|
for (i = 0, aux = auxfuncs; i < qfo->num_funcs; i++) {
|
|
qfo_func_t *func = qfo->funcs + i;
|
|
qfo_def_t *def = 0;
|
|
int num_locals = 0;
|
|
qfot_type_t *type;
|
|
|
|
if (func->locals_space) {
|
|
num_locals = qfo->spaces[func->locals_space].num_defs;
|
|
def = qfo->spaces[func->locals_space].defs;
|
|
}
|
|
if (!func->line_info && !num_locals)
|
|
continue;
|
|
memset (aux, 0, sizeof (*aux));
|
|
aux->function = i + 1;
|
|
aux->source_line = func->line;
|
|
aux->line_info = func->line_info;
|
|
if (func->line_info)
|
|
qfo->lines[func->line_info].fa.func = aux - auxfuncs;
|
|
if (num_locals) {
|
|
aux->local_defs = ld - locals;
|
|
for (j = 0; j < num_locals; j++)
|
|
convert_def (qfo, def++, ld++);
|
|
}
|
|
aux->num_locals = num_locals;
|
|
//FIXME check type
|
|
type = QFO_POINTER (qfo, qfo_type_space, qfot_type_t, func->type);
|
|
aux->return_type = type->t.func.return_type;
|
|
aux++;
|
|
}
|
|
memcpy (linenos, qfo->lines, qfo->num_lines * sizeof (pr_lineno_t));
|
|
return sym;
|
|
}
|