X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=bfd%2Fsrec.c;h=b556f267379d958874116b6b1fa855e7a9e4856f;hb=86fb1dece37497b267579ed4f062d280cd5760cd;hp=3ac8e59b619ec3e49536c4b0073d392b42a030e7;hpb=16423a4a99210a59fc20ea069a5305ccee9414a5;p=deliverable%2Fbinutils-gdb.git diff --git a/bfd/srec.c b/bfd/srec.c index 3ac8e59b61..b556f26737 100644 --- a/bfd/srec.c +++ b/bfd/srec.c @@ -1,464 +1,1388 @@ -/* Copyright (C) 1990, 1991 Free Software Foundation, Inc. +/* BFD back-end for s-record objects. + Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, + 2000, 2001, 2002 + Free Software Foundation, Inc. + Written by Steve Chamberlain of Cygnus Support . -This file is part of BFD, the Binary File Diddler. +This file is part of BFD, the Binary File Descriptor library. -BFD is free software; you can redistribute it and/or modify +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 1, or (at your option) -any later version. +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. -BFD is distributed in the hope that it will be useful, +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 BFD; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* +SUBSECTION + S-Record handling + +DESCRIPTION + + Ordinary S-Records cannot hold anything but addresses and + data, so that's all that we implement. + + The only interesting thing is that S-Records may come out of + order and there is no header, so an initial scan is required + to discover the minimum and maximum addresses used to create + the vma and size of the only section we create. We + arbitrarily call this section ".text". + + When bfd_get_section_contents is called the file is read + again, and this time the data is placed into a bfd_alloc'd + area. + + Any number of sections may be created for output, we save them + up and output them when it's time to close the bfd. + + An s record looks like: + +EXAMPLE + S
+ +DESCRIPTION + Where + o length + is the number of bytes following upto the checksum. Note that + this is not the number of chars following, since it takes two + chars to represent a byte. + o type + is one of: + 0) header record + 1) two byte address data record + 2) three byte address data record + 3) four byte address data record + 7) four byte address termination record + 8) three byte address termination record + 9) two byte address termination record + + o address + is the start address of the data following, or in the case of + a termination record, the start address of the image + o data + is the data. + o checksum + is the sum of all the raw byte data in the record, from the length + upwards, modulo 256 and subtracted from 255. + +SUBSECTION + Symbol S-Record handling + +DESCRIPTION + Some ICE equipment understands an addition to the standard + S-Record format; symbols and their addresses can be sent + before the data. + + The format of this is: + ($$ + (
)*) + $$ + + so a short symbol table could look like: + +EXAMPLE + $$ flash.x + $$ flash.c + _port6 $0 + _delay $4 + _start $14 + _etext $8036 + _edata $8036 + _end $8036 + $$ + +DESCRIPTION + We allow symbols to be anywhere in the data stream - the module names + are always ignored. + +*/ + +#include "bfd.h" +#include "sysdep.h" +#include "libbfd.h" +#include "libiberty.h" +#include "safe-ctype.h" + +static void srec_get_symbol_info PARAMS ((bfd *, asymbol *, symbol_info *)); +static void srec_print_symbol + PARAMS ((bfd *, PTR, asymbol *, bfd_print_symbol_type)); +static void srec_init PARAMS ((void)); +static boolean srec_mkobject PARAMS ((bfd *)); +static int srec_get_byte PARAMS ((bfd *, boolean *)); +static void srec_bad_byte PARAMS ((bfd *, unsigned int, int, boolean)); +static boolean srec_scan PARAMS ((bfd *)); +static const bfd_target *srec_object_p PARAMS ((bfd *)); +static const bfd_target *symbolsrec_object_p PARAMS ((bfd *)); +static boolean srec_read_section PARAMS ((bfd *, asection *, bfd_byte *)); + +static boolean srec_write_record PARAMS ((bfd *, unsigned int, bfd_vma, + const bfd_byte *, + const bfd_byte *)); +static boolean srec_write_header PARAMS ((bfd *)); +static boolean srec_write_symbols PARAMS ((bfd *)); +static boolean srec_new_symbol PARAMS ((bfd *, const char *, bfd_vma)); +static boolean srec_get_section_contents + PARAMS ((bfd *, asection *, PTR, file_ptr, bfd_size_type)); +static boolean srec_set_arch_mach + PARAMS ((bfd *, enum bfd_architecture, unsigned long)); +static boolean srec_set_section_contents + PARAMS ((bfd *, sec_ptr, PTR, file_ptr, bfd_size_type)); +static boolean internal_srec_write_object_contents PARAMS ((bfd *, int)); +static boolean srec_write_object_contents PARAMS ((bfd *)); +static boolean symbolsrec_write_object_contents PARAMS ((bfd *)); +static int srec_sizeof_headers PARAMS ((bfd *, boolean)); +static long srec_get_symtab_upper_bound PARAMS ((bfd *)); +static long srec_get_symtab PARAMS ((bfd *, asymbol **)); + +/* Macros for converting between hex and binary. */ + +static const char digs[] = "0123456789ABCDEF"; + +#define NIBBLE(x) hex_value(x) +#define HEX(buffer) ((NIBBLE((buffer)[0])<<4) + NIBBLE((buffer)[1])) +#define TOHEX(d, x, ch) \ + d[1] = digs[(x) & 0xf]; \ + d[0] = digs[((x)>>4)&0xf]; \ + ch += ((x) & 0xff); +#define ISHEX(x) hex_p(x) + +/* Initialize by filling in the hex conversion array. */ - bfd backend for srecord objects. - - Srecords cannot hold anything but addresses and data, so that's all - that we impliment. - - The only interesting thing is that srecords may come out of order and - there is no header, so an initial scan is required to discover the - minimum and maximum addresses used to create the vma and size of the - only section we create. We arbitarily call this section ".text". - - When bfd_get_section_contents is called the file is read again, and - this time the data is placed into a malloced area. - - Any number of sections may be created for output, we just output them - in the order provided to bfd_set_section_contents. - - - Steve Chamberlain steve@cygnus.com - - */ - - -/* $Id$ - * $Log$ - * Revision 1.1 1991/03/21 21:11:20 gumby - * Initial revision - * - * Revision 1.1 1991/03/13 00:22:29 chrisb - * Initial revision - * - * Revision 1.3 1991/03/10 19:11:40 rich - * Modified Files: - * bfd.c coff-code.h libbfd.c libbfd.h srec.c sunos.c - * - * Working bugs out of coff support. - * - * Revision 1.2 1991/03/07 02:26:18 sac - * Tidied up xfer table - * - * Revision 1.1 1991/03/05 16:28:12 sac - * Initial revision - * - */ +static void +srec_init () +{ + static boolean inited = false; -#include "libbfd.h" + if (inited == false) + { + inited = true; + hex_init (); + } +} +/* The maximum number of address+data+crc bytes on a line is FF. */ +#define MAXCHUNK 0xff -static char digs[] = "0123456789ABCDEF"; +/* Default size for a CHUNK. */ +#define DEFAULT_CHUNK 16 -/* Macros for converting between hex and binary */ +/* The number of data bytes we actually fit onto a line on output. + This variable can be modified by objcopy's --srec-len parameter. + For a 0x75 byte record you should set --srec-len=0x70. */ +unsigned int Chunk = DEFAULT_CHUNK; -#define NIBBLE(x) ((x >= '0' && x <= '9') ? (x - '0') : (x - 'A' + 10)) -#define HEX(buffer) ((NIBBLE((buffer)->high) <<4) + NIBBLE((buffer)->low)) -#define TOHEX(d,x) \ - ((d)->low = digs[(x) & 0xf], (d)->high = digs[((x)>>4)&0xf], x) +/* The type of srec output (free or forced to S3). + This variable can be modified by objcopy's --srec-forceS3 + parameter. */ +boolean S3Forced = 0; -typedef struct { - char high; - char low; -} byte_as_two_char_type; +/* When writing an S-record file, the S-records can not be output as + they are seen. This structure is used to hold them in memory. */ -/* The maximum number of bytes on a line is FF */ -#define MAXCHUNK 0xff -/* The number of bytes we fit onto a line on output */ -#define CHUNK 16 +struct srec_data_list_struct +{ + struct srec_data_list_struct *next; + bfd_byte *data; + bfd_vma where; + bfd_size_type size; +}; + +typedef struct srec_data_list_struct srec_data_list_type; -/* The shape of an srecord .. */ -typedef struct +/* When scanning the S-record file, a linked list of srec_symbol + structures is built to represent the symbol table (if there is + one). */ + +struct srec_symbol { - char S; - char type; - byte_as_two_char_type size; - union { - struct { - byte_as_two_char_type address[4]; - byte_as_two_char_type data[MAXCHUNK]; - /* If there isn't MAXCHUNK bytes of data then the checksum will - appear earlier */ - byte_as_two_char_type checksum; - char nl; - } type_3; - struct { - byte_as_two_char_type address[4]; - byte_as_two_char_type data[MAXCHUNK]; - byte_as_two_char_type checksum; - char nl; - } type_6; - - struct { - byte_as_two_char_type address[3]; - byte_as_two_char_type data[MAXCHUNK]; - byte_as_two_char_type checksum; - char nl; - } type_2; - - struct { - byte_as_two_char_type address[2]; - byte_as_two_char_type data[MAXCHUNK]; - byte_as_two_char_type checksum; - char nl; - } type_1; - byte_as_two_char_type data[MAXCHUNK]; - } u; -} srec_type; - - -/* - called once per input srecord, used to work out vma and size of data. - */ + struct srec_symbol *next; + const char *name; + bfd_vma val; +}; -static void -size_srec(abfd, section, address, raw, length) -bfd *abfd; -asection *section; -bfd_vma address; -byte_as_two_char_type *raw; -unsigned int length; +/* The S-record tdata information. */ + +typedef struct srec_data_struct + { + srec_data_list_type *head; + srec_data_list_type *tail; + unsigned int type; + struct srec_symbol *symbols; + struct srec_symbol *symtail; + asymbol *csymbols; + } +tdata_type; + +static boolean srec_write_section PARAMS ((bfd *, tdata_type *, + srec_data_list_type *)); +static boolean srec_write_terminator PARAMS ((bfd *, tdata_type *)); + +/* Set up the S-record tdata information. */ + +static boolean +srec_mkobject (abfd) + bfd *abfd; { - if (address < section->vma) - section->vma = address; + srec_init (); + + if (abfd->tdata.srec_data == NULL) + { + bfd_size_type amt = sizeof (tdata_type); + tdata_type *tdata = (tdata_type *) bfd_alloc (abfd, amt); + if (tdata == NULL) + return false; + abfd->tdata.srec_data = tdata; + tdata->type = 1; + tdata->head = NULL; + tdata->tail = NULL; + tdata->symbols = NULL; + tdata->symtail = NULL; + tdata->csymbols = NULL; + } - if (address + length > section->vma + section->size) - section->size = (address+length) - section->vma; + return true; } -/* - called once per input srecord, copies data from input into malloced area - */ +/* Read a byte from an S record file. Set *ERRORPTR if an error + occurred. Return EOF on error or end of file. */ -static void -fillup(abfd, section, address, raw, length) -bfd *abfd; -asection *section; -bfd_vma address; -byte_as_two_char_type *raw; -unsigned int length; +static int +srec_get_byte (abfd, errorptr) + bfd *abfd; + boolean *errorptr; { - unsigned int i; - bfd_byte *dst = (bfd_byte *)(section->used_by_bfd) + address - section->vma; - for (i = 0; i < length; i++) { - *dst = HEX(raw); - dst++; - raw++; - } + bfd_byte c; + + if (bfd_bread (&c, (bfd_size_type) 1, abfd) != 1) + { + if (bfd_get_error () != bfd_error_file_truncated) + *errorptr = true; + return EOF; + } + + return (int) (c & 0xff); } -/* - pass over an srecord file calling one of the above functions on each - record - */ +/* Report a problem in an S record file. FIXME: This probably should + not call fprintf, but we really do need some mechanism for printing + error messages. */ + static void -pass_over(abfd, func, section) -bfd *abfd; -void (*func)(); -asection *section; +srec_bad_byte (abfd, lineno, c, error) + bfd *abfd; + unsigned int lineno; + int c; + boolean error; { - unsigned int bytes_on_line; - boolean eof = false; - bfd_vma address; - /* To the front of the file */ - bfd_seek(abfd, (file_ptr)0, SEEK_SET); - while (eof == false) + if (c == EOF) { - srec_type buffer; - - /* Find first 'S' */ - eof = bfd_read(&buffer.S, 1, 1, abfd) != 1; - while (buffer.S != 'S' && !eof) { - eof = bfd_read(&buffer.S, 1, 1, abfd) != 1; - } - if (eof) break; - - bfd_read(&buffer.type, 1, 3, abfd); - - bytes_on_line = HEX(&buffer.size); - - bfd_read(buffer.u.data, 1 , bytes_on_line * 2, abfd); - - switch (buffer.type) { - case '6': - /* Prologue - ignore */ - break; - case '3': - address = (HEX(buffer.u.type_3.address+0) << 24) - + (HEX(buffer.u.type_3.address+1) << 16) - + (HEX(buffer.u.type_3.address+2) << 8) - + (HEX(buffer.u.type_3.address+3)); - func(abfd,section, address, buffer.u.type_2.data, bytes_on_line -1); - - break; - - case '2': - address = (HEX(buffer.u.type_2.address+0) << 16)+ - (HEX(buffer.u.type_2.address+1) << 8) + - (HEX(buffer.u.type_2.address+2)); - func(abfd,section, address, buffer.u.type_2.data, bytes_on_line -1); - - break; - case '1': - address = - (HEX(buffer.u.type_1.address+0) << 8) - + (HEX(buffer.u.type_1.address+1)); - func(abfd, section, address, buffer.u.type_1.data, bytes_on_line -1); - break; - - } + if (! error) + bfd_set_error (bfd_error_file_truncated); + } + else + { + char buf[10]; + + if (! ISPRINT (c)) + sprintf (buf, "\\%03o", (unsigned int) c); + else + { + buf[0] = c; + buf[1] = '\0'; + } + (*_bfd_error_handler) + (_("%s:%d: Unexpected character `%s' in S-record file\n"), + bfd_archive_filename (abfd), lineno, buf); + bfd_set_error (bfd_error_bad_value); } } +/* Add a new symbol found in an S-record file. */ + +static boolean +srec_new_symbol (abfd, name, val) + bfd *abfd; + const char *name; + bfd_vma val; +{ + struct srec_symbol *n; + bfd_size_type amt = sizeof (struct srec_symbol); -bfd_target * + n = (struct srec_symbol *) bfd_alloc (abfd, amt); + if (n == NULL) + return false; + + n->name = name; + n->val = val; + + if (abfd->tdata.srec_data->symbols == NULL) + abfd->tdata.srec_data->symbols = n; + else + abfd->tdata.srec_data->symtail->next = n; + abfd->tdata.srec_data->symtail = n; + n->next = NULL; + + ++abfd->symcount; + + return true; +} + +/* Read the S record file and turn it into sections. We create a new + section for each contiguous set of bytes. */ + +static boolean +srec_scan (abfd) + bfd *abfd; +{ + int c; + unsigned int lineno = 1; + boolean error = false; + bfd_byte *buf = NULL; + size_t bufsize = 0; + asection *sec = NULL; + char *symbuf = NULL; + + if (bfd_seek (abfd, (file_ptr) 0, SEEK_SET) != 0) + goto error_return; + + while ((c = srec_get_byte (abfd, &error)) != EOF) + { + /* We only build sections from contiguous S-records, so if this + is not an S-record, then stop building a section. */ + if (c != 'S' && c != '\r' && c != '\n') + sec = NULL; + + switch (c) + { + default: + srec_bad_byte (abfd, lineno, c, error); + goto error_return; + + case '\n': + ++lineno; + break; + + case '\r': + break; + + case '$': + /* Starting a module name, which we ignore. */ + while ((c = srec_get_byte (abfd, &error)) != '\n' + && c != EOF) + ; + if (c == EOF) + { + srec_bad_byte (abfd, lineno, c, error); + goto error_return; + } + + ++lineno; + + break; + + case ' ': + do + { + bfd_size_type alc; + char *p, *symname; + bfd_vma symval; + + /* Starting a symbol definition. */ + while ((c = srec_get_byte (abfd, &error)) != EOF + && (c == ' ' || c == '\t')) + ; + + if (c == '\n' || c == '\r') + break; + + if (c == EOF) + { + srec_bad_byte (abfd, lineno, c, error); + goto error_return; + } + + alc = 10; + symbuf = (char *) bfd_malloc (alc + 1); + if (symbuf == NULL) + goto error_return; + + p = symbuf; + + *p++ = c; + while ((c = srec_get_byte (abfd, &error)) != EOF + && ! ISSPACE (c)) + { + if ((bfd_size_type) (p - symbuf) >= alc) + { + char *n; + + alc *= 2; + n = (char *) bfd_realloc (symbuf, alc + 1); + if (n == NULL) + goto error_return; + p = n + (p - symbuf); + symbuf = n; + } + + *p++ = c; + } + + if (c == EOF) + { + srec_bad_byte (abfd, lineno, c, error); + goto error_return; + } + + *p++ = '\0'; + symname = bfd_alloc (abfd, (bfd_size_type) (p - symbuf)); + if (symname == NULL) + goto error_return; + strcpy (symname, symbuf); + free (symbuf); + symbuf = NULL; + + while ((c = srec_get_byte (abfd, &error)) != EOF + && (c == ' ' || c == '\t')) + ; + if (c == EOF) + { + srec_bad_byte (abfd, lineno, c, error); + goto error_return; + } + + /* Skip a dollar sign before the hex value. */ + if (c == '$') + { + c = srec_get_byte (abfd, &error); + if (c == EOF) + { + srec_bad_byte (abfd, lineno, c, error); + goto error_return; + } + } + + symval = 0; + while (ISHEX (c)) + { + symval <<= 4; + symval += NIBBLE (c); + c = srec_get_byte (abfd, &error); + } + + if (! srec_new_symbol (abfd, symname, symval)) + goto error_return; + } + while (c == ' ' || c == '\t') + ; + + if (c == '\n') + ++lineno; + else if (c != '\r') + { + srec_bad_byte (abfd, lineno, c, error); + goto error_return; + } + + break; + + case 'S': + { + file_ptr pos; + char hdr[3]; + unsigned int bytes; + bfd_vma address; + bfd_byte *data; + + /* Starting an S-record. */ + + pos = bfd_tell (abfd) - 1; + + if (bfd_bread (hdr, (bfd_size_type) 3, abfd) != 3) + goto error_return; + + if (! ISHEX (hdr[1]) || ! ISHEX (hdr[2])) + { + if (! ISHEX (hdr[1])) + c = hdr[1]; + else + c = hdr[2]; + srec_bad_byte (abfd, lineno, c, error); + goto error_return; + } + + bytes = HEX (hdr + 1); + if (bytes * 2 > bufsize) + { + if (buf != NULL) + free (buf); + buf = (bfd_byte *) bfd_malloc ((bfd_size_type) bytes * 2); + if (buf == NULL) + goto error_return; + bufsize = bytes * 2; + } + + if (bfd_bread (buf, (bfd_size_type) bytes * 2, abfd) != bytes * 2) + goto error_return; + + /* Ignore the checksum byte. */ + --bytes; + + address = 0; + data = buf; + switch (hdr[0]) + { + case '0': + case '5': + /* Prologue--ignore the file name, but stop building a + section at this point. */ + sec = NULL; + break; + + case '3': + address = HEX (data); + data += 2; + --bytes; + /* Fall through. */ + case '2': + address = (address << 8) | HEX (data); + data += 2; + --bytes; + /* Fall through. */ + case '1': + address = (address << 8) | HEX (data); + data += 2; + address = (address << 8) | HEX (data); + data += 2; + bytes -= 2; + + if (sec != NULL + && sec->vma + sec->_raw_size == address) + { + /* This data goes at the end of the section we are + currently building. */ + sec->_raw_size += bytes; + } + else + { + char secbuf[20]; + char *secname; + bfd_size_type amt; + + sprintf (secbuf, ".sec%d", bfd_count_sections (abfd) + 1); + amt = strlen (secbuf) + 1; + secname = (char *) bfd_alloc (abfd, amt); + strcpy (secname, secbuf); + sec = bfd_make_section (abfd, secname); + if (sec == NULL) + goto error_return; + sec->flags = SEC_HAS_CONTENTS | SEC_LOAD | SEC_ALLOC; + sec->vma = address; + sec->lma = address; + sec->_raw_size = bytes; + sec->filepos = pos; + } + + break; + + case '7': + address = HEX (data); + data += 2; + /* Fall through. */ + case '8': + address = (address << 8) | HEX (data); + data += 2; + /* Fall through. */ + case '9': + address = (address << 8) | HEX (data); + data += 2; + address = (address << 8) | HEX (data); + data += 2; + + /* This is a termination record. */ + abfd->start_address = address; + + if (buf != NULL) + free (buf); + + return true; + } + } + break; + } + } + + if (error) + goto error_return; + + if (buf != NULL) + free (buf); + + return true; + + error_return: + if (symbuf != NULL) + free (symbuf); + if (buf != NULL) + free (buf); + return false; +} + +/* Check whether an existing file is an S-record file. */ + +static const bfd_target * srec_object_p (abfd) -bfd *abfd; + bfd *abfd; { - char b; - asection *section; - bfd_seek(abfd, (file_ptr)0, SEEK_SET); - bfd_read(&b, 1,1,abfd); - if (b != 'S') return (bfd_target*)NULL; + bfd_byte b[4]; - /* - We create one section called data for all the contents, - and allocate enough room for the entire file - */ + srec_init (); + + if (bfd_seek (abfd, (file_ptr) 0, SEEK_SET) != 0 + || bfd_bread (b, (bfd_size_type) 4, abfd) != 4) + return NULL; + + if (b[0] != 'S' || !ISHEX (b[1]) || !ISHEX (b[2]) || !ISHEX (b[3])) + { + bfd_set_error (bfd_error_wrong_format); + return NULL; + } + if (! srec_mkobject (abfd) + || ! srec_scan (abfd)) + return NULL; - section = bfd_make_section(abfd, ".text"); - section->size = 0; - section->vma = 0xffffffff; - pass_over(abfd, size_srec, section); + if (abfd->symcount > 0) + abfd->flags |= HAS_SYMS; return abfd->xvec; } +/* Check whether an existing file is an S-record file with symbols. */ +static const bfd_target * +symbolsrec_object_p (abfd) + bfd *abfd; +{ + char b[2]; + + srec_init (); + if (bfd_seek (abfd, (file_ptr) 0, SEEK_SET) != 0 + || bfd_bread (b, (bfd_size_type) 2, abfd) != 2) + return NULL; + if (b[0] != '$' || b[1] != '$') + { + bfd_set_error (bfd_error_wrong_format); + return NULL; + } + if (! srec_mkobject (abfd) + || ! srec_scan (abfd)) + return NULL; + if (abfd->symcount > 0) + abfd->flags |= HAS_SYMS; + + return abfd->xvec; +} +/* Read in the contents of a section in an S-record file. */ static boolean -srec_get_section_contents (abfd, section, location, offset, count) -bfd *abfd; -sec_ptr section; -void *location; -file_ptr offset; -unsigned int count; +srec_read_section (abfd, section, contents) + bfd *abfd; + asection *section; + bfd_byte *contents; { - if (section->used_by_bfd == (bfd_byte *)NULL) { - section->used_by_bfd = (bfd_byte *)malloc(section->size); - pass_over(abfd, fillup, section); - } - (void) memcpy(location, (bfd_byte *)(section->used_by_bfd) + offset, count); + int c; + bfd_size_type sofar = 0; + boolean error = false; + bfd_byte *buf = NULL; + size_t bufsize = 0; + + if (bfd_seek (abfd, section->filepos, SEEK_SET) != 0) + goto error_return; + + while ((c = srec_get_byte (abfd, &error)) != EOF) + { + bfd_byte hdr[3]; + unsigned int bytes; + bfd_vma address; + bfd_byte *data; + + if (c == '\r' || c == '\n') + continue; + + /* This is called after srec_scan has already been called, so we + ought to know the exact format. */ + BFD_ASSERT (c == 'S'); + + if (bfd_bread (hdr, (bfd_size_type) 3, abfd) != 3) + goto error_return; + + BFD_ASSERT (ISHEX (hdr[1]) && ISHEX (hdr[2])); + + bytes = HEX (hdr + 1); + + if (bytes * 2 > bufsize) + { + if (buf != NULL) + free (buf); + buf = (bfd_byte *) bfd_malloc ((bfd_size_type) bytes * 2); + if (buf == NULL) + goto error_return; + bufsize = bytes * 2; + } + + if (bfd_bread (buf, (bfd_size_type) bytes * 2, abfd) != bytes * 2) + goto error_return; + + address = 0; + data = buf; + switch (hdr[0]) + { + default: + BFD_ASSERT (sofar == section->_raw_size); + if (buf != NULL) + free (buf); + return true; + + case '3': + address = HEX (data); + data += 2; + --bytes; + /* Fall through. */ + case '2': + address = (address << 8) | HEX (data); + data += 2; + --bytes; + /* Fall through. */ + case '1': + address = (address << 8) | HEX (data); + data += 2; + address = (address << 8) | HEX (data); + data += 2; + bytes -= 2; + + if (address != section->vma + sofar) + { + /* We've come to the end of this section. */ + BFD_ASSERT (sofar == section->_raw_size); + if (buf != NULL) + free (buf); + return true; + } + + /* Don't consider checksum. */ + --bytes; + + while (bytes-- != 0) + { + contents[sofar] = HEX (data); + data += 2; + ++sofar; + } + + break; + } + } + + if (error) + goto error_return; + + BFD_ASSERT (sofar == section->_raw_size); + + if (buf != NULL) + free (buf); + return true; + + error_return: + if (buf != NULL) + free (buf); + return false; } - +/* Get the contents of a section in an S-record file. */ -boolean -srec_set_arch_mach (abfd, arch, machine) -bfd *abfd; -enum bfd_architecture arch; -unsigned long machine; +static boolean +srec_get_section_contents (abfd, section, location, offset, count) + bfd *abfd; + asection *section; + PTR location; + file_ptr offset; + bfd_size_type count; { - abfd->obj_arch = arch; - abfd->obj_machine = machine; + if (section->used_by_bfd == NULL) + { + section->used_by_bfd = bfd_alloc (abfd, section->_raw_size); + if (section->used_by_bfd == NULL && section->_raw_size != 0) + return false; + + if (! srec_read_section (abfd, section, section->used_by_bfd)) + return false; + } + + memcpy (location, (bfd_byte *) section->used_by_bfd + offset, + (size_t) count); + return true; } +/* Set the architecture. We accept an unknown architecture here. */ + +static boolean +srec_set_arch_mach (abfd, arch, mach) + bfd *abfd; + enum bfd_architecture arch; + unsigned long mach; +{ + if (arch == bfd_arch_unknown) + { + abfd->arch_info = &bfd_default_arch_struct; + return true; + } + return bfd_default_set_arch_mach (abfd, arch, mach); +} +/* We have to save up all the Srecords for a splurge before output. */ -boolean +static boolean srec_set_section_contents (abfd, section, location, offset, bytes_to_do) -bfd *abfd; -sec_ptr section; -unsigned char *location; -file_ptr offset; -int bytes_to_do; + bfd *abfd; + sec_ptr section; + PTR location; + file_ptr offset; + bfd_size_type bytes_to_do; { - bfd_vma address; - int bytes_written; - - int type; - unsigned int i; - srec_type buffer; - bytes_written = 0; - if (section->size <= 0xffff) - type = 1; - else if (section->size <= 0xffffff) - type = 2; - else - type = 3; + tdata_type *tdata = abfd->tdata.srec_data; + register srec_data_list_type *entry; - buffer.S = 'S'; - buffer.type = '0' + type; + entry = ((srec_data_list_type *) + bfd_alloc (abfd, (bfd_size_type) sizeof (srec_data_list_type))); + if (entry == NULL) + return false; - while (bytes_written < bytes_to_do) { - unsigned int size; - unsigned int check_sum; - byte_as_two_char_type *data; - int bytes_this_chunk = bytes_to_do - bytes_written; + if (bytes_to_do + && (section->flags & SEC_ALLOC) + && (section->flags & SEC_LOAD)) + { + bfd_byte *data; - if (bytes_this_chunk > CHUNK) { - bytes_this_chunk = CHUNK; + data = (bfd_byte *) bfd_alloc (abfd, bytes_to_do); + if (data == NULL) + return false; + memcpy ((PTR) data, location, (size_t) bytes_to_do); + + /* Ff S3Forced is true then always select S3 records, + regardless of the siez of the addresses. */ + if (S3Forced) + tdata->type = 3; + else if ((section->lma + offset + bytes_to_do - 1) <= 0xffff) + ; /* The default, S1, is OK. */ + else if ((section->lma + offset + bytes_to_do - 1) <= 0xffffff + && tdata->type <= 2) + tdata->type = 2; + else + tdata->type = 3; + + entry->data = data; + entry->where = section->lma + offset; + entry->size = bytes_to_do; + + /* Sort the records by address. Optimize for the common case of + adding a record to the end of the list. */ + if (tdata->tail != NULL + && entry->where >= tdata->tail->where) + { + tdata->tail->next = entry; + entry->next = NULL; + tdata->tail = entry; + } + else + { + register srec_data_list_type **look; + + for (look = &tdata->head; + *look != NULL && (*look)->where < entry->where; + look = &(*look)->next) + ; + entry->next = *look; + *look = entry; + if (entry->next == NULL) + tdata->tail = entry; + } } + return true; +} - address = section->vma + offset + bytes_written; +/* Write a record of type, of the supplied number of bytes. The + supplied bytes and length don't have a checksum. That's worked out + here. */ - switch (type) { - case 3: - check_sum = TOHEX(buffer.u.type_3.address, address >> 24); - check_sum += TOHEX(buffer.u.type_3.address+1, address >> 16); - check_sum += TOHEX(buffer.u.type_3.address+2, address >> 8); - check_sum += TOHEX(buffer.u.type_3.address+3, address >> 0); - size = bytes_this_chunk + 5; - data = buffer.u.type_3.data; +static boolean +srec_write_record (abfd, type, address, data, end) + bfd *abfd; + unsigned int type; + bfd_vma address; + const bfd_byte *data; + const bfd_byte *end; +{ + char buffer[2 * MAXCHUNK + 6]; + unsigned int check_sum = 0; + const bfd_byte *src = data; + char *dst = buffer; + char *length; + bfd_size_type wrlen; + + *dst++ = 'S'; + *dst++ = '0' + type; + + length = dst; + dst += 2; /* Leave room for dst. */ + switch (type) + { + case 3: + case 7: + TOHEX (dst, (address >> 24), check_sum); + dst += 2; + case 8: case 2: - check_sum = TOHEX(buffer.u.type_3.address, address >> 16); - check_sum += TOHEX(buffer.u.type_3.address+1, address >> 8); - check_sum += TOHEX(buffer.u.type_3.address+2, address >> 0); - size = bytes_this_chunk + 4; - data = buffer.u.type_2.data; + TOHEX (dst, (address >> 16), check_sum); + dst += 2; + case 9: + case 1: + case 0: + TOHEX (dst, (address >> 8), check_sum); + dst += 2; + TOHEX (dst, (address), check_sum); + dst += 2; break; - case 1: - check_sum = TOHEX(buffer.u.type_3.address+0, address >> 8); - check_sum += TOHEX(buffer.u.type_3.address+1, address >> 0); - size = bytes_this_chunk + 3; - data = buffer.u.type_1.data; } + for (src = data; src < end; src++) + { + TOHEX (dst, *src, check_sum); + dst += 2; + } + + /* Fill in the length. */ + TOHEX (length, (dst - length) / 2, check_sum); + check_sum &= 0xff; + check_sum = 255 - check_sum; + TOHEX (dst, check_sum, check_sum); + dst += 2; + + *dst++ = '\r'; + *dst++ = '\n'; + wrlen = dst - buffer; + if (bfd_bwrite ((PTR) buffer, wrlen, abfd) != wrlen) + return false; + return true; +} - for (i = 0; i < bytes_this_chunk; i++) { - check_sum += TOHEX(data, (location[i])); - data++; +static boolean +srec_write_header (abfd) + bfd *abfd; +{ + unsigned int len = strlen (abfd->filename); + + /* I'll put an arbitary 40 char limit on header size. */ + if (len > 40) + len = 40; + + return srec_write_record (abfd, 0, (bfd_vma) 0, + abfd->filename, abfd->filename + len); +} + +static boolean +srec_write_section (abfd, tdata, list) + bfd *abfd; + tdata_type *tdata; + srec_data_list_type *list; +{ + unsigned int octets_written = 0; + bfd_byte *location = list->data; + + /* Validate number of data bytes to write. The srec length byte + counts the address, data and crc bytes. S1 (tdata->type == 1) + records have two address bytes, S2 (tdata->type == 2) records + have three, and S3 (tdata->type == 3) records have four. + The total length can't exceed 255, and a zero data length will + spin for a long time. */ + if (Chunk == 0) + Chunk = 1; + else if (Chunk > MAXCHUNK - tdata->type - 2) + Chunk = MAXCHUNK - tdata->type - 2; + + while (octets_written < list->size) + { + bfd_vma address; + unsigned int octets_this_chunk = list->size - octets_written; + + if (octets_this_chunk > Chunk) + octets_this_chunk = Chunk; + + address = list->where + octets_written / bfd_octets_per_byte (abfd); + + if (! srec_write_record (abfd, + tdata->type, + address, + location, + location + octets_this_chunk)) + return false; + + octets_written += octets_this_chunk; + location += octets_this_chunk; } - check_sum += TOHEX(&(buffer.size), size ); - (void) TOHEX(data, ~check_sum); - data++; + return true; +} - * ( (char *)(data)) = '\n'; - bfd_write(&buffer, 1, (char *)data - (char *)&buffer + 1 , abfd); +static boolean +srec_write_terminator (abfd, tdata) + bfd *abfd; + tdata_type *tdata; +{ + return srec_write_record (abfd, 10 - tdata->type, + abfd->start_address, NULL, NULL); +} - bytes_written += bytes_this_chunk; - location += bytes_this_chunk; - } +static boolean +srec_write_symbols (abfd) + bfd *abfd; +{ + /* Dump out the symbols of a bfd. */ + int i; + int count = bfd_get_symcount (abfd); + if (count) + { + bfd_size_type len; + asymbol **table = bfd_get_outsymbols (abfd); + len = strlen (abfd->filename); + if (bfd_bwrite ("$$ ", (bfd_size_type) 3, abfd) != 3 + || bfd_bwrite (abfd->filename, len, abfd) != len + || bfd_bwrite ("\r\n", (bfd_size_type) 2, abfd) != 2) + return false; + + for (i = 0; i < count; i++) + { + asymbol *s = table[i]; + if (! bfd_is_local_label (abfd, s) + && (s->flags & BSF_DEBUGGING) == 0) + { + /* Just dump out non debug symbols. */ + char buf[42], *p; + + len = strlen (s->name); + if (bfd_bwrite (" ", (bfd_size_type) 2, abfd) != 2 + || bfd_bwrite (s->name, len, abfd) != len) + return false; + + sprintf_vma (buf + 1, (s->value + + s->section->output_section->lma + + s->section->output_offset)); + p = buf + 1; + while (p[0] == '0' && p[1] != 0) + p++; + len = strlen (p); + p[len] = '\r'; + p[len + 1] = '\n'; + *--p = ' '; + len += 3; + if (bfd_bwrite (p, len, abfd) != len) + return false; + } + } + if (bfd_bwrite ("$$ \r\n", (bfd_size_type) 5, abfd) != 5) + return false; + } return true; } +static boolean +internal_srec_write_object_contents (abfd, symbols) + bfd *abfd; + int symbols; +{ + tdata_type *tdata = abfd->tdata.srec_data; + srec_data_list_type *list; + + if (symbols) + { + if (! srec_write_symbols (abfd)) + return false; + } + + if (! srec_write_header (abfd)) + return false; + + /* Now wander though all the sections provided and output them. */ + list = tdata->head; + + while (list != (srec_data_list_type *) NULL) + { + if (! srec_write_section (abfd, tdata, list)) + return false; + list = list->next; + } + return srec_write_terminator (abfd, tdata); +} + +static boolean +srec_write_object_contents (abfd) + bfd *abfd; +{ + return internal_srec_write_object_contents (abfd, 0); +} + +static boolean +symbolsrec_write_object_contents (abfd) + bfd *abfd; +{ + return internal_srec_write_object_contents (abfd, 1); +} + +static int +srec_sizeof_headers (abfd, exec) + bfd *abfd ATTRIBUTE_UNUSED; + boolean exec ATTRIBUTE_UNUSED; +{ + return 0; +} + +/* Return the amount of memory needed to read the symbol table. */ + +static long +srec_get_symtab_upper_bound (abfd) + bfd *abfd; +{ + return (bfd_get_symcount (abfd) + 1) * sizeof (asymbol *); +} + +/* Return the symbol table. */ -boolean -srec_close_and_cleanup (abfd) -bfd *abfd; +static long +srec_get_symtab (abfd, alocation) + bfd *abfd; + asymbol **alocation; { - asection *s; - if (bfd_read_p (abfd) == false) { - switch (abfd->format) { - case bfd_archive: - if (!_bfd_write_archive_contents (abfd)) { + bfd_size_type symcount = bfd_get_symcount (abfd); + asymbol *csymbols; + unsigned int i; + + csymbols = abfd->tdata.srec_data->csymbols; + if (csymbols == NULL) + { + asymbol *c; + struct srec_symbol *s; + + csymbols = (asymbol *) bfd_alloc (abfd, symcount * sizeof (asymbol)); + if (csymbols == NULL && symcount != 0) return false; - } - break; - case bfd_object: - bfd_write("S9030000FC\n", 1,11,abfd); + abfd->tdata.srec_data->csymbols = csymbols; + + for (s = abfd->tdata.srec_data->symbols, c = csymbols; + s != NULL; + s = s->next, ++c) + { + c->the_bfd = abfd; + c->name = s->name; + c->value = s->val; + c->flags = BSF_GLOBAL; + c->section = bfd_abs_section_ptr; + c->udata.p = NULL; + } + } + + for (i = 0; i < symcount; i++) + *alocation++ = csymbols++; + *alocation = NULL; + + return symcount; +} + +static void +srec_get_symbol_info (ignore_abfd, symbol, ret) + bfd *ignore_abfd ATTRIBUTE_UNUSED; + asymbol *symbol; + symbol_info *ret; +{ + bfd_symbol_info (symbol, ret); +} + +static void +srec_print_symbol (abfd, afile, symbol, how) + bfd *abfd; + PTR afile; + asymbol *symbol; + bfd_print_symbol_type how; +{ + FILE *file = (FILE *) afile; + switch (how) + { + case bfd_print_symbol_name: + fprintf (file, "%s", symbol->name); break; default: - bfd_error = invalid_operation; - return false; - } - } - for (s = abfd->sections; s != (asection *)NULL;s = s->next) { - if (s->used_by_bfd != (void *)NULL) { - free(s->used_by_bfd); + bfd_print_symbol_vandf (abfd, (PTR) file, symbol); + fprintf (file, " %-5s %s", + symbol->section->name, + symbol->name); + } - } - return true; } -/*SUPPRESS 460 */ -bfd_target srec_vec = +#define srec_close_and_cleanup _bfd_generic_close_and_cleanup +#define srec_bfd_free_cached_info _bfd_generic_bfd_free_cached_info +#define srec_new_section_hook _bfd_generic_new_section_hook + +#define srec_bfd_is_local_label_name bfd_generic_is_local_label_name +#define srec_get_lineno _bfd_nosymbols_get_lineno +#define srec_find_nearest_line _bfd_nosymbols_find_nearest_line +#define srec_make_empty_symbol _bfd_generic_make_empty_symbol +#define srec_bfd_make_debug_symbol _bfd_nosymbols_bfd_make_debug_symbol +#define srec_read_minisymbols _bfd_generic_read_minisymbols +#define srec_minisymbol_to_symbol _bfd_generic_minisymbol_to_symbol + +#define srec_get_reloc_upper_bound \ + ((long (*) PARAMS ((bfd *, asection *))) bfd_0l) +#define srec_canonicalize_reloc \ + ((long (*) PARAMS ((bfd *, asection *, arelent **, asymbol **))) bfd_0l) +#define srec_bfd_reloc_type_lookup _bfd_norelocs_bfd_reloc_type_lookup + +#define srec_get_section_contents_in_window \ + _bfd_generic_get_section_contents_in_window + +#define srec_bfd_get_relocated_section_contents \ + bfd_generic_get_relocated_section_contents +#define srec_bfd_relax_section bfd_generic_relax_section +#define srec_bfd_gc_sections bfd_generic_gc_sections +#define srec_bfd_merge_sections bfd_generic_merge_sections +#define srec_bfd_link_hash_table_create _bfd_generic_link_hash_table_create +#define srec_bfd_link_hash_table_free _bfd_generic_link_hash_table_free +#define srec_bfd_link_add_symbols _bfd_generic_link_add_symbols +#define srec_bfd_link_just_syms _bfd_generic_link_just_syms +#define srec_bfd_final_link _bfd_generic_final_link +#define srec_bfd_link_split_section _bfd_generic_link_split_section + +const bfd_target srec_vec = { "srec", /* name */ - bfd_target_srec_flavour_enum, - true, /* target byte order */ - true, /* target headers byte order */ + bfd_target_srec_flavour, + BFD_ENDIAN_UNKNOWN, /* target byte order */ + BFD_ENDIAN_UNKNOWN, /* target headers byte order */ (HAS_RELOC | EXEC_P | /* object flags */ HAS_LINENO | HAS_DEBUG | - HAS_SYMS | HAS_LOCALS | DYNAMIC | WP_TEXT | D_PAGED), - (SEC_CODE|SEC_DATA|SEC_ROM - |SEC_ALLOC | SEC_LOAD | SEC_RELOC), /* section flags */ - 0, /* valid reloc types */ + HAS_SYMS | HAS_LOCALS | WP_TEXT | D_PAGED), + (SEC_CODE | SEC_DATA | SEC_ROM | SEC_HAS_CONTENTS + | SEC_ALLOC | SEC_LOAD | SEC_RELOC), /* section flags */ + 0, /* leading underscore */ ' ', /* ar_pad_char */ 16, /* ar_max_namelen */ - srec_close_and_cleanup, /* _close_and_cleanup */ - srec_set_section_contents, /* bfd_set_section_contents */ - srec_get_section_contents, - bfd_true, /* new_section_hook */ - 0, /* _core_file_failing_command */ - 0, /* _core_file_failing_signal */ - 0, /* _core_file_matches_ex...p */ - - bfd_false, /* bfd_slurp_armap */ - bfd_false, /* bfd_slurp_extended_name_table */ - bfd_void, /* bfd_truncate_arname */ - bfd_0u, /* get_symtab_upper_bound */ - bfd_0u, /* canonicalize_symtab */ - bfd_void, /* bfd_reclaim_symbol_table */ - bfd_0u, /* get_reloc_upper_bound */ - bfd_0u, /* bfd_canonicalize_reloc */ - bfd_void, /* bfd_reclaim_reloc */ - bfd_0, /* bfd_get_symcount_upper_bound */ - (symindex (*)())bfd_0, /* bfd_get_first_symbol */ - (symindex (*)())bfd_0, /* bfd_get_next_symbol */ - bfd_false, /* bfd_classify_symbol */ - bfd_false, /* bfd_symbol_hasclass */ - (char* (*)())bfd_0, /* bfd_symbol_name */ - bfd_0, /* bfd_symbol_value */ - - _do_getblong, _do_putblong, _do_getbshort, _do_putbshort, /* data */ - _do_getblong, _do_putblong, _do_getbshort, _do_putbshort, /* hdrs */ - - {_bfd_dummy_target, - srec_object_p, /* bfd_check_format */ - (struct bfd_target *(*)()) bfd_nullvoidptr, - (struct bfd_target *(*)()) bfd_nullvoidptr, - }, + bfd_getb64, bfd_getb_signed_64, bfd_putb64, + bfd_getb32, bfd_getb_signed_32, bfd_putb32, + bfd_getb16, bfd_getb_signed_16, bfd_putb16, /* data */ + bfd_getb64, bfd_getb_signed_64, bfd_putb64, + bfd_getb32, bfd_getb_signed_32, bfd_putb32, + bfd_getb16, bfd_getb_signed_16, bfd_putb16, /* hdrs */ + + { + _bfd_dummy_target, + srec_object_p, /* bfd_check_format */ + _bfd_dummy_target, + _bfd_dummy_target, + }, + { + bfd_false, + srec_mkobject, + _bfd_generic_mkarchive, + bfd_false, + }, + { /* bfd_write_contents */ + bfd_false, + srec_write_object_contents, + _bfd_write_archive_contents, + bfd_false, + }, + + BFD_JUMP_TABLE_GENERIC (srec), + BFD_JUMP_TABLE_COPY (_bfd_generic), + BFD_JUMP_TABLE_CORE (_bfd_nocore), + BFD_JUMP_TABLE_ARCHIVE (_bfd_noarchive), + BFD_JUMP_TABLE_SYMBOLS (srec), + BFD_JUMP_TABLE_RELOCS (srec), + BFD_JUMP_TABLE_WRITE (srec), + BFD_JUMP_TABLE_LINK (srec), + BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic), + + NULL, + + (PTR) 0 +}; + +const bfd_target symbolsrec_vec = +{ + "symbolsrec", /* name */ + bfd_target_srec_flavour, + BFD_ENDIAN_UNKNOWN, /* target byte order */ + BFD_ENDIAN_UNKNOWN, /* target headers byte order */ + (HAS_RELOC | EXEC_P | /* object flags */ + HAS_LINENO | HAS_DEBUG | + HAS_SYMS | HAS_LOCALS | WP_TEXT | D_PAGED), + (SEC_CODE | SEC_DATA | SEC_ROM | SEC_HAS_CONTENTS + | SEC_ALLOC | SEC_LOAD | SEC_RELOC), /* section flags */ + 0, /* leading underscore */ + ' ', /* ar_pad_char */ + 16, /* ar_max_namelen */ + bfd_getb64, bfd_getb_signed_64, bfd_putb64, + bfd_getb32, bfd_getb_signed_32, bfd_putb32, + bfd_getb16, bfd_getb_signed_16, bfd_putb16, /* data */ + bfd_getb64, bfd_getb_signed_64, bfd_putb64, + bfd_getb32, bfd_getb_signed_32, bfd_putb32, + bfd_getb16, bfd_getb_signed_16, bfd_putb16, /* hdrs */ + + { + _bfd_dummy_target, + symbolsrec_object_p, /* bfd_check_format */ + _bfd_dummy_target, + _bfd_dummy_target, + }, { bfd_false, - bfd_true, /* mkobject */ + srec_mkobject, _bfd_generic_mkarchive, bfd_false, }, - (asymbol * (*)()) bfd_nullvoidptr, /* bfd_make_empty_symbol */ - bfd_void, /* bfd_prit_symbol */ - (alent *(*)())bfd_nullvoidptr, /* srec_get_lineno,*/ - srec_set_arch_mach, /* bfd_set_arch_mach,*/ - bfd_false, /* write_armap*/ - (bfd *(*)())bfd_nullvoidptr, /* openr_next_archived_file */ - bfd_false, /* bfd_find_nearest_line */ + { /* bfd_write_contents */ + bfd_false, + symbolsrec_write_object_contents, + _bfd_write_archive_contents, + bfd_false, + }, + + BFD_JUMP_TABLE_GENERIC (srec), + BFD_JUMP_TABLE_COPY (_bfd_generic), + BFD_JUMP_TABLE_CORE (_bfd_nocore), + BFD_JUMP_TABLE_ARCHIVE (_bfd_noarchive), + BFD_JUMP_TABLE_SYMBOLS (srec), + BFD_JUMP_TABLE_RELOCS (srec), + BFD_JUMP_TABLE_WRITE (srec), + BFD_JUMP_TABLE_LINK (srec), + BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic), + + NULL, + + (PTR) 0 };