mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-10 23:32:09 +00:00
f4ae24e0e0
The alignment is specified as a power of 2 (ie, actual alignment = 1 << alignment) allowing old object files to be compatible (as their alignment is 0). This is necessary for (in part for #30) as it turned out even global vectors were not aligned correctly. Currently, only data spaces even vaguely respect alignment. This may need to be fixed in the future.
1347 lines
40 KiB
C
1347 lines
40 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 "compat.h"
|
|
|
|
#include "tools/qfcc/include/codespace.h"
|
|
#include "tools/qfcc/include/debug.h"
|
|
#include "tools/qfcc/include/def.h"
|
|
#include "tools/qfcc/include/defspace.h"
|
|
#include "tools/qfcc/include/emit.h"
|
|
#include "tools/qfcc/include/expr.h"
|
|
#include "tools/qfcc/include/function.h"
|
|
#include "tools/qfcc/include/obj_file.h"
|
|
#include "tools/qfcc/include/obj_type.h"
|
|
#include "tools/qfcc/include/options.h"
|
|
#include "tools/qfcc/include/qfcc.h"
|
|
#include "tools/qfcc/include/reloc.h"
|
|
#include "tools/qfcc/include/statements.h"
|
|
#include "tools/qfcc/include/strpool.h"
|
|
#include "tools/qfcc/include/symtab.h"
|
|
#include "tools/qfcc/include/type.h"
|
|
#include "tools/qfcc/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->locals && func->locals->space) {
|
|
qfo->num_spaces++;
|
|
qfo_count_space_stuff (qfo, func->locals->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_space_stuff (qfo, pr->debug_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)
|
|
{
|
|
size_t size = strings->size * sizeof (*strings->strings);
|
|
strings->qfo_space = space - qfo->spaces;
|
|
space->type = qfos_string;
|
|
space->num_defs = 0;
|
|
space->defs = 0;
|
|
space->strings = 0;
|
|
if (strings->strings) {
|
|
space->strings = malloc (size);
|
|
memcpy (space->strings, strings->strings, size);
|
|
}
|
|
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)
|
|
{
|
|
size_t size = code->size * sizeof (*code->code);
|
|
code->qfo_space = space - qfo->spaces;
|
|
space->type = qfos_code;
|
|
space->num_defs = 0;
|
|
space->defs = 0;
|
|
space->code = 0;
|
|
if (code->code) {
|
|
space->code = malloc (size);
|
|
memcpy (space->code, code->code, size);
|
|
}
|
|
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)
|
|
{
|
|
size_t size = data->size * sizeof (*data->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->data = 0;
|
|
if (data->data) {
|
|
space->data = malloc (size);
|
|
memcpy (space->data, data->data, size);
|
|
}
|
|
space->data_size = data->size;
|
|
space->alignment = qfo_log2 (data->alignment);
|
|
}
|
|
|
|
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->data = 0;
|
|
space->data_size = data->size;
|
|
space->id = qfo_entity_space;
|
|
space->alignment = qfo_log2 (data->alignment);
|
|
}
|
|
|
|
static void
|
|
qfo_init_type_space (qfo_t *qfo, qfo_def_t **defs, qfo_reloc_t **relocs,
|
|
qfo_mspace_t *space, defspace_t *data)
|
|
{
|
|
size_t size = data->size * sizeof (*data->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->data = 0;
|
|
if (data->data) {
|
|
space->data = malloc (size);
|
|
memcpy (space->data, data->data, size);
|
|
}
|
|
space->data_size = data->size;
|
|
space->id = qfo_type_space;
|
|
}
|
|
|
|
static void
|
|
qfo_init_debug_space (qfo_t *qfo, qfo_def_t **defs, qfo_reloc_t **relocs,
|
|
qfo_mspace_t *space, defspace_t *data)
|
|
{
|
|
size_t size = data->size * sizeof (*data->data);
|
|
data->qfo_space = space - qfo->spaces;
|
|
space->type = qfos_debug;
|
|
space->defs = *defs;
|
|
space->num_defs = qfo_encode_defs (qfo, data->defs, defs, relocs);
|
|
space->data = 0;
|
|
if (data->data) {
|
|
space->data = malloc (size);
|
|
memcpy (space->data, data->data, size);
|
|
}
|
|
space->data_size = data->size;
|
|
space->id = qfo_debug_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->locals && f->locals->space) {
|
|
space->id = f->locals->space->qfo_space;
|
|
qfo_init_data_space (qfo, defs, relocs, space++, f->locals->space);
|
|
q->locals_space = f->locals->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);
|
|
q->params_start = f->params_start;
|
|
}
|
|
}
|
|
|
|
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_init_debug_space (qfo, &def, &reloc, &qfo->spaces[qfo_debug_space],
|
|
pr->debug_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->code);
|
|
case qfos_data:
|
|
// data size > 0 but data == null -> all data is zero
|
|
if (!space->data)
|
|
return 0;
|
|
return space->data_size * sizeof (*space->data);
|
|
case qfos_string:
|
|
return space->data_size * sizeof (*space->strings);
|
|
case qfos_entity:
|
|
return 0;
|
|
case qfos_type:
|
|
return space->data_size * sizeof (*space->data);
|
|
case qfos_debug:
|
|
return space->data_size * sizeof (*space->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:
|
|
case qfos_debug:
|
|
for (val = (pr_type_t *) space, c = 0; c < size; c++, val++)
|
|
val->value = LittleLong (val->value);
|
|
break;
|
|
}
|
|
}
|
|
|
|
int
|
|
qfo_write (qfo_t *qfo, const char *filename)
|
|
{
|
|
unsigned size;
|
|
int space_offset;
|
|
unsigned 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) {
|
|
perror (va (0, "failed to open %s for writing", filename));
|
|
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);
|
|
header->progs_version = LittleLong (options.code.progsversion);
|
|
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].data) {
|
|
int space_size = qfo_space_size (qfo->spaces + i);
|
|
spaces[i].data = LittleLong (space_data - data);
|
|
memcpy (space_data, qfo->spaces[i].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);
|
|
spaces[i].alignment = LittleLong (qfo->spaces[i].alignment);
|
|
}
|
|
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);
|
|
funcs[i].params_start = LittleLong (qfo->funcs[i].params_start);
|
|
}
|
|
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;
|
|
unsigned 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;
|
|
}
|
|
header->progs_version = LittleLong (header->progs_version);
|
|
if (header->progs_version != PROG_ID_VERSION
|
|
&& header->progs_version != PROG_V6P_VERSION
|
|
&& header->progs_version != PROG_VERSION) {
|
|
fprintf (stderr, "not a compatible qfo file\n");
|
|
free (data);
|
|
return 0;
|
|
}
|
|
// qfprogs leaves progsversion as 0
|
|
if (options.code.progsversion
|
|
&& header->progs_version != options.code.progsversion) {
|
|
fprintf (stderr, "qfo file target VM does not match current target\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);
|
|
qfo->progs_version = header->progs_version; //already swapped
|
|
|
|
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].strings = data + LittleLong (spaces[i].data);
|
|
qfo_byteswap_space (qfo->spaces[i].data,
|
|
qfo->spaces[i].data_size, qfo->spaces[i].type);
|
|
}
|
|
qfo->spaces[i].id = LittleLong (spaces[i].id);
|
|
qfo->spaces[i].alignment = LittleLong (spaces[i].alignment);
|
|
}
|
|
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 {
|
|
unsigned i;
|
|
for (i = 0; i < qfo->num_spaces; i++)
|
|
free (qfo->spaces[i].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, pr_ptr_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->meta) {
|
|
case ty_alias: //XXX
|
|
case ty_basic:
|
|
// field, pointer and function types store their basic type in
|
|
// the same location.
|
|
return type_def->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_int;
|
|
case ty_array:
|
|
case ty_class:
|
|
return ev_invalid;
|
|
}
|
|
return ev_invalid;
|
|
}
|
|
|
|
static __attribute__((pure)) int
|
|
get_type_size (qfo_t *qfo, pr_ptr_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->meta) {
|
|
case ty_alias:
|
|
return get_type_size (qfo, type_def->alias.aux_type);
|
|
case ty_basic:
|
|
// field, pointer and function types store their basic type in
|
|
// the same location.
|
|
return pr_type_size[type_def->type];
|
|
case ty_struct:
|
|
for (i = size = 0; i < type_def->strct.num_fields; i++)
|
|
size += get_type_size (qfo, type_def->strct.fields[i].type);
|
|
return size;
|
|
case ty_union:
|
|
for (i = size = 0; i < type_def->strct.num_fields; i++) {
|
|
int s;
|
|
s = get_type_size (qfo, type_def->strct.fields[i].type);
|
|
if (s > size)
|
|
size = s;
|
|
}
|
|
return size;
|
|
case ty_enum:
|
|
return pr_type_size[ev_int];
|
|
case ty_array:
|
|
return type_def->array.size
|
|
* get_type_size (qfo, type_def->array.type);
|
|
case ty_class:
|
|
return 0; // FIXME
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
qfo_log2 (unsigned x)
|
|
{
|
|
int log2 = 0;
|
|
|
|
while (x > 1) {
|
|
x >>= 1;
|
|
log2++;
|
|
}
|
|
return log2;
|
|
}
|
|
|
|
static __attribute__((pure)) int
|
|
get_type_alignment_log (qfo_t *qfo, pr_ptr_t type)
|
|
{
|
|
qfot_type_t *type_def;
|
|
int i, alignment;
|
|
if (type >= qfo->spaces[qfo_type_space].data_size)
|
|
return 0;
|
|
type_def = QFO_POINTER (qfo, qfo_type_space, qfot_type_t, type);
|
|
switch ((ty_meta_e)type_def->meta) {
|
|
case ty_alias:
|
|
return get_type_alignment_log (qfo, type_def->alias.aux_type);
|
|
case ty_basic:
|
|
// field, pointer and function types store their basic type in
|
|
// the same location.
|
|
return qfo_log2 (ev_types[type_def->type]->alignment);
|
|
case ty_struct:
|
|
case ty_union:
|
|
for (i = alignment = 0; i < type_def->strct.num_fields; i++) {
|
|
qfot_var_t *field = type_def->strct.fields + i;
|
|
int a;
|
|
a = get_type_alignment_log (qfo, field->type);
|
|
if (a > alignment) {
|
|
alignment = a;
|
|
}
|
|
}
|
|
return alignment;
|
|
case ty_enum:
|
|
return qfo_log2 (ev_types[ev_int]->alignment);
|
|
case ty_array:
|
|
return get_type_alignment_log (qfo, type_def->array.type);
|
|
case ty_class:
|
|
return 0; // FIXME
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static __attribute__((pure)) dparmsize_t
|
|
get_paramsize (qfo_t *qfo, pr_ptr_t type)
|
|
{
|
|
dparmsize_t paramsize = {
|
|
get_type_size (qfo, type),
|
|
get_type_alignment_log (qfo, type),
|
|
};
|
|
return paramsize;
|
|
}
|
|
|
|
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->meta == ty_alias) {
|
|
type = QFO_POINTER (qfo, qfo_type_space, qfot_type_t,
|
|
type->alias.aux_type);
|
|
}
|
|
if (type->meta != ty_basic || type->type != ev_func)
|
|
return;
|
|
df->numparams = num_params = type->func.num_params;
|
|
if (num_params < 0)
|
|
num_params = ~num_params;
|
|
for (i = 0; i < num_params; i++) {
|
|
df->param_size[i] = get_paramsize (qfo, type->func.param_types[i]);
|
|
}
|
|
}
|
|
|
|
static void
|
|
qfo_def_to_ddef (qfo_t *qfo, const qfo_def_t *def, ddef_t *ddef)
|
|
{
|
|
ddef->type = get_def_type (qfo, def->type);
|
|
ddef->ofs = def->offset;
|
|
ddef->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_def_to_prdef (qfo_t *qfo, const qfo_def_t *def, pr_def_t *prdef)
|
|
{
|
|
prdef->type = get_def_type (qfo, def->type);
|
|
prdef->size = get_type_size (qfo, def->type);
|
|
prdef->ofs = def->offset;
|
|
prdef->name = def->name;
|
|
prdef->type_encoding = def->type;
|
|
}
|
|
|
|
static void
|
|
qfo_relocate_refs (qfo_t *qfo)
|
|
{
|
|
unsigned 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
static unsigned
|
|
align_globals_size (unsigned size)
|
|
{
|
|
if (options.code.progsversion == PROG_ID_VERSION)
|
|
return size;
|
|
return RUP (size, 16 / sizeof (pr_type_t));
|
|
}
|
|
|
|
static pr_uint_t
|
|
qfo_count_locals (const qfo_t *qfo, pr_uint_t *big_locals)
|
|
{
|
|
if (options.code.progsversion == PROG_VERSION) {
|
|
// Ruamoko progs keep their locals on the stack rather than a
|
|
// "protected" area in the globals
|
|
*big_locals = 0;
|
|
return 0;
|
|
}
|
|
|
|
pr_uint_t locals_size = 0;
|
|
for (pr_uint_t 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 += align_globals_size (qfo->spaces[i].data_size);
|
|
}
|
|
}
|
|
return locals_size;
|
|
}
|
|
|
|
typedef struct globals_info_s {
|
|
pr_uint_t locals_start;
|
|
pr_uint_t locals_size;
|
|
pr_uint_t big_locals;
|
|
pr_uint_t near_data_size;
|
|
pr_uint_t type_encodings_start;
|
|
pr_uint_t xdefs_start;
|
|
pr_uint_t xdefs_size;
|
|
pr_uint_t globals_size;
|
|
} globals_info_t;
|
|
|
|
|
|
static globals_info_t __attribute__((pure))
|
|
qfo_count_globals (qfo_t *qfo, dprograms_t *progs, int word_align)
|
|
{
|
|
globals_info_t info = {};
|
|
info.globals_size = qfo->spaces[qfo_near_data_space].data_size;
|
|
info.globals_size = RUP (info.globals_size, word_align);
|
|
|
|
info.locals_start = info.globals_size;
|
|
info.locals_size = qfo_count_locals (qfo, &info.big_locals);
|
|
info.globals_size += info.locals_size;
|
|
info.near_data_size = info.globals_size;
|
|
|
|
info.globals_size = RUP (info.globals_size, word_align);
|
|
info.globals_size += qfo->spaces[qfo_far_data_space].data_size;
|
|
|
|
info.type_encodings_start = info.globals_size;
|
|
info.globals_size += qfo->spaces[qfo_type_space].data_size;
|
|
info.globals_size = RUP (info.globals_size, type_xdef.alignment);
|
|
|
|
info.xdefs_start = info.globals_size;
|
|
info.xdefs_size = progs->globaldefs.count + progs->fielddefs.count;
|
|
info.xdefs_size *= type_size (&type_xdef);
|
|
info.globals_size += info.xdefs_size;
|
|
|
|
return info;
|
|
}
|
|
|
|
static pr_uint_t
|
|
qfo_next_offset (pr_chunk_t chunk, pr_uint_t size, pr_uint_t align)
|
|
{
|
|
size *= chunk.count;
|
|
return RUP (chunk.offset + size, align);
|
|
}
|
|
|
|
dprograms_t *
|
|
qfo_to_progs (qfo_t *in_qfo, int *size)
|
|
{
|
|
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;
|
|
pr_type_t *xdef_data;
|
|
dprograms_t *progs;
|
|
qfo_def_t *types_def = 0;
|
|
qfo_def_t *xdefs_def = 0;
|
|
unsigned i, j;
|
|
int big_func = 0;
|
|
pr_xdefs_t *xdefs = 0;
|
|
xdef_t *xdef;
|
|
|
|
// id progs were aligned to only 4 bytes, but 8 is much more reasonable
|
|
pr_uint_t byte_align = 8;
|
|
if (options.code.progsversion == PROG_VERSION) {
|
|
byte_align = __alignof__ (pr_lvec4_t);
|
|
} else if (options.code.progsversion == PROG_V6P_VERSION) {
|
|
byte_align = 16;
|
|
}
|
|
pr_uint_t word_align = byte_align / sizeof (pr_int_t);
|
|
|
|
|
|
qfo_t *qfo = alloca (sizeof (qfo_t)
|
|
+ in_qfo->num_spaces * sizeof (qfo_mspace_t));
|
|
*qfo = *in_qfo;
|
|
qfo->spaces = (qfo_mspace_t *) (qfo + 1);
|
|
for (i = 0; i < qfo->num_spaces; i++) {
|
|
qfo->spaces[i] = in_qfo->spaces[i];
|
|
}
|
|
|
|
*size = RUP (sizeof (dprograms_t), 16);
|
|
progs = calloc (1, *size);
|
|
progs->version = options.code.progsversion;
|
|
// crc is set in qfcc.c if enabled
|
|
|
|
// these are in order in which they usually appear in the file rather
|
|
// than the order they appear in the struct, though with the offsets
|
|
// it doesn't matter too much. However, as people expect a certain
|
|
// layout, it does matter enough to preserve the traditional file order.
|
|
progs->strings.offset = *size;
|
|
progs->strings.count = qfo->spaces[qfo_strings_space].data_size;
|
|
|
|
progs->statements.offset = qfo_next_offset (progs->strings,
|
|
sizeof (char),
|
|
byte_align);
|
|
progs->statements.count = qfo->spaces[qfo_code_space].data_size;
|
|
|
|
progs->functions.offset = qfo_next_offset (progs->statements,
|
|
sizeof (dstatement_t),
|
|
byte_align);
|
|
progs->functions.count = qfo->num_funcs + 1;
|
|
|
|
progs->globaldefs.offset = qfo_next_offset (progs->functions,
|
|
sizeof (dfunction_t),
|
|
byte_align);
|
|
progs->globaldefs.count = qfo->spaces[qfo_near_data_space].num_defs;
|
|
//ddef offsets are 16 bits so the ddef ofs will likely be invalid
|
|
//thus it will be forced invalid and the true offset written to the
|
|
//.xdefs array in the progs file
|
|
progs->globaldefs.count += qfo->spaces[qfo_far_data_space].num_defs;
|
|
|
|
progs->fielddefs.offset = qfo_next_offset (progs->globaldefs,
|
|
sizeof (ddef_t),
|
|
byte_align);
|
|
progs->fielddefs.count = qfo->spaces[qfo_entity_space].num_defs;
|
|
|
|
progs->globals.offset = qfo_next_offset (progs->fielddefs,
|
|
sizeof (ddef_t),
|
|
byte_align);
|
|
globals_info_t globals_info = qfo_count_globals (qfo, progs, word_align);
|
|
progs->globals.count = globals_info.globals_size;
|
|
|
|
progs->entityfields = qfo->spaces[qfo_entity_space].data_size;
|
|
// qfo_debug_space does not go in the progs file
|
|
|
|
*size = qfo_next_offset (progs->globals, sizeof (pr_type_t), 1);
|
|
|
|
progs = realloc (progs, *size);
|
|
memset (progs + 1, 0, *size - sizeof (dprograms_t));
|
|
|
|
#define qfo_block(t,b) (t *) ((byte *) progs + progs->b.offset)
|
|
strings = qfo_block (char, strings);
|
|
statements = qfo_block (dstatement_t, statements);
|
|
functions = qfo_block (dfunction_t, functions);
|
|
functions++; // skip over null function
|
|
globaldefs = qfo_block (ddef_t, globaldefs);
|
|
fielddefs = qfo_block (ddef_t, fielddefs);
|
|
globals = qfo_block (pr_type_t, globals);
|
|
locals = globals + globals_info.locals_start;
|
|
far_data = globals + globals_info.near_data_size;
|
|
type_data = globals + globals_info.type_encodings_start;
|
|
xdef_data = globals + globals_info.xdefs_start;
|
|
|
|
memcpy (strings, qfo->spaces[qfo_strings_space].strings,
|
|
qfo->spaces[qfo_strings_space].data_size * sizeof (char));
|
|
qfo->spaces[qfo_strings_space].strings = strings;
|
|
|
|
memcpy (statements, qfo->spaces[qfo_code_space].code,
|
|
qfo->spaces[qfo_code_space].data_size * sizeof (dstatement_t));
|
|
qfo->spaces[qfo_code_space].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->locals = space->data_size;
|
|
if (options.code.progsversion < PROG_VERSION) {
|
|
df->params_start = globals_info.locals_start;
|
|
// finalize the offsets of the local defs
|
|
for (j = 0; j < space->num_defs; j++)
|
|
space->defs[j].offset += globals_info.locals_start;
|
|
if (!options.code.local_merging)
|
|
globals_info.locals_start += align_globals_size (df->locals);
|
|
} else {
|
|
// relative to start of locals for Ruamoko progs
|
|
df->params_start = qf->params_start;
|
|
}
|
|
df->profile = 0;
|
|
df->name = qf->name;
|
|
df->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;
|
|
const char *defname = QFO_GETSTR (qfo, def->name);
|
|
if (!strcmp (defname, ".type_encodings"))
|
|
types_def = def;
|
|
if (!strcmp (defname, ".xdefs"))
|
|
xdefs_def = def;
|
|
qfo_def_to_ddef (qfo, def, globaldefs++);
|
|
}
|
|
|
|
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;
|
|
qfo_def_to_ddef (qfo, def, globaldefs);
|
|
// the correct offset will be written to the far data space
|
|
globaldefs->ofs = -1;
|
|
globaldefs++;
|
|
}
|
|
|
|
for (i = 0; i < qfo->spaces[qfo_type_space].num_defs; i++) {
|
|
qfo->spaces[qfo_type_space].defs[i].offset += globals_info.type_encodings_start;
|
|
}
|
|
|
|
for (i = 0; i < qfo->spaces[qfo_entity_space].num_defs; i++) {
|
|
qfo_def_to_ddef (qfo, qfo->spaces[qfo_entity_space].defs + i,
|
|
fielddefs + i);
|
|
}
|
|
|
|
// copy near data
|
|
memcpy (globals, qfo->spaces[qfo_near_data_space].data,
|
|
qfo->spaces[qfo_near_data_space].data_size * sizeof (pr_type_t));
|
|
qfo->spaces[qfo_near_data_space].data = globals;
|
|
// clear locals data
|
|
memset (locals, 0, globals_info.locals_size * sizeof (pr_type_t));
|
|
// copy far data
|
|
memcpy (far_data, qfo->spaces[qfo_far_data_space].data,
|
|
qfo->spaces[qfo_far_data_space].data_size * sizeof (pr_type_t));
|
|
qfo->spaces[qfo_far_data_space].data = far_data;
|
|
// copy type data
|
|
memcpy (type_data, qfo->spaces[qfo_type_space].data,
|
|
qfo->spaces[qfo_type_space].data_size * sizeof (pr_type_t));
|
|
qfo->spaces[qfo_type_space].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 = globals_info.type_encodings_start;
|
|
encodings->size = qfo->spaces[qfo_type_space].data_size;
|
|
}
|
|
if (xdefs_def) {
|
|
xdefs = (pr_xdefs_t *) &globals[xdefs_def->offset];
|
|
xdef = (xdef_t *) xdef_data;
|
|
xdefs->xdefs = globals_info.xdefs_start;
|
|
xdefs->num_xdefs = progs->globaldefs.count + progs->fielddefs.count;
|
|
for (i = 0; i < qfo->spaces[qfo_near_data_space].num_defs;
|
|
i++, xdef++) {
|
|
qfo_def_t *def = qfo->spaces[qfo_near_data_space].defs + i;
|
|
xdef->type = def->type + globals_info.type_encodings_start;
|
|
xdef->ofs = def->offset;
|
|
}
|
|
for (i = 0; i < qfo->spaces[qfo_far_data_space].num_defs;
|
|
i++, xdef++) {
|
|
qfo_def_t *def = qfo->spaces[qfo_far_data_space].defs + i;
|
|
xdef->type = def->type + globals_info.type_encodings_start;
|
|
xdef->ofs = def->offset;
|
|
}
|
|
for (i = 0; i < qfo->spaces[qfo_entity_space].num_defs;
|
|
i++, xdef++) {
|
|
qfo_def_t *def = qfo->spaces[qfo_entity_space].defs + i;
|
|
xdef->type = def->type + globals_info.type_encodings_start;
|
|
xdef->ofs = def->offset;
|
|
}
|
|
}
|
|
|
|
// 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 == globals_info.big_locals)
|
|
big_func = i;
|
|
for (j = 0; j < space->num_defs; j++)
|
|
space->defs[j].offset -= df->params_start;
|
|
}
|
|
|
|
if (options.verbosity >= 0) {
|
|
const char *big_function = "";
|
|
if (big_func)
|
|
big_function = va (0, " (%s)", strings + qfo->funcs[big_func].name);
|
|
printf ("%6i strofs\n", progs->strings.count);
|
|
printf ("%6i statements\n", progs->statements.count);
|
|
printf ("%6i functions\n", progs->functions.count);
|
|
printf ("%6i global defs\n", progs->globaldefs.count);
|
|
printf ("%6i field defs\n", progs->fielddefs.count);
|
|
printf ("%6i globals\n", progs->globals.count);
|
|
printf (" %6i near globals\n", globals_info.near_data_size);
|
|
printf (" %6i locals size%s\n",
|
|
globals_info.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);
|
|
if (xdefs) {
|
|
printf (" %6i extended defs\n",
|
|
xdefs->num_xdefs * type_size (&type_xdef));
|
|
}
|
|
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;
|
|
unsigned i, j;
|
|
pr_auxfunction_t *auxfuncs;
|
|
pr_auxfunction_t *aux;
|
|
pr_lineno_t *linenos;
|
|
pr_def_t *locals, *ld, *debug_defs;
|
|
pr_type_t *debug_data;
|
|
|
|
*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;
|
|
unsigned 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;
|
|
sym->num_debug_defs = qfo->spaces[qfo_debug_space].num_defs;
|
|
sym->debug_data_size = qfo->spaces[qfo_debug_space].data_size;
|
|
|
|
*size += sym->num_auxfunctions * sizeof (pr_auxfunction_t);
|
|
*size += sym->num_linenos * sizeof (pr_lineno_t);
|
|
*size += sym->num_locals * sizeof (pr_def_t);
|
|
*size += sym->num_debug_defs * sizeof (pr_def_t);
|
|
*size += sym->debug_data_size * sizeof (pr_type_t);
|
|
sym = realloc (sym, *size);
|
|
|
|
auxfuncs = (pr_auxfunction_t *)(sym + 1);
|
|
linenos = (pr_lineno_t *)(auxfuncs + sym->num_auxfunctions);
|
|
locals = (pr_def_t *)(linenos + sym->num_linenos);
|
|
debug_defs = locals + sym->num_locals;
|
|
debug_data = (pr_type_t *)(debug_defs + sym->num_debug_defs);
|
|
|
|
sym->auxfunctions = (char *) auxfuncs - (char *) sym;
|
|
sym->linenos = (char *) linenos - (char *) sym;
|
|
sym->locals = (char *) locals - (char *) sym;
|
|
sym->debug_defs = (char *) debug_defs - (char *) sym;
|
|
sym->debug_data = (char *) debug_data - (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;
|
|
unsigned 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++, def++, ld++) {
|
|
qfo_def_to_prdef (qfo, def, ld);
|
|
}
|
|
}
|
|
aux->num_locals = num_locals;
|
|
//FIXME check type
|
|
type = QFO_POINTER (qfo, qfo_type_space, qfot_type_t, func->type);
|
|
if (type->meta == ty_alias) {
|
|
type = QFO_POINTER (qfo, qfo_type_space, qfot_type_t,
|
|
type->alias.aux_type);
|
|
}
|
|
aux->return_type = type->func.return_type;
|
|
aux++;
|
|
}
|
|
memcpy (linenos, qfo->lines, qfo->num_lines * sizeof (pr_lineno_t));
|
|
for (i = 0; i < sym->num_debug_defs; i++) {
|
|
qfo_def_t *def = &qfo->spaces[qfo_debug_space].defs[i];
|
|
pr_def_t *prdef = &debug_defs[i];
|
|
qfo_def_to_prdef (qfo, def, prdef);
|
|
}
|
|
memcpy (debug_data, qfo->spaces[qfo_debug_space].data,
|
|
sym->debug_data_size * sizeof (*debug_data));
|
|
return sym;
|
|
}
|