/* Write coff objects
Copyright (C) 2003, 2004, 2005
Craig Franklin
This file is part of gputils.
gputils 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, or (at your option)
any later version.
gputils 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 gputils; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#include "stdhdr.h"
#include "libgputils.h"
/* String table offsets are 16 bits so this coff has a limit on the
maximum string table size. */
#define MAX_STRING_TABLE 0xffff
/* write the symbol or section name into the string table */
static int
_gp_coffgen_addstring(char *name, char *table)
{
int nbytes;
int offset;
size_t sizeof_name = strlen(name) + 1;
assert(name != NULL);
/* read the number of bytes in the string table */
offset = nbytes = gp_getl32(&table[0]);
/* check the length against the max string table size */
nbytes += sizeof_name;
assert(!(nbytes > MAX_STRING_TABLE));
/* copy the string to the table */
memmove(&table[offset], name, sizeof_name);
/* write the new byte count */
gp_putl32(&table[0], nbytes);
return offset;
}
static void
_gp_coffgen_addname(char *name, char *ptr, size_t sizeof_ptr, char *table)
{
int length;
int offset;
if (name == NULL)
return;
length = strlen(name);
if (length < 9) {
/* The string will fit in the structure. */
strncpy(ptr, name, sizeof_ptr);
} else {
offset = _gp_coffgen_addstring(name, table);
/* write zeros and offset */
gp_putl32(&ptr[0], 0);
gp_putl32(&ptr[4], offset);
}
return;
}
/* write the file header */
static void
_gp_coffgen_write_filehdr(gp_object_type *object, FILE *fp)
{
gp_fputl16(MICROCHIP_MAGIC, fp);
gp_fputl16(object->num_sections, fp);
gp_fputl32(object->time, fp);
gp_fputl32(object->symbol_ptr, fp);
gp_fputl32(object->num_symbols, fp);
gp_fputl16(OPT_HDR_SIZ, fp);
gp_fputl16(object->flags, fp);
return;
}
/* write the optional header */
static void
_gp_coffgen_write_opthdr(gp_object_type *object, FILE *fp)
{
unsigned long coff_type = gp_processor_coff_type(object->processor);
/* make sure we catch unfinished processors */
assert(coff_type);
/* write the data to file */
gp_fputl16(OPTMAGIC, fp);
gp_fputl16(1, fp);
gp_fputl32(coff_type, fp);
gp_fputl32(gp_processor_rom_width(object->class), fp);
gp_fputl32(8, fp);
return;
}
/* write the section header */
static void
_gp_coffgen_write_scnhdr(gp_section_type *section, char *table, FILE *fp)
{
char name[8];
_gp_coffgen_addname(section->name, name, sizeof(name), table);
gp_fputvar(&name[0], 8, fp);
gp_fputl32(section->address, fp);
gp_fputl32(section->address, fp);
gp_fputl32(section->size, fp);
gp_fputl32(section->data_ptr, fp);
gp_fputl32(section->reloc_ptr, fp);
gp_fputl32(section->lineno_ptr, fp);
gp_fputl16(section->num_reloc, fp);
gp_fputl16(section->num_lineno, fp);
gp_fputl32(section->flags, fp);
return;
}
/* write the section data */
static void
_gp_coffgen_write_data(enum proc_class class,
gp_section_type *section,
FILE *fp)
{
unsigned int org;
unsigned int last;
unsigned int data;
if ((class == PROC_CLASS_PIC16E) &&
((section->flags & STYP_TEXT) ||
(section->flags & STYP_DATA_ROM)))
org = section->address >> 1;
else
org = section->address;
if ((section->flags & STYP_TEXT) || (section->flags & STYP_DATA_ROM)) {
/* the section is executable, so each word is two bytes */
last = org + (section->size / 2);
} else {
/* the section is data, so each word is one byte */
last = org + section->size;
}
#ifdef GPUTILS_DEBUG
printf("section \"%s\"\nsize= %i\ndata:\n", section->name, section->size);
print_i_memory(section->data, class == PROC_CLASS_PIC16E ? 1 : 0);
#endif
for ( ; org < last; org++) {
data = i_memory_get(section->data, org);
assert(data & MEM_USED_MASK);
if ((section->flags & STYP_TEXT) || (section->flags & STYP_DATA_ROM)) {
gp_fputl16(data & 0xffff, fp);
} else {
fputc((int)(data & 0xff), fp);
}
}
return;
}
/* write the section relocations */
static void
_gp_coffgen_write_reloc(gp_section_type *section, FILE *fp)
{
gp_reloc_type *current = section->relocations;
while (current != NULL) {
gp_fputl32(current->address, fp);
gp_fputl32(current->symbol->number, fp);
gp_fputl16(current->offset, fp);
gp_fputl16(current->type, fp);
current = current->next;
}
return;
}
/* write the section linenumbers */
static void
_gp_coffgen_write_linenum(gp_section_type *section, FILE *fp)
{
gp_linenum_type *current = section->line_numbers;
while (current != NULL) {
gp_fputl32(current->symbol->number, fp);
gp_fputl16(current->line_number, fp);
gp_fputl32(current->address, fp);
gp_fputl16(0, fp);
gp_fputl32(0, fp);
current = current->next;
}
return;
}
/* write the auxiliary symbols */
static void
_gp_coffgen_write_auxsymbols(gp_aux_type *aux, char *table, FILE *fp)
{
unsigned int offset;
while(aux != NULL) {
switch (aux->type) {
case AUX_DIRECT:
/* add the direct string to the string table */
offset = _gp_coffgen_addstring(aux->_aux_symbol._aux_direct.string,
table);
gp_fputl32(aux->_aux_symbol._aux_direct.command, fp);
gp_fputl32(offset, fp);
gp_fputl32(0, fp);
gp_fputl32(0, fp);
gp_fputl16(0, fp);
break;
case AUX_FILE:
/* add the filename to the string table */
offset = _gp_coffgen_addstring(aux->_aux_symbol._aux_file.filename,
table);
gp_fputl32(offset, fp);
gp_fputl32(aux->_aux_symbol._aux_file.line_number, fp);
gp_fputl32(0, fp);
gp_fputl32(0, fp);
gp_fputl16(0, fp);
break;
case AUX_IDENT:
/* add the ident string to the string table */
offset = _gp_coffgen_addstring(aux->_aux_symbol._aux_ident.string,
table);
gp_fputl32(offset, fp);
gp_fputl32(0, fp);
gp_fputl32(0, fp);
gp_fputl32(0, fp);
gp_fputl16(0, fp);
break;
case AUX_SCN:
/* write section auxiliary symbol */
gp_fputl32(aux->_aux_symbol._aux_scn.length, fp);
gp_fputl16(aux->_aux_symbol._aux_scn.nreloc, fp);
gp_fputl16(aux->_aux_symbol._aux_scn.nlineno, fp);
gp_fputl32(0, fp);
gp_fputl32(0, fp);
gp_fputl16(0, fp);
break;
default:
/* copy the data to the file */
gp_fputvar(&aux->_aux_symbol.data[0], 18, fp);
}
aux = aux->next;
}
return;
}
/* write the symbol table */
static void
_gp_coffgen_write_symbols(gp_object_type *object, char *table, FILE *fp)
{
gp_symbol_type *current;
char name[8];
current = object->symbols;
while(current != NULL) {
_gp_coffgen_addname(current->name, name, sizeof(name), table);
gp_fputvar(&name[0], 8, fp);
gp_fputl32(current->value, fp);
if (current->section_number < 1) {
gp_fputl16(current->section_number, fp);
} else {
gp_fputl16(current->section->number, fp);
}
gp_fputl16(current->type, fp);
fputc(current->class, fp);
fputc(current->num_auxsym, fp);
if (current->num_auxsym)
_gp_coffgen_write_auxsymbols(current->aux_list, table, fp);
current = current->next;
}
return;
}
int
_has_data(gp_section_type *section)
{
if (section->size == 0)
return 0;
if (section->flags & STYP_TEXT)
return 1;
if (section->flags & STYP_DATA)
return 1;
if (section->flags & STYP_DATA_ROM)
return 1;
return 0;
}
/* update all the coff pointers */
static int
_gp_updateptr(gp_object_type *object)
{
int loc = 0;
gp_section_type *section = NULL;
gp_symbol_type *symbol = NULL;
int section_number = 1;
int symbol_number = 0;
loc = FILE_HDR_SIZ + OPT_HDR_SIZ + (SEC_HDR_SIZ * object->num_sections);
/* update the data pointers in the section headers */
section = object->sections;
while (section != NULL) {
section->number = section_number;
section_number++;
section->data_ptr = 0;
if (_has_data(section)) {
section->data_ptr = loc;
loc += section->size;
}
section = section->next;
}
/* update the relocation pointers in the section headers */
section = object->sections;
while (section != NULL) {
section->reloc_ptr = 0;
if (section->num_reloc != 0) {
section->reloc_ptr = loc;
loc += (section->num_reloc * RELOC_SIZ);
}
section = section->next;
}
/* update the line number pointers in the section headers */
section = object->sections;
while (section != NULL) {
section->lineno_ptr = 0;
if (section->num_lineno != 0) {
section->lineno_ptr = loc;
loc += (section->num_lineno * LINENO_SIZ);
}
section = section->next;
}
/* update symbol table pointer */
object->symbol_ptr = loc;
/* update the symbol numbers */
symbol = object->symbols;
while (symbol != NULL) {
symbol->number = symbol_number;
symbol_number += 1 + symbol->num_auxsym;
symbol = symbol->next;
}
return 0;
}
/* write the coff file */
int
gp_write_coff(gp_object_type *object, int numerrors)
{
FILE *coff;
gp_section_type *section = NULL;
char table[MAX_STRING_TABLE]; /* string table */
if (numerrors) {
unlink(object->filename);
return 0;
}
coff = fopen(object->filename, "wb");
if (coff == NULL) {
perror(object->filename);
exit(1);
}
/* update file pointers in the coff */
_gp_updateptr(object);
/* initialize the string table byte count */
gp_putl32(&table[0], 4);
/* write the data to the file */
_gp_coffgen_write_filehdr(object, coff);
_gp_coffgen_write_opthdr(object, coff);
section = object->sections;
/* write section headers */
while (section != NULL) {
_gp_coffgen_write_scnhdr(section, &table[0], coff);
section = section->next;
}
/* write section data */
section = object->sections;
while (section != NULL) {
if (_has_data(section)) {
_gp_coffgen_write_data(object->class, section, coff);
}
section = section->next;
}
/* write section relocations */
section = object->sections;
while (section != NULL) {
if (section->num_reloc != 0) {
_gp_coffgen_write_reloc(section, coff);
}
section = section->next;
}
/* write section line numbers */
section = object->sections;
while (section != NULL) {
if (section->num_lineno != 0) {
_gp_coffgen_write_linenum(section, coff);
}
section = section->next;
}
/* write symbols */
if (object->num_symbols != 0) {
_gp_coffgen_write_symbols(object, &table[0], coff);
}
/* write string table */
fwrite(&table[0], 1, gp_getl32(&table[0]), coff);
fclose(coff);
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1