diff options
Diffstat (limited to 'elfcopy/ascii.c')
-rw-r--r-- | elfcopy/ascii.c | 1078 |
1 files changed, 1078 insertions, 0 deletions
diff --git a/elfcopy/ascii.c b/elfcopy/ascii.c new file mode 100644 index 0000000000000..968e5c667f631 --- /dev/null +++ b/elfcopy/ascii.c @@ -0,0 +1,1078 @@ +/*- + * Copyright (c) 2010,2011 Kai Wang + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#include <sys/param.h> +#include <ctype.h> +#include <err.h> +#include <gelf.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "elfcopy.h" + +ELFTC_VCSID("$Id: ascii.c 2358 2011-12-19 18:22:32Z kaiwang27 $"); + +static void append_data(struct section *s, const void *buf, size_t sz); +static char hex_digit(uint8_t n); +static int hex_value(int x); +static void finalize_data_section(struct section *s); +static int ishexdigit(int x); +static int ihex_read(const char *line, char *type, uint64_t *addr, + uint64_t *num, uint8_t *data, size_t *sz); +static void ihex_write(int ofd, int type, uint64_t addr, uint64_t num, + const void *buf, size_t sz); +static void ihex_write_00(int ofd, uint64_t addr, const void *buf, size_t sz); +static void ihex_write_01(int ofd); +static void ihex_write_04(int ofd, uint16_t addr); +static void ihex_write_05(int ofd, uint64_t e_entry); +static struct section *new_data_section(struct elfcopy *ecp, int sec_index, + uint64_t off, uint64_t addr); +static int read_num(const char *line, int *len, uint64_t *num, size_t sz, + int *checksum); +static int srec_read(const char *line, char *type, uint64_t *addr, + uint8_t *data, size_t *sz); +static void srec_write(int ofd, char type, uint64_t addr, const void *buf, + size_t sz); +static void srec_write_symtab(int ofd, const char *ofn, Elf *e, Elf_Scn *scn, + GElf_Shdr *sh); +static void srec_write_S0(int ofd, const char *ofn); +static void srec_write_Sd(int ofd, char dr, uint64_t addr, const void *buf, + size_t sz, size_t rlen); +static void srec_write_Se(int ofd, uint64_t e_entry, int forceS3); +static void write_num(char *line, int *len, uint64_t num, size_t sz, + int *checksum); + +#define _LINE_BUFSZ 1024 +#define _DATA_BUFSZ 256 + +/* + * Convert ELF object to S-Record. + */ +void +create_srec(struct elfcopy *ecp, int ifd, int ofd, const char *ofn) +{ + Elf *e; + Elf_Scn *scn; + Elf_Data *d; + GElf_Ehdr eh; + GElf_Shdr sh; + uint64_t max_addr; + size_t rlen; + int elferr, addr_sz; + char dr; + + if ((e = elf_begin(ifd, ELF_C_READ, NULL)) == NULL) + errx(EXIT_FAILURE, "elf_begin() failed: %s", + elf_errmsg(-1)); + + /* Output a symbol table for `symbolsrec' target. */ + if (!strncmp(ecp->otgt, "symbolsrec", strlen("symbolsrec"))) { + scn = NULL; + while ((scn = elf_nextscn(e, scn)) != NULL) { + if (gelf_getshdr(scn, &sh) == NULL) { + warnx("gelf_getshdr failed: %s", + elf_errmsg(-1)); + (void) elf_errno(); + continue; + } + if (sh.sh_type != SHT_SYMTAB) + continue; + srec_write_symtab(ofd, ofn, e, scn, &sh); + break; + } + } + + if (ecp->flags & SREC_FORCE_S3) + dr = '3'; + else { + /* + * Find maximum address size in the first iteration. + */ + max_addr = 0; + scn = NULL; + while ((scn = elf_nextscn(e, scn)) != NULL) { + if (gelf_getshdr(scn, &sh) == NULL) { + warnx("gelf_getshdr failed: %s", + elf_errmsg(-1)); + (void) elf_errno(); + continue; + } + if ((sh.sh_flags & SHF_ALLOC) == 0 || + sh.sh_type == SHT_NOBITS || + sh.sh_size == 0) + continue; + if ((uint64_t) sh.sh_addr > max_addr) + max_addr = sh.sh_addr; + } + elferr = elf_errno(); + if (elferr != 0) + warnx("elf_nextscn failed: %s", elf_errmsg(elferr)); + + if (max_addr <= 0xFFFF) + dr = '1'; + else if (max_addr <= 0xFFFFFF) + dr = '2'; + else + dr = '3'; + } + + if (ecp->flags & SREC_FORCE_LEN) { + addr_sz = dr - '0' + 1; + if (ecp->srec_len < 1) + rlen = 1; + else if (ecp->srec_len + addr_sz + 1 > 255) + rlen = 255 - (addr_sz + 1); + else + rlen = ecp->srec_len; + } else + rlen = 16; + + /* Generate S0 record which contains the output filename. */ + srec_write_S0(ofd, ofn); + + /* Generate S{1,2,3} data records for section data. */ + scn = NULL; + while ((scn = elf_nextscn(e, scn)) != NULL) { + if (gelf_getshdr(scn, &sh) == NULL) { + warnx("gelf_getshdr failed: %s", elf_errmsg(-1)); + (void) elf_errno(); + continue; + } + if ((sh.sh_flags & SHF_ALLOC) == 0 || + sh.sh_type == SHT_NOBITS || + sh.sh_size == 0) + continue; + if (sh.sh_addr > 0xFFFFFFFF) { + warnx("address space too big for S-Record file"); + continue; + } + (void) elf_errno(); + if ((d = elf_getdata(scn, NULL)) == NULL) { + elferr = elf_errno(); + if (elferr != 0) + warnx("elf_getdata failed: %s", elf_errmsg(-1)); + continue; + } + if (d->d_buf == NULL || d->d_size == 0) + continue; + srec_write_Sd(ofd, dr, sh.sh_addr, d->d_buf, d->d_size, rlen); + } + elferr = elf_errno(); + if (elferr != 0) + warnx("elf_nextscn failed: %s", elf_errmsg(elferr)); + + /* Generate S{7,8,9} end of block recrod. */ + if (gelf_getehdr(e, &eh) == NULL) + errx(EXIT_FAILURE, "gelf_getehdr() failed: %s", + elf_errmsg(-1)); + srec_write_Se(ofd, eh.e_entry, ecp->flags & SREC_FORCE_S3); +} + +void +create_elf_from_srec(struct elfcopy *ecp, int ifd) +{ + char line[_LINE_BUFSZ], name[_LINE_BUFSZ]; + uint8_t data[_DATA_BUFSZ]; + GElf_Ehdr oeh; + struct section *s, *shtab; + FILE *ifp; + uint64_t addr, entry, off, sec_addr; + uintmax_t st_value; + size_t sz; + int _ifd, first, sec_index, in_symtab, symtab_created; + char *rlt; + char type; + + if ((_ifd = dup(ifd)) < 0) + err(EXIT_FAILURE, "dup failed"); + if ((ifp = fdopen(_ifd, "r")) == NULL) + err(EXIT_FAILURE, "fdopen failed"); + + /* Create EHDR for output .o file. */ + if (gelf_newehdr(ecp->eout, ecp->oec) == NULL) + errx(EXIT_FAILURE, "gelf_newehdr failed: %s", + elf_errmsg(-1)); + if (gelf_getehdr(ecp->eout, &oeh) == NULL) + errx(EXIT_FAILURE, "gelf_getehdr() failed: %s", + elf_errmsg(-1)); + + /* Initialise e_ident fields. */ + oeh.e_ident[EI_CLASS] = ecp->oec; + oeh.e_ident[EI_DATA] = ecp->oed; + /* + * TODO: Set OSABI according to the OS platform where elfcopy(1) + * was build. (probably) + */ + oeh.e_ident[EI_OSABI] = ELFOSABI_NONE; + oeh.e_machine = ecp->oem; + oeh.e_type = ET_REL; + oeh.e_entry = 0; + + ecp->flags |= RELOCATABLE; + + /* Create .shstrtab section */ + init_shstrtab(ecp); + ecp->shstrtab->off = 0; + + /* Data sections are inserted after EHDR. */ + off = gelf_fsize(ecp->eout, ELF_T_EHDR, 1, EV_CURRENT); + if (off == 0) + errx(EXIT_FAILURE, "gelf_fsize() failed: %s", elf_errmsg(-1)); + + /* Create data sections. */ + s = NULL; + first = 1; + sec_index = 1; + sec_addr = entry = 0; + while (fgets(line, _LINE_BUFSZ, ifp) != NULL) { + if (line[0] == '\r' || line[0] == '\n') + continue; + if (line[0] == '$' && line[1] == '$') { + ecp->flags |= SYMTAB_EXIST; + while ((rlt = fgets(line, _LINE_BUFSZ, ifp)) != NULL) { + if (line[0] == '$' && line[1] == '$') + break; + } + if (rlt == NULL) + break; + continue; + } + if (line[0] != 'S' || line[1] < '0' || line[1] > '9') { + warnx("Invalid srec record"); + continue; + } + if (srec_read(line, &type, &addr, data, &sz) < 0) { + warnx("Invalid srec record or mismatched checksum"); + continue; + } + switch (type) { + case '1': + case '2': + case '3': + if (sz == 0) + break; + if (first || sec_addr != addr) { + if (s != NULL) + finalize_data_section(s); + s = new_data_section(ecp, sec_index, off, + addr); + if (s == NULL) { + warnx("new_data_section failed"); + break; + } + sec_index++; + sec_addr = addr; + first = 0; + } + append_data(s, data, sz); + off += sz; + sec_addr += sz; + break; + case '7': + case '8': + case '9': + entry = addr; + break; + default: + break; + } + } + if (s != NULL) + finalize_data_section(s); + if (ferror(ifp)) + warn("fgets failed"); + + /* Insert .shstrtab after data sections. */ + if ((ecp->shstrtab->os = elf_newscn(ecp->eout)) == NULL) + errx(EXIT_FAILURE, "elf_newscn failed: %s", + elf_errmsg(-1)); + insert_to_sec_list(ecp, ecp->shstrtab, 1); + + /* Insert section header table here. */ + shtab = insert_shtab(ecp, 1); + + /* + * Rescan and create symbol table if we found '$$' section in + * the first scan. + */ + symtab_created = 0; + in_symtab = 0; + if (ecp->flags & SYMTAB_EXIST) { + if (fseek(ifp, 0, SEEK_SET) < 0) { + warn("fseek failed"); + ecp->flags &= ~SYMTAB_EXIST; + goto done; + } + while (fgets(line, _LINE_BUFSZ, ifp) != NULL) { + if (in_symtab) { + if (line[0] == '$' && line[1] == '$') { + in_symtab = 0; + continue; + } + if (sscanf(line, "%s $%jx", name, + &st_value) != 2) { + warnx("Invalid symbolsrec record"); + continue; + } + if (!symtab_created) { + create_external_symtab(ecp); + symtab_created = 1; + } + add_to_symtab(ecp, name, st_value, 0, SHN_ABS, + ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, 1); + } + if (line[0] == '$' && line[1] == '$') { + in_symtab = 1; + continue; + } + } + } + if (ferror(ifp)) + warn("fgets failed"); + if (symtab_created) { + finalize_external_symtab(ecp); + create_symtab_data(ecp); + /* Count in .symtab and .strtab section headers. */ + shtab->sz += gelf_fsize(ecp->eout, ELF_T_SHDR, 2, EV_CURRENT); + } else + ecp->flags &= ~SYMTAB_EXIST; + +done: + fclose(ifp); + + /* Set entry point. */ + oeh.e_entry = entry; + + /* + * Write the underlying ehdr. Note that it should be called + * before elf_setshstrndx() since it will overwrite e->e_shstrndx. + */ + if (gelf_update_ehdr(ecp->eout, &oeh) == 0) + errx(EXIT_FAILURE, "gelf_update_ehdr() failed: %s", + elf_errmsg(-1)); + + /* Generate section name string table (.shstrtab). */ + set_shstrtab(ecp); + + /* Update sh_name pointer for each section header entry. */ + update_shdr(ecp, 0); + + /* Renew oeh to get the updated e_shstrndx. */ + if (gelf_getehdr(ecp->eout, &oeh) == NULL) + errx(EXIT_FAILURE, "gelf_getehdr() failed: %s", + elf_errmsg(-1)); + + /* Resync section offsets. */ + resync_sections(ecp); + + /* Store SHDR offset in EHDR. */ + oeh.e_shoff = shtab->off; + + /* Update ehdr since we modified e_shoff. */ + if (gelf_update_ehdr(ecp->eout, &oeh) == 0) + errx(EXIT_FAILURE, "gelf_update_ehdr() failed: %s", + elf_errmsg(-1)); + + /* Write out the output elf object. */ + if (elf_update(ecp->eout, ELF_C_WRITE) < 0) + errx(EXIT_FAILURE, "elf_update() failed: %s", + elf_errmsg(-1)); + + /* Release allocated resource. */ + free_elf(ecp); +} + +void +create_ihex(int ifd, int ofd) +{ + Elf *e; + Elf_Scn *scn; + Elf_Data *d; + GElf_Ehdr eh; + GElf_Shdr sh; + int elferr; + uint16_t addr_hi, old_addr_hi; + + if ((e = elf_begin(ifd, ELF_C_READ, NULL)) == NULL) + errx(EXIT_FAILURE, "elf_begin() failed: %s", + elf_errmsg(-1)); + + old_addr_hi = 0; + scn = NULL; + while ((scn = elf_nextscn(e, scn)) != NULL) { + if (gelf_getshdr(scn, &sh) == NULL) { + warnx("gelf_getshdr failed: %s", elf_errmsg(-1)); + (void) elf_errno(); + continue; + } + if ((sh.sh_flags & SHF_ALLOC) == 0 || + sh.sh_type == SHT_NOBITS || + sh.sh_size == 0) + continue; + if (sh.sh_addr > 0xFFFFFFFF) { + warnx("address space too big for Intel Hex file"); + continue; + } + (void) elf_errno(); + if ((d = elf_getdata(scn, NULL)) == NULL) { + elferr = elf_errno(); + if (elferr != 0) + warnx("elf_getdata failed: %s", elf_errmsg(-1)); + continue; + } + if (d->d_buf == NULL || d->d_size == 0) + continue; + addr_hi = (sh.sh_addr >> 16) & 0xFFFF; + if (addr_hi > 0 && addr_hi != old_addr_hi) { + /* Write 04 record if addr_hi is new. */ + old_addr_hi = addr_hi; + ihex_write_04(ofd, addr_hi); + } + ihex_write_00(ofd, sh.sh_addr, d->d_buf, d->d_size); + } + elferr = elf_errno(); + if (elferr != 0) + warnx("elf_nextscn failed: %s", elf_errmsg(elferr)); + + if (gelf_getehdr(e, &eh) == NULL) + errx(EXIT_FAILURE, "gelf_getehdr() failed: %s", + elf_errmsg(-1)); + ihex_write_05(ofd, eh.e_entry); + ihex_write_01(ofd); +} + +void +create_elf_from_ihex(struct elfcopy *ecp, int ifd) +{ + char line[_LINE_BUFSZ]; + uint8_t data[_DATA_BUFSZ]; + GElf_Ehdr oeh; + struct section *s, *shtab; + FILE *ifp; + uint64_t addr, addr_base, entry, num, off, rec_addr, sec_addr; + size_t sz; + int _ifd, first, sec_index; + char type; + + if ((_ifd = dup(ifd)) < 0) + err(EXIT_FAILURE, "dup failed"); + if ((ifp = fdopen(_ifd, "r")) == NULL) + err(EXIT_FAILURE, "fdopen failed"); + + /* Create EHDR for output .o file. */ + if (gelf_newehdr(ecp->eout, ecp->oec) == NULL) + errx(EXIT_FAILURE, "gelf_newehdr failed: %s", + elf_errmsg(-1)); + if (gelf_getehdr(ecp->eout, &oeh) == NULL) + errx(EXIT_FAILURE, "gelf_getehdr() failed: %s", + elf_errmsg(-1)); + + /* Initialise e_ident fields. */ + oeh.e_ident[EI_CLASS] = ecp->oec; + oeh.e_ident[EI_DATA] = ecp->oed; + /* + * TODO: Set OSABI according to the OS platform where elfcopy(1) + * was build. (probably) + */ + oeh.e_ident[EI_OSABI] = ELFOSABI_NONE; + oeh.e_machine = ecp->oem; + oeh.e_type = ET_REL; + oeh.e_entry = 0; + + ecp->flags |= RELOCATABLE; + + /* Create .shstrtab section */ + init_shstrtab(ecp); + ecp->shstrtab->off = 0; + + /* Data sections are inserted after EHDR. */ + off = gelf_fsize(ecp->eout, ELF_T_EHDR, 1, EV_CURRENT); + if (off == 0) + errx(EXIT_FAILURE, "gelf_fsize() failed: %s", elf_errmsg(-1)); + + /* Create data sections. */ + s = NULL; + first = 1; + sec_index = 1; + addr_base = rec_addr = sec_addr = entry = 0; + while (fgets(line, _LINE_BUFSZ, ifp) != NULL) { + if (line[0] == '\r' || line[0] == '\n') + continue; + if (line[0] != ':') { + warnx("Invalid ihex record"); + continue; + } + if (ihex_read(line, &type, &addr, &num, data, &sz) < 0) { + warnx("Invalid ihex record or mismatched checksum"); + continue; + } + switch (type) { + case '0': + /* Data record. */ + if (sz == 0) + break; + rec_addr = addr_base + addr; + if (first || sec_addr != rec_addr) { + if (s != NULL) + finalize_data_section(s); + s = new_data_section(ecp, sec_index, off, + rec_addr); + if (s == NULL) { + warnx("new_data_section failed"); + break; + } + sec_index++; + sec_addr = rec_addr; + first = 0; + } + append_data(s, data, sz); + off += sz; + sec_addr += sz; + break; + case '1': + /* End of file record. */ + goto done; + case '2': + /* Extended segment address record. */ + addr_base = addr << 4; + break; + case '3': + /* Start segment address record (CS:IP). Ignored. */ + break; + case '4': + /* Extended linear address record. */ + addr_base = num << 16; + break; + case '5': + /* Start linear address record. */ + entry = num; + break; + default: + break; + } + } +done: + if (s != NULL) + finalize_data_section(s); + if (ferror(ifp)) + warn("fgets failed"); + fclose(ifp); + + /* Insert .shstrtab after data sections. */ + if ((ecp->shstrtab->os = elf_newscn(ecp->eout)) == NULL) + errx(EXIT_FAILURE, "elf_newscn failed: %s", + elf_errmsg(-1)); + insert_to_sec_list(ecp, ecp->shstrtab, 1); + + /* Insert section header table here. */ + shtab = insert_shtab(ecp, 1); + + /* Set entry point. */ + oeh.e_entry = entry; + + /* + * Write the underlying ehdr. Note that it should be called + * before elf_setshstrndx() since it will overwrite e->e_shstrndx. + */ + if (gelf_update_ehdr(ecp->eout, &oeh) == 0) + errx(EXIT_FAILURE, "gelf_update_ehdr() failed: %s", + elf_errmsg(-1)); + + /* Generate section name string table (.shstrtab). */ + set_shstrtab(ecp); + + /* Update sh_name pointer for each section header entry. */ + update_shdr(ecp, 0); + + /* Renew oeh to get the updated e_shstrndx. */ + if (gelf_getehdr(ecp->eout, &oeh) == NULL) + errx(EXIT_FAILURE, "gelf_getehdr() failed: %s", + elf_errmsg(-1)); + + /* Resync section offsets. */ + resync_sections(ecp); + + /* Store SHDR offset in EHDR. */ + oeh.e_shoff = shtab->off; + + /* Update ehdr since we modified e_shoff. */ + if (gelf_update_ehdr(ecp->eout, &oeh) == 0) + errx(EXIT_FAILURE, "gelf_update_ehdr() failed: %s", + elf_errmsg(-1)); + + /* Write out the output elf object. */ + if (elf_update(ecp->eout, ELF_C_WRITE) < 0) + errx(EXIT_FAILURE, "elf_update() failed: %s", + elf_errmsg(-1)); + + /* Release allocated resource. */ + free_elf(ecp); +} + +#define _SEC_NAMESZ 64 +#define _SEC_INIT_CAP 1024 + +static struct section * +new_data_section(struct elfcopy *ecp, int sec_index, uint64_t off, + uint64_t addr) +{ + char *name; + + if ((name = malloc(_SEC_NAMESZ)) == NULL) + errx(EXIT_FAILURE, "malloc failed"); + snprintf(name, _SEC_NAMESZ, ".sec%d", sec_index); + + return (create_external_section(ecp, name, name, NULL, 0, off, + SHT_PROGBITS, ELF_T_BYTE, SHF_ALLOC | SHF_WRITE, 1, addr, 0)); +} + +static void +finalize_data_section(struct section *s) +{ + Elf_Data *od; + + if ((od = elf_newdata(s->os)) == NULL) + errx(EXIT_FAILURE, "elf_newdata() failed: %s", + elf_errmsg(-1)); + od->d_align = s->align; + od->d_off = 0; + od->d_buf = s->buf; + od->d_size = s->sz; + od->d_version = EV_CURRENT; +} + +static void +append_data(struct section *s, const void *buf, size_t sz) +{ + uint8_t *p; + + if (s->buf == NULL) { + s->sz = 0; + s->cap = _SEC_INIT_CAP; + if ((s->buf = malloc(s->cap)) == NULL) + err(EXIT_FAILURE, "malloc failed"); + } + + while (sz + s->sz > s->cap) { + s->cap *= 2; + if ((s->buf = realloc(s->buf, s->cap)) == NULL) + err(EXIT_FAILURE, "realloc failed"); + } + + p = s->buf; + memcpy(&p[s->sz], buf, sz); + s->sz += sz; +} + +static int +srec_read(const char *line, char *type, uint64_t *addr, uint8_t *data, + size_t *sz) +{ + uint64_t count, _checksum, num; + size_t addr_sz; + int checksum, i, len; + + checksum = 0; + len = 2; + if (read_num(line, &len, &count, 1, &checksum) < 0) + return (-1); + *type = line[1]; + switch (*type) { + case '0': + case '1': + case '5': + case '9': + addr_sz = 2; + break; + case '2': + case '8': + addr_sz = 3; + break; + case '3': + case '7': + addr_sz = 4; + break; + default: + return (-1); + } + + if (read_num(line, &len, addr, addr_sz, &checksum) < 0) + return (-1); + + count -= addr_sz + 1; + if (*type >= '0' && *type <= '3') { + for (i = 0; (uint64_t) i < count; i++) { + if (read_num(line, &len, &num, 1, &checksum) < 0) + return -1; + data[i] = (uint8_t) num; + } + *sz = count; + } else + *sz = 0; + + if (read_num(line, &len, &_checksum, 1, NULL) < 0) + return (-1); + + if ((int) _checksum != (~checksum & 0xFF)) + return (-1); + + return (0); +} + +static void +srec_write_symtab(int ofd, const char *ofn, Elf *e, Elf_Scn *scn, GElf_Shdr *sh) +{ + char line[_LINE_BUFSZ]; + GElf_Sym sym; + Elf_Data *d; + const char *name; + size_t sc; + int elferr, i; + +#define _WRITE_LINE do { \ + if (write(ofd, line, strlen(line)) != (ssize_t) strlen(line)) \ + errx(EXIT_FAILURE, "write failed"); \ + } while (0) + + + (void) elf_errno(); + if ((d = elf_getdata(scn, NULL)) == NULL) { + elferr = elf_errno(); + if (elferr != 0) + warnx("elf_getdata failed: %s", + elf_errmsg(-1)); + return; + } + if (d->d_buf == NULL || d->d_size == 0) + return; + + snprintf(line, sizeof(line), "$$ %s\r\n", ofn); + _WRITE_LINE; + sc = d->d_size / sh->sh_entsize; + for (i = 1; (size_t) i < sc; i++) { + if (gelf_getsym(d, i, &sym) != &sym) { + warnx("gelf_getsym failed: %s", elf_errmsg(-1)); + continue; + } + if (GELF_ST_TYPE(sym.st_info) == STT_SECTION || + GELF_ST_TYPE(sym.st_info) == STT_FILE) + continue; + if ((name = elf_strptr(e, sh->sh_link, sym.st_name)) == NULL) { + warnx("elf_strptr failed: %s", elf_errmsg(-1)); + continue; + } + snprintf(line, sizeof(line), " %s $%jx\r\n", name, + (uintmax_t) sym.st_value); + _WRITE_LINE; + } + snprintf(line, sizeof(line), "$$ \r\n"); + _WRITE_LINE; + +#undef _WRITE_LINE +} + +static void +srec_write_S0(int ofd, const char *ofn) +{ + + srec_write(ofd, '0', 0, ofn, strlen(ofn)); +} + +static void +srec_write_Sd(int ofd, char dr, uint64_t addr, const void *buf, size_t sz, + size_t rlen) +{ + const uint8_t *p, *pe; + + p = buf; + pe = p + sz; + while (pe - p >= (int) rlen) { + srec_write(ofd, dr, addr, p, rlen); + addr += rlen; + p += rlen; + } + if (pe - p > 0) + srec_write(ofd, dr, addr, p, pe - p); +} + +static void +srec_write_Se(int ofd, uint64_t e_entry, int forceS3) +{ + char er; + + if (e_entry > 0xFFFFFFFF) { + warnx("address space too big for S-Record file"); + return; + } + + if (forceS3) + er = '7'; + else { + if (e_entry <= 0xFFFF) + er = '9'; + else if (e_entry <= 0xFFFFFF) + er = '8'; + else + er = '7'; + } + + srec_write(ofd, er, e_entry, NULL, 0); +} + +static void +srec_write(int ofd, char type, uint64_t addr, const void *buf, size_t sz) +{ + char line[_LINE_BUFSZ]; + const uint8_t *p, *pe; + int len, addr_sz, checksum; + + if (type == '0' || type == '1' || type == '5' || type == '9') + addr_sz = 2; + else if (type == '2' || type == '8') + addr_sz = 3; + else + addr_sz = 4; + + checksum = 0; + line[0] = 'S'; + line[1] = type; + len = 2; + write_num(line, &len, addr_sz + sz + 1, 1, &checksum); + write_num(line, &len, addr, addr_sz, &checksum); + for (p = buf, pe = p + sz; p < pe; p++) + write_num(line, &len, *p, 1, &checksum); + write_num(line, &len, ~checksum & 0xFF, 1, NULL); + line[len++] = '\r'; + line[len++] = '\n'; + if (write(ofd, line, len) != (ssize_t) len) + err(EXIT_FAILURE, "write failed"); +} + +static void +ihex_write_00(int ofd, uint64_t addr, const void *buf, size_t sz) +{ + uint16_t addr_hi, old_addr_hi; + const uint8_t *p, *pe; + + old_addr_hi = (addr >> 16) & 0xFFFF; + p = buf; + pe = p + sz; + while (pe - p >= 16) { + ihex_write(ofd, 0, addr, 0, p, 16); + addr += 16; + p += 16; + addr_hi = (addr >> 16) & 0xFFFF; + if (addr_hi != old_addr_hi) { + old_addr_hi = addr_hi; + ihex_write_04(ofd, addr_hi); + } + } + if (pe - p > 0) + ihex_write(ofd, 0, addr, 0, p, pe - p); +} + +static int +ihex_read(const char *line, char *type, uint64_t *addr, uint64_t *num, + uint8_t *data, size_t *sz) +{ + uint64_t count, _checksum; + int checksum, i, len; + + *sz = 0; + checksum = 0; + len = 1; + if (read_num(line, &len, &count, 1, &checksum) < 0) + return (-1); + if (read_num(line, &len, addr, 2, &checksum) < 0) + return (-1); + if (line[len++] != '0') + return (-1); + *type = line[len++]; + checksum += *type - '0'; + switch (*type) { + case '0': + for (i = 0; (uint64_t) i < count; i++) { + if (read_num(line, &len, num, 1, &checksum) < 0) + return (-1); + data[i] = (uint8_t) *num; + } + *sz = count; + break; + case '1': + if (count != 0) + return (-1); + break; + case '2': + case '4': + if (count != 2) + return (-1); + if (read_num(line, &len, num, 2, &checksum) < 0) + return (-1); + break; + case '3': + case '5': + if (count != 4) + return (-1); + if (read_num(line, &len, num, 4, &checksum) < 0) + return (-1); + break; + default: + return (-1); + } + + if (read_num(line, &len, &_checksum, 1, &checksum) < 0) + return (-1); + + if ((checksum & 0xFF) != 0) { + return (-1); + } + + return (0); +} + +static void +ihex_write_01(int ofd) +{ + + ihex_write(ofd, 1, 0, 0, NULL, 0); +} + +static void +ihex_write_04(int ofd, uint16_t addr) +{ + + ihex_write(ofd, 4, 0, addr, NULL, 2); +} + +static void +ihex_write_05(int ofd, uint64_t e_entry) +{ + + if (e_entry > 0xFFFFFFFF) { + warnx("address space too big for Intel Hex file"); + return; + } + + ihex_write(ofd, 5, 0, e_entry, NULL, 4); +} + +static void +ihex_write(int ofd, int type, uint64_t addr, uint64_t num, const void *buf, + size_t sz) +{ + char line[_LINE_BUFSZ]; + const uint8_t *p, *pe; + int len, checksum; + + if (sz > 16) + errx(EXIT_FAILURE, "Internal: ihex_write() sz too big"); + checksum = 0; + line[0] = ':'; + len = 1; + write_num(line, &len, sz, 1, &checksum); + write_num(line, &len, addr, 2, &checksum); + write_num(line, &len, type, 1, &checksum); + if (sz > 0) { + if (buf != NULL) { + for (p = buf, pe = p + sz; p < pe; p++) + write_num(line, &len, *p, 1, &checksum); + } else + write_num(line, &len, num, sz, &checksum); + } + write_num(line, &len, (~checksum + 1) & 0xFF, 1, NULL); + line[len++] = '\r'; + line[len++] = '\n'; + if (write(ofd, line, len) != (ssize_t) len) + err(EXIT_FAILURE, "write failed"); +} + +static int +read_num(const char *line, int *len, uint64_t *num, size_t sz, int *checksum) +{ + uint8_t b; + + *num = 0; + for (; sz > 0; sz--) { + if (!ishexdigit(line[*len]) || !ishexdigit(line[*len + 1])) + return (-1); + b = (hex_value(line[*len]) << 4) | hex_value(line[*len + 1]); + *num = (*num << 8) | b; + *len += 2; + if (checksum != NULL) + *checksum = (*checksum + b) & 0xFF; + } + + return (0); +} + +static void +write_num(char *line, int *len, uint64_t num, size_t sz, int *checksum) +{ + uint8_t b; + + for (; sz > 0; sz--) { + b = (num >> ((sz - 1) * 8)) & 0xFF; + line[*len] = hex_digit((b >> 4) & 0xF); + line[*len + 1] = hex_digit(b & 0xF); + *len += 2; + if (checksum != NULL) + *checksum = (*checksum + b) & 0xFF; + } +} + +static char +hex_digit(uint8_t n) +{ + + return ((n < 10) ? '0' + n : 'A' + (n - 10)); +} + +static int +hex_value(int x) +{ + + if (isdigit(x)) + return (x - '0'); + else if (x >= 'a' && x <= 'f') + return (x - 'a' + 10); + else + return (x - 'A' + 10); +} + +static int +ishexdigit(int x) +{ + + if (isdigit(x)) + return (1); + if ((x >= 'a' && x <= 'f') || (x >= 'A' && x <= 'F')) + return (1); + + return (0); +} |