xref: /netbsd-src/sys/arch/hpc/stand/hpcboot/load_elf.cpp (revision 48360965f30c307b6836d0d898d15ce6c1d9b387)
1 /*	$NetBSD: load_elf.cpp,v 1.19 2008/11/07 16:28:26 rafal Exp $	*/
2 
3 /*-
4  * Copyright (c) 2001 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by UCHIYAMA Yasushi.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <hpcmenu.h>
33 #include <menu/window.h>
34 #include <menu/rootwindow.h>
35 
36 #include <load.h>
37 #include <load_elf.h>
38 #include <console.h>
39 #include <memory.h>
40 #include <file.h>
41 
42 #define	ROUND4(x)	(((x) + 3) & ~3)
43 
44 ElfLoader::ElfLoader(Console *&cons, MemoryManager *&mem)
45 	: Loader(cons, mem)
46 {
47 
48 	_sym_blk.enable = FALSE;
49 	_ph = NULL;
50 	_sh = NULL;
51 
52 	DPRINTF((TEXT("Loader: ELF\n")));
53 }
54 
55 ElfLoader::~ElfLoader(void)
56 {
57 
58 	if (_sym_blk.header != NULL)
59 		free(_sym_blk.header);
60 	if (_ph != NULL)
61 		free(_ph);
62 	if (_sh != NULL)
63 		free(_sh);
64 }
65 
66 BOOL
67 ElfLoader::setFile(File *&file)
68 {
69 	size_t sz;
70 	Loader::setFile(file);
71 
72 	// read ELF header and check it
73 	if (!read_header())
74 		return FALSE;
75 
76 	// read section header
77 	sz = _eh.e_shnum * _eh.e_shentsize;
78 	if ((_sh = static_cast<Elf_Shdr *>(malloc(sz))) == NULL) {
79 		DPRINTF((TEXT("can't allocate section header table.\n")));
80 		return FALSE;
81 	}
82 	if (_file->read(_sh, sz, _eh.e_shoff) != sz) {
83 		DPRINTF((TEXT("section header read error.\n")));
84 		return FALSE;
85 	}
86 
87 	// read program header
88 	sz = _eh.e_phnum * _eh.e_phentsize;
89 	if ((_ph = static_cast<Elf_Phdr *>(malloc(sz))) == NULL) {
90 		DPRINTF((TEXT("can't allocate program header table.\n")));
91 		return FALSE;
92 	}
93 	if (_file->read(_ph, sz, _eh.e_phoff) != sz) {
94 		DPRINTF((TEXT("program header read error.\n")));
95 		return FALSE;
96 	}
97 
98 	return TRUE;
99 }
100 
101 size_t
102 ElfLoader::memorySize()
103 {
104 	int i;
105 	Elf_Phdr *ph = _ph;
106 	size_t sz = 0;
107 	size_t extra = 0;
108 
109 	DPRINTF((TEXT("file size: ")));
110 	for (i = 0; i < _eh.e_phnum; i++, ph++) {
111 		if (ph->p_type == PT_LOAD) {
112 			size_t filesz = ph->p_filesz;
113 			DPRINTF((TEXT("%s0x%x"),
114 				 sz == 0 ? "" : "+",
115 				 filesz));
116 			sz += _mem->roundPage(filesz);
117 			// compensate for partial last tag
118 			extra += _mem->getTaggedPageSize();
119 			if (filesz < ph->p_memsz)
120 				// compensate for zero clear
121 				extra += _mem->getTaggedPageSize();
122 
123 		}
124 	}
125 
126 	// reserve for symbols
127 	size_t symblk_sz = symbol_block_size();
128 	if (symblk_sz) {
129 		sz += symblk_sz;
130 		DPRINTF((TEXT(" = 0x%x]"), symblk_sz));
131 		// XXX: compensate for partial tags after ELF header and symtab
132 		extra += 2 * _mem->getTaggedPageSize();
133 	}
134 
135 	sz += extra;
136 	DPRINTF((TEXT("+[extra: 0x%x]"), extra));
137 
138 	DPRINTF((TEXT(" = 0x%x bytes\n"), sz));
139 	return sz;
140 }
141 
142 kaddr_t
143 ElfLoader::jumpAddr()
144 {
145 	DPRINTF((TEXT("kernel entry address: 0x%08x\n"), _eh.e_entry));
146 	return _eh.e_entry;
147 }
148 
149 BOOL
150 ElfLoader::load()
151 {
152 	Elf_Phdr *ph;
153 	vaddr_t kv;
154 	int i;
155 
156 	_load_segment_start();
157 
158 	for (i = 0, ph = _ph; i < _eh.e_phnum; i++, ph++) {
159 		if (ph->p_type == PT_LOAD) {
160 			size_t filesz = ph->p_filesz;
161 			size_t memsz = ph->p_memsz;
162 			kv = ph->p_paddr;
163 			off_t fileofs = ph->p_offset;
164 			DPRINTF((TEXT("seg[%d] paddr 0x%08x file size 0x%x mem size 0x%x\n"),
165 			    i, kv, filesz, memsz));
166 			_load_segment(kv, memsz, fileofs, filesz);
167 			kv += ROUND4(memsz);
168 		}
169 	}
170 
171 	load_symbol_block(kv);
172 
173 	// tag chain still opening
174 
175 	return _load_success();
176 }
177 
178 //
179 // Prepare ELF headers for symbol table.
180 //
181 //   ELF header
182 //   section header
183 //   shstrtab
184 //   symtab
185 //   strtab
186 //
187 size_t
188 ElfLoader::symbol_block_size()
189 {
190 	size_t shstrsize = ROUND4(_sh[_eh.e_shstrndx].sh_size);
191 	size_t shtab_sz = _eh.e_shentsize * _eh.e_shnum;
192 	off_t shstrtab_offset = sizeof(Elf_Ehdr) + shtab_sz;
193 	int i;
194 
195 	memset(&_sym_blk, 0, sizeof(_sym_blk));
196 	_sym_blk.enable = FALSE;
197 	_sym_blk.header_size = sizeof(Elf_Ehdr) + shtab_sz + shstrsize;
198 
199 	// inquire string and symbol table size
200 	_sym_blk.header = static_cast<char *>(malloc(_sym_blk.header_size));
201 	if (_sym_blk.header == NULL) {
202 		MessageBox(HPC_MENU._root->_window,
203 		    TEXT("Can't determine symbol block size."),
204 		    TEXT("WARNING"),
205 		    MB_ICONWARNING | MB_OK);
206 		UpdateWindow(HPC_MENU._root->_window);
207 		return (0);
208 	}
209 
210 	// set pointer for symbol block
211 	Elf_Ehdr *eh = reinterpret_cast<Elf_Ehdr *>(_sym_blk.header);
212 	Elf_Shdr *sh = reinterpret_cast<Elf_Shdr *>
213 	    (_sym_blk.header + sizeof(Elf_Ehdr));
214 	char *shstrtab = _sym_blk.header + shstrtab_offset;
215 
216 	// initialize headers
217 	memset(_sym_blk.header, 0, _sym_blk.header_size);
218 	memcpy(eh, &_eh, sizeof(Elf_Ehdr));
219 	eh->e_phoff = 0;
220 	eh->e_phnum = 0;
221 	eh->e_entry = 0; // XXX NetBSD kernel check this member. see machdep.c
222 	eh->e_shoff = sizeof(Elf_Ehdr);
223 	memcpy(sh, _sh, shtab_sz);
224 
225 	// inquire symbol/string table information
226 	_file->read(shstrtab, shstrsize, _sh[_eh.e_shstrndx].sh_offset);
227 	for (i = 0; i < _eh.e_shnum; i++, sh++) {
228 		if (strcmp(".strtab", shstrtab + sh->sh_name) == 0) {
229 			_sym_blk.shstr = sh;
230 			_sym_blk.stroff = sh->sh_offset;
231 		} else if (strcmp(".symtab", shstrtab + sh->sh_name) == 0) {
232 			_sym_blk.shsym = sh;
233 			_sym_blk.symoff = sh->sh_offset;
234 		}
235 
236 		sh->sh_offset = (i == _eh.e_shstrndx) ? shstrtab_offset : 0;
237 	}
238 
239 	if (_sym_blk.shstr == NULL || _sym_blk.shsym == NULL) {
240 		if (HPC_PREFERENCE.safety_message) {
241 			MessageBox(HPC_MENU._root->_window,
242 			    TEXT("No symbol and/or string table in binary.\n(not fatal)"),
243 			    TEXT("Information"),
244 			    MB_ICONINFORMATION | MB_OK);
245 			UpdateWindow(HPC_MENU._root->_window);
246 		}
247 		free(_sym_blk.header);
248 		_sym_blk.header = NULL;
249 
250 		return (0);
251 	}
252 
253 	// set Section Headers for symbol/string table
254 	_sym_blk.shsym->sh_offset = shstrtab_offset + shstrsize;
255 	_sym_blk.shstr->sh_offset = shstrtab_offset + shstrsize +
256 	    ROUND4(_sym_blk.shsym->sh_size);
257 	_sym_blk.enable = TRUE;
258 
259 	DPRINTF((TEXT("+[ksyms: header 0x%x, symtab 0x%x, strtab 0x%x"),
260 	    _sym_blk.header_size, _sym_blk.shsym->sh_size,
261 	    _sym_blk.shstr->sh_size));
262 
263 	// return total amount of symbol block
264 	return (_sym_blk.header_size + ROUND4(_sym_blk.shsym->sh_size) +
265 	    _sym_blk.shstr->sh_size);
266 }
267 
268 void
269 ElfLoader::load_symbol_block(vaddr_t kv)
270 {
271 	size_t sz;
272 
273 	if (!_sym_blk.enable)
274 		return;
275 
276 	DPRINTF((TEXT("ksyms\n")));
277 
278 	// load header
279 	_load_memory(kv, _sym_blk.header_size, _sym_blk.header);
280 	kv += _sym_blk.header_size;
281 
282 	// load symbol table
283 	sz = _sym_blk.shsym->sh_size;
284 	_load_segment(kv, sz, _sym_blk.symoff, sz);
285 	kv += ROUND4(sz);
286 
287 	// load string table
288 	sz = _sym_blk.shstr->sh_size;
289 	_load_segment(kv, sz, _sym_blk.stroff, sz);
290 }
291 
292 BOOL
293 ElfLoader::read_header()
294 {
295 	// read ELF header
296 	_file->read(&_eh, sizeof(Elf_Ehdr), 0);
297 
298 	// check ELF Magic.
299 	if (!is_elf_file()) {
300 		DPRINTF((TEXT("not a ELF file.\n")));
301 		return FALSE;
302 	}
303 
304 	// Windows CE is 32bit little-endian only.
305 	if (_eh.e_ident[EI_DATA] != ELFDATA2LSB ||
306 	    _eh.e_ident[EI_CLASS] != ELFCLASS32) {
307 		DPRINTF((TEXT("invalid class/data(%d/%d)\n"),
308 		    _eh.e_ident[EI_CLASS], _eh.e_ident[EI_DATA]));
309 		return FALSE;
310 	}
311 
312 	// Is native architecture?
313 	switch(_eh.e_machine) {
314 		ELF32_MACHDEP_ID_CASES;
315 	default:
316 		DPRINTF((TEXT("not a native architecture. machine = %d\n"),
317 		    _eh.e_machine));
318 		return FALSE;
319 	}
320 
321 	// Check object type
322 	if (_eh.e_type != ET_EXEC) {
323 		DPRINTF((TEXT("not a executable file. type = %d\n"),
324 		    _eh.e_type));
325 		return FALSE;
326 	}
327 
328 	if (_eh.e_phoff == 0 || _eh.e_phnum == 0 || _eh.e_phnum > 16 ||
329 	    _eh.e_phentsize != sizeof(Elf_Phdr)) {
330 		DPRINTF((TEXT("invalid program header information.\n")));
331 		return FALSE;
332 	}
333 
334 	return TRUE;
335 }
336