Path: blob/main/contrib/elftoolchain/elfcopy/ascii.c
39534 views
/*-1* Copyright (c) 2010,2011 Kai Wang2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12*13* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND14* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE15* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE16* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE17* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL18* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS19* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)20* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT21* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY22* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF23* SUCH DAMAGE.24*/2526#include <sys/param.h>27#include <ctype.h>28#include <err.h>29#include <gelf.h>30#include <stdint.h>31#include <stdio.h>32#include <stdlib.h>33#include <string.h>34#include <unistd.h>3536#include "elfcopy.h"3738ELFTC_VCSID("$Id: ascii.c 3757 2019-06-28 01:15:28Z emaste $");3940static void append_data(struct section *s, const void *buf, size_t sz);41static char hex_digit(uint8_t n);42static int hex_value(int x);43static void finalize_data_section(struct section *s);44static int ishexdigit(int x);45static int ihex_read(const char *line, char *type, uint64_t *addr,46uint64_t *num, uint8_t *data, size_t *sz);47static void ihex_write(int ofd, int type, uint64_t addr, uint64_t num,48const void *buf, size_t sz);49static void ihex_write_00(int ofd, uint64_t addr, const void *buf, size_t sz);50static void ihex_write_01(int ofd);51static void ihex_write_04(int ofd, uint16_t addr);52static void ihex_write_05(int ofd, uint64_t e_entry);53static struct section *new_data_section(struct elfcopy *ecp, int sec_index,54uint64_t off, uint64_t addr);55static int read_num(const char *line, int *len, uint64_t *num, size_t sz,56int *checksum);57static int srec_read(const char *line, char *type, uint64_t *addr,58uint8_t *data, size_t *sz);59static void srec_write(int ofd, char type, uint64_t addr, const void *buf,60size_t sz);61static void srec_write_symtab(int ofd, const char *ofn, Elf *e, Elf_Scn *scn,62GElf_Shdr *sh);63static void srec_write_S0(int ofd, const char *ofn);64static void srec_write_Sd(int ofd, char dr, uint64_t addr, const void *buf,65size_t sz, size_t rlen);66static void srec_write_Se(int ofd, uint64_t e_entry, int forceS3);67static void write_num(char *line, int *len, uint64_t num, size_t sz,68int *checksum);6970#define _LINE_BUFSZ 102471#define _DATA_BUFSZ 2567273/*74* Convert ELF object to S-Record.75*/76void77create_srec(struct elfcopy *ecp, int ifd, int ofd, const char *ofn)78{79Elf *e;80Elf_Scn *scn;81Elf_Data *d;82GElf_Ehdr eh;83GElf_Shdr sh;84uint64_t max_addr;85size_t rlen;86int elferr, addr_sz;87char dr;8889if ((e = elf_begin(ifd, ELF_C_READ, NULL)) == NULL)90errx(EXIT_FAILURE, "elf_begin() failed: %s",91elf_errmsg(-1));9293/* Output a symbol table for `symbolsrec' target. */94if (!strncmp(ecp->otgt, "symbolsrec", strlen("symbolsrec"))) {95scn = NULL;96while ((scn = elf_nextscn(e, scn)) != NULL) {97if (gelf_getshdr(scn, &sh) == NULL) {98warnx("gelf_getshdr failed: %s",99elf_errmsg(-1));100(void) elf_errno();101continue;102}103if (sh.sh_type != SHT_SYMTAB)104continue;105srec_write_symtab(ofd, ofn, e, scn, &sh);106break;107}108}109110if (ecp->flags & SREC_FORCE_S3)111dr = '3';112else {113/*114* Find maximum address size in the first iteration.115*/116max_addr = 0;117scn = NULL;118while ((scn = elf_nextscn(e, scn)) != NULL) {119if (gelf_getshdr(scn, &sh) == NULL) {120warnx("gelf_getshdr failed: %s",121elf_errmsg(-1));122(void) elf_errno();123continue;124}125if ((sh.sh_flags & SHF_ALLOC) == 0 ||126sh.sh_type == SHT_NOBITS ||127sh.sh_size == 0)128continue;129if ((uint64_t) sh.sh_addr > max_addr)130max_addr = sh.sh_addr;131}132elferr = elf_errno();133if (elferr != 0)134warnx("elf_nextscn failed: %s", elf_errmsg(elferr));135136if (max_addr <= 0xFFFF)137dr = '1';138else if (max_addr <= 0xFFFFFF)139dr = '2';140else141dr = '3';142}143144if (ecp->flags & SREC_FORCE_LEN) {145addr_sz = dr - '0' + 1;146if (ecp->srec_len < 1)147rlen = 1;148else if (ecp->srec_len + addr_sz + 1 > 255)149rlen = 255 - (addr_sz + 1);150else151rlen = ecp->srec_len;152} else153rlen = 16;154155/* Generate S0 record which contains the output filename. */156srec_write_S0(ofd, ofn);157158/* Generate S{1,2,3} data records for section data. */159scn = NULL;160while ((scn = elf_nextscn(e, scn)) != NULL) {161if (gelf_getshdr(scn, &sh) == NULL) {162warnx("gelf_getshdr failed: %s", elf_errmsg(-1));163(void) elf_errno();164continue;165}166if ((sh.sh_flags & SHF_ALLOC) == 0 ||167sh.sh_type == SHT_NOBITS ||168sh.sh_size == 0)169continue;170if (sh.sh_addr > 0xFFFFFFFF) {171warnx("address space too big for S-Record file");172continue;173}174(void) elf_errno();175if ((d = elf_getdata(scn, NULL)) == NULL) {176elferr = elf_errno();177if (elferr != 0)178warnx("elf_getdata failed: %s", elf_errmsg(-1));179continue;180}181if (d->d_buf == NULL || d->d_size == 0)182continue;183srec_write_Sd(ofd, dr, sh.sh_addr, d->d_buf, d->d_size, rlen);184}185elferr = elf_errno();186if (elferr != 0)187warnx("elf_nextscn failed: %s", elf_errmsg(elferr));188189/* Generate S{7,8,9} end of block record. */190if (gelf_getehdr(e, &eh) == NULL)191errx(EXIT_FAILURE, "gelf_getehdr() failed: %s",192elf_errmsg(-1));193srec_write_Se(ofd, eh.e_entry, ecp->flags & SREC_FORCE_S3);194}195196void197create_elf_from_srec(struct elfcopy *ecp, int ifd)198{199char line[_LINE_BUFSZ], name[_LINE_BUFSZ];200uint8_t data[_DATA_BUFSZ];201GElf_Ehdr oeh;202struct section *s, *shtab;203FILE *ifp;204uint64_t addr, entry, off, sec_addr;205uintmax_t st_value;206size_t sz;207int _ifd, first, sec_index, in_symtab, symtab_created;208char *rlt;209char type;210211if ((_ifd = dup(ifd)) < 0)212err(EXIT_FAILURE, "dup failed");213if ((ifp = fdopen(_ifd, "r")) == NULL)214err(EXIT_FAILURE, "fdopen failed");215216/* Create EHDR for output .o file. */217if (gelf_newehdr(ecp->eout, ecp->oec) == NULL)218errx(EXIT_FAILURE, "gelf_newehdr failed: %s",219elf_errmsg(-1));220if (gelf_getehdr(ecp->eout, &oeh) == NULL)221errx(EXIT_FAILURE, "gelf_getehdr() failed: %s",222elf_errmsg(-1));223224/* Initialise e_ident fields. */225oeh.e_ident[EI_CLASS] = ecp->oec;226oeh.e_ident[EI_DATA] = ecp->oed;227/*228* TODO: Set OSABI according to the OS platform where elfcopy(1)229* was build. (probably)230*/231oeh.e_ident[EI_OSABI] = ELFOSABI_NONE;232oeh.e_machine = ecp->oem;233oeh.e_type = ET_REL;234oeh.e_entry = 0;235236ecp->flags |= RELOCATABLE;237238/* Create .shstrtab section */239init_shstrtab(ecp);240ecp->shstrtab->off = 0;241242/* Data sections are inserted after EHDR. */243off = gelf_fsize(ecp->eout, ELF_T_EHDR, 1, EV_CURRENT);244if (off == 0)245errx(EXIT_FAILURE, "gelf_fsize() failed: %s", elf_errmsg(-1));246247/* Create data sections. */248s = NULL;249first = 1;250sec_index = 1;251sec_addr = entry = 0;252while (fgets(line, _LINE_BUFSZ, ifp) != NULL) {253sz = 0;254if (line[0] == '\r' || line[0] == '\n')255continue;256if (line[0] == '$' && line[1] == '$') {257ecp->flags |= SYMTAB_EXIST;258while ((rlt = fgets(line, _LINE_BUFSZ, ifp)) != NULL) {259if (line[0] == '$' && line[1] == '$')260break;261}262if (rlt == NULL)263break;264continue;265}266if (line[0] != 'S' || line[1] < '0' || line[1] > '9') {267warnx("Invalid srec record");268continue;269}270if (srec_read(line, &type, &addr, data, &sz) < 0) {271warnx("Invalid srec record or mismatched checksum");272continue;273}274switch (type) {275case '1':276case '2':277case '3':278if (sz == 0)279break;280if (first || sec_addr != addr) {281if (s != NULL)282finalize_data_section(s);283s = new_data_section(ecp, sec_index, off,284addr);285if (s == NULL) {286warnx("new_data_section failed");287break;288}289sec_index++;290sec_addr = addr;291first = 0;292}293append_data(s, data, sz);294off += sz;295sec_addr += sz;296break;297case '7':298case '8':299case '9':300entry = addr;301break;302default:303break;304}305}306if (s != NULL)307finalize_data_section(s);308if (ferror(ifp))309warn("fgets failed");310311/* Insert .shstrtab after data sections. */312if ((ecp->shstrtab->os = elf_newscn(ecp->eout)) == NULL)313errx(EXIT_FAILURE, "elf_newscn failed: %s",314elf_errmsg(-1));315insert_to_sec_list(ecp, ecp->shstrtab, 1);316317/* Insert section header table here. */318shtab = insert_shtab(ecp, 1);319320/*321* Rescan and create symbol table if we found '$$' section in322* the first scan.323*/324symtab_created = 0;325in_symtab = 0;326if (ecp->flags & SYMTAB_EXIST) {327if (fseek(ifp, 0, SEEK_SET) < 0) {328warn("fseek failed");329ecp->flags &= ~SYMTAB_EXIST;330goto done;331}332while (fgets(line, _LINE_BUFSZ, ifp) != NULL) {333if (in_symtab) {334if (line[0] == '$' && line[1] == '$') {335in_symtab = 0;336continue;337}338if (sscanf(line, "%s $%jx", name,339&st_value) != 2) {340warnx("Invalid symbolsrec record");341continue;342}343if (!symtab_created) {344create_external_symtab(ecp);345symtab_created = 1;346}347add_to_symtab(ecp, name, st_value, 0, SHN_ABS,348ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, 1);349}350if (line[0] == '$' && line[1] == '$') {351in_symtab = 1;352continue;353}354}355}356if (ferror(ifp))357warn("fgets failed");358if (symtab_created) {359finalize_external_symtab(ecp);360create_symtab_data(ecp);361/* Count in .symtab and .strtab section headers. */362shtab->sz += gelf_fsize(ecp->eout, ELF_T_SHDR, 2, EV_CURRENT);363} else364ecp->flags &= ~SYMTAB_EXIST;365366done:367fclose(ifp);368369/* Set entry point. */370oeh.e_entry = entry;371372/*373* Write the underlying ehdr. Note that it should be called374* before elf_setshstrndx() since it will overwrite e->e_shstrndx.375*/376if (gelf_update_ehdr(ecp->eout, &oeh) == 0)377errx(EXIT_FAILURE, "gelf_update_ehdr() failed: %s",378elf_errmsg(-1));379380/* Update sh_name pointer for each section header entry. */381update_shdr(ecp, 0);382383/* Renew oeh to get the updated e_shstrndx. */384if (gelf_getehdr(ecp->eout, &oeh) == NULL)385errx(EXIT_FAILURE, "gelf_getehdr() failed: %s",386elf_errmsg(-1));387388/* Resync section offsets. */389resync_sections(ecp);390391/* Store SHDR offset in EHDR. */392oeh.e_shoff = shtab->off;393394/* Update ehdr since we modified e_shoff. */395if (gelf_update_ehdr(ecp->eout, &oeh) == 0)396errx(EXIT_FAILURE, "gelf_update_ehdr() failed: %s",397elf_errmsg(-1));398399/* Write out the output elf object. */400if (elf_update(ecp->eout, ELF_C_WRITE) < 0)401errx(EXIT_FAILURE, "elf_update() failed: %s",402elf_errmsg(-1));403404/* Release allocated resource. */405free_elf(ecp);406}407408void409create_ihex(int ifd, int ofd)410{411Elf *e;412Elf_Scn *scn;413Elf_Data *d;414GElf_Ehdr eh;415GElf_Shdr sh;416int elferr;417uint16_t addr_hi, old_addr_hi;418419if ((e = elf_begin(ifd, ELF_C_READ, NULL)) == NULL)420errx(EXIT_FAILURE, "elf_begin() failed: %s",421elf_errmsg(-1));422423old_addr_hi = 0;424scn = NULL;425while ((scn = elf_nextscn(e, scn)) != NULL) {426if (gelf_getshdr(scn, &sh) == NULL) {427warnx("gelf_getshdr failed: %s", elf_errmsg(-1));428(void) elf_errno();429continue;430}431if ((sh.sh_flags & SHF_ALLOC) == 0 ||432sh.sh_type == SHT_NOBITS ||433sh.sh_size == 0)434continue;435if (sh.sh_addr > 0xFFFFFFFF) {436warnx("address space too big for Intel Hex file");437continue;438}439(void) elf_errno();440if ((d = elf_getdata(scn, NULL)) == NULL) {441elferr = elf_errno();442if (elferr != 0)443warnx("elf_getdata failed: %s", elf_errmsg(-1));444continue;445}446if (d->d_buf == NULL || d->d_size == 0)447continue;448addr_hi = (sh.sh_addr >> 16) & 0xFFFF;449if (addr_hi > 0 && addr_hi != old_addr_hi) {450/* Write 04 record if addr_hi is new. */451old_addr_hi = addr_hi;452ihex_write_04(ofd, addr_hi);453}454ihex_write_00(ofd, sh.sh_addr, d->d_buf, d->d_size);455}456elferr = elf_errno();457if (elferr != 0)458warnx("elf_nextscn failed: %s", elf_errmsg(elferr));459460if (gelf_getehdr(e, &eh) == NULL)461errx(EXIT_FAILURE, "gelf_getehdr() failed: %s",462elf_errmsg(-1));463ihex_write_05(ofd, eh.e_entry);464ihex_write_01(ofd);465}466467void468create_elf_from_ihex(struct elfcopy *ecp, int ifd)469{470char line[_LINE_BUFSZ];471uint8_t data[_DATA_BUFSZ];472GElf_Ehdr oeh;473struct section *s, *shtab;474FILE *ifp;475uint64_t addr, addr_base, entry, num, off, rec_addr, sec_addr;476size_t sz;477int _ifd, first, sec_index;478char type;479480if ((_ifd = dup(ifd)) < 0)481err(EXIT_FAILURE, "dup failed");482if ((ifp = fdopen(_ifd, "r")) == NULL)483err(EXIT_FAILURE, "fdopen failed");484485/* Create EHDR for output .o file. */486if (gelf_newehdr(ecp->eout, ecp->oec) == NULL)487errx(EXIT_FAILURE, "gelf_newehdr failed: %s",488elf_errmsg(-1));489if (gelf_getehdr(ecp->eout, &oeh) == NULL)490errx(EXIT_FAILURE, "gelf_getehdr() failed: %s",491elf_errmsg(-1));492493/* Initialise e_ident fields. */494oeh.e_ident[EI_CLASS] = ecp->oec;495oeh.e_ident[EI_DATA] = ecp->oed;496/*497* TODO: Set OSABI according to the OS platform where elfcopy(1)498* was build. (probably)499*/500oeh.e_ident[EI_OSABI] = ELFOSABI_NONE;501oeh.e_machine = ecp->oem;502oeh.e_type = ET_REL;503oeh.e_entry = 0;504505ecp->flags |= RELOCATABLE;506507/* Create .shstrtab section */508init_shstrtab(ecp);509ecp->shstrtab->off = 0;510511/* Data sections are inserted after EHDR. */512off = gelf_fsize(ecp->eout, ELF_T_EHDR, 1, EV_CURRENT);513if (off == 0)514errx(EXIT_FAILURE, "gelf_fsize() failed: %s", elf_errmsg(-1));515516/* Create data sections. */517s = NULL;518first = 1;519sec_index = 1;520addr_base = rec_addr = sec_addr = entry = 0;521while (fgets(line, _LINE_BUFSZ, ifp) != NULL) {522if (line[0] == '\r' || line[0] == '\n')523continue;524if (line[0] != ':') {525warnx("Invalid ihex record");526continue;527}528if (ihex_read(line, &type, &addr, &num, data, &sz) < 0) {529warnx("Invalid ihex record or mismatched checksum");530continue;531}532switch (type) {533case '0':534/* Data record. */535if (sz == 0)536break;537rec_addr = addr_base + addr;538if (first || sec_addr != rec_addr) {539if (s != NULL)540finalize_data_section(s);541s = new_data_section(ecp, sec_index, off,542rec_addr);543if (s == NULL) {544warnx("new_data_section failed");545break;546}547sec_index++;548sec_addr = rec_addr;549first = 0;550}551append_data(s, data, sz);552off += sz;553sec_addr += sz;554break;555case '1':556/* End of file record. */557goto done;558case '2':559/* Extended segment address record. */560addr_base = addr << 4;561break;562case '3':563/* Start segment address record (CS:IP). Ignored. */564break;565case '4':566/* Extended linear address record. */567addr_base = num << 16;568break;569case '5':570/* Start linear address record. */571entry = num;572break;573default:574break;575}576}577done:578if (s != NULL)579finalize_data_section(s);580if (ferror(ifp))581warn("fgets failed");582fclose(ifp);583584/* Insert .shstrtab after data sections. */585if ((ecp->shstrtab->os = elf_newscn(ecp->eout)) == NULL)586errx(EXIT_FAILURE, "elf_newscn failed: %s",587elf_errmsg(-1));588insert_to_sec_list(ecp, ecp->shstrtab, 1);589590/* Insert section header table here. */591shtab = insert_shtab(ecp, 1);592593/* Set entry point. */594oeh.e_entry = entry;595596/*597* Write the underlying ehdr. Note that it should be called598* before elf_setshstrndx() since it will overwrite e->e_shstrndx.599*/600if (gelf_update_ehdr(ecp->eout, &oeh) == 0)601errx(EXIT_FAILURE, "gelf_update_ehdr() failed: %s",602elf_errmsg(-1));603604/* Update sh_name pointer for each section header entry. */605update_shdr(ecp, 0);606607/* Renew oeh to get the updated e_shstrndx. */608if (gelf_getehdr(ecp->eout, &oeh) == NULL)609errx(EXIT_FAILURE, "gelf_getehdr() failed: %s",610elf_errmsg(-1));611612/* Resync section offsets. */613resync_sections(ecp);614615/* Store SHDR offset in EHDR. */616oeh.e_shoff = shtab->off;617618/* Update ehdr since we modified e_shoff. */619if (gelf_update_ehdr(ecp->eout, &oeh) == 0)620errx(EXIT_FAILURE, "gelf_update_ehdr() failed: %s",621elf_errmsg(-1));622623/* Write out the output elf object. */624if (elf_update(ecp->eout, ELF_C_WRITE) < 0)625errx(EXIT_FAILURE, "elf_update() failed: %s",626elf_errmsg(-1));627628/* Release allocated resource. */629free_elf(ecp);630}631632#define _SEC_NAMESZ 64633#define _SEC_INIT_CAP 1024634635static struct section *636new_data_section(struct elfcopy *ecp, int sec_index, uint64_t off,637uint64_t addr)638{639char *name;640641if ((name = malloc(_SEC_NAMESZ)) == NULL)642errx(EXIT_FAILURE, "malloc failed");643snprintf(name, _SEC_NAMESZ, ".sec%d", sec_index);644645return (create_external_section(ecp, name, name, NULL, 0, off,646SHT_PROGBITS, ELF_T_BYTE, SHF_ALLOC | SHF_WRITE, 1, addr, 0));647}648649static void650finalize_data_section(struct section *s)651{652Elf_Data *od;653654if ((od = elf_newdata(s->os)) == NULL)655errx(EXIT_FAILURE, "elf_newdata() failed: %s",656elf_errmsg(-1));657od->d_align = s->align;658od->d_off = 0;659od->d_buf = s->buf;660od->d_size = s->sz;661od->d_version = EV_CURRENT;662}663664static void665append_data(struct section *s, const void *buf, size_t sz)666{667uint8_t *p;668669if (s->buf == NULL) {670s->sz = 0;671s->cap = _SEC_INIT_CAP;672if ((s->buf = malloc(s->cap)) == NULL)673err(EXIT_FAILURE, "malloc failed");674}675676while (sz + s->sz > s->cap) {677s->cap *= 2;678if ((s->buf = realloc(s->buf, s->cap)) == NULL)679err(EXIT_FAILURE, "realloc failed");680}681682p = s->buf;683memcpy(&p[s->sz], buf, sz);684s->sz += sz;685}686687static int688srec_read(const char *line, char *type, uint64_t *addr, uint8_t *data,689size_t *sz)690{691uint64_t count, _checksum, num;692size_t addr_sz;693int checksum, i, len;694695checksum = 0;696len = 2;697if (read_num(line, &len, &count, 1, &checksum) < 0)698return (-1);699*type = line[1];700switch (*type) {701case '0':702case '1':703case '5':704case '9':705addr_sz = 2;706break;707case '2':708case '8':709addr_sz = 3;710break;711case '3':712case '7':713addr_sz = 4;714break;715default:716return (-1);717}718719if (read_num(line, &len, addr, addr_sz, &checksum) < 0)720return (-1);721722count -= addr_sz + 1;723if (*type >= '0' && *type <= '3') {724for (i = 0; (uint64_t) i < count; i++) {725if (read_num(line, &len, &num, 1, &checksum) < 0)726return -1;727data[i] = (uint8_t) num;728}729*sz = count;730} else731*sz = 0;732733if (read_num(line, &len, &_checksum, 1, NULL) < 0)734return (-1);735736if ((int) _checksum != (~checksum & 0xFF))737return (-1);738739return (0);740}741742static void743srec_write_symtab(int ofd, const char *ofn, Elf *e, Elf_Scn *scn, GElf_Shdr *sh)744{745char line[_LINE_BUFSZ];746GElf_Sym sym;747Elf_Data *d;748const char *name;749size_t sc;750int elferr, i;751752#define _WRITE_LINE do { \753if (write(ofd, line, strlen(line)) != (ssize_t) strlen(line)) \754errx(EXIT_FAILURE, "write failed"); \755} while (0)756757758(void) elf_errno();759if ((d = elf_getdata(scn, NULL)) == NULL) {760elferr = elf_errno();761if (elferr != 0)762warnx("elf_getdata failed: %s",763elf_errmsg(-1));764return;765}766if (d->d_buf == NULL || d->d_size == 0)767return;768769snprintf(line, sizeof(line), "$$ %s\r\n", ofn);770_WRITE_LINE;771sc = d->d_size / sh->sh_entsize;772for (i = 1; (size_t) i < sc; i++) {773if (gelf_getsym(d, i, &sym) != &sym) {774warnx("gelf_getsym failed: %s", elf_errmsg(-1));775continue;776}777if (GELF_ST_TYPE(sym.st_info) == STT_SECTION ||778GELF_ST_TYPE(sym.st_info) == STT_FILE)779continue;780if ((name = elf_strptr(e, sh->sh_link, sym.st_name)) == NULL) {781warnx("elf_strptr failed: %s", elf_errmsg(-1));782continue;783}784snprintf(line, sizeof(line), " %s $%jx\r\n", name,785(uintmax_t) sym.st_value);786_WRITE_LINE;787}788snprintf(line, sizeof(line), "$$ \r\n");789_WRITE_LINE;790791#undef _WRITE_LINE792}793794static void795srec_write_S0(int ofd, const char *ofn)796{797798srec_write(ofd, '0', 0, ofn, strlen(ofn));799}800801static void802srec_write_Sd(int ofd, char dr, uint64_t addr, const void *buf, size_t sz,803size_t rlen)804{805const uint8_t *p, *pe;806807p = buf;808pe = p + sz;809while (pe - p >= (int) rlen) {810srec_write(ofd, dr, addr, p, rlen);811addr += rlen;812p += rlen;813}814if (pe - p > 0)815srec_write(ofd, dr, addr, p, pe - p);816}817818static void819srec_write_Se(int ofd, uint64_t e_entry, int forceS3)820{821char er;822823if (e_entry > 0xFFFFFFFF) {824warnx("address space too big for S-Record file");825return;826}827828if (forceS3)829er = '7';830else {831if (e_entry <= 0xFFFF)832er = '9';833else if (e_entry <= 0xFFFFFF)834er = '8';835else836er = '7';837}838839srec_write(ofd, er, e_entry, NULL, 0);840}841842static void843srec_write(int ofd, char type, uint64_t addr, const void *buf, size_t sz)844{845char line[_LINE_BUFSZ];846const uint8_t *p, *pe;847int len, addr_sz, checksum;848849if (type == '0' || type == '1' || type == '5' || type == '9')850addr_sz = 2;851else if (type == '2' || type == '8')852addr_sz = 3;853else854addr_sz = 4;855856checksum = 0;857line[0] = 'S';858line[1] = type;859len = 2;860write_num(line, &len, addr_sz + sz + 1, 1, &checksum);861write_num(line, &len, addr, addr_sz, &checksum);862for (p = buf, pe = p + sz; p < pe; p++)863write_num(line, &len, *p, 1, &checksum);864write_num(line, &len, ~checksum & 0xFF, 1, NULL);865line[len++] = '\r';866line[len++] = '\n';867if (write(ofd, line, len) != (ssize_t) len)868err(EXIT_FAILURE, "write failed");869}870871static void872ihex_write_00(int ofd, uint64_t addr, const void *buf, size_t sz)873{874uint16_t addr_hi, old_addr_hi;875const uint8_t *p, *pe;876877old_addr_hi = (addr >> 16) & 0xFFFF;878p = buf;879pe = p + sz;880while (pe - p >= 16) {881ihex_write(ofd, 0, addr, 0, p, 16);882addr += 16;883p += 16;884addr_hi = (addr >> 16) & 0xFFFF;885if (addr_hi != old_addr_hi) {886old_addr_hi = addr_hi;887ihex_write_04(ofd, addr_hi);888}889}890if (pe - p > 0)891ihex_write(ofd, 0, addr, 0, p, pe - p);892}893894static int895ihex_read(const char *line, char *type, uint64_t *addr, uint64_t *num,896uint8_t *data, size_t *sz)897{898uint64_t count, _checksum;899int checksum, i, len;900901*sz = 0;902checksum = 0;903len = 1;904if (read_num(line, &len, &count, 1, &checksum) < 0)905return (-1);906if (read_num(line, &len, addr, 2, &checksum) < 0)907return (-1);908if (line[len++] != '0')909return (-1);910*type = line[len++];911checksum += *type - '0';912switch (*type) {913case '0':914for (i = 0; (uint64_t) i < count; i++) {915if (read_num(line, &len, num, 1, &checksum) < 0)916return (-1);917data[i] = (uint8_t) *num;918}919*sz = count;920break;921case '1':922if (count != 0)923return (-1);924break;925case '2':926case '4':927if (count != 2)928return (-1);929if (read_num(line, &len, num, 2, &checksum) < 0)930return (-1);931break;932case '3':933case '5':934if (count != 4)935return (-1);936if (read_num(line, &len, num, 4, &checksum) < 0)937return (-1);938break;939default:940return (-1);941}942943if (read_num(line, &len, &_checksum, 1, &checksum) < 0)944return (-1);945946if ((checksum & 0xFF) != 0) {947return (-1);948}949950return (0);951}952953static void954ihex_write_01(int ofd)955{956957ihex_write(ofd, 1, 0, 0, NULL, 0);958}959960static void961ihex_write_04(int ofd, uint16_t addr)962{963964ihex_write(ofd, 4, 0, addr, NULL, 2);965}966967static void968ihex_write_05(int ofd, uint64_t e_entry)969{970971if (e_entry > 0xFFFFFFFF) {972warnx("address space too big for Intel Hex file");973return;974}975976ihex_write(ofd, 5, 0, e_entry, NULL, 4);977}978979static void980ihex_write(int ofd, int type, uint64_t addr, uint64_t num, const void *buf,981size_t sz)982{983char line[_LINE_BUFSZ];984const uint8_t *p, *pe;985int len, checksum;986987if (sz > 16)988errx(EXIT_FAILURE, "Internal: ihex_write() sz too big");989checksum = 0;990line[0] = ':';991len = 1;992write_num(line, &len, sz, 1, &checksum);993write_num(line, &len, addr, 2, &checksum);994write_num(line, &len, type, 1, &checksum);995if (sz > 0) {996if (buf != NULL) {997for (p = buf, pe = p + sz; p < pe; p++)998write_num(line, &len, *p, 1, &checksum);999} else1000write_num(line, &len, num, sz, &checksum);1001}1002write_num(line, &len, (~checksum + 1) & 0xFF, 1, NULL);1003line[len++] = '\r';1004line[len++] = '\n';1005if (write(ofd, line, len) != (ssize_t) len)1006err(EXIT_FAILURE, "write failed");1007}10081009static int1010read_num(const char *line, int *len, uint64_t *num, size_t sz, int *checksum)1011{1012uint8_t b;10131014*num = 0;1015for (; sz > 0; sz--) {1016if (!ishexdigit(line[*len]) || !ishexdigit(line[*len + 1]))1017return (-1);1018b = (hex_value(line[*len]) << 4) | hex_value(line[*len + 1]);1019*num = (*num << 8) | b;1020*len += 2;1021if (checksum != NULL)1022*checksum = (*checksum + b) & 0xFF;1023}10241025return (0);1026}10271028static void1029write_num(char *line, int *len, uint64_t num, size_t sz, int *checksum)1030{1031uint8_t b;10321033for (; sz > 0; sz--) {1034b = (num >> ((sz - 1) * 8)) & 0xFF;1035line[*len] = hex_digit((b >> 4) & 0xF);1036line[*len + 1] = hex_digit(b & 0xF);1037*len += 2;1038if (checksum != NULL)1039*checksum = (*checksum + b) & 0xFF;1040}1041}10421043static char1044hex_digit(uint8_t n)1045{10461047return ((n < 10) ? '0' + n : 'A' + (n - 10));1048}10491050static int1051hex_value(int x)1052{10531054if (isdigit(x))1055return (x - '0');1056else if (x >= 'a' && x <= 'f')1057return (x - 'a' + 10);1058else1059return (x - 'A' + 10);1060}10611062static int1063ishexdigit(int x)1064{10651066if (isdigit(x))1067return (1);1068if ((x >= 'a' && x <= 'f') || (x >= 'A' && x <= 'F'))1069return (1);10701071return (0);1072}107310741075