| /* BFD back-end for Intel Hex objects. | |
| Copyright (C) 1995-2022 Free Software Foundation, Inc. | |
| Written by Ian Lance Taylor of Cygnus Support <ian@cygnus.com>. | |
| This file is part of BFD, the Binary File Descriptor library. | |
| 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 3 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 the Free Software | |
| Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, | |
| MA 02110-1301, USA. */ | |
| /* This is what Intel Hex files look like: | |
| 1. INTEL FORMATS | |
| A. Intel 1 | |
| 16-bit address-field format, for files 64k bytes in length or less. | |
| DATA RECORD | |
| Byte 1 Header = colon(:) | |
| 2..3 The number of data bytes in hex notation | |
| 4..5 High byte of the record load address | |
| 6..7 Low byte of the record load address | |
| 8..9 Record type, must be "00" | |
| 10..x Data bytes in hex notation: | |
| x = (number of bytes - 1) * 2 + 11 | |
| x+1..x+2 Checksum in hex notation | |
| x+3..x+4 Carriage return, line feed | |
| END RECORD | |
| Byte 1 Header = colon (:) | |
| 2..3 The byte count, must be "00" | |
| 4..7 Transfer-address (usually "0000") | |
| the jump-to address, execution start address | |
| 8..9 Record type, must be "01" | |
| 10..11 Checksum, in hex notation | |
| 12..13 Carriage return, line feed | |
| B. INTEL 2 | |
| MCS-86 format, using a 20-bit address for files larger than 64K bytes. | |
| DATA RECORD | |
| Byte 1 Header = colon (:) | |
| 2..3 The byte count of this record, hex notation | |
| 4..5 High byte of the record load address | |
| 6..7 Low byte of the record load address | |
| 8..9 Record type, must be "00" | |
| 10..x The data bytes in hex notation: | |
| x = (number of data bytes - 1) * 2 + 11 | |
| x+1..x+2 Checksum in hex notation | |
| x+3..x+4 Carriage return, line feed | |
| EXTENDED ADDRESS RECORD | |
| Byte 1 Header = colon(:) | |
| 2..3 The byte count, must be "02" | |
| 4..7 Load address, must be "0000" | |
| 8..9 Record type, must be "02" | |
| 10..11 High byte of the offset address | |
| 12..13 Low byte of the offset address | |
| 14..15 Checksum in hex notation | |
| 16..17 Carriage return, line feed | |
| The checksums are the two's complement of the 8-bit sum | |
| without carry of the byte count, offset address, and the | |
| record type. | |
| START ADDRESS RECORD | |
| Byte 1 Header = colon (:) | |
| 2..3 The byte count, must be "04" | |
| 4..7 Load address, must be "0000" | |
| 8..9 Record type, must be "03" | |
| 10..13 8086 CS value | |
| 14..17 8086 IP value | |
| 18..19 Checksum in hex notation | |
| 20..21 Carriage return, line feed | |
| Another document reports these additional types: | |
| EXTENDED LINEAR ADDRESS RECORD | |
| Byte 1 Header = colon (:) | |
| 2..3 The byte count, must be "02" | |
| 4..7 Load address, must be "0000" | |
| 8..9 Record type, must be "04" | |
| 10..13 Upper 16 bits of address of subsequent records | |
| 14..15 Checksum in hex notation | |
| 16..17 Carriage return, line feed | |
| START LINEAR ADDRESS RECORD | |
| Byte 1 Header = colon (:) | |
| 2..3 The byte count, must be "02" | |
| 4..7 Load address, must be "0000" | |
| 8..9 Record type, must be "05" | |
| 10..13 Upper 16 bits of start address | |
| 14..15 Checksum in hex notation | |
| 16..17 Carriage return, line feed | |
| The MRI compiler uses this, which is a repeat of type 5: | |
| EXTENDED START RECORD | |
| Byte 1 Header = colon (:) | |
| 2..3 The byte count, must be "04" | |
| 4..7 Load address, must be "0000" | |
| 8..9 Record type, must be "05" | |
| 10..13 Upper 16 bits of start address | |
| 14..17 Lower 16 bits of start address | |
| 18..19 Checksum in hex notation | |
| 20..21 Carriage return, line feed. */ | |
| #include "sysdep.h" | |
| #include "bfd.h" | |
| #include "libbfd.h" | |
| #include "libiberty.h" | |
| #include "safe-ctype.h" | |
| /* The number of bytes we put on one line during output. */ | |
| #define CHUNK 16 | |
| /* Macros for converting between hex and binary. */ | |
| #define NIBBLE(x) (hex_value (x)) | |
| #define HEX2(buffer) ((NIBBLE ((buffer)[0]) << 4) + NIBBLE ((buffer)[1])) | |
| #define HEX4(buffer) ((HEX2 (buffer) << 8) + HEX2 ((buffer) + 2)) | |
| #define ISHEX(x) (hex_p (x)) | |
| /* When we write out an ihex value, the values can not be output as | |
| they are seen. Instead, we hold them in memory in this structure. */ | |
| struct ihex_data_list | |
| { | |
| struct ihex_data_list *next; | |
| bfd_byte *data; | |
| bfd_vma where; | |
| bfd_size_type size; | |
| }; | |
| /* The ihex tdata information. */ | |
| struct ihex_data_struct | |
| { | |
| struct ihex_data_list *head; | |
| struct ihex_data_list *tail; | |
| }; | |
| /* Initialize by filling in the hex conversion array. */ | |
| static void | |
| ihex_init (void) | |
| { | |
| static bool inited; | |
| if (! inited) | |
| { | |
| inited = true; | |
| hex_init (); | |
| } | |
| } | |
| /* Create an ihex object. */ | |
| static bool | |
| ihex_mkobject (bfd *abfd) | |
| { | |
| struct ihex_data_struct *tdata; | |
| tdata = (struct ihex_data_struct *) bfd_alloc (abfd, sizeof (* tdata)); | |
| if (tdata == NULL) | |
| return false; | |
| abfd->tdata.ihex_data = tdata; | |
| tdata->head = NULL; | |
| tdata->tail = NULL; | |
| return true; | |
| } | |
| /* Read a byte from a BFD. Set *ERRORPTR if an error occurred. | |
| Return EOF on error or end of file. */ | |
| static inline int | |
| ihex_get_byte (bfd *abfd, bool *errorptr) | |
| { | |
| 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); | |
| } | |
| /* Report a problem in an Intel Hex file. */ | |
| static void | |
| ihex_bad_byte (bfd *abfd, unsigned int lineno, int c, bool error) | |
| { | |
| if (c == EOF) | |
| { | |
| if (! error) | |
| bfd_set_error (bfd_error_file_truncated); | |
| } | |
| else | |
| { | |
| char buf[10]; | |
| if (! ISPRINT (c)) | |
| sprintf (buf, "\\%03o", (unsigned int) c & 0xff); | |
| else | |
| { | |
| buf[0] = c; | |
| buf[1] = '\0'; | |
| } | |
| _bfd_error_handler | |
| /* xgettext:c-format */ | |
| (_("%pB:%d: unexpected character `%s' in Intel Hex file"), | |
| abfd, lineno, buf); | |
| bfd_set_error (bfd_error_bad_value); | |
| } | |
| } | |
| /* Read an Intel hex file and turn it into sections. We create a new | |
| section for each contiguous set of bytes. */ | |
| static bool | |
| ihex_scan (bfd *abfd) | |
| { | |
| bfd_vma segbase; | |
| bfd_vma extbase; | |
| asection *sec; | |
| unsigned int lineno; | |
| bool error; | |
| bfd_byte *buf = NULL; | |
| size_t bufsize; | |
| int c; | |
| if (bfd_seek (abfd, (file_ptr) 0, SEEK_SET) != 0) | |
| goto error_return; | |
| abfd->start_address = 0; | |
| segbase = 0; | |
| extbase = 0; | |
| sec = NULL; | |
| lineno = 1; | |
| error = false; | |
| bufsize = 0; | |
| while ((c = ihex_get_byte (abfd, &error)) != EOF) | |
| { | |
| if (c == '\r') | |
| continue; | |
| else if (c == '\n') | |
| { | |
| ++lineno; | |
| continue; | |
| } | |
| else if (c != ':') | |
| { | |
| ihex_bad_byte (abfd, lineno, c, error); | |
| goto error_return; | |
| } | |
| else | |
| { | |
| file_ptr pos; | |
| unsigned char hdr[8]; | |
| unsigned int i; | |
| unsigned int len; | |
| bfd_vma addr; | |
| unsigned int type; | |
| unsigned int chars; | |
| unsigned int chksum; | |
| /* This is a data record. */ | |
| pos = bfd_tell (abfd) - 1; | |
| /* Read the header bytes. */ | |
| if (bfd_bread (hdr, (bfd_size_type) 8, abfd) != 8) | |
| goto error_return; | |
| for (i = 0; i < 8; i++) | |
| { | |
| if (! ISHEX (hdr[i])) | |
| { | |
| ihex_bad_byte (abfd, lineno, hdr[i], error); | |
| goto error_return; | |
| } | |
| } | |
| len = HEX2 (hdr); | |
| addr = HEX4 (hdr + 2); | |
| type = HEX2 (hdr + 6); | |
| /* Read the data bytes. */ | |
| chars = len * 2 + 2; | |
| if (chars >= bufsize) | |
| { | |
| buf = (bfd_byte *) bfd_realloc (buf, (bfd_size_type) chars); | |
| if (buf == NULL) | |
| goto error_return; | |
| bufsize = chars; | |
| } | |
| if (bfd_bread (buf, (bfd_size_type) chars, abfd) != chars) | |
| goto error_return; | |
| for (i = 0; i < chars; i++) | |
| { | |
| if (! ISHEX (buf[i])) | |
| { | |
| ihex_bad_byte (abfd, lineno, buf[i], error); | |
| goto error_return; | |
| } | |
| } | |
| /* Check the checksum. */ | |
| chksum = len + addr + (addr >> 8) + type; | |
| for (i = 0; i < len; i++) | |
| chksum += HEX2 (buf + 2 * i); | |
| if (((- chksum) & 0xff) != (unsigned int) HEX2 (buf + 2 * i)) | |
| { | |
| _bfd_error_handler | |
| /* xgettext:c-format */ | |
| (_("%pB:%u: bad checksum in Intel Hex file (expected %u, found %u)"), | |
| abfd, lineno, | |
| (- chksum) & 0xff, (unsigned int) HEX2 (buf + 2 * i)); | |
| bfd_set_error (bfd_error_bad_value); | |
| goto error_return; | |
| } | |
| switch (type) | |
| { | |
| case 0: | |
| /* This is a data record. */ | |
| if (sec != NULL | |
| && sec->vma + sec->size == extbase + segbase + addr) | |
| { | |
| /* This data goes at the end of the section we are | |
| currently building. */ | |
| sec->size += len; | |
| } | |
| else if (len > 0) | |
| { | |
| char secbuf[20]; | |
| char *secname; | |
| size_t amt; | |
| flagword flags; | |
| sprintf (secbuf, ".sec%d", bfd_count_sections (abfd) + 1); | |
| amt = strlen (secbuf) + 1; | |
| secname = (char *) bfd_alloc (abfd, amt); | |
| if (secname == NULL) | |
| goto error_return; | |
| strcpy (secname, secbuf); | |
| flags = SEC_HAS_CONTENTS | SEC_LOAD | SEC_ALLOC; | |
| sec = bfd_make_section_with_flags (abfd, secname, flags); | |
| if (sec == NULL) | |
| goto error_return; | |
| sec->vma = extbase + segbase + addr; | |
| sec->lma = extbase + segbase + addr; | |
| sec->size = len; | |
| sec->filepos = pos; | |
| } | |
| break; | |
| case 1: | |
| /* An end record. */ | |
| if (abfd->start_address == 0) | |
| abfd->start_address = addr; | |
| free (buf); | |
| return true; | |
| case 2: | |
| /* An extended address record. */ | |
| if (len != 2) | |
| { | |
| _bfd_error_handler | |
| /* xgettext:c-format */ | |
| (_("%pB:%u: bad extended address record length in Intel Hex file"), | |
| abfd, lineno); | |
| bfd_set_error (bfd_error_bad_value); | |
| goto error_return; | |
| } | |
| segbase = HEX4 (buf) << 4; | |
| sec = NULL; | |
| break; | |
| case 3: | |
| /* An extended start address record. */ | |
| if (len != 4) | |
| { | |
| _bfd_error_handler | |
| /* xgettext:c-format */ | |
| (_("%pB:%u: bad extended start address length in Intel Hex file"), | |
| abfd, lineno); | |
| bfd_set_error (bfd_error_bad_value); | |
| goto error_return; | |
| } | |
| abfd->start_address += (HEX4 (buf) << 4) + HEX4 (buf + 4); | |
| sec = NULL; | |
| break; | |
| case 4: | |
| /* An extended linear address record. */ | |
| if (len != 2) | |
| { | |
| _bfd_error_handler | |
| /* xgettext:c-format */ | |
| (_("%pB:%u: bad extended linear address record length in Intel Hex file"), | |
| abfd, lineno); | |
| bfd_set_error (bfd_error_bad_value); | |
| goto error_return; | |
| } | |
| extbase = HEX4 (buf) << 16; | |
| sec = NULL; | |
| break; | |
| case 5: | |
| /* An extended linear start address record. */ | |
| if (len != 2 && len != 4) | |
| { | |
| _bfd_error_handler | |
| /* xgettext:c-format */ | |
| (_("%pB:%u: bad extended linear start address length in Intel Hex file"), | |
| abfd, lineno); | |
| bfd_set_error (bfd_error_bad_value); | |
| goto error_return; | |
| } | |
| if (len == 2) | |
| abfd->start_address += HEX4 (buf) << 16; | |
| else | |
| abfd->start_address = (HEX4 (buf) << 16) + HEX4 (buf + 4); | |
| sec = NULL; | |
| break; | |
| default: | |
| _bfd_error_handler | |
| /* xgettext:c-format */ | |
| (_("%pB:%u: unrecognized ihex type %u in Intel Hex file"), | |
| abfd, lineno, type); | |
| bfd_set_error (bfd_error_bad_value); | |
| goto error_return; | |
| } | |
| } | |
| } | |
| if (error) | |
| goto error_return; | |
| free (buf); | |
| return true; | |
| error_return: | |
| free (buf); | |
| return false; | |
| } | |
| /* Try to recognize an Intel Hex file. */ | |
| static bfd_cleanup | |
| ihex_object_p (bfd *abfd) | |
| { | |
| void * tdata_save; | |
| bfd_byte b[9]; | |
| unsigned int i; | |
| unsigned int type; | |
| ihex_init (); | |
| if (bfd_seek (abfd, (file_ptr) 0, SEEK_SET) != 0) | |
| return NULL; | |
| if (bfd_bread (b, (bfd_size_type) 9, abfd) != 9) | |
| { | |
| if (bfd_get_error () == bfd_error_file_truncated) | |
| bfd_set_error (bfd_error_wrong_format); | |
| return NULL; | |
| } | |
| if (b[0] != ':') | |
| { | |
| bfd_set_error (bfd_error_wrong_format); | |
| return NULL; | |
| } | |
| for (i = 1; i < 9; i++) | |
| { | |
| if (! ISHEX (b[i])) | |
| { | |
| bfd_set_error (bfd_error_wrong_format); | |
| return NULL; | |
| } | |
| } | |
| type = HEX2 (b + 7); | |
| if (type > 5) | |
| { | |
| bfd_set_error (bfd_error_wrong_format); | |
| return NULL; | |
| } | |
| /* OK, it looks like it really is an Intel Hex file. */ | |
| tdata_save = abfd->tdata.any; | |
| if (! ihex_mkobject (abfd) || ! ihex_scan (abfd)) | |
| { | |
| if (abfd->tdata.any != tdata_save && abfd->tdata.any != NULL) | |
| bfd_release (abfd, abfd->tdata.any); | |
| abfd->tdata.any = tdata_save; | |
| return NULL; | |
| } | |
| return _bfd_no_cleanup; | |
| } | |
| /* Read the contents of a section in an Intel Hex file. */ | |
| static bool | |
| ihex_read_section (bfd *abfd, asection *section, bfd_byte *contents) | |
| { | |
| int c; | |
| bfd_byte *p; | |
| bfd_byte *buf = NULL; | |
| size_t bufsize; | |
| bool error; | |
| if (bfd_seek (abfd, section->filepos, SEEK_SET) != 0) | |
| goto error_return; | |
| p = contents; | |
| bufsize = 0; | |
| error = false; | |
| while ((c = ihex_get_byte (abfd, &error)) != EOF) | |
| { | |
| unsigned char hdr[8]; | |
| unsigned int len; | |
| unsigned int type; | |
| unsigned int i; | |
| if (c == '\r' || c == '\n') | |
| continue; | |
| /* This is called after ihex_scan has succeeded, so we ought to | |
| know the exact format. */ | |
| BFD_ASSERT (c == ':'); | |
| if (bfd_bread (hdr, (bfd_size_type) 8, abfd) != 8) | |
| goto error_return; | |
| len = HEX2 (hdr); | |
| type = HEX2 (hdr + 6); | |
| /* We should only see type 0 records here. */ | |
| if (type != 0) | |
| { | |
| _bfd_error_handler | |
| (_("%pB: internal error in ihex_read_section"), abfd); | |
| bfd_set_error (bfd_error_bad_value); | |
| goto error_return; | |
| } | |
| if (len * 2 > bufsize) | |
| { | |
| buf = (bfd_byte *) bfd_realloc (buf, (bfd_size_type) len * 2); | |
| if (buf == NULL) | |
| goto error_return; | |
| bufsize = len * 2; | |
| } | |
| if (bfd_bread (buf, (bfd_size_type) len * 2, abfd) != len * 2) | |
| goto error_return; | |
| for (i = 0; i < len; i++) | |
| *p++ = HEX2 (buf + 2 * i); | |
| if ((bfd_size_type) (p - contents) >= section->size) | |
| { | |
| /* We've read everything in the section. */ | |
| free (buf); | |
| return true; | |
| } | |
| /* Skip the checksum. */ | |
| if (bfd_bread (buf, (bfd_size_type) 2, abfd) != 2) | |
| goto error_return; | |
| } | |
| if ((bfd_size_type) (p - contents) < section->size) | |
| { | |
| _bfd_error_handler | |
| (_("%pB: bad section length in ihex_read_section"), abfd); | |
| bfd_set_error (bfd_error_bad_value); | |
| goto error_return; | |
| } | |
| free (buf); | |
| return true; | |
| error_return: | |
| free (buf); | |
| return false; | |
| } | |
| /* Get the contents of a section in an Intel Hex file. */ | |
| static bool | |
| ihex_get_section_contents (bfd *abfd, | |
| asection *section, | |
| void * location, | |
| file_ptr offset, | |
| bfd_size_type count) | |
| { | |
| if (section->used_by_bfd == NULL) | |
| { | |
| section->used_by_bfd = bfd_alloc (abfd, section->size); | |
| if (section->used_by_bfd == NULL) | |
| return false; | |
| if (! ihex_read_section (abfd, section, | |
| (bfd_byte *) section->used_by_bfd)) | |
| return false; | |
| } | |
| memcpy (location, (bfd_byte *) section->used_by_bfd + offset, | |
| (size_t) count); | |
| return true; | |
| } | |
| /* Set the contents of a section in an Intel Hex file. */ | |
| static bool | |
| ihex_set_section_contents (bfd *abfd, | |
| asection *section, | |
| const void * location, | |
| file_ptr offset, | |
| bfd_size_type count) | |
| { | |
| struct ihex_data_list *n; | |
| bfd_byte *data; | |
| struct ihex_data_struct *tdata; | |
| if (count == 0 | |
| || (section->flags & SEC_ALLOC) == 0 | |
| || (section->flags & SEC_LOAD) == 0) | |
| return true; | |
| n = (struct ihex_data_list *) bfd_alloc (abfd, sizeof (* n)); | |
| if (n == NULL) | |
| return false; | |
| data = (bfd_byte *) bfd_alloc (abfd, count); | |
| if (data == NULL) | |
| return false; | |
| memcpy (data, location, (size_t) count); | |
| n->data = data; | |
| n->where = section->lma + offset; | |
| n->size = count; | |
| /* Sort the records by address. Optimize for the common case of | |
| adding a record to the end of the list. */ | |
| tdata = abfd->tdata.ihex_data; | |
| if (tdata->tail != NULL | |
| && n->where >= tdata->tail->where) | |
| { | |
| tdata->tail->next = n; | |
| n->next = NULL; | |
| tdata->tail = n; | |
| } | |
| else | |
| { | |
| struct ihex_data_list **pp; | |
| for (pp = &tdata->head; | |
| *pp != NULL && (*pp)->where < n->where; | |
| pp = &(*pp)->next) | |
| ; | |
| n->next = *pp; | |
| *pp = n; | |
| if (n->next == NULL) | |
| tdata->tail = n; | |
| } | |
| return true; | |
| } | |
| /* Write a record out to an Intel Hex file. */ | |
| static bool | |
| ihex_write_record (bfd *abfd, | |
| size_t count, | |
| unsigned int addr, | |
| unsigned int type, | |
| bfd_byte *data) | |
| { | |
| static const char digs[] = "0123456789ABCDEF"; | |
| char buf[9 + CHUNK * 2 + 4]; | |
| char *p; | |
| unsigned int chksum; | |
| unsigned int i; | |
| size_t total; | |
| #define TOHEX(buf, v) \ | |
| ((buf)[0] = digs[((v) >> 4) & 0xf], (buf)[1] = digs[(v) & 0xf]) | |
| buf[0] = ':'; | |
| TOHEX (buf + 1, count); | |
| TOHEX (buf + 3, (addr >> 8) & 0xff); | |
| TOHEX (buf + 5, addr & 0xff); | |
| TOHEX (buf + 7, type); | |
| chksum = count + addr + (addr >> 8) + type; | |
| for (i = 0, p = buf + 9; i < count; i++, p += 2, data++) | |
| { | |
| TOHEX (p, *data); | |
| chksum += *data; | |
| } | |
| TOHEX (p, (- chksum) & 0xff); | |
| p[2] = '\r'; | |
| p[3] = '\n'; | |
| total = 9 + count * 2 + 4; | |
| if (bfd_bwrite (buf, (bfd_size_type) total, abfd) != total) | |
| return false; | |
| return true; | |
| } | |
| /* Write out an Intel Hex file. */ | |
| static bool | |
| ihex_write_object_contents (bfd *abfd) | |
| { | |
| bfd_vma segbase; | |
| bfd_vma extbase; | |
| struct ihex_data_list *l; | |
| segbase = 0; | |
| extbase = 0; | |
| for (l = abfd->tdata.ihex_data->head; l != NULL; l = l->next) | |
| { | |
| bfd_vma where; | |
| bfd_byte *p; | |
| bfd_size_type count; | |
| where = l->where; | |
| #ifdef BFD64 | |
| /* IHex only supports 32-bit addresses, and we want to check | |
| that 64-bit addresses are in range. This isn't quite as | |
| obvious as it may seem, since some targets have 32-bit | |
| addresses that are sign extended to 64 bits. So complain | |
| only if addresses overflow both unsigned and signed 32-bit | |
| integers. */ | |
| if (where > 0xffffffff | |
| && where + 0x80000000 > 0xffffffff) | |
| { | |
| _bfd_error_handler | |
| /* xgettext:c-format */ | |
| (_("%pB 64-bit address %#" PRIx64 | |
| " out of range for Intel Hex file"), | |
| abfd, (uint64_t) where); | |
| bfd_set_error (bfd_error_bad_value); | |
| return false; | |
| } | |
| where &= 0xffffffff; | |
| #endif | |
| p = l->data; | |
| count = l->size; | |
| while (count > 0) | |
| { | |
| size_t now; | |
| unsigned int rec_addr; | |
| now = count; | |
| if (count > CHUNK) | |
| now = CHUNK; | |
| if (where < extbase | |
| || where - extbase < segbase | |
| || where - extbase - segbase > 0xffff) | |
| { | |
| bfd_byte addr[2]; | |
| /* We need a new base address. */ | |
| if (extbase == 0 && where <= 0xfffff) | |
| { | |
| segbase = where & 0xf0000; | |
| addr[0] = (bfd_byte)(segbase >> 12) & 0xff; | |
| addr[1] = (bfd_byte)(segbase >> 4) & 0xff; | |
| if (! ihex_write_record (abfd, 2, 0, 2, addr)) | |
| return false; | |
| } | |
| else | |
| { | |
| /* The extended address record and the extended | |
| linear address record are combined, at least by | |
| some readers. We need an extended linear address | |
| record here, so if we've already written out an | |
| extended address record, zero it out to avoid | |
| confusion. */ | |
| if (segbase != 0) | |
| { | |
| addr[0] = 0; | |
| addr[1] = 0; | |
| if (! ihex_write_record (abfd, 2, 0, 2, addr)) | |
| return false; | |
| segbase = 0; | |
| } | |
| extbase = where & 0xffff0000; | |
| if (where > extbase + 0xffff) | |
| { | |
| _bfd_error_handler | |
| /* xgettext:c-format */ | |
| (_("%pB: address %#" PRIx64 | |
| " out of range for Intel Hex file"), | |
| abfd, (uint64_t) where); | |
| bfd_set_error (bfd_error_bad_value); | |
| return false; | |
| } | |
| addr[0] = (bfd_byte)(extbase >> 24) & 0xff; | |
| addr[1] = (bfd_byte)(extbase >> 16) & 0xff; | |
| if (! ihex_write_record (abfd, 2, 0, 4, addr)) | |
| return false; | |
| } | |
| } | |
| rec_addr = where - (extbase + segbase); | |
| /* Output records shouldn't cross 64K boundaries. */ | |
| if (rec_addr + now > 0xffff) | |
| now = 0x10000 - rec_addr; | |
| if (! ihex_write_record (abfd, now, rec_addr, 0, p)) | |
| return false; | |
| where += now; | |
| p += now; | |
| count -= now; | |
| } | |
| } | |
| if (abfd->start_address != 0) | |
| { | |
| bfd_vma start; | |
| bfd_byte startbuf[4]; | |
| start = abfd->start_address; | |
| if (start <= 0xfffff) | |
| { | |
| startbuf[0] = (bfd_byte)((start & 0xf0000) >> 12) & 0xff; | |
| startbuf[1] = 0; | |
| startbuf[2] = (bfd_byte)(start >> 8) & 0xff; | |
| startbuf[3] = (bfd_byte)start & 0xff; | |
| if (! ihex_write_record (abfd, 4, 0, 3, startbuf)) | |
| return false; | |
| } | |
| else | |
| { | |
| startbuf[0] = (bfd_byte)(start >> 24) & 0xff; | |
| startbuf[1] = (bfd_byte)(start >> 16) & 0xff; | |
| startbuf[2] = (bfd_byte)(start >> 8) & 0xff; | |
| startbuf[3] = (bfd_byte)start & 0xff; | |
| if (! ihex_write_record (abfd, 4, 0, 5, startbuf)) | |
| return false; | |
| } | |
| } | |
| if (! ihex_write_record (abfd, 0, 0, 1, NULL)) | |
| return false; | |
| return true; | |
| } | |
| /* Set the architecture for the output file. The architecture is | |
| irrelevant, so we ignore errors about unknown architectures. */ | |
| static bool | |
| ihex_set_arch_mach (bfd *abfd, | |
| enum bfd_architecture arch, | |
| unsigned long mach) | |
| { | |
| if (! bfd_default_set_arch_mach (abfd, arch, mach)) | |
| { | |
| if (arch != bfd_arch_unknown) | |
| return false; | |
| } | |
| return true; | |
| } | |
| /* Get the size of the headers, for the linker. */ | |
| static int | |
| ihex_sizeof_headers (bfd *abfd ATTRIBUTE_UNUSED, | |
| struct bfd_link_info *info ATTRIBUTE_UNUSED) | |
| { | |
| return 0; | |
| } | |
| /* Some random definitions for the target vector. */ | |
| #define ihex_close_and_cleanup _bfd_generic_close_and_cleanup | |
| #define ihex_bfd_free_cached_info _bfd_generic_bfd_free_cached_info | |
| #define ihex_new_section_hook _bfd_generic_new_section_hook | |
| #define ihex_get_section_contents_in_window _bfd_generic_get_section_contents_in_window | |
| #define ihex_get_symtab_upper_bound _bfd_long_bfd_0 | |
| #define ihex_canonicalize_symtab _bfd_nosymbols_canonicalize_symtab | |
| #define ihex_make_empty_symbol _bfd_generic_make_empty_symbol | |
| #define ihex_print_symbol _bfd_nosymbols_print_symbol | |
| #define ihex_get_symbol_info _bfd_nosymbols_get_symbol_info | |
| #define ihex_get_symbol_version_string _bfd_nosymbols_get_symbol_version_string | |
| #define ihex_bfd_is_target_special_symbol _bfd_bool_bfd_asymbol_false | |
| #define ihex_bfd_is_local_label_name _bfd_nosymbols_bfd_is_local_label_name | |
| #define ihex_get_lineno _bfd_nosymbols_get_lineno | |
| #define ihex_find_nearest_line _bfd_nosymbols_find_nearest_line | |
| #define ihex_find_line _bfd_nosymbols_find_line | |
| #define ihex_find_inliner_info _bfd_nosymbols_find_inliner_info | |
| #define ihex_bfd_make_debug_symbol _bfd_nosymbols_bfd_make_debug_symbol | |
| #define ihex_read_minisymbols _bfd_nosymbols_read_minisymbols | |
| #define ihex_minisymbol_to_symbol _bfd_nosymbols_minisymbol_to_symbol | |
| #define ihex_bfd_get_relocated_section_contents bfd_generic_get_relocated_section_contents | |
| #define ihex_bfd_relax_section bfd_generic_relax_section | |
| #define ihex_bfd_gc_sections bfd_generic_gc_sections | |
| #define ihex_bfd_lookup_section_flags bfd_generic_lookup_section_flags | |
| #define ihex_bfd_merge_sections bfd_generic_merge_sections | |
| #define ihex_bfd_is_group_section bfd_generic_is_group_section | |
| #define ihex_bfd_group_name bfd_generic_group_name | |
| #define ihex_bfd_discard_group bfd_generic_discard_group | |
| #define ihex_section_already_linked _bfd_generic_section_already_linked | |
| #define ihex_bfd_define_common_symbol bfd_generic_define_common_symbol | |
| #define ihex_bfd_link_hide_symbol _bfd_generic_link_hide_symbol | |
| #define ihex_bfd_define_start_stop bfd_generic_define_start_stop | |
| #define ihex_bfd_link_hash_table_create _bfd_generic_link_hash_table_create | |
| #define ihex_bfd_link_add_symbols _bfd_generic_link_add_symbols | |
| #define ihex_bfd_link_just_syms _bfd_generic_link_just_syms | |
| #define ihex_bfd_copy_link_hash_symbol_type _bfd_generic_copy_link_hash_symbol_type | |
| #define ihex_bfd_final_link _bfd_generic_final_link | |
| #define ihex_bfd_link_split_section _bfd_generic_link_split_section | |
| #define ihex_bfd_link_check_relocs _bfd_generic_link_check_relocs | |
| /* The Intel Hex target vector. */ | |
| const bfd_target ihex_vec = | |
| { | |
| "ihex", /* Name. */ | |
| bfd_target_ihex_flavour, | |
| BFD_ENDIAN_UNKNOWN, /* Target byte order. */ | |
| BFD_ENDIAN_UNKNOWN, /* Target headers byte order. */ | |
| 0, /* Object flags. */ | |
| (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD), /* Section flags. */ | |
| 0, /* Leading underscore. */ | |
| ' ', /* AR_pad_char. */ | |
| 16, /* AR_max_namelen. */ | |
| 0, /* match priority. */ | |
| TARGET_KEEP_UNUSED_SECTION_SYMBOLS, /* keep unused section symbols. */ | |
| 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, /* Headers. */ | |
| { | |
| _bfd_dummy_target, | |
| ihex_object_p, /* bfd_check_format. */ | |
| _bfd_dummy_target, | |
| _bfd_dummy_target, | |
| }, | |
| { | |
| _bfd_bool_bfd_false_error, | |
| ihex_mkobject, | |
| _bfd_generic_mkarchive, | |
| _bfd_bool_bfd_false_error, | |
| }, | |
| { /* bfd_write_contents. */ | |
| _bfd_bool_bfd_false_error, | |
| ihex_write_object_contents, | |
| _bfd_write_archive_contents, | |
| _bfd_bool_bfd_false_error, | |
| }, | |
| BFD_JUMP_TABLE_GENERIC (ihex), | |
| BFD_JUMP_TABLE_COPY (_bfd_generic), | |
| BFD_JUMP_TABLE_CORE (_bfd_nocore), | |
| BFD_JUMP_TABLE_ARCHIVE (_bfd_noarchive), | |
| BFD_JUMP_TABLE_SYMBOLS (ihex), | |
| BFD_JUMP_TABLE_RELOCS (_bfd_norelocs), | |
| BFD_JUMP_TABLE_WRITE (ihex), | |
| BFD_JUMP_TABLE_LINK (ihex), | |
| BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic), | |
| NULL, | |
| NULL | |
| }; |