/* Read coff objects
Copyright 2001-2005 Craig Franklin
Copyright 2016 Molnár Károly
*/
#include "stdhdr.h"
#include "libgputils.h"
typedef struct lazy_linking {
union {
gp_symbol_t *symbol;
gp_aux_t *aux;
} read;
struct lazy_linking *next;
} lazy_linking_t;
/*------------------------------------------------------------------------------------------------*/
static uint16_t
_check_getl16(const uint8_t *Address, const gp_binary_t *Data)
{
assert(Data);
if (Address < (Data->file + Data->size)) {
return gp_getl16(Address);
}
gp_error("Bad object file format.");
exit(0);
/* just to make the compiler satisfied */
return 0;
}
/*------------------------------------------------------------------------------------------------*/
static uint32_t
_check_getl32(const uint8_t *Address, const gp_binary_t *Data)
{
assert(Data);
if (Address < (Data->file + Data->size)) {
return gp_getl32(Address);
}
gp_error("Bad object file format.");
exit(0);
/* just to make the compiler satisfied */
return 0;
}
/*------------------------------------------------------------------------------------------------*/
/* Return optional header size (0 for no header). */
static unsigned
_read_file_header(gp_object_t *Object, const uint8_t *File, const gp_binary_t *Data)
{
bool isnew = false;
unsigned opt_hdr;
uint16_t version;
/* 'f_magic' -- magic number */
version = _check_getl16(&File[0], Data);
if (version == MICROCHIP_MAGIC_v2) {
isnew = true;
}
else if (version != MICROCHIP_MAGIC_v1) {
gp_error("Invalid magic number in \"%s\".", Object->filename);
}
Object->isnew = isnew;
Object->version = version;
/* 'f_nscns' -- number of sections */
Object->section_list.num_nodes = _check_getl16(&File[2], Data);
/* 'f_timdat' -- time and date stamp */
Object->time = _check_getl32(&File[4], Data);
/* 'f_symptr' -- file ptr to symtab */
Object->symbol_ptr = _check_getl32(&File[8], Data);
/* 'f_nsyms' -- # symtab entries */
Object->num_symbols = _check_getl32(&File[12], Data);
/* 'f_opthdr' -- sizeof(opt hdr) */
opt_hdr = _check_getl16(&File[16], Data);
if (opt_hdr && opt_hdr != (unsigned)(isnew ? OPT_HDR_SIZ_v2 : OPT_HDR_SIZ_v1)) {
gp_error("Incorrect optional header size (%u) in \"%s\".", opt_hdr, Object->filename);
}
Object->symbol_size = (version == MICROCHIP_MAGIC_v1) ? SYMBOL_SIZE_v1 : SYMBOL_SIZE_v2;
/* 'f_flags' -- flags */
Object->flags = _check_getl16(&File[18], Data);
return opt_hdr;
}
/*------------------------------------------------------------------------------------------------*/
static void
_read_opt_header(gp_object_t *Object, const uint8_t *File, const gp_binary_t *Data)
{
uint16_t opt_magic;
uint32_t vstamp;
uint32_t proc_code;
uint32_t rom_width;
uint32_t ram_width;
unsigned data_idx;
/* 'opt_magic' */
opt_magic = _check_getl16(&File[0], Data);
if (opt_magic != ((Object->isnew) ? OPTMAGIC_v2 : OPTMAGIC_v1)) {
gp_error("Invalid optional magic number (%#04x) in \"%s\".", opt_magic, Object->filename);
}
data_idx = 2;
if (Object->isnew) {
/* 'vstamp' -- version stamp of compiler */
vstamp = _check_getl32(&File[data_idx], Data);
data_idx += 4;
}
else {
/* 'vstamp' -- version stamp of compiler */
vstamp = _check_getl16(&File[data_idx], Data);
data_idx += 2;
}
if ((!Object->isnew) && (vstamp != 1)) {
gp_error("Invalid assembler version (%ld) in \"%s\".", vstamp, Object->filename);
}
/* 'proc_type' -- processor type */
proc_code = _check_getl32(&File[data_idx], Data);
data_idx += 4;
/* 'rom_width_bits' -- ROM width bits */
rom_width = _check_getl32(&File[data_idx], Data);
data_idx += 4;
Object->processor = gp_processor_coff_proc(proc_code);
if (Object->processor == NULL) {
/* Fallback to a generic processor of matching rom width. */
switch (rom_width) {
case 8: Object->processor = gp_find_processor("pic18cxx"); break;
case 12: Object->processor = gp_find_processor("pic16c5x"); break;
case 14: Object->processor = gp_find_processor("pic16cxx"); break;
case 16: Object->processor = gp_find_processor("pic17cxx"); break;
}
if (Object->processor == NULL) {
gp_error("Invalid processor type (%#04x) in \"%s\".", proc_code, Object->filename);
}
else {
gp_warning("Unknown processor type (%#04x) in \"%s\" defaulted to %s.",
proc_code, Object->filename, gp_processor_name(Object->processor, 0));
}
}
Object->pclass = gp_processor_class(Object->processor);
if (gp_processor_rom_width(Object->pclass) != rom_width) {
if ((Object->pclass == PROC_CLASS_EEPROM8) && (rom_width == 16)) {
Object->processor = gp_find_processor("eeprom16");
Object->pclass = gp_processor_class(Object->processor);
}
else {
gp_error("Invalid rom width for selected processor (%u) in \"%s\".",
rom_width, Object->filename);
}
}
/* 'ram_width_bits' -- RAM width bits */
ram_width = _check_getl32(&File[data_idx], Data);
if (ram_width != 8) {
gp_error("Invalid ram width (%u) in \"%s\".", ram_width, Object->filename);
}
}
/*------------------------------------------------------------------------------------------------*/
static char *
_read_symbol_name(const uint8_t *File, const char *String_table, const gp_binary_t *Data)
{
char buffer[COFF_SSYMBOL_NAME_MAX + 1];
uint32_t string_offset;
/* 's_zeros' -- first four characters are 0 */
if (_check_getl32(&File[0], Data) == 0) {
/* Long name, read this from the string table. */
string_offset = _check_getl32(&File[4], Data);
/* 's_offset' -- pointer to the string table */
return GP_Strdup(&String_table[string_offset]);
}
/* 'name' -- symbol name if less than 8 characters */
memcpy(buffer, &File[0], COFF_SSYMBOL_NAME_MAX);
/* The name can occupy all 8 chars without a null terminator. */
buffer[COFF_SSYMBOL_NAME_MAX] = '\0';
return GP_Strdup(buffer);
}
/*------------------------------------------------------------------------------------------------*/
static void _read_section_header(gp_object_t *Object, gp_section_t *Section,
const uint8_t *File, const char *String_table, const gp_binary_t *Data) {
Section->name = _read_symbol_name(File, String_table, Data);
Section->symbol = Object->gp_coffgen_find_section_symbol(Section->name);
/* 's_paddr' -- physical address */
Section->address = _check_getl32(&File[8], Data);
/* 's_vaddr' -- virtual address */
Section->virtual_address = _check_getl32(&File[12], Data);
if (Section->address != Section->virtual_address) {
gp_error("Virtual address does not equal physical address in \"%s\".", Object->filename);
}
/* 's_size' -- section size */
Section->size = _check_getl32(&File[16], Data);
/* 's_scnptr' -- file ptr to raw data */
Section->data_ptr = _check_getl32(&File[20], Data);
/* 's_relptr' -- file ptr to relocation */
Section->reloc_ptr = _check_getl32(&File[24], Data);
/* 's_lnnoptr' -- file ptr to line numbers */
Section->lineno_ptr = _check_getl32(&File[28], Data);
/* 's_nreloc' -- # reloc entries */
Section->relocation_list.num_nodes = _check_getl16(&File[32], Data);
/* 's_nlnno' -- # line number entries */
Section->line_number_list.num_nodes = _check_getl16(&File[34], Data);
/* 's_flags' -- section flags */
Section->flags = _check_getl32(&File[36], Data);
Section->data = (Section->data_ptr != 0) ? gp_mem_i_create() : NULL;
if (FlagsIsNotAllClr(Section->flags, STYP_ROM_AREA)) {
Section->address = gp_processor_byte_from_insn_c(Object->pclass, Section->address);
}
if ((FlagsIsAllSet(Section->flags, STYP_TEXT | STYP_ABS)) && (Section->address & 1)) {
gp_error("Absolute code section \"%s\" must start at a word-aligned address.\n", Section->name);
}
Section->shadow_address = Section->address;
}
/*------------------------------------------------------------------------------------------------*/
static void
_read_symbol(gp_object_t *Object, int i, gp_symbol_t *Symbol, const uint8_t *File,
const char *String_table, lazy_linking_t *Lazy_linking, const gp_binary_t *Data)
{
uint32_t type;
uint32_t data_idx;
lazy_linking_t *current_lazy;
Symbol->name = _read_symbol_name(File, String_table, Data);
data_idx = 8;
/* 'value' -- symbol value */
Symbol->value = _check_getl32(&File[data_idx], Data);
data_idx += 4;
/* 'sec_num' -- section number */
Symbol->section_number = _check_getl16(&File[data_idx], Data);
data_idx += 2;
if (Object->isnew) {
/* 'type' */
type = _check_getl32(&File[data_idx], Data);
data_idx += 4;
Symbol->type = type & T_MASK_v2;
Symbol->derived_type = type >> T_SHIFT_v2;
}
else {
/* TODO: Make sure the old format had this alignment. */
/* 'type' */
type = _check_getl16(&File[data_idx], Data);
data_idx += 2;
Symbol->type = type & T_MASK_v1;
Symbol->derived_type = type >> T_SHIFT_v1;
}
/* 'st_class' -- storage pclass */
Symbol->pclass = File[data_idx];
data_idx += 1;
/* 'num_auxsym' -- number of auxiliary symbols */
Symbol->aux_list.num_nodes = File[data_idx];
data_idx += 1;
Lazy_linking[i].read.symbol = Symbol;
/* update those aux entries that pointed to us */
current_lazy = Lazy_linking[i].next;
while (current_lazy) {
assert(current_lazy < &Lazy_linking[i]);
switch (current_lazy->read.aux->type) {
case AUX_FCN_CALLS:
current_lazy->read.aux->_aux_symbol._aux_fcn_calls.callee = Symbol;
break;
default:
assert(!"Lazy symbol binding not implemented.");
}
current_lazy = current_lazy->next;
}
}
/*------------------------------------------------------------------------------------------------*/
static void
_read_aux(gp_object_t *Object, uint32_t Symbol_index, gp_aux_t *Aux, unsigned Aux_type,
const uint8_t *File, const char *String_table, lazy_linking_t *Lazy_linking, const gp_binary_t *Data)
{
uint32_t string_offset;
uint32_t calleendx;
Aux->type = Aux_type;
switch (Aux_type) {
case AUX_DIRECT:
/* 'x_command' */
Aux->_aux_symbol._aux_direct.command = File[0];
/* 'x_offset' -- String table offset for direct string. */
string_offset = _check_getl32(&File[4], Data);
Aux->_aux_symbol._aux_direct.string = GP_Strdup(&String_table[string_offset]);
break;
case AUX_FILE:
/* 'x_offset' -- String table offset for filename. */
string_offset = _check_getl32(&File[0], Data);
Aux->_aux_symbol._aux_file.filename = GP_Strdup(&String_table[string_offset]);
/* 'x_incline' -- Line number at which this file was included, 0->not included. */
Aux->_aux_symbol._aux_file.line_number = _check_getl32(&File[4], Data);
/* 'x_flags' */
Aux->_aux_symbol._aux_file.flags = File[8];
break;
case AUX_IDENT:
/* 'x_tagndx' -- Symbol index of struct/union/enum tagname. */
string_offset = _check_getl32(&File[0], Data);
Aux->_aux_symbol._aux_ident.string = GP_Strdup(&String_table[string_offset]);
break;
case AUX_SECTION:
/* 'x_scnlen' -- Section Length. */
Aux->_aux_symbol._aux_scn.length = _check_getl32(&File[0], Data);
/* 'x_nreloc' -- Number of relocation entries. */
Aux->_aux_symbol._aux_scn.nreloc = _check_getl16(&File[4], Data);
/* 'x_nlinno' -- Number of line numbers. */
Aux->_aux_symbol._aux_scn.nlineno = _check_getl16(&File[6], Data);
break;
case AUX_FCN_CALLS: {
/* 'x_calleendx' -- Symbol table entry of callee - 1. */
calleendx = _check_getl32(&File[0], Data);
/* First symbol index is 0. */
if (calleendx < Symbol_index) {
Aux->_aux_symbol._aux_fcn_calls.callee = Lazy_linking[calleendx].read.symbol;
}
else if (calleendx == UINT32_MAX) {
/* "higher order function", call through a pointer */
Aux->_aux_symbol._aux_fcn_calls.callee = NULL;
}
else {
/* Symbol not read yet, link for lazy binding. */
Lazy_linking[Symbol_index].read.aux = Aux;
Lazy_linking[Symbol_index].next = Lazy_linking[calleendx].next;
Lazy_linking[calleendx].next = &Lazy_linking[Symbol_index];
}
/* 'x_is_interrupt' -- 0: not, 1: low, 2: high */
Aux->_aux_symbol._aux_fcn_calls.is_interrupt = _check_getl32(&File[4], Data);
break;
}
default:
memcpy(&Aux->_aux_symbol.data[0], File, Object->symbol_size);
}
}
/*------------------------------------------------------------------------------------------------*/
static void
_read_reloc(gp_object_t *Object, const gp_section_t *Section, gp_reloc_t *Relocation,
const uint8_t *File, const gp_binary_t *Data)
{
gp_symbol_t *symbol;
uint32_t symbol_index;
/* 'r_vaddr' -- entry relative virtual address */
Relocation->address = _check_getl32(&File[0], Data);
/* 'r_symndx' -- index into symbol table */
symbol_index = _check_getl32(&File[4], Data);
symbol = Object->symbol_ptr_array[symbol_index];
Relocation->symbol = symbol;
/* 'r_offset' -- offset to be added to address of symbol 'r_symndx' */
Relocation->offset = _check_getl16(&File[8], Data);
/* 'r_type' -- relocation type */
Relocation->type = _check_getl16(&File[10], Data);
if (Relocation->address >= Section->size) {
gp_error("Relocation at address 0x%0*X in section \"%s\" of \"%s\" exceeds the section size.",
Object->pclass->addr_digits, Relocation->address, Section->name, Object->filename);
}
}
/*------------------------------------------------------------------------------------------------*/
static void
_read_lineno(gp_object_t *Object, const gp_section_t *Section, unsigned Org_to_byte_shift,
gp_linenum_t *Line_number, const uint8_t *File, const gp_binary_t *Data)
{
uint32_t symbol_index;
uint32_t insn_address;
/* 'l_srcndx' -- symbol table index of associated source file */
symbol_index = _check_getl32(&File[0], Data);
Line_number->symbol = Object->symbol_ptr_array[symbol_index];
/* 'l_lnno' -- line number */
Line_number->line_number = _check_getl16(&File[4], Data);
/* 'l_paddr' -- address of code for this lineno */
insn_address = _check_getl32(&File[6], Data);
Line_number->address = gp_byte_from_insn(Org_to_byte_shift, insn_address);
/* FIXME: function index and flags are unused, so far.
'l_flags' -- bit flags for the line number
Line_number->l_flags = _check_getl16(&File[10], Data);
'l_fcnndx' -- symbol table index of associated function, if there is one
Line_number->l_fcnndx = _check_getl32(&File[12], Data);
*/
}
/*------------------------------------------------------------------------------------------------*/
static void _read_symbol_table(gp_object_t *Object, const uint8_t *File, const gp_binary_t *Data) {
unsigned number = Object->num_symbols;
if (!number) return;
/* create a block of symbols */
Object->gp_coffgen_make_block_symbol();
/* setup pointer to string table */
const char*string_table = (const char *)&File[Object->symbol_ptr + (Object->symbol_size * number)];
/* setup lazy linking of symbol table indices */
lazy_linking_t*lazy_linking = new lazy_linking_t[number];
/* read the symbols */
File = &File[Object->symbol_ptr];
gp_symbol_list_t::iterator symbol = Object->symbol_list.begin();
const char*section_name = NULL;
for (unsigned i = 0; i < number; i++) {
/* read the symbol */
_read_symbol(Object, i, &*symbol, File, string_table, lazy_linking, Data);
if ((symbol->pclass == C_SECTION) || (symbol->pclass == C_FILE)) {
section_name = symbol->name;
}
symbol->section_name = section_name;
symbol->number = i;
unsigned num_auxsym = symbol->aux_list.num_nodes;
File += Object->symbol_size;
if (num_auxsym > 0) {
gp_aux_t*aux = gp_coffgen_make_block_aux(&*symbol, num_auxsym);
unsigned aux_type = gp_coffgen_determine_aux_symbol(&*symbol);
/* read the aux symbols */
unsigned j;
for (j = 0; j < num_auxsym; j++) {
i++;
_read_aux(Object, i, aux, aux_type, File, string_table, lazy_linking, Data);
/* AUX_FUNCTION may be followed by AUX_FCN_CALLS */
if (aux_type == AUX_FUNCTION) {
aux_type = AUX_FCN_CALLS;
}
aux = aux->next;
File += Object->symbol_size;
}
/* advance the through the list */
for (j = 0; j < num_auxsym; j++) {
/* COFF places all symbols including auxiliary, in the symbol table.
However, in memory, gputils attaches auxiliary symbols to their
associated primary symbol. When reading COFF, space is reserved
for the auxiliary symbols but not used. Later the space is freed.
This simplifies assigning the pointer in the relocations. */
symbol++;
}
} /* if (num_auxsym > 0) */
symbol++;
} /* for (i = 0; i < number; i++) */
free(lazy_linking);
}
/*------------------------------------------------------------------------------------------------*/
static void _read_sections(gp_object_t *Object, const uint8_t *File, const gp_binary_t *Data) {
unsigned i;
unsigned j;
const uint8_t *section_ptr;
const uint8_t *data_ptr;
const char *string_table;
gp_section_t *section;
gp_reloc_t *relocation;
gp_linenum_t *linenum;
unsigned num_sections;
unsigned number;
uint32_t byte_addr;
uint32_t header_size;
unsigned org_to_byte_shift;
/* move to the start of the section headers */
section_ptr = File + (Object->isnew ? (FILE_HDR_SIZ_v2 + OPT_HDR_SIZ_v2) :
(FILE_HDR_SIZ_v1 + OPT_HDR_SIZ_v1));
header_size = (Object->isnew) ? SEC_HDR_SIZ_v2 : SEC_HDR_SIZ_v1;
/* setup pointer to string table */
string_table = (const char *)&File[Object->symbol_ptr + (Object->symbol_size * Object->num_symbols)];
num_sections = Object->section_list.num_nodes;
section = gp_coffgen_make_block_section(Object, num_sections);
for (i = 0; i < num_sections; i++) {
_read_section_header(Object, section, section_ptr, string_table, Data);
section->number = i + 1;
section_ptr += header_size;
/* read the data */
number = section->size;
if ((number > 0) && (section->data_ptr > 0)) {
byte_addr = section->address;
data_ptr = &File[section->data_ptr];
for (j = 0; j < number; j++) {
gp_mem_b_put(section->data, byte_addr + j, data_ptr[j], section->name, NULL);
}
}
/* read the relocations */
number = section->relocation_list.num_nodes;
if ((number > 0) && (section->reloc_ptr > 0)) {
data_ptr = &File[section->reloc_ptr];
relocation = gp_coffgen_make_block_reloc(section, number);
for (j = 0; j < number; j++) {
_read_reloc(Object, section, relocation, data_ptr, Data);
data_ptr += RELOC_SIZ;
relocation = relocation->next;
}
}
/* read the line numbers */
number = section->line_number_list.num_nodes;
if ((number > 0) && (section->lineno_ptr > 0)) {
if (FlagsIsNotAllClr(section->flags, STYP_ROM_AREA)) {
org_to_byte_shift = Object->pclass->org_to_byte_shift;
}
else {
org_to_byte_shift = 0;
}
data_ptr = &File[section->lineno_ptr];
linenum = gp_coffgen_make_block_linenum(section, number);
for (j = 0; j < number; j++) {
_read_lineno(Object, section, org_to_byte_shift, linenum, data_ptr, Data);
data_ptr += LINENO_SIZ;
linenum = linenum->next;
}
}
section = section->next;
}
}
/*------------------------------------------------------------------------------------------------*/
/* Add section pointers and omit from chain the auxiliary entries. */
static void _clean_symbol_table(gp_object_t *Object) {
gp_symbol_list_t::iterator curr_symbol, next_symbol, aux_symbol;
unsigned i;
curr_symbol = Object->symbol_list.begin();
while (curr_symbol!=Object->symbol_list.end()) {
if (curr_symbol->section_number > N_UNDEF) {
/* Assign section pointer, section numbers start at 1 (N_SCNUM) not 0 (N_UNDEF). */
curr_symbol->section = Object->section_ptr_array[curr_symbol->section_number - N_SCNUM];
}
else {
curr_symbol->section = NULL;
}
if (curr_symbol->aux_list.num_nodes > 0) {
/* Omit from chain this auxiliary entries. */
next_symbol = curr_symbol;
++next_symbol;
for (i = 0; i < curr_symbol->aux_list.num_nodes; ++i) {
aux_symbol = next_symbol;
++next_symbol;
Object->gp_coffgen_del_symbol(aux_symbol, false);
}
}
++curr_symbol;
}
}
/*------------------------------------------------------------------------------------------------*/
FUNC(gp_coff_t) gp_identify_coff_file(const char *File_name)
{
FILE *file;
char magic[SARMAG + 1];
int n;
if ((file = fopen(File_name, "rb")) == NULL) {
return GP_COFF_SYS_ERR;
}
/* Read the magic number. Archive magic numbers are longest, so read their size. */
n = fread(magic, 1, SARMAG, file);
fclose(file);
if (n == SARMAG) {
if (gp_getu16((const uint8_t *)magic) == MICROCHIP_MAGIC_v1) {
return GP_COFF_OBJECT;
}
if (gp_getu16((const uint8_t *)magic) == MICROCHIP_MAGIC_v2) {
return GP_COFF_OBJECT_V2;
}
if (strncmp(magic, ARMAG, SARMAG) == 0) {
return GP_COFF_ARCHIVE;
}
}
return GP_COFF_UNKNOWN;
}
/*------------------------------------------------------------------------------------------------*/
/* Read a binary file and store it in memory. */
FUNC(gp_binary_t*) gp_read_file(const char *File_name) {
FILE*infile = fopen(File_name, "rb");
if (!infile) {
perror(File_name);
exit(1);
}
gp_binary_t*file = new gp_binary_t;
/* determine the size of the file */
struct stat statbuf;
fstat(fileno(infile), &statbuf);
file->size = statbuf.st_size;
/* read the object file into memory */
file->file = new uint8_t[file->size];
off_t n = (off_t)fread(file->file, 1, (size_t)file->size, infile);
if (n != file->size) {
gp_error("File \"%s\" could't be read in one piece.", File_name);
}
fclose(infile);
return file;
}
/*------------------------------------------------------------------------------------------------*/
/* free a binary file. */
void gp_free_file(gp_binary_t*Data) {
if (!Data) return;
delete[] Data->file;
delete Data;
}
/*------------------------------------------------------------------------------------------------*/
FUNC(gp_object_t*) gp_convert_file(const char *File_name, const gp_binary_t *Data) {
gp_object_t *object;
/* initialize object file */
object = gp_coffgen_new_object(File_name);
/* read the object */
if (_read_file_header(object, Data->file, Data) != 0) {
_read_opt_header(object, Data->file + (object->isnew ? FILE_HDR_SIZ_v2 : FILE_HDR_SIZ_v1), Data);
}
_read_symbol_table(object, Data->file, Data);
_read_sections(object, Data->file, Data);
_clean_symbol_table(object);
if (object->section_ptr_array) {
/* It is no longer necessary later. */
free(object->section_ptr_array);
object->section_ptr_array = NULL;
}
if (object->symbol_ptr_array) {
/* It is no longer necessary later. */
free(object->symbol_ptr_array);
object->symbol_ptr_array = NULL;
}
return object;
}
/*------------------------------------------------------------------------------------------------*/
FUNC(gp_object_t*) gp_read_coff(const char *File_name) {
gp_binary_t *data;
gp_object_t *object;
data = gp_read_file(File_name);
if (data == NULL) {
return NULL;
}
object = gp_convert_file(File_name, data);
gp_free_file(data);
return object;
}
Vorgefundene Kodierung: UTF-8 | 0
|