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
39ELFTC_VCSID("$Id: ascii.c 3757 2019-06-28 01:15:28Z emaste $");
40
41static void append_data(struct section *s, const void *buf, size_t sz);
42static char hex_digit(uint8_t n);
43static int hex_value(int x);
44static void finalize_data_section(struct section *s);
45static int ishexdigit(int x);
46static int ihex_read(const char *line, char *type, uint64_t *addr,
47    uint64_t *num, uint8_t *data, size_t *sz);
48static void ihex_write(int ofd, int type, uint64_t addr, uint64_t num,
49    const void *buf, size_t sz);
50static void ihex_write_00(int ofd, uint64_t addr, const void *buf, size_t sz);
51static void ihex_write_01(int ofd);
52static void ihex_write_04(int ofd, uint16_t addr);
53static void ihex_write_05(int ofd, uint64_t e_entry);
54static struct section *new_data_section(struct elfcopy *ecp, int sec_index,
55    uint64_t off, uint64_t addr);
56static int read_num(const char *line, int *len, uint64_t *num, size_t sz,
57    int *checksum);
58static int srec_read(const char *line, char *type, uint64_t *addr,
59    uint8_t *data, size_t *sz);
60static void srec_write(int ofd, char type, uint64_t addr, const void *buf,
61    size_t sz);
62static void srec_write_symtab(int ofd, const char *ofn, Elf *e, Elf_Scn *scn,
63    GElf_Shdr *sh);
64static void srec_write_S0(int ofd, const char *ofn);
65static void srec_write_Sd(int ofd, char dr, uint64_t addr, const void *buf,
66    size_t sz, size_t rlen);
67static void srec_write_Se(int ofd, uint64_t e_entry, int forceS3);
68static 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 */
77void
78create_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
197void
198create_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
367done:
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
409void
410create_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
468void
469create_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	}
578done:
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
636static struct section *
637new_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
650static void
651finalize_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
665static void
666append_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
688static int
689srec_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
743static void
744srec_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
795static void
796srec_write_S0(int ofd, const char *ofn)
797{
798
799	srec_write(ofd, '0', 0, ofn, strlen(ofn));
800}
801
802static void
803srec_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
819static void
820srec_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
843static void
844srec_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
872static void
873ihex_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
895static int
896ihex_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
954static void
955ihex_write_01(int ofd)
956{
957
958	ihex_write(ofd, 1, 0, 0, NULL, 0);
959}
960
961static void
962ihex_write_04(int ofd, uint16_t addr)
963{
964
965	ihex_write(ofd, 4, 0, addr, NULL, 2);
966}
967
968static void
969ihex_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
980static void
981ihex_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
1010static int
1011read_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
1029static void
1030write_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
1044static char
1045hex_digit(uint8_t n)
1046{
1047
1048	return ((n < 10) ? '0' + n : 'A' + (n - 10));
1049}
1050
1051static int
1052hex_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
1063static int
1064ishexdigit(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}
1074