1/*	$NetBSD: elf.c,v 1.8 2007/03/04 05:59:53 christos Exp $	*/
2
3/*-
4 * Copyright (c) 1999 Shin Takemura.
5 * All rights reserved.
6 *
7 * This software is part of the PocketBSD.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 *    must display the following acknowledgement:
19 *	This product includes software developed by the PocketBSD project
20 *	and its contributors.
21 * 4. Neither the name of the project nor the names of its contributors
22 *    may be used to endorse or promote products derived from this software
23 *    without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 *
37 */
38#include <pbsdboot.h>
39
40#define	ELFSIZE 32
41//#include <sys/param.h>
42//#include <sys/exec.h>
43#include <sys/exec_elf.h>
44
45#if 1
46#define LOAD_DEBUG_INFO
47#define DEBUG_INFO_ALIGN 4096
48#define ROUNDUP(a, n)	((((int)(a)) + (n)-1)/(n)*(n))
49#endif
50
51static int
52scanfile(int fd, void **start, void **end, void **entry, int load);
53
54static long total_bytes = 0;
55
56int
57getinfo(int fd, void **start, void **end)
58{
59	return (scanfile(fd, start, end, NULL, 0));
60}
61
62int
63loadfile(int fd, void **entry)
64{
65	return (scanfile(fd, NULL, NULL, entry, 1));
66}
67
68enum {
69	VMEM_CLEAR, VMEM_LOAD, VMEM_COPY
70};
71
72int
73vmem_sub(int opr, void* xxx, void *addr, int nbytes, int *byte_count)
74{
75	int n;
76	void *end_addr, *vaddr;
77	int count = 0;
78	int progress = 0;
79	int fd = (int)xxx;
80	void *src_addr = (void *)xxx;
81
82	debug_printf(TEXT("loadfile_sub(%x-%x, %S)\n"),
83		     addr, addr + nbytes,
84		     opr == VMEM_CLEAR ? "clear" : (opr == VMEM_LOAD ? "load" : "copy"));
85
86	for (end_addr = addr + nbytes;
87	     addr < end_addr;
88	     addr += n) {
89		if ((vaddr = vmem_get(addr, &n)) == NULL) {
90			debug_printf(TEXT("vmem_get(0x%x) failed.\n"), addr);
91			msg_printf(MSG_ERROR, whoami, TEXT("vmem_get(0x%x) failed.\n"), addr);
92			return (-1);
93		}
94		if (end_addr < addr + n) {
95			n = end_addr - addr;
96		}
97		switch (opr) {
98		case VMEM_CLEAR:
99			memset(vaddr, 0, n);
100			break;
101		case VMEM_LOAD:
102			if (read(fd, vaddr, n) != n) {
103				debug_printf(TEXT("read segment error.\n"));
104				msg_printf(MSG_ERROR, whoami, TEXT("read segment error.\n"));
105				return (-1);
106			}
107			break;
108		case VMEM_COPY:
109			memcpy(vaddr, src_addr, n);
110			src_addr += n;
111			break;
112		}
113		if (total_bytes != 0) {
114			int tmp_progress = *byte_count * 100 / total_bytes;
115			*byte_count += n;
116			if (progress != tmp_progress) {
117				progress =  tmp_progress;
118				if (CheckCancel(progress)) {
119					return (-1);
120				}
121			}
122		}
123	}
124	return (0);
125}
126
127
128static int
129scanfile(int fd, void **start, void **end, void **entry, int load)
130{
131	Elf_Ehdr elfx, *elf = &elfx;
132	int i, first;
133	long byte_count;
134	int progress;
135	Elf_Shdr *shtbl = NULL;
136	Elf_Phdr *phtbl = NULL;
137	void *min_addr, *max_addr;
138	int sh_symidx, sh_stridx;
139	int dbg_hdr_size = sizeof(Elf_Ehdr) + sizeof(Elf_Shdr) * 2;
140
141	if (lseek(fd, 0, SEEK_SET) == -1)  {
142		debug_printf(TEXT("seek error\n"));
143		msg_printf(MSG_ERROR, whoami, TEXT("seek error.\n"));
144		goto error_cleanup;
145	}
146	if (read(fd, (void*)elf, sizeof(Elf_Ehdr)) != sizeof(Elf_Ehdr)) {
147		debug_printf(TEXT("read header error\n"));
148		msg_printf(MSG_ERROR, whoami, TEXT("read header error.\n"));
149		goto error_cleanup;
150	}
151
152	if ((phtbl = (Elf_Phdr *)alloc(sizeof(*phtbl) * elf->e_phnum)) == NULL ||
153		(shtbl = (Elf_Shdr *)alloc(sizeof(*shtbl) * elf->e_shnum)) == NULL) {
154		debug_printf(TEXT("alloc() error\n"));
155		msg_printf(MSG_ERROR, whoami, TEXT("malloc() error.\n"));
156		goto error_cleanup;
157	}
158
159	if (lseek(fd, elf->e_phoff, SEEK_SET) == -1)  {
160		debug_printf(TEXT("seek for program header table error\n"));
161		msg_printf(MSG_ERROR, whoami, TEXT("seek for program header table error.\n"));
162		goto error_cleanup;
163	}
164	if (read(fd, (void *)phtbl, sizeof(Elf_Phdr) * elf->e_phnum)
165			!= (int)(sizeof(Elf_Phdr) * elf->e_phnum)) {
166		debug_printf(TEXT("read program header table error\n"));
167		msg_printf(MSG_ERROR, whoami, TEXT("read program header table error.\n"));
168		goto error_cleanup;
169	}
170
171	if (lseek(fd, elf->e_shoff, SEEK_SET) == -1)  {
172		debug_printf(TEXT("seek for segment header table error.\n"));
173		msg_printf(MSG_ERROR, whoami, TEXT("seek for segment header table error.\n"));
174		goto error_cleanup;
175	}
176	if (read(fd, (void *)shtbl, sizeof(Elf_Shdr) * elf->e_shnum)
177			!= (int)(sizeof(Elf_Shdr) * elf->e_shnum)) {
178		debug_printf(TEXT("read segment header table error\n"));
179		msg_printf(MSG_ERROR, whoami, TEXT("read segment header table error.\n"));
180		goto error_cleanup;
181	}
182
183	/*
184	 *  scan program header table
185	 */
186	first = 1;
187	byte_count = 0;
188	progress = 0;
189	for (i = 0; i < elf->e_phnum; i++) {
190		if (phtbl[i].p_type != PT_LOAD) {
191			continue;
192		}
193
194		if (first || max_addr < (void *)(phtbl[i].p_vaddr + phtbl[i].p_memsz)) {
195			max_addr = (void *)(phtbl[i].p_vaddr + phtbl[i].p_memsz);
196		}
197		if (first || (void *)phtbl[i].p_vaddr < min_addr) {
198			min_addr = (void *)phtbl[i].p_vaddr;
199		}
200
201		if (load) {
202			if (lseek(fd, phtbl[i].p_offset, SEEK_SET) == -1)  {
203				debug_printf(TEXT("seek for segment error\n"));
204				msg_printf(MSG_ERROR, whoami, TEXT("seek for segment error.\n"));
205				goto error_cleanup;
206			}
207
208			if (vmem_sub(VMEM_LOAD, (void*)fd,
209				     (void *)phtbl[i].p_vaddr,
210				     phtbl[i].p_filesz,
211				     &byte_count) != 0) {
212				goto error_cleanup;
213			}
214			if (vmem_sub(VMEM_CLEAR, NULL,
215				     (void *)phtbl[i].p_vaddr + phtbl[i].p_filesz,
216				     phtbl[i].p_memsz - phtbl[i].p_filesz,
217				     &byte_count) != 0) {
218				goto error_cleanup;
219			}
220		} else {
221			byte_count += phtbl[i].p_memsz;
222		}
223
224		first = 0;
225	}
226
227	if (first) {
228		debug_printf(TEXT("can't find loadable segment\n"));
229		msg_printf(MSG_ERROR, whoami, TEXT("can't find loadable segment\n"));
230		goto error_cleanup;
231	}
232	total_bytes = byte_count;
233
234	debug_printf(TEXT("entry=%x  addr=%x-%x\n"),
235		     elf->e_entry, min_addr, max_addr);
236
237#ifdef LOAD_DEBUG_INFO
238	if (pref.load_debug_info) {
239		/*
240		 *  scan section header table
241		 *  to search for debugging information
242		 */
243		sh_symidx = -1;
244		sh_stridx = -1;
245		for (i = 0; i < elf->e_shnum; i++) {
246			if (shtbl[i].sh_type == SHT_SYMTAB) {
247				sh_symidx = i;
248			}
249			if ((shtbl[i].sh_type == SHT_STRTAB)
250			    && (shtbl[i].sh_size >= 0x4000)) {
251				sh_stridx = i;
252			}
253		}
254		if (sh_symidx == -1 || sh_stridx == -1) {
255			debug_printf(TEXT("debugging information not found\n"));
256		} else
257		if (load) {
258			Elf_Ehdr dbg_eh;
259			Elf_Shdr dbg_sh[2];
260			memset(&dbg_eh, 0, sizeof(Elf_Ehdr));
261			memset(dbg_sh, 0, sizeof(Elf_Shdr) * 2);
262
263			memcpy(dbg_eh.e_ident, elf->e_ident,
264			       sizeof(elf->e_ident));
265			dbg_eh.e_machine = elf->e_machine;
266			dbg_eh.e_version = elf->e_version;
267			dbg_eh.e_entry = 0;
268			dbg_eh.e_phoff = 0;
269			dbg_eh.e_shoff = sizeof(Elf_Ehdr);
270			dbg_eh.e_flags = elf->e_flags;
271			dbg_eh.e_ehsize = sizeof(Elf_Ehdr);
272			dbg_eh.e_phentsize = 0;
273			dbg_eh.e_phnum = 0;
274			dbg_eh.e_shentsize = sizeof(Elf_Shdr);
275			dbg_eh.e_shnum = 2;
276			dbg_eh.e_shstrndx = 0;	/* ??? */
277
278			/*
279			 *  XXX, pass debug info size in e_entry.
280			 */
281			dbg_eh.e_entry = ROUNDUP(dbg_hdr_size +
282						 shtbl[sh_symidx].sh_size +
283						 shtbl[sh_stridx].sh_size,
284						 DEBUG_INFO_ALIGN);
285
286			if (vmem_sub(VMEM_COPY, (void*)&dbg_eh,
287				     max_addr,
288				     sizeof(Elf_Ehdr),
289				     &byte_count) != 0) {
290				goto error_cleanup;
291			}
292
293			dbg_sh[0].sh_type = shtbl[sh_symidx].sh_type;
294			dbg_sh[0].sh_offset = dbg_hdr_size;
295			dbg_sh[0].sh_size = shtbl[sh_symidx].sh_size;
296			dbg_sh[0].sh_addralign = shtbl[sh_symidx].sh_addralign;
297			dbg_sh[1].sh_type = shtbl[sh_stridx].sh_type;
298			dbg_sh[1].sh_offset = dbg_hdr_size + shtbl[sh_symidx].sh_size;
299			dbg_sh[1].sh_size = shtbl[sh_stridx].sh_size;
300			dbg_sh[1].sh_addralign = shtbl[sh_stridx].sh_addralign;
301			if (vmem_sub(VMEM_COPY, (void*)dbg_sh,
302				     max_addr + sizeof(Elf_Ehdr),
303				     sizeof(Elf_Shdr) * 2,
304				     &byte_count) != 0) {
305				goto error_cleanup;
306			}
307
308			if (lseek(fd, shtbl[sh_symidx].sh_offset, SEEK_SET) == -1)  {
309				debug_printf(TEXT("seek for debug symbol error\n"));
310				msg_printf(MSG_ERROR, whoami,
311					   TEXT("seek for segment error.\n"));
312				goto error_cleanup;
313			}
314			if (vmem_sub(VMEM_LOAD, (void*)fd,
315				     max_addr + dbg_hdr_size,
316				     shtbl[sh_symidx].sh_size,
317				     &byte_count) != 0) {
318				goto error_cleanup;
319			}
320
321			if (lseek(fd, shtbl[sh_stridx].sh_offset, SEEK_SET) == -1)  {
322				debug_printf(TEXT("seek for string table error\n"));
323				msg_printf(MSG_ERROR, whoami,
324					   TEXT("seek for segment error.\n"));
325				goto error_cleanup;
326			}
327			if (vmem_sub(VMEM_LOAD, (void*)fd,
328				     max_addr + dbg_hdr_size + shtbl[sh_symidx].sh_size,
329				     shtbl[sh_stridx].sh_size,
330				     &byte_count) != 0) {
331				goto error_cleanup;
332			}
333		} else {
334			/*
335			 *  make space for debugging information
336			 */
337			int dbg_info_size = ROUNDUP(dbg_hdr_size +
338						    shtbl[sh_symidx].sh_size +
339						    shtbl[sh_stridx].sh_size,
340						    DEBUG_INFO_ALIGN);
341			debug_printf(TEXT("%x bytes debug information\n"),
342				     dbg_info_size);
343			max_addr += dbg_info_size;
344			total_bytes += dbg_info_size;
345		}
346	}
347#endif /* LOAD_DEBUG_INFO */
348
349	if (phtbl) dealloc(phtbl, sizeof(*phtbl) * elf->e_phnum);
350	if (shtbl) dealloc(shtbl, sizeof(*shtbl) * elf->e_shnum);
351
352	if (start) *start = min_addr;
353	if (end) *end = max_addr;
354	if (entry) *entry = (void *)elf->e_entry;
355	return (0);
356
357 error_cleanup:
358	if (phtbl) dealloc(phtbl, sizeof(*phtbl) * elf->e_phnum);
359	if (shtbl) dealloc(shtbl, sizeof(*shtbl) * elf->e_shnum);
360	return (-1);
361}
362