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