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