]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/elftoolchain/elfcopy/ascii.c
Merge OpenSSL 1.0.2f.
[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 3177 2015-03-30 18:19:41Z 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 recrod. */
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                 if (line[0] == '\r' || line[0] == '\n')
255                         continue;
256                 if (line[0] == '$' && line[1] == '$') {
257                         ecp->flags |= SYMTAB_EXIST;
258                         while ((rlt = fgets(line, _LINE_BUFSZ, ifp)) != NULL) {
259                                 if (line[0] == '$' && line[1] == '$')
260                                         break;
261                         }
262                         if (rlt == NULL)
263                                 break;
264                         continue;
265                 }
266                 if (line[0] != 'S' || line[1] < '0' || line[1] > '9') {
267                         warnx("Invalid srec record");
268                         continue;
269                 }
270                 if (srec_read(line, &type, &addr, data, &sz) < 0) {
271                         warnx("Invalid srec record or mismatched checksum");
272                         continue;
273                 }
274                 switch (type) {
275                 case '1':
276                 case '2':
277                 case '3':
278                         if (sz == 0)
279                                 break;
280                         if (first || sec_addr != addr) {
281                                 if (s != NULL)
282                                         finalize_data_section(s);
283                                 s = new_data_section(ecp, sec_index, off,
284                                     addr);
285                                 if (s == NULL) {
286                                         warnx("new_data_section failed");
287                                         break;
288                                 }
289                                 sec_index++;
290                                 sec_addr = addr;
291                                 first = 0;
292                         }
293                         append_data(s, data, sz);
294                         off += sz;
295                         sec_addr += sz;
296                         break;
297                 case '7':
298                 case '8':
299                 case '9':
300                         entry = addr;
301                         break;
302                 default:
303                         break;
304                 }
305         }
306         if (s != NULL)
307                 finalize_data_section(s);
308         if (ferror(ifp))
309                 warn("fgets failed");
310
311         /* Insert .shstrtab after data sections. */
312         if ((ecp->shstrtab->os = elf_newscn(ecp->eout)) == NULL)
313                 errx(EXIT_FAILURE, "elf_newscn failed: %s",
314                     elf_errmsg(-1));
315         insert_to_sec_list(ecp, ecp->shstrtab, 1);
316
317         /* Insert section header table here. */
318         shtab = insert_shtab(ecp, 1);
319
320         /*
321          * Rescan and create symbol table if we found '$$' section in
322          * the first scan.
323          */
324         symtab_created = 0;
325         in_symtab = 0;
326         if (ecp->flags & SYMTAB_EXIST) {
327                 if (fseek(ifp, 0, SEEK_SET) < 0) {
328                         warn("fseek failed");
329                         ecp->flags &= ~SYMTAB_EXIST;
330                         goto done;
331                 }
332                 while (fgets(line, _LINE_BUFSZ, ifp) != NULL) {
333                         if (in_symtab) {
334                                 if (line[0] == '$' && line[1] == '$') {
335                                         in_symtab = 0;
336                                         continue;
337                                 }
338                                 if (sscanf(line, "%s $%jx", name,
339                                     &st_value) != 2) {
340                                         warnx("Invalid symbolsrec record");
341                                         continue;
342                                 }
343                                 if (!symtab_created) {
344                                         create_external_symtab(ecp);
345                                         symtab_created = 1;
346                                 }
347                                 add_to_symtab(ecp, name, st_value, 0, SHN_ABS,
348                                     ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, 1);
349                         }
350                         if (line[0] == '$' && line[1] == '$') {
351                                 in_symtab = 1;
352                                 continue;
353                         }
354                 }
355         }
356         if (ferror(ifp))
357                 warn("fgets failed");
358         if (symtab_created) {
359                 finalize_external_symtab(ecp);
360                 create_symtab_data(ecp);
361                 /* Count in .symtab and .strtab section headers.  */
362                 shtab->sz += gelf_fsize(ecp->eout, ELF_T_SHDR, 2, EV_CURRENT);
363         } else
364                 ecp->flags &= ~SYMTAB_EXIST;
365
366 done:
367         fclose(ifp);
368
369         /* Set entry point. */
370         oeh.e_entry = entry;
371
372         /*
373          * Write the underlying ehdr. Note that it should be called
374          * before elf_setshstrndx() since it will overwrite e->e_shstrndx.
375          */
376         if (gelf_update_ehdr(ecp->eout, &oeh) == 0)
377                 errx(EXIT_FAILURE, "gelf_update_ehdr() failed: %s",
378                     elf_errmsg(-1));
379
380         /* Generate section name string table (.shstrtab). */
381         set_shstrtab(ecp);
382
383         /* Update sh_name pointer for each section header entry. */
384         update_shdr(ecp, 0);
385
386         /* Renew oeh to get the updated e_shstrndx. */
387         if (gelf_getehdr(ecp->eout, &oeh) == NULL)
388                 errx(EXIT_FAILURE, "gelf_getehdr() failed: %s",
389                     elf_errmsg(-1));
390
391         /* Resync section offsets. */
392         resync_sections(ecp);
393
394         /* Store SHDR offset in EHDR. */
395         oeh.e_shoff = shtab->off;
396
397         /* Update ehdr since we modified e_shoff. */
398         if (gelf_update_ehdr(ecp->eout, &oeh) == 0)
399                 errx(EXIT_FAILURE, "gelf_update_ehdr() failed: %s",
400                     elf_errmsg(-1));
401
402         /* Write out the output elf object. */
403         if (elf_update(ecp->eout, ELF_C_WRITE) < 0)
404                 errx(EXIT_FAILURE, "elf_update() failed: %s",
405                     elf_errmsg(-1));
406
407         /* Release allocated resource. */
408         free_elf(ecp);
409 }
410
411 void
412 create_ihex(int ifd, int ofd)
413 {
414         Elf *e;
415         Elf_Scn *scn;
416         Elf_Data *d;
417         GElf_Ehdr eh;
418         GElf_Shdr sh;
419         int elferr;
420         uint16_t addr_hi, old_addr_hi;
421
422         if ((e = elf_begin(ifd, ELF_C_READ, NULL)) == NULL)
423                 errx(EXIT_FAILURE, "elf_begin() failed: %s",
424                     elf_errmsg(-1));
425
426         old_addr_hi = 0;
427         scn = NULL;
428         while ((scn = elf_nextscn(e, scn)) != NULL) {
429                 if (gelf_getshdr(scn, &sh) == NULL) {
430                         warnx("gelf_getshdr failed: %s", elf_errmsg(-1));
431                         (void) elf_errno();
432                         continue;
433                 }
434                 if ((sh.sh_flags & SHF_ALLOC) == 0 ||
435                     sh.sh_type == SHT_NOBITS ||
436                     sh.sh_size == 0)
437                         continue;
438                 if (sh.sh_addr > 0xFFFFFFFF) {
439                         warnx("address space too big for Intel Hex file");
440                         continue;
441                 }
442                 (void) elf_errno();
443                 if ((d = elf_getdata(scn, NULL)) == NULL) {
444                         elferr = elf_errno();
445                         if (elferr != 0)
446                                 warnx("elf_getdata failed: %s", elf_errmsg(-1));
447                         continue;
448                 }
449                 if (d->d_buf == NULL || d->d_size == 0)
450                         continue;
451                 addr_hi = (sh.sh_addr >> 16) & 0xFFFF;
452                 if (addr_hi > 0 && addr_hi != old_addr_hi) {
453                         /* Write 04 record if addr_hi is new. */
454                         old_addr_hi = addr_hi;
455                         ihex_write_04(ofd, addr_hi);
456                 }
457                 ihex_write_00(ofd, sh.sh_addr, d->d_buf, d->d_size);
458         }
459         elferr = elf_errno();
460         if (elferr != 0)
461                 warnx("elf_nextscn failed: %s", elf_errmsg(elferr));
462
463         if (gelf_getehdr(e, &eh) == NULL)
464                 errx(EXIT_FAILURE, "gelf_getehdr() failed: %s",
465                     elf_errmsg(-1));
466         ihex_write_05(ofd, eh.e_entry);
467         ihex_write_01(ofd);
468 }
469
470 void
471 create_elf_from_ihex(struct elfcopy *ecp, int ifd)
472 {
473         char line[_LINE_BUFSZ];
474         uint8_t data[_DATA_BUFSZ];
475         GElf_Ehdr oeh;
476         struct section *s, *shtab;
477         FILE *ifp;
478         uint64_t addr, addr_base, entry, num, off, rec_addr, sec_addr;
479         size_t sz;
480         int _ifd, first, sec_index;
481         char type;
482
483         if ((_ifd = dup(ifd)) < 0)
484                 err(EXIT_FAILURE, "dup failed");
485         if ((ifp = fdopen(_ifd, "r")) == NULL)
486                 err(EXIT_FAILURE, "fdopen failed");
487
488         /* Create EHDR for output .o file. */
489         if (gelf_newehdr(ecp->eout, ecp->oec) == NULL)
490                 errx(EXIT_FAILURE, "gelf_newehdr failed: %s",
491                     elf_errmsg(-1));
492         if (gelf_getehdr(ecp->eout, &oeh) == NULL)
493                 errx(EXIT_FAILURE, "gelf_getehdr() failed: %s",
494                     elf_errmsg(-1));
495
496         /* Initialise e_ident fields. */
497         oeh.e_ident[EI_CLASS] = ecp->oec;
498         oeh.e_ident[EI_DATA] = ecp->oed;
499         /*
500          * TODO: Set OSABI according to the OS platform where elfcopy(1)
501          * was build. (probably)
502          */
503         oeh.e_ident[EI_OSABI] = ELFOSABI_NONE;
504         oeh.e_machine = ecp->oem;
505         oeh.e_type = ET_REL;
506         oeh.e_entry = 0;
507
508         ecp->flags |= RELOCATABLE;
509
510         /* Create .shstrtab section */
511         init_shstrtab(ecp);
512         ecp->shstrtab->off = 0;
513
514         /* Data sections are inserted after EHDR. */
515         off = gelf_fsize(ecp->eout, ELF_T_EHDR, 1, EV_CURRENT);
516         if (off == 0)
517                 errx(EXIT_FAILURE, "gelf_fsize() failed: %s", elf_errmsg(-1));
518
519         /* Create data sections. */
520         s = NULL;
521         first = 1;
522         sec_index = 1;
523         addr_base = rec_addr = sec_addr = entry = 0;
524         while (fgets(line, _LINE_BUFSZ, ifp) != NULL) {
525                 if (line[0] == '\r' || line[0] == '\n')
526                         continue;
527                 if (line[0] != ':') {
528                         warnx("Invalid ihex record");
529                         continue;
530                 }
531                 if (ihex_read(line, &type, &addr, &num, data, &sz) < 0) {
532                         warnx("Invalid ihex record or mismatched checksum");
533                         continue;
534                 }
535                 switch (type) {
536                 case '0':
537                         /* Data record. */
538                         if (sz == 0)
539                                 break;
540                         rec_addr = addr_base + addr;
541                         if (first || sec_addr != rec_addr) {
542                                 if (s != NULL)
543                                         finalize_data_section(s);
544                                 s = new_data_section(ecp, sec_index, off,
545                                     rec_addr);
546                                 if (s == NULL) {
547                                         warnx("new_data_section failed");
548                                         break;
549                                 }
550                                 sec_index++;
551                                 sec_addr = rec_addr;
552                                 first = 0;
553                         }
554                         append_data(s, data, sz);
555                         off += sz;
556                         sec_addr += sz;
557                         break;
558                 case '1':
559                         /* End of file record. */
560                         goto done;
561                 case '2':
562                         /* Extended segment address record. */
563                         addr_base = addr << 4;
564                         break;
565                 case '3':
566                         /* Start segment address record (CS:IP). Ignored. */
567                         break;
568                 case '4':
569                         /* Extended linear address record. */
570                         addr_base = num << 16;
571                         break;
572                 case '5':
573                         /* Start linear address record. */
574                         entry = num;
575                         break;
576                 default:
577                         break;
578                 }
579         }
580 done:
581         if (s != NULL)
582                 finalize_data_section(s);
583         if (ferror(ifp))
584                 warn("fgets failed");
585         fclose(ifp);
586
587         /* Insert .shstrtab after data sections. */
588         if ((ecp->shstrtab->os = elf_newscn(ecp->eout)) == NULL)
589                 errx(EXIT_FAILURE, "elf_newscn failed: %s",
590                     elf_errmsg(-1));
591         insert_to_sec_list(ecp, ecp->shstrtab, 1);
592
593         /* Insert section header table here. */
594         shtab = insert_shtab(ecp, 1);
595
596         /* Set entry point. */
597         oeh.e_entry = entry;
598
599         /*
600          * Write the underlying ehdr. Note that it should be called
601          * before elf_setshstrndx() since it will overwrite e->e_shstrndx.
602          */
603         if (gelf_update_ehdr(ecp->eout, &oeh) == 0)
604                 errx(EXIT_FAILURE, "gelf_update_ehdr() failed: %s",
605                     elf_errmsg(-1));
606
607         /* Generate section name string table (.shstrtab). */
608         set_shstrtab(ecp);
609
610         /* Update sh_name pointer for each section header entry. */
611         update_shdr(ecp, 0);
612
613         /* Renew oeh to get the updated e_shstrndx. */
614         if (gelf_getehdr(ecp->eout, &oeh) == NULL)
615                 errx(EXIT_FAILURE, "gelf_getehdr() failed: %s",
616                     elf_errmsg(-1));
617
618         /* Resync section offsets. */
619         resync_sections(ecp);
620
621         /* Store SHDR offset in EHDR. */
622         oeh.e_shoff = shtab->off;
623
624         /* Update ehdr since we modified e_shoff. */
625         if (gelf_update_ehdr(ecp->eout, &oeh) == 0)
626                 errx(EXIT_FAILURE, "gelf_update_ehdr() failed: %s",
627                     elf_errmsg(-1));
628
629         /* Write out the output elf object. */
630         if (elf_update(ecp->eout, ELF_C_WRITE) < 0)
631                 errx(EXIT_FAILURE, "elf_update() failed: %s",
632                     elf_errmsg(-1));
633
634         /* Release allocated resource. */
635         free_elf(ecp);
636 }
637
638 #define _SEC_NAMESZ     64
639 #define _SEC_INIT_CAP   1024
640
641 static struct section *
642 new_data_section(struct elfcopy *ecp, int sec_index, uint64_t off,
643     uint64_t addr)
644 {
645         char *name;
646
647         if ((name = malloc(_SEC_NAMESZ)) == NULL)
648                 errx(EXIT_FAILURE, "malloc failed");
649         snprintf(name, _SEC_NAMESZ, ".sec%d", sec_index);
650
651         return (create_external_section(ecp, name, name, NULL, 0, off,
652                 SHT_PROGBITS, ELF_T_BYTE, SHF_ALLOC | SHF_WRITE, 1, addr, 0));
653 }
654
655 static void
656 finalize_data_section(struct section *s)
657 {
658         Elf_Data *od;
659
660         if ((od = elf_newdata(s->os)) == NULL)
661                 errx(EXIT_FAILURE, "elf_newdata() failed: %s",
662                     elf_errmsg(-1));
663         od->d_align = s->align;
664         od->d_off = 0;
665         od->d_buf = s->buf;
666         od->d_size = s->sz;
667         od->d_version = EV_CURRENT;
668 }
669
670 static void
671 append_data(struct section *s, const void *buf, size_t sz)
672 {
673         uint8_t *p;
674
675         if (s->buf == NULL) {
676                 s->sz = 0;
677                 s->cap = _SEC_INIT_CAP;
678                 if ((s->buf = malloc(s->cap)) == NULL)
679                         err(EXIT_FAILURE, "malloc failed");
680         }
681
682         while (sz + s->sz > s->cap) {
683                 s->cap *= 2;
684                 if ((s->buf = realloc(s->buf, s->cap)) == NULL)
685                         err(EXIT_FAILURE, "realloc failed");
686         }
687
688         p = s->buf;
689         memcpy(&p[s->sz], buf, sz);
690         s->sz += sz;
691 }
692
693 static int
694 srec_read(const char *line, char *type, uint64_t *addr, uint8_t *data,
695     size_t *sz)
696 {
697         uint64_t count, _checksum, num;
698         size_t addr_sz;
699         int checksum, i, len;
700
701         checksum = 0;
702         len = 2;
703         if (read_num(line, &len, &count, 1, &checksum) < 0)
704                 return (-1);
705         *type = line[1];
706         switch (*type) {
707         case '0':
708         case '1':
709         case '5':
710         case '9':
711                 addr_sz = 2;
712                 break;
713         case '2':
714         case '8':
715                 addr_sz = 3;
716                 break;
717         case '3':
718         case '7':
719                 addr_sz = 4;
720                 break;
721         default:
722                 return (-1);
723         }
724
725         if (read_num(line, &len, addr, addr_sz, &checksum) < 0)
726                 return (-1);
727
728         count -= addr_sz + 1;
729         if (*type >= '0' && *type <= '3') {
730                 for (i = 0; (uint64_t) i < count; i++) {
731                         if (read_num(line, &len, &num, 1, &checksum) < 0)
732                                 return -1;
733                         data[i] = (uint8_t) num;
734                 }
735                 *sz = count;
736         } else
737                 *sz = 0;
738
739         if (read_num(line, &len, &_checksum, 1, NULL) < 0)
740                 return (-1);
741
742         if ((int) _checksum != (~checksum & 0xFF))
743                 return (-1);
744
745         return (0);
746 }
747
748 static void
749 srec_write_symtab(int ofd, const char *ofn, Elf *e, Elf_Scn *scn, GElf_Shdr *sh)
750 {
751         char line[_LINE_BUFSZ];
752         GElf_Sym sym;
753         Elf_Data *d;
754         const char *name;
755         size_t sc;
756         int elferr, i;
757
758 #define _WRITE_LINE do {                                                \
759         if (write(ofd, line, strlen(line)) != (ssize_t) strlen(line))   \
760                 errx(EXIT_FAILURE, "write failed");                             \
761         } while (0)
762
763
764         (void) elf_errno();
765         if ((d = elf_getdata(scn, NULL)) == NULL) {
766                 elferr = elf_errno();
767                 if (elferr != 0)
768                         warnx("elf_getdata failed: %s",
769                             elf_errmsg(-1));
770                 return;
771         }
772         if (d->d_buf == NULL || d->d_size == 0)
773                 return;
774
775         snprintf(line, sizeof(line), "$$ %s\r\n", ofn);
776         _WRITE_LINE;
777         sc = d->d_size / sh->sh_entsize;
778         for (i = 1; (size_t) i < sc; i++) {
779                 if (gelf_getsym(d, i, &sym) != &sym) {
780                         warnx("gelf_getsym failed: %s", elf_errmsg(-1));
781                         continue;
782                 }
783                 if (GELF_ST_TYPE(sym.st_info) == STT_SECTION ||
784                     GELF_ST_TYPE(sym.st_info) == STT_FILE)
785                         continue;
786                 if ((name = elf_strptr(e, sh->sh_link, sym.st_name)) == NULL) {
787                         warnx("elf_strptr failed: %s", elf_errmsg(-1));
788                         continue;
789                 }
790                 snprintf(line, sizeof(line), "  %s $%jx\r\n", name,
791                     (uintmax_t) sym.st_value);
792                 _WRITE_LINE;
793         }
794         snprintf(line, sizeof(line), "$$ \r\n");
795         _WRITE_LINE;
796
797 #undef  _WRITE_LINE
798 }
799
800 static void
801 srec_write_S0(int ofd, const char *ofn)
802 {
803
804         srec_write(ofd, '0', 0, ofn, strlen(ofn));
805 }
806
807 static void
808 srec_write_Sd(int ofd, char dr, uint64_t addr, const void *buf, size_t sz,
809     size_t rlen)
810 {
811         const uint8_t *p, *pe;
812
813         p = buf;
814         pe = p + sz;
815         while (pe - p >= (int) rlen) {
816                 srec_write(ofd, dr, addr, p, rlen);
817                 addr += rlen;
818                 p += rlen;
819         }
820         if (pe - p > 0)
821                 srec_write(ofd, dr, addr, p, pe - p);
822 }
823
824 static void
825 srec_write_Se(int ofd, uint64_t e_entry, int forceS3)
826 {
827         char er;
828
829         if (e_entry > 0xFFFFFFFF) {
830                 warnx("address space too big for S-Record file");
831                 return;
832         }
833
834         if (forceS3)
835                 er = '7';
836         else {
837                 if (e_entry <= 0xFFFF)
838                         er = '9';
839                 else if (e_entry <= 0xFFFFFF)
840                         er = '8';
841                 else
842                         er = '7';
843         }
844
845         srec_write(ofd, er, e_entry, NULL, 0);
846 }
847
848 static void
849 srec_write(int ofd, char type, uint64_t addr, const void *buf, size_t sz)
850 {
851         char line[_LINE_BUFSZ];
852         const uint8_t *p, *pe;
853         int len, addr_sz, checksum;
854
855         if (type == '0' || type == '1' || type == '5' || type == '9')
856                 addr_sz = 2;
857         else if (type == '2' || type == '8')
858                 addr_sz = 3;
859         else
860                 addr_sz = 4;
861
862         checksum = 0;
863         line[0] = 'S';
864         line[1] = type;
865         len = 2;
866         write_num(line, &len, addr_sz + sz + 1, 1, &checksum);
867         write_num(line, &len, addr, addr_sz, &checksum);
868         for (p = buf, pe = p + sz; p < pe; p++)
869                 write_num(line, &len, *p, 1, &checksum);
870         write_num(line, &len, ~checksum & 0xFF, 1, NULL);
871         line[len++] = '\r';
872         line[len++] = '\n';
873         if (write(ofd, line, len) != (ssize_t) len)
874                 err(EXIT_FAILURE, "write failed");
875 }
876
877 static void
878 ihex_write_00(int ofd, uint64_t addr, const void *buf, size_t sz)
879 {
880         uint16_t addr_hi, old_addr_hi;
881         const uint8_t *p, *pe;
882
883         old_addr_hi = (addr >> 16) & 0xFFFF;
884         p = buf;
885         pe = p + sz;
886         while (pe - p >= 16) {
887                 ihex_write(ofd, 0, addr, 0, p, 16);
888                 addr += 16;
889                 p += 16;
890                 addr_hi = (addr >> 16) & 0xFFFF;
891                 if (addr_hi != old_addr_hi) {
892                         old_addr_hi = addr_hi;
893                         ihex_write_04(ofd, addr_hi);
894                 }
895         }
896         if (pe - p > 0)
897                 ihex_write(ofd, 0, addr, 0, p, pe - p);
898 }
899
900 static int
901 ihex_read(const char *line, char *type, uint64_t *addr, uint64_t *num,
902     uint8_t *data, size_t *sz)
903 {
904         uint64_t count, _checksum;
905         int checksum, i, len;
906
907         *sz = 0;
908         checksum = 0;
909         len = 1;
910         if (read_num(line, &len, &count, 1, &checksum) < 0)
911                 return (-1);
912         if (read_num(line, &len, addr, 2, &checksum) < 0)
913                 return (-1);
914         if (line[len++] != '0')
915                 return (-1);
916         *type = line[len++];
917         checksum += *type - '0';
918         switch (*type) {
919         case '0':
920                 for (i = 0; (uint64_t) i < count; i++) {
921                         if (read_num(line, &len, num, 1, &checksum) < 0)
922                                 return (-1);
923                         data[i] = (uint8_t) *num;
924                 }
925                 *sz = count;
926                 break;
927         case '1':
928                 if (count != 0)
929                         return (-1);
930                 break;
931         case '2':
932         case '4':
933                 if (count != 2)
934                         return (-1);
935                 if (read_num(line, &len, num, 2, &checksum) < 0)
936                         return (-1);
937                 break;
938         case '3':
939         case '5':
940                 if (count != 4)
941                         return (-1);
942                 if (read_num(line, &len, num, 4, &checksum) < 0)
943                         return (-1);
944                 break;
945         default:
946                 return (-1);
947         }
948
949         if (read_num(line, &len, &_checksum, 1, &checksum) < 0)
950                 return (-1);
951
952         if ((checksum & 0xFF) != 0) {
953                 return (-1);
954         }
955
956         return (0);
957 }
958
959 static void
960 ihex_write_01(int ofd)
961 {
962
963         ihex_write(ofd, 1, 0, 0, NULL, 0);
964 }
965
966 static void
967 ihex_write_04(int ofd, uint16_t addr)
968 {
969
970         ihex_write(ofd, 4, 0, addr, NULL, 2);
971 }
972
973 static void
974 ihex_write_05(int ofd, uint64_t e_entry)
975 {
976
977         if (e_entry > 0xFFFFFFFF) {
978                 warnx("address space too big for Intel Hex file");
979                 return;
980         }
981
982         ihex_write(ofd, 5, 0, e_entry, NULL, 4);
983 }
984
985 static void
986 ihex_write(int ofd, int type, uint64_t addr, uint64_t num, const void *buf,
987     size_t sz)
988 {
989         char line[_LINE_BUFSZ];
990         const uint8_t *p, *pe;
991         int len, checksum;
992
993         if (sz > 16)
994                 errx(EXIT_FAILURE, "Internal: ihex_write() sz too big");
995         checksum = 0;
996         line[0] = ':';
997         len = 1;
998         write_num(line, &len, sz, 1, &checksum);
999         write_num(line, &len, addr, 2, &checksum);
1000         write_num(line, &len, type, 1, &checksum);
1001         if (sz > 0) {
1002                 if (buf != NULL) {
1003                         for (p = buf, pe = p + sz; p < pe; p++)
1004                                 write_num(line, &len, *p, 1, &checksum);
1005                 } else
1006                         write_num(line, &len, num, sz, &checksum);
1007         }
1008         write_num(line, &len, (~checksum + 1) & 0xFF, 1, NULL);
1009         line[len++] = '\r';
1010         line[len++] = '\n';
1011         if (write(ofd, line, len) != (ssize_t) len)
1012                 err(EXIT_FAILURE, "write failed");
1013 }
1014
1015 static int
1016 read_num(const char *line, int *len, uint64_t *num, size_t sz, int *checksum)
1017 {
1018         uint8_t b;
1019
1020         *num = 0;
1021         for (; sz > 0; sz--) {
1022                 if (!ishexdigit(line[*len]) || !ishexdigit(line[*len + 1]))
1023                         return (-1);
1024                 b = (hex_value(line[*len]) << 4) | hex_value(line[*len + 1]);
1025                 *num = (*num << 8) | b;
1026                 *len += 2;
1027                 if (checksum != NULL)
1028                         *checksum = (*checksum + b) & 0xFF;
1029         }
1030
1031         return (0);
1032 }
1033
1034 static void
1035 write_num(char *line, int *len, uint64_t num, size_t sz, int *checksum)
1036 {
1037         uint8_t b;
1038
1039         for (; sz > 0; sz--) {
1040                 b = (num >> ((sz - 1) * 8)) & 0xFF;
1041                 line[*len] = hex_digit((b >> 4) & 0xF);
1042                 line[*len + 1] = hex_digit(b & 0xF);
1043                 *len += 2;
1044                 if (checksum != NULL)
1045                         *checksum = (*checksum + b) & 0xFF;
1046         }
1047 }
1048
1049 static char
1050 hex_digit(uint8_t n)
1051 {
1052
1053         return ((n < 10) ? '0' + n : 'A' + (n - 10));
1054 }
1055
1056 static int
1057 hex_value(int x)
1058 {
1059
1060         if (isdigit(x))
1061                 return (x - '0');
1062         else if (x >= 'a' && x <= 'f')
1063                 return (x - 'a' + 10);
1064         else
1065                 return (x - 'A' + 10);
1066 }
1067
1068 static int
1069 ishexdigit(int x)
1070 {
1071
1072         if (isdigit(x))
1073                 return (1);
1074         if ((x >= 'a' && x <= 'f') || (x >= 'A' && x <= 'F'))
1075                 return (1);
1076
1077         return (0);
1078 }