]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/elftoolchain/elfcopy/ascii.c
MFC r355413: Update ELF Tool Chain to upstream r3769
[FreeBSD/FreeBSD.git] / contrib / elftoolchain / elfcopy / ascii.c
1 /*-
2  * Copyright (c) 2010,2011 Kai Wang
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/param.h>
28 #include <ctype.h>
29 #include <err.h>
30 #include <gelf.h>
31 #include <stdint.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36
37 #include "elfcopy.h"
38
39 ELFTC_VCSID("$Id: ascii.c 3757 2019-06-28 01:15:28Z emaste $");
40
41 static void append_data(struct section *s, const void *buf, size_t sz);
42 static char hex_digit(uint8_t n);
43 static int hex_value(int x);
44 static void finalize_data_section(struct section *s);
45 static int ishexdigit(int x);
46 static int ihex_read(const char *line, char *type, uint64_t *addr,
47     uint64_t *num, uint8_t *data, size_t *sz);
48 static void ihex_write(int ofd, int type, uint64_t addr, uint64_t num,
49     const void *buf, size_t sz);
50 static void ihex_write_00(int ofd, uint64_t addr, const void *buf, size_t sz);
51 static void ihex_write_01(int ofd);
52 static void ihex_write_04(int ofd, uint16_t addr);
53 static void ihex_write_05(int ofd, uint64_t e_entry);
54 static struct section *new_data_section(struct elfcopy *ecp, int sec_index,
55     uint64_t off, uint64_t addr);
56 static int read_num(const char *line, int *len, uint64_t *num, size_t sz,
57     int *checksum);
58 static int srec_read(const char *line, char *type, uint64_t *addr,
59     uint8_t *data, size_t *sz);
60 static void srec_write(int ofd, char type, uint64_t addr, const void *buf,
61     size_t sz);
62 static void srec_write_symtab(int ofd, const char *ofn, Elf *e, Elf_Scn *scn,
63     GElf_Shdr *sh);
64 static void srec_write_S0(int ofd, const char *ofn);
65 static void srec_write_Sd(int ofd, char dr, uint64_t addr, const void *buf,
66     size_t sz, size_t rlen);
67 static void srec_write_Se(int ofd, uint64_t e_entry, int forceS3);
68 static void write_num(char *line, int *len, uint64_t num, size_t sz,
69     int *checksum);
70
71 #define _LINE_BUFSZ     1024
72 #define _DATA_BUFSZ     256
73
74 /*
75  * Convert ELF object to S-Record.
76  */
77 void
78 create_srec(struct elfcopy *ecp, int ifd, int ofd, const char *ofn)
79 {
80         Elf *e;
81         Elf_Scn *scn;
82         Elf_Data *d;
83         GElf_Ehdr eh;
84         GElf_Shdr sh;
85         uint64_t max_addr;
86         size_t rlen;
87         int elferr, addr_sz;
88         char dr;
89
90         if ((e = elf_begin(ifd, ELF_C_READ, NULL)) == NULL)
91                 errx(EXIT_FAILURE, "elf_begin() failed: %s",
92                     elf_errmsg(-1));
93
94         /* Output a symbol table for `symbolsrec' target. */
95         if (!strncmp(ecp->otgt, "symbolsrec", strlen("symbolsrec"))) {
96                 scn = NULL;
97                 while ((scn = elf_nextscn(e, scn)) != NULL) {
98                         if (gelf_getshdr(scn, &sh) == NULL) {
99                                 warnx("gelf_getshdr failed: %s",
100                                     elf_errmsg(-1));
101                                 (void) elf_errno();
102                                 continue;
103                         }
104                         if (sh.sh_type != SHT_SYMTAB)
105                                 continue;
106                         srec_write_symtab(ofd, ofn, e, scn, &sh);
107                         break;
108                 }
109         }
110
111         if (ecp->flags & SREC_FORCE_S3)
112                 dr = '3';
113         else {
114                 /*
115                  * Find maximum address size in the first iteration.
116                  */
117                 max_addr = 0;
118                 scn = NULL;
119                 while ((scn = elf_nextscn(e, scn)) != NULL) {
120                         if (gelf_getshdr(scn, &sh) == NULL) {
121                                 warnx("gelf_getshdr failed: %s",
122                                     elf_errmsg(-1));
123                                 (void) elf_errno();
124                                 continue;
125                         }
126                         if ((sh.sh_flags & SHF_ALLOC) == 0 ||
127                             sh.sh_type == SHT_NOBITS ||
128                             sh.sh_size == 0)
129                                 continue;
130                         if ((uint64_t) sh.sh_addr > max_addr)
131                                 max_addr = sh.sh_addr;
132                 }
133                 elferr = elf_errno();
134                 if (elferr != 0)
135                         warnx("elf_nextscn failed: %s", elf_errmsg(elferr));
136
137                 if (max_addr <= 0xFFFF)
138                         dr = '1';
139                 else if (max_addr <= 0xFFFFFF)
140                         dr = '2';
141                 else
142                         dr = '3';
143         }
144
145         if (ecp->flags & SREC_FORCE_LEN) {
146                 addr_sz = dr - '0' + 1;
147                 if (ecp->srec_len < 1)
148                         rlen = 1;
149                 else if (ecp->srec_len + addr_sz + 1 > 255)
150                         rlen = 255 - (addr_sz + 1);
151                 else
152                         rlen = ecp->srec_len;
153         } else
154                 rlen = 16;
155
156         /* Generate S0 record which contains the output filename. */
157         srec_write_S0(ofd, ofn);
158
159         /* Generate S{1,2,3} data records for section data. */
160         scn = NULL;
161         while ((scn = elf_nextscn(e, scn)) != NULL) {
162                 if (gelf_getshdr(scn, &sh) == NULL) {
163                         warnx("gelf_getshdr failed: %s", elf_errmsg(-1));
164                         (void) elf_errno();
165                         continue;
166                 }
167                 if ((sh.sh_flags & SHF_ALLOC) == 0 ||
168                     sh.sh_type == SHT_NOBITS ||
169                     sh.sh_size == 0)
170                         continue;
171                 if (sh.sh_addr > 0xFFFFFFFF) {
172                         warnx("address space too big for S-Record file");
173                         continue;
174                 }
175                 (void) elf_errno();
176                 if ((d = elf_getdata(scn, NULL)) == NULL) {
177                         elferr = elf_errno();
178                         if (elferr != 0)
179                                 warnx("elf_getdata failed: %s", elf_errmsg(-1));
180                         continue;
181                 }
182                 if (d->d_buf == NULL || d->d_size == 0)
183                         continue;
184                 srec_write_Sd(ofd, dr, sh.sh_addr, d->d_buf, d->d_size, rlen);
185         }
186         elferr = elf_errno();
187         if (elferr != 0)
188                 warnx("elf_nextscn failed: %s", elf_errmsg(elferr));
189
190         /* Generate S{7,8,9} end of block record. */
191         if (gelf_getehdr(e, &eh) == NULL)
192                 errx(EXIT_FAILURE, "gelf_getehdr() failed: %s",
193                     elf_errmsg(-1));
194         srec_write_Se(ofd, eh.e_entry, ecp->flags & SREC_FORCE_S3);
195 }
196
197 void
198 create_elf_from_srec(struct elfcopy *ecp, int ifd)
199 {
200         char line[_LINE_BUFSZ], name[_LINE_BUFSZ];
201         uint8_t data[_DATA_BUFSZ];
202         GElf_Ehdr oeh;
203         struct section *s, *shtab;
204         FILE *ifp;
205         uint64_t addr, entry, off, sec_addr;
206         uintmax_t st_value;
207         size_t sz;
208         int _ifd, first, sec_index, in_symtab, symtab_created;
209         char *rlt;
210         char type;
211
212         if ((_ifd = dup(ifd)) < 0)
213                 err(EXIT_FAILURE, "dup failed");
214         if ((ifp = fdopen(_ifd, "r")) == NULL)
215                 err(EXIT_FAILURE, "fdopen failed");
216
217         /* Create EHDR for output .o file. */
218         if (gelf_newehdr(ecp->eout, ecp->oec) == NULL)
219                 errx(EXIT_FAILURE, "gelf_newehdr failed: %s",
220                     elf_errmsg(-1));
221         if (gelf_getehdr(ecp->eout, &oeh) == NULL)
222                 errx(EXIT_FAILURE, "gelf_getehdr() failed: %s",
223                     elf_errmsg(-1));
224
225         /* Initialise e_ident fields. */
226         oeh.e_ident[EI_CLASS] = ecp->oec;
227         oeh.e_ident[EI_DATA] = ecp->oed;
228         /*
229          * TODO: Set OSABI according to the OS platform where elfcopy(1)
230          * was build. (probably)
231          */
232         oeh.e_ident[EI_OSABI] = ELFOSABI_NONE;
233         oeh.e_machine = ecp->oem;
234         oeh.e_type = ET_REL;
235         oeh.e_entry = 0;
236
237         ecp->flags |= RELOCATABLE;
238
239         /* Create .shstrtab section */
240         init_shstrtab(ecp);
241         ecp->shstrtab->off = 0;
242
243         /* Data sections are inserted after EHDR. */
244         off = gelf_fsize(ecp->eout, ELF_T_EHDR, 1, EV_CURRENT);
245         if (off == 0)
246                 errx(EXIT_FAILURE, "gelf_fsize() failed: %s", elf_errmsg(-1));
247
248         /* Create data sections. */
249         s = NULL;
250         first = 1;
251         sec_index = 1;
252         sec_addr = entry = 0;
253         while (fgets(line, _LINE_BUFSZ, ifp) != NULL) {
254                 sz = 0;
255                 if (line[0] == '\r' || line[0] == '\n')
256                         continue;
257                 if (line[0] == '$' && line[1] == '$') {
258                         ecp->flags |= SYMTAB_EXIST;
259                         while ((rlt = fgets(line, _LINE_BUFSZ, ifp)) != NULL) {
260                                 if (line[0] == '$' && line[1] == '$')
261                                         break;
262                         }
263                         if (rlt == NULL)
264                                 break;
265                         continue;
266                 }
267                 if (line[0] != 'S' || line[1] < '0' || line[1] > '9') {
268                         warnx("Invalid srec record");
269                         continue;
270                 }
271                 if (srec_read(line, &type, &addr, data, &sz) < 0) {
272                         warnx("Invalid srec record or mismatched checksum");
273                         continue;
274                 }
275                 switch (type) {
276                 case '1':
277                 case '2':
278                 case '3':
279                         if (sz == 0)
280                                 break;
281                         if (first || sec_addr != addr) {
282                                 if (s != NULL)
283                                         finalize_data_section(s);
284                                 s = new_data_section(ecp, sec_index, off,
285                                     addr);
286                                 if (s == NULL) {
287                                         warnx("new_data_section failed");
288                                         break;
289                                 }
290                                 sec_index++;
291                                 sec_addr = addr;
292                                 first = 0;
293                         }
294                         append_data(s, data, sz);
295                         off += sz;
296                         sec_addr += sz;
297                         break;
298                 case '7':
299                 case '8':
300                 case '9':
301                         entry = addr;
302                         break;
303                 default:
304                         break;
305                 }
306         }
307         if (s != NULL)
308                 finalize_data_section(s);
309         if (ferror(ifp))
310                 warn("fgets failed");
311
312         /* Insert .shstrtab after data sections. */
313         if ((ecp->shstrtab->os = elf_newscn(ecp->eout)) == NULL)
314                 errx(EXIT_FAILURE, "elf_newscn failed: %s",
315                     elf_errmsg(-1));
316         insert_to_sec_list(ecp, ecp->shstrtab, 1);
317
318         /* Insert section header table here. */
319         shtab = insert_shtab(ecp, 1);
320
321         /*
322          * Rescan and create symbol table if we found '$$' section in
323          * the first scan.
324          */
325         symtab_created = 0;
326         in_symtab = 0;
327         if (ecp->flags & SYMTAB_EXIST) {
328                 if (fseek(ifp, 0, SEEK_SET) < 0) {
329                         warn("fseek failed");
330                         ecp->flags &= ~SYMTAB_EXIST;
331                         goto done;
332                 }
333                 while (fgets(line, _LINE_BUFSZ, ifp) != NULL) {
334                         if (in_symtab) {
335                                 if (line[0] == '$' && line[1] == '$') {
336                                         in_symtab = 0;
337                                         continue;
338                                 }
339                                 if (sscanf(line, "%s $%jx", name,
340                                     &st_value) != 2) {
341                                         warnx("Invalid symbolsrec record");
342                                         continue;
343                                 }
344                                 if (!symtab_created) {
345                                         create_external_symtab(ecp);
346                                         symtab_created = 1;
347                                 }
348                                 add_to_symtab(ecp, name, st_value, 0, SHN_ABS,
349                                     ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, 1);
350                         }
351                         if (line[0] == '$' && line[1] == '$') {
352                                 in_symtab = 1;
353                                 continue;
354                         }
355                 }
356         }
357         if (ferror(ifp))
358                 warn("fgets failed");
359         if (symtab_created) {
360                 finalize_external_symtab(ecp);
361                 create_symtab_data(ecp);
362                 /* Count in .symtab and .strtab section headers.  */
363                 shtab->sz += gelf_fsize(ecp->eout, ELF_T_SHDR, 2, EV_CURRENT);
364         } else
365                 ecp->flags &= ~SYMTAB_EXIST;
366
367 done:
368         fclose(ifp);
369
370         /* Set entry point. */
371         oeh.e_entry = entry;
372
373         /*
374          * Write the underlying ehdr. Note that it should be called
375          * before elf_setshstrndx() since it will overwrite e->e_shstrndx.
376          */
377         if (gelf_update_ehdr(ecp->eout, &oeh) == 0)
378                 errx(EXIT_FAILURE, "gelf_update_ehdr() failed: %s",
379                     elf_errmsg(-1));
380
381         /* Update sh_name pointer for each section header entry. */
382         update_shdr(ecp, 0);
383
384         /* Renew oeh to get the updated e_shstrndx. */
385         if (gelf_getehdr(ecp->eout, &oeh) == NULL)
386                 errx(EXIT_FAILURE, "gelf_getehdr() failed: %s",
387                     elf_errmsg(-1));
388
389         /* Resync section offsets. */
390         resync_sections(ecp);
391
392         /* Store SHDR offset in EHDR. */
393         oeh.e_shoff = shtab->off;
394
395         /* Update ehdr since we modified e_shoff. */
396         if (gelf_update_ehdr(ecp->eout, &oeh) == 0)
397                 errx(EXIT_FAILURE, "gelf_update_ehdr() failed: %s",
398                     elf_errmsg(-1));
399
400         /* Write out the output elf object. */
401         if (elf_update(ecp->eout, ELF_C_WRITE) < 0)
402                 errx(EXIT_FAILURE, "elf_update() failed: %s",
403                     elf_errmsg(-1));
404
405         /* Release allocated resource. */
406         free_elf(ecp);
407 }
408
409 void
410 create_ihex(int ifd, int ofd)
411 {
412         Elf *e;
413         Elf_Scn *scn;
414         Elf_Data *d;
415         GElf_Ehdr eh;
416         GElf_Shdr sh;
417         int elferr;
418         uint16_t addr_hi, old_addr_hi;
419
420         if ((e = elf_begin(ifd, ELF_C_READ, NULL)) == NULL)
421                 errx(EXIT_FAILURE, "elf_begin() failed: %s",
422                     elf_errmsg(-1));
423
424         old_addr_hi = 0;
425         scn = NULL;
426         while ((scn = elf_nextscn(e, scn)) != NULL) {
427                 if (gelf_getshdr(scn, &sh) == NULL) {
428                         warnx("gelf_getshdr failed: %s", elf_errmsg(-1));
429                         (void) elf_errno();
430                         continue;
431                 }
432                 if ((sh.sh_flags & SHF_ALLOC) == 0 ||
433                     sh.sh_type == SHT_NOBITS ||
434                     sh.sh_size == 0)
435                         continue;
436                 if (sh.sh_addr > 0xFFFFFFFF) {
437                         warnx("address space too big for Intel Hex file");
438                         continue;
439                 }
440                 (void) elf_errno();
441                 if ((d = elf_getdata(scn, NULL)) == NULL) {
442                         elferr = elf_errno();
443                         if (elferr != 0)
444                                 warnx("elf_getdata failed: %s", elf_errmsg(-1));
445                         continue;
446                 }
447                 if (d->d_buf == NULL || d->d_size == 0)
448                         continue;
449                 addr_hi = (sh.sh_addr >> 16) & 0xFFFF;
450                 if (addr_hi > 0 && addr_hi != old_addr_hi) {
451                         /* Write 04 record if addr_hi is new. */
452                         old_addr_hi = addr_hi;
453                         ihex_write_04(ofd, addr_hi);
454                 }
455                 ihex_write_00(ofd, sh.sh_addr, d->d_buf, d->d_size);
456         }
457         elferr = elf_errno();
458         if (elferr != 0)
459                 warnx("elf_nextscn failed: %s", elf_errmsg(elferr));
460
461         if (gelf_getehdr(e, &eh) == NULL)
462                 errx(EXIT_FAILURE, "gelf_getehdr() failed: %s",
463                     elf_errmsg(-1));
464         ihex_write_05(ofd, eh.e_entry);
465         ihex_write_01(ofd);
466 }
467
468 void
469 create_elf_from_ihex(struct elfcopy *ecp, int ifd)
470 {
471         char line[_LINE_BUFSZ];
472         uint8_t data[_DATA_BUFSZ];
473         GElf_Ehdr oeh;
474         struct section *s, *shtab;
475         FILE *ifp;
476         uint64_t addr, addr_base, entry, num, off, rec_addr, sec_addr;
477         size_t sz;
478         int _ifd, first, sec_index;
479         char type;
480
481         if ((_ifd = dup(ifd)) < 0)
482                 err(EXIT_FAILURE, "dup failed");
483         if ((ifp = fdopen(_ifd, "r")) == NULL)
484                 err(EXIT_FAILURE, "fdopen failed");
485
486         /* Create EHDR for output .o file. */
487         if (gelf_newehdr(ecp->eout, ecp->oec) == NULL)
488                 errx(EXIT_FAILURE, "gelf_newehdr failed: %s",
489                     elf_errmsg(-1));
490         if (gelf_getehdr(ecp->eout, &oeh) == NULL)
491                 errx(EXIT_FAILURE, "gelf_getehdr() failed: %s",
492                     elf_errmsg(-1));
493
494         /* Initialise e_ident fields. */
495         oeh.e_ident[EI_CLASS] = ecp->oec;
496         oeh.e_ident[EI_DATA] = ecp->oed;
497         /*
498          * TODO: Set OSABI according to the OS platform where elfcopy(1)
499          * was build. (probably)
500          */
501         oeh.e_ident[EI_OSABI] = ELFOSABI_NONE;
502         oeh.e_machine = ecp->oem;
503         oeh.e_type = ET_REL;
504         oeh.e_entry = 0;
505
506         ecp->flags |= RELOCATABLE;
507
508         /* Create .shstrtab section */
509         init_shstrtab(ecp);
510         ecp->shstrtab->off = 0;
511
512         /* Data sections are inserted after EHDR. */
513         off = gelf_fsize(ecp->eout, ELF_T_EHDR, 1, EV_CURRENT);
514         if (off == 0)
515                 errx(EXIT_FAILURE, "gelf_fsize() failed: %s", elf_errmsg(-1));
516
517         /* Create data sections. */
518         s = NULL;
519         first = 1;
520         sec_index = 1;
521         addr_base = rec_addr = sec_addr = entry = 0;
522         while (fgets(line, _LINE_BUFSZ, ifp) != NULL) {
523                 if (line[0] == '\r' || line[0] == '\n')
524                         continue;
525                 if (line[0] != ':') {
526                         warnx("Invalid ihex record");
527                         continue;
528                 }
529                 if (ihex_read(line, &type, &addr, &num, data, &sz) < 0) {
530                         warnx("Invalid ihex record or mismatched checksum");
531                         continue;
532                 }
533                 switch (type) {
534                 case '0':
535                         /* Data record. */
536                         if (sz == 0)
537                                 break;
538                         rec_addr = addr_base + addr;
539                         if (first || sec_addr != rec_addr) {
540                                 if (s != NULL)
541                                         finalize_data_section(s);
542                                 s = new_data_section(ecp, sec_index, off,
543                                     rec_addr);
544                                 if (s == NULL) {
545                                         warnx("new_data_section failed");
546                                         break;
547                                 }
548                                 sec_index++;
549                                 sec_addr = rec_addr;
550                                 first = 0;
551                         }
552                         append_data(s, data, sz);
553                         off += sz;
554                         sec_addr += sz;
555                         break;
556                 case '1':
557                         /* End of file record. */
558                         goto done;
559                 case '2':
560                         /* Extended segment address record. */
561                         addr_base = addr << 4;
562                         break;
563                 case '3':
564                         /* Start segment address record (CS:IP). Ignored. */
565                         break;
566                 case '4':
567                         /* Extended linear address record. */
568                         addr_base = num << 16;
569                         break;
570                 case '5':
571                         /* Start linear address record. */
572                         entry = num;
573                         break;
574                 default:
575                         break;
576                 }
577         }
578 done:
579         if (s != NULL)
580                 finalize_data_section(s);
581         if (ferror(ifp))
582                 warn("fgets failed");
583         fclose(ifp);
584
585         /* Insert .shstrtab after data sections. */
586         if ((ecp->shstrtab->os = elf_newscn(ecp->eout)) == NULL)
587                 errx(EXIT_FAILURE, "elf_newscn failed: %s",
588                     elf_errmsg(-1));
589         insert_to_sec_list(ecp, ecp->shstrtab, 1);
590
591         /* Insert section header table here. */
592         shtab = insert_shtab(ecp, 1);
593
594         /* Set entry point. */
595         oeh.e_entry = entry;
596
597         /*
598          * Write the underlying ehdr. Note that it should be called
599          * before elf_setshstrndx() since it will overwrite e->e_shstrndx.
600          */
601         if (gelf_update_ehdr(ecp->eout, &oeh) == 0)
602                 errx(EXIT_FAILURE, "gelf_update_ehdr() failed: %s",
603                     elf_errmsg(-1));
604
605         /* Update sh_name pointer for each section header entry. */
606         update_shdr(ecp, 0);
607
608         /* Renew oeh to get the updated e_shstrndx. */
609         if (gelf_getehdr(ecp->eout, &oeh) == NULL)
610                 errx(EXIT_FAILURE, "gelf_getehdr() failed: %s",
611                     elf_errmsg(-1));
612
613         /* Resync section offsets. */
614         resync_sections(ecp);
615
616         /* Store SHDR offset in EHDR. */
617         oeh.e_shoff = shtab->off;
618
619         /* Update ehdr since we modified e_shoff. */
620         if (gelf_update_ehdr(ecp->eout, &oeh) == 0)
621                 errx(EXIT_FAILURE, "gelf_update_ehdr() failed: %s",
622                     elf_errmsg(-1));
623
624         /* Write out the output elf object. */
625         if (elf_update(ecp->eout, ELF_C_WRITE) < 0)
626                 errx(EXIT_FAILURE, "elf_update() failed: %s",
627                     elf_errmsg(-1));
628
629         /* Release allocated resource. */
630         free_elf(ecp);
631 }
632
633 #define _SEC_NAMESZ     64
634 #define _SEC_INIT_CAP   1024
635
636 static struct section *
637 new_data_section(struct elfcopy *ecp, int sec_index, uint64_t off,
638     uint64_t addr)
639 {
640         char *name;
641
642         if ((name = malloc(_SEC_NAMESZ)) == NULL)
643                 errx(EXIT_FAILURE, "malloc failed");
644         snprintf(name, _SEC_NAMESZ, ".sec%d", sec_index);
645
646         return (create_external_section(ecp, name, name, NULL, 0, off,
647                 SHT_PROGBITS, ELF_T_BYTE, SHF_ALLOC | SHF_WRITE, 1, addr, 0));
648 }
649
650 static void
651 finalize_data_section(struct section *s)
652 {
653         Elf_Data *od;
654
655         if ((od = elf_newdata(s->os)) == NULL)
656                 errx(EXIT_FAILURE, "elf_newdata() failed: %s",
657                     elf_errmsg(-1));
658         od->d_align = s->align;
659         od->d_off = 0;
660         od->d_buf = s->buf;
661         od->d_size = s->sz;
662         od->d_version = EV_CURRENT;
663 }
664
665 static void
666 append_data(struct section *s, const void *buf, size_t sz)
667 {
668         uint8_t *p;
669
670         if (s->buf == NULL) {
671                 s->sz = 0;
672                 s->cap = _SEC_INIT_CAP;
673                 if ((s->buf = malloc(s->cap)) == NULL)
674                         err(EXIT_FAILURE, "malloc failed");
675         }
676
677         while (sz + s->sz > s->cap) {
678                 s->cap *= 2;
679                 if ((s->buf = realloc(s->buf, s->cap)) == NULL)
680                         err(EXIT_FAILURE, "realloc failed");
681         }
682
683         p = s->buf;
684         memcpy(&p[s->sz], buf, sz);
685         s->sz += sz;
686 }
687
688 static int
689 srec_read(const char *line, char *type, uint64_t *addr, uint8_t *data,
690     size_t *sz)
691 {
692         uint64_t count, _checksum, num;
693         size_t addr_sz;
694         int checksum, i, len;
695
696         checksum = 0;
697         len = 2;
698         if (read_num(line, &len, &count, 1, &checksum) < 0)
699                 return (-1);
700         *type = line[1];
701         switch (*type) {
702         case '0':
703         case '1':
704         case '5':
705         case '9':
706                 addr_sz = 2;
707                 break;
708         case '2':
709         case '8':
710                 addr_sz = 3;
711                 break;
712         case '3':
713         case '7':
714                 addr_sz = 4;
715                 break;
716         default:
717                 return (-1);
718         }
719
720         if (read_num(line, &len, addr, addr_sz, &checksum) < 0)
721                 return (-1);
722
723         count -= addr_sz + 1;
724         if (*type >= '0' && *type <= '3') {
725                 for (i = 0; (uint64_t) i < count; i++) {
726                         if (read_num(line, &len, &num, 1, &checksum) < 0)
727                                 return -1;
728                         data[i] = (uint8_t) num;
729                 }
730                 *sz = count;
731         } else
732                 *sz = 0;
733
734         if (read_num(line, &len, &_checksum, 1, NULL) < 0)
735                 return (-1);
736
737         if ((int) _checksum != (~checksum & 0xFF))
738                 return (-1);
739
740         return (0);
741 }
742
743 static void
744 srec_write_symtab(int ofd, const char *ofn, Elf *e, Elf_Scn *scn, GElf_Shdr *sh)
745 {
746         char line[_LINE_BUFSZ];
747         GElf_Sym sym;
748         Elf_Data *d;
749         const char *name;
750         size_t sc;
751         int elferr, i;
752
753 #define _WRITE_LINE do {                                                \
754         if (write(ofd, line, strlen(line)) != (ssize_t) strlen(line))   \
755                 errx(EXIT_FAILURE, "write failed");                             \
756         } while (0)
757
758
759         (void) elf_errno();
760         if ((d = elf_getdata(scn, NULL)) == NULL) {
761                 elferr = elf_errno();
762                 if (elferr != 0)
763                         warnx("elf_getdata failed: %s",
764                             elf_errmsg(-1));
765                 return;
766         }
767         if (d->d_buf == NULL || d->d_size == 0)
768                 return;
769
770         snprintf(line, sizeof(line), "$$ %s\r\n", ofn);
771         _WRITE_LINE;
772         sc = d->d_size / sh->sh_entsize;
773         for (i = 1; (size_t) i < sc; i++) {
774                 if (gelf_getsym(d, i, &sym) != &sym) {
775                         warnx("gelf_getsym failed: %s", elf_errmsg(-1));
776                         continue;
777                 }
778                 if (GELF_ST_TYPE(sym.st_info) == STT_SECTION ||
779                     GELF_ST_TYPE(sym.st_info) == STT_FILE)
780                         continue;
781                 if ((name = elf_strptr(e, sh->sh_link, sym.st_name)) == NULL) {
782                         warnx("elf_strptr failed: %s", elf_errmsg(-1));
783                         continue;
784                 }
785                 snprintf(line, sizeof(line), "  %s $%jx\r\n", name,
786                     (uintmax_t) sym.st_value);
787                 _WRITE_LINE;
788         }
789         snprintf(line, sizeof(line), "$$ \r\n");
790         _WRITE_LINE;
791
792 #undef  _WRITE_LINE
793 }
794
795 static void
796 srec_write_S0(int ofd, const char *ofn)
797 {
798
799         srec_write(ofd, '0', 0, ofn, strlen(ofn));
800 }
801
802 static void
803 srec_write_Sd(int ofd, char dr, uint64_t addr, const void *buf, size_t sz,
804     size_t rlen)
805 {
806         const uint8_t *p, *pe;
807
808         p = buf;
809         pe = p + sz;
810         while (pe - p >= (int) rlen) {
811                 srec_write(ofd, dr, addr, p, rlen);
812                 addr += rlen;
813                 p += rlen;
814         }
815         if (pe - p > 0)
816                 srec_write(ofd, dr, addr, p, pe - p);
817 }
818
819 static void
820 srec_write_Se(int ofd, uint64_t e_entry, int forceS3)
821 {
822         char er;
823
824         if (e_entry > 0xFFFFFFFF) {
825                 warnx("address space too big for S-Record file");
826                 return;
827         }
828
829         if (forceS3)
830                 er = '7';
831         else {
832                 if (e_entry <= 0xFFFF)
833                         er = '9';
834                 else if (e_entry <= 0xFFFFFF)
835                         er = '8';
836                 else
837                         er = '7';
838         }
839
840         srec_write(ofd, er, e_entry, NULL, 0);
841 }
842
843 static void
844 srec_write(int ofd, char type, uint64_t addr, const void *buf, size_t sz)
845 {
846         char line[_LINE_BUFSZ];
847         const uint8_t *p, *pe;
848         int len, addr_sz, checksum;
849
850         if (type == '0' || type == '1' || type == '5' || type == '9')
851                 addr_sz = 2;
852         else if (type == '2' || type == '8')
853                 addr_sz = 3;
854         else
855                 addr_sz = 4;
856
857         checksum = 0;
858         line[0] = 'S';
859         line[1] = type;
860         len = 2;
861         write_num(line, &len, addr_sz + sz + 1, 1, &checksum);
862         write_num(line, &len, addr, addr_sz, &checksum);
863         for (p = buf, pe = p + sz; p < pe; p++)
864                 write_num(line, &len, *p, 1, &checksum);
865         write_num(line, &len, ~checksum & 0xFF, 1, NULL);
866         line[len++] = '\r';
867         line[len++] = '\n';
868         if (write(ofd, line, len) != (ssize_t) len)
869                 err(EXIT_FAILURE, "write failed");
870 }
871
872 static void
873 ihex_write_00(int ofd, uint64_t addr, const void *buf, size_t sz)
874 {
875         uint16_t addr_hi, old_addr_hi;
876         const uint8_t *p, *pe;
877
878         old_addr_hi = (addr >> 16) & 0xFFFF;
879         p = buf;
880         pe = p + sz;
881         while (pe - p >= 16) {
882                 ihex_write(ofd, 0, addr, 0, p, 16);
883                 addr += 16;
884                 p += 16;
885                 addr_hi = (addr >> 16) & 0xFFFF;
886                 if (addr_hi != old_addr_hi) {
887                         old_addr_hi = addr_hi;
888                         ihex_write_04(ofd, addr_hi);
889                 }
890         }
891         if (pe - p > 0)
892                 ihex_write(ofd, 0, addr, 0, p, pe - p);
893 }
894
895 static int
896 ihex_read(const char *line, char *type, uint64_t *addr, uint64_t *num,
897     uint8_t *data, size_t *sz)
898 {
899         uint64_t count, _checksum;
900         int checksum, i, len;
901
902         *sz = 0;
903         checksum = 0;
904         len = 1;
905         if (read_num(line, &len, &count, 1, &checksum) < 0)
906                 return (-1);
907         if (read_num(line, &len, addr, 2, &checksum) < 0)
908                 return (-1);
909         if (line[len++] != '0')
910                 return (-1);
911         *type = line[len++];
912         checksum += *type - '0';
913         switch (*type) {
914         case '0':
915                 for (i = 0; (uint64_t) i < count; i++) {
916                         if (read_num(line, &len, num, 1, &checksum) < 0)
917                                 return (-1);
918                         data[i] = (uint8_t) *num;
919                 }
920                 *sz = count;
921                 break;
922         case '1':
923                 if (count != 0)
924                         return (-1);
925                 break;
926         case '2':
927         case '4':
928                 if (count != 2)
929                         return (-1);
930                 if (read_num(line, &len, num, 2, &checksum) < 0)
931                         return (-1);
932                 break;
933         case '3':
934         case '5':
935                 if (count != 4)
936                         return (-1);
937                 if (read_num(line, &len, num, 4, &checksum) < 0)
938                         return (-1);
939                 break;
940         default:
941                 return (-1);
942         }
943
944         if (read_num(line, &len, &_checksum, 1, &checksum) < 0)
945                 return (-1);
946
947         if ((checksum & 0xFF) != 0) {
948                 return (-1);
949         }
950
951         return (0);
952 }
953
954 static void
955 ihex_write_01(int ofd)
956 {
957
958         ihex_write(ofd, 1, 0, 0, NULL, 0);
959 }
960
961 static void
962 ihex_write_04(int ofd, uint16_t addr)
963 {
964
965         ihex_write(ofd, 4, 0, addr, NULL, 2);
966 }
967
968 static void
969 ihex_write_05(int ofd, uint64_t e_entry)
970 {
971
972         if (e_entry > 0xFFFFFFFF) {
973                 warnx("address space too big for Intel Hex file");
974                 return;
975         }
976
977         ihex_write(ofd, 5, 0, e_entry, NULL, 4);
978 }
979
980 static void
981 ihex_write(int ofd, int type, uint64_t addr, uint64_t num, const void *buf,
982     size_t sz)
983 {
984         char line[_LINE_BUFSZ];
985         const uint8_t *p, *pe;
986         int len, checksum;
987
988         if (sz > 16)
989                 errx(EXIT_FAILURE, "Internal: ihex_write() sz too big");
990         checksum = 0;
991         line[0] = ':';
992         len = 1;
993         write_num(line, &len, sz, 1, &checksum);
994         write_num(line, &len, addr, 2, &checksum);
995         write_num(line, &len, type, 1, &checksum);
996         if (sz > 0) {
997                 if (buf != NULL) {
998                         for (p = buf, pe = p + sz; p < pe; p++)
999                                 write_num(line, &len, *p, 1, &checksum);
1000                 } else
1001                         write_num(line, &len, num, sz, &checksum);
1002         }
1003         write_num(line, &len, (~checksum + 1) & 0xFF, 1, NULL);
1004         line[len++] = '\r';
1005         line[len++] = '\n';
1006         if (write(ofd, line, len) != (ssize_t) len)
1007                 err(EXIT_FAILURE, "write failed");
1008 }
1009
1010 static int
1011 read_num(const char *line, int *len, uint64_t *num, size_t sz, int *checksum)
1012 {
1013         uint8_t b;
1014
1015         *num = 0;
1016         for (; sz > 0; sz--) {
1017                 if (!ishexdigit(line[*len]) || !ishexdigit(line[*len + 1]))
1018                         return (-1);
1019                 b = (hex_value(line[*len]) << 4) | hex_value(line[*len + 1]);
1020                 *num = (*num << 8) | b;
1021                 *len += 2;
1022                 if (checksum != NULL)
1023                         *checksum = (*checksum + b) & 0xFF;
1024         }
1025
1026         return (0);
1027 }
1028
1029 static void
1030 write_num(char *line, int *len, uint64_t num, size_t sz, int *checksum)
1031 {
1032         uint8_t b;
1033
1034         for (; sz > 0; sz--) {
1035                 b = (num >> ((sz - 1) * 8)) & 0xFF;
1036                 line[*len] = hex_digit((b >> 4) & 0xF);
1037                 line[*len + 1] = hex_digit(b & 0xF);
1038                 *len += 2;
1039                 if (checksum != NULL)
1040                         *checksum = (*checksum + b) & 0xFF;
1041         }
1042 }
1043
1044 static char
1045 hex_digit(uint8_t n)
1046 {
1047
1048         return ((n < 10) ? '0' + n : 'A' + (n - 10));
1049 }
1050
1051 static int
1052 hex_value(int x)
1053 {
1054
1055         if (isdigit(x))
1056                 return (x - '0');
1057         else if (x >= 'a' && x <= 'f')
1058                 return (x - 'a' + 10);
1059         else
1060                 return (x - 'A' + 10);
1061 }
1062
1063 static int
1064 ishexdigit(int x)
1065 {
1066
1067         if (isdigit(x))
1068                 return (1);
1069         if ((x >= 'a' && x <= 'f') || (x >= 'A' && x <= 'F'))
1070                 return (1);
1071
1072         return (0);
1073 }