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