ef.c revision 109607
1/* 2 * Copyright (c) 2000, Boris Popov 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 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by Boris Popov. 16 * 4. Neither the name of the author nor the names of any co-contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 * $FreeBSD: head/usr.sbin/kldxref/ef.c 109607 2003-01-21 03:51:53Z jake $ 33 */ 34 35#include <sys/param.h> 36#include <sys/linker.h> 37#include <string.h> 38#include <stdio.h> 39#include <stdlib.h> 40#include <unistd.h> 41#include <errno.h> 42#include <fcntl.h> 43#include <machine/elf.h> 44#define FREEBSD_ELF 45#include <link.h> 46 47#include <err.h> 48 49#include "ef.h" 50 51static void ef_print_phdr(Elf_Phdr *); 52static u_long ef_get_offset(elf_file_t, Elf_Off); 53static int ef_parse_dynamic(elf_file_t); 54 55void 56ef_print_phdr(Elf_Phdr *phdr) 57{ 58 59 if ((phdr->p_flags & PF_W) == 0) { 60 printf("text=0x%lx ", (long)phdr->p_filesz); 61 } else { 62 printf("data=0x%lx", (long)phdr->p_filesz); 63 if (phdr->p_filesz < phdr->p_memsz) 64 printf("+0x%lx", (long)(phdr->p_memsz - phdr->p_filesz)); 65 printf(" "); 66 } 67} 68 69u_long 70ef_get_offset(elf_file_t ef, Elf_Off off) 71{ 72 Elf_Phdr *ph; 73 int i; 74 75 for (i = 0; i < ef->ef_nsegs; i++) { 76 ph = ef->ef_segs[i]; 77 if (off >= ph->p_vaddr && off < ph->p_vaddr + ph->p_memsz) { 78 return ph->p_offset + (off - ph->p_vaddr); 79 } 80 } 81 return 0; 82} 83 84/* 85 * next three functions copied from link_elf.c 86 */ 87static unsigned long 88elf_hash(const char *name) 89{ 90 const unsigned char *p = (const unsigned char *) name; 91 unsigned long h = 0; 92 unsigned long g; 93 94 while (*p != '\0') { 95 h = (h << 4) + *p++; 96 if ((g = h & 0xf0000000) != 0) 97 h ^= g >> 24; 98 h &= ~g; 99 } 100 return h; 101} 102 103int 104ef_lookup_symbol(elf_file_t ef, const char* name, Elf_Sym** sym) 105{ 106 unsigned long symnum; 107 Elf_Sym* symp; 108 char *strp; 109 unsigned long hash; 110 111 /* First, search hashed global symbols */ 112 hash = elf_hash(name); 113 symnum = ef->ef_buckets[hash % ef->ef_nbuckets]; 114 115 while (symnum != STN_UNDEF) { 116 if (symnum >= ef->ef_nchains) { 117 warnx("ef_lookup_symbol: file %s have corrupted symbol table\n", 118 ef->ef_name); 119 return ENOENT; 120 } 121 122 symp = ef->ef_symtab + symnum; 123 if (symp->st_name == 0) { 124 warnx("ef_lookup_symbol: file %s have corrupted symbol table\n", 125 ef->ef_name); 126 return ENOENT; 127 } 128 129 strp = ef->ef_strtab + symp->st_name; 130 131 if (strcmp(name, strp) == 0) { 132 if (symp->st_shndx != SHN_UNDEF || 133 (symp->st_value != 0 && 134 ELF_ST_TYPE(symp->st_info) == STT_FUNC)) { 135 *sym = symp; 136 return 0; 137 } else 138 return ENOENT; 139 } 140 141 symnum = ef->ef_chains[symnum]; 142 } 143 144 return ENOENT; 145} 146 147int 148ef_parse_dynamic(elf_file_t ef) 149{ 150 Elf_Dyn *dp; 151 Elf_Hashelt hashhdr[2]; 152/* int plttype = DT_REL;*/ 153 int error; 154 Elf_Off rel_off; 155 Elf_Off rela_off; 156 int rel_sz; 157 int rela_sz; 158 int rel_entry; 159 int rela_entry; 160 161 rel_off = rela_off = 0; 162 rel_sz = rela_sz = 0; 163 rel_entry = rela_entry = 0; 164 for (dp = ef->ef_dyn; dp->d_tag != DT_NULL; dp++) { 165 switch (dp->d_tag) { 166 case DT_HASH: 167 error = ef_read(ef, ef_get_offset(ef, dp->d_un.d_ptr), 168 sizeof(hashhdr), hashhdr); 169 if (error) { 170 warnx("can't read hash header (%lx)", 171 ef_get_offset(ef, dp->d_un.d_ptr)); 172 return error; 173 } 174 ef->ef_nbuckets = hashhdr[0]; 175 ef->ef_nchains = hashhdr[1]; 176 error = ef_read_entry(ef, -1, 177 (hashhdr[0] + hashhdr[1]) * sizeof(Elf_Hashelt), 178 (void**)&ef->ef_hashtab); 179 if (error) { 180 warnx("can't read hash table"); 181 return error; 182 } 183 ef->ef_buckets = ef->ef_hashtab; 184 ef->ef_chains = ef->ef_buckets + ef->ef_nbuckets; 185 break; 186 case DT_STRTAB: 187 ef->ef_stroff = dp->d_un.d_ptr; 188 break; 189 case DT_STRSZ: 190 ef->ef_strsz = dp->d_un.d_val; 191 break; 192 case DT_SYMTAB: 193 ef->ef_symoff = dp->d_un.d_ptr; 194 break; 195 case DT_SYMENT: 196 if (dp->d_un.d_val != sizeof(Elf_Sym)) 197 return EFTYPE; 198 break; 199 case DT_REL: 200 if (rel_off != 0) 201 warnx("second DT_REL entry ignored"); 202 rel_off = dp->d_un.d_ptr; 203 break; 204 case DT_RELSZ: 205 if (rel_sz != 0) 206 warnx("second DT_RELSZ entry ignored"); 207 rel_sz = dp->d_un.d_val; 208 break; 209 case DT_RELENT: 210 if (rel_entry != 0) 211 warnx("second DT_RELENT entry ignored"); 212 rel_entry = dp->d_un.d_val; 213 break; 214 case DT_RELA: 215 if (rela_off != 0) 216 warnx("second DT_RELA entry ignored"); 217 rela_off = dp->d_un.d_ptr; 218 break; 219 case DT_RELASZ: 220 if (rela_sz != 0) 221 warnx("second DT_RELASZ entry ignored"); 222 rela_sz = dp->d_un.d_val; 223 break; 224 case DT_RELAENT: 225 if (rela_entry != 0) 226 warnx("second DT_RELAENT entry ignored"); 227 rela_entry = dp->d_un.d_val; 228 break; 229 } 230 } 231 if (ef->ef_symoff == 0) { 232 warnx("%s: no .dynsym section found\n", ef->ef_name); 233 return EFTYPE; 234 } 235 if (ef->ef_stroff == 0) { 236 warnx("%s: no .dynstr section found\n", ef->ef_name); 237 return EFTYPE; 238 } 239 if (ef_read_entry(ef, ef_get_offset(ef, ef->ef_symoff), 240 ef->ef_nchains * sizeof(Elf_Sym), 241 (void**)&ef->ef_symtab) != 0) { 242 if (ef->ef_verbose) 243 warnx("%s: can't load .dynsym section (0x%lx)", 244 ef->ef_name, (long)ef->ef_symoff); 245 return EIO; 246 } 247 if (ef_read_entry(ef, ef_get_offset(ef, ef->ef_stroff), ef->ef_strsz, 248 (void**)&ef->ef_strtab) != 0) { 249 warnx("can't load .dynstr section"); 250 return EIO; 251 } 252 if (rel_off != 0) { 253 if (rel_entry == 0) { 254 warnx("%s: no DT_RELENT for DT_REL", ef->ef_name); 255 return (EFTYPE); 256 } 257 if (rel_entry != sizeof(Elf_Rel)) { 258 warnx("%s: inconsistent DT_RELENT value", 259 ef->ef_name); 260 return (EFTYPE); 261 } 262 if (rel_sz % rel_entry != 0) { 263 warnx("%s: inconsistent values for DT_RELSZ and " 264 "DT_RELENT", ef->ef_name); 265 return (EFTYPE); 266 } 267 if (ef_read_entry(ef, ef_get_offset(ef, rel_off), rel_sz, 268 (void **)&ef->ef_rel) != 0) { 269 warnx("%s: cannot load DT_REL section", ef->ef_name); 270 return (EIO); 271 } 272 ef->ef_relsz = rel_sz / rel_entry; 273 if (ef->ef_verbose) 274 warnx("%s: %d REL entries", ef->ef_name, 275 ef->ef_relsz); 276 } 277 if (rela_off != 0) { 278 if (rela_entry == 0) { 279 warnx("%s: no DT_RELAENT for DT_RELA", ef->ef_name); 280 return (EFTYPE); 281 } 282 if (rela_entry != sizeof(Elf_Rela)) { 283 warnx("%s: inconsistent DT_RELAENT value", 284 ef->ef_name); 285 return (EFTYPE); 286 } 287 if (rela_sz % rela_entry != 0) { 288 warnx("%s: inconsistent values for DT_RELASZ and " 289 "DT_RELAENT", ef->ef_name); 290 return (EFTYPE); 291 } 292 if (ef_read_entry(ef, ef_get_offset(ef, rela_off), rela_sz, 293 (void **)&ef->ef_rela) != 0) { 294 warnx("%s: cannot load DT_RELA section", ef->ef_name); 295 return (EIO); 296 } 297 ef->ef_relasz = rela_sz / rela_entry; 298 if (ef->ef_verbose) 299 warnx("%s: %d RELA entries", ef->ef_name, 300 ef->ef_relasz); 301 } 302 return 0; 303} 304 305int 306ef_read(elf_file_t ef, Elf_Off offset, size_t len, void*dest) 307{ 308 ssize_t r; 309 310 if (offset != (Elf_Off)-1) { 311 if (lseek(ef->ef_fd, offset, SEEK_SET) == -1) 312 return EIO; 313 } 314 315 r = read(ef->ef_fd, dest, len); 316 if (r != -1 && (size_t)r == len) 317 return 0; 318 else 319 return EIO; 320} 321 322int 323ef_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void**ptr) 324{ 325 int error; 326 327 *ptr = malloc(len); 328 if (*ptr == NULL) 329 return ENOMEM; 330 error = ef_read(ef, offset, len, *ptr); 331 if (error) 332 free(*ptr); 333 return error; 334} 335 336int 337ef_seg_read(elf_file_t ef, Elf_Off offset, size_t len, void*dest) 338{ 339 u_long ofs = ef_get_offset(ef, offset); 340 341 if (ofs == 0) { 342 if (ef->ef_verbose) 343 warnx("ef_seg_read(%s): zero offset (%lx:%ld)", 344 ef->ef_name, (long)offset, ofs); 345 return EFAULT; 346 } 347 return ef_read(ef, ofs, len, dest); 348} 349 350int 351ef_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len, void*dest) 352{ 353 u_long ofs = ef_get_offset(ef, offset); 354 int error; 355 356 if (ofs == 0) { 357 if (ef->ef_verbose) 358 warnx("ef_seg_read(%s): zero offset (%lx:%ld)", 359 ef->ef_name, (long)offset, ofs); 360 return EFAULT; 361 } 362 if ((error = ef_read(ef, ofs, len, dest)) != 0) 363 return (error); 364 return (ef_reloc(ef, offset, len, dest)); 365} 366 367int 368ef_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void**ptr) 369{ 370 int error; 371 372 *ptr = malloc(len); 373 if (*ptr == NULL) 374 return ENOMEM; 375 error = ef_seg_read(ef, offset, len, *ptr); 376 if (error) 377 free(*ptr); 378 return error; 379} 380 381int 382ef_seg_read_entry_rel(elf_file_t ef, Elf_Off offset, size_t len, void**ptr) 383{ 384 int error; 385 386 *ptr = malloc(len); 387 if (*ptr == NULL) 388 return ENOMEM; 389 error = ef_seg_read_rel(ef, offset, len, *ptr); 390 if (error) 391 free(*ptr); 392 return error; 393} 394 395int 396ef_open(const char *filename, elf_file_t ef, int verbose) 397{ 398 Elf_Ehdr *hdr; 399 int fd; 400 int error; 401 int phlen, res; 402 int nsegs; 403 Elf_Phdr *phdr, *phdyn, *phphdr, *phlimit; 404 405 bzero(ef, sizeof(*ef)); 406 if (filename == NULL) 407 return EFTYPE; 408 ef->ef_verbose = verbose; 409 if ((fd = open(filename, O_RDONLY)) == -1) 410 return errno; 411 ef->ef_fd = fd; 412 ef->ef_name = strdup(filename); 413 hdr = (Elf_Ehdr *)&ef->ef_hdr; 414 do { 415 res = read(fd, hdr, sizeof(*hdr)); 416 error = EFTYPE; 417 if (res != sizeof(*hdr)) 418 break; 419 if (!IS_ELF(*hdr)) 420 break; 421 if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || 422 hdr->e_ident[EI_DATA] != ELF_TARG_DATA || 423 hdr->e_ident[EI_VERSION] != EV_CURRENT || 424 hdr->e_version != EV_CURRENT || 425 hdr->e_machine != ELF_TARG_MACH || 426 hdr->e_phentsize != sizeof(Elf_Phdr)) 427 break; 428 phlen = hdr->e_phnum * sizeof(Elf_Phdr); 429 if (ef_read_entry(ef, hdr->e_phoff, phlen, 430 (void**)&ef->ef_ph) != 0) 431 break; 432 phdr = ef->ef_ph; 433 phlimit = phdr + hdr->e_phnum; 434 nsegs = 0; 435 phdyn = NULL; 436 phphdr = NULL; 437 while (phdr < phlimit) { 438 if (verbose > 1) 439 ef_print_phdr(phdr); 440 switch (phdr->p_type) { 441 case PT_LOAD: 442 if (nsegs == 2) { 443 warnx("%s: too many sections", 444 filename); 445 break; 446 } 447 ef->ef_segs[nsegs++] = phdr; 448 break; 449 case PT_PHDR: 450 phphdr = phdr; 451 break; 452 case PT_DYNAMIC: 453 phdyn = phdr; 454 break; 455 } 456 phdr++; 457 } 458 if (verbose > 1) 459 printf("\n"); 460 ef->ef_nsegs = nsegs; 461 if (phdyn == NULL) { 462 warnx("file isn't dynamically-linked"); 463 break; 464 } 465 if (ef_read_entry(ef, phdyn->p_offset, 466 phdyn->p_filesz, (void**)&ef->ef_dyn) != 0) { 467 printf("ef_read_entry failed\n"); 468 break; 469 } 470 error = ef_parse_dynamic(ef); 471 if (error) 472 break; 473 if (hdr->e_type == ET_DYN) { 474 ef->ef_type = EFT_KLD; 475/* pad = (u_int)dest & PAGE_MASK; 476 if (pad) 477 dest += PAGE_SIZE - pad;*/ 478 error = 0; 479 } else if (hdr->e_type == ET_EXEC) { 480/* dest = hdr->e_entry; 481 if (dest == 0) 482 break;*/ 483 ef->ef_type = EFT_KERNEL; 484 error = 0; 485 } else 486 break; 487 } while(0); 488 if (error) { 489 ef_close(ef); 490 if (ef->ef_verbose) 491 warnc(error, "elf_open(%s)", filename); 492 } 493 return error; 494} 495 496int 497ef_close(elf_file_t ef) 498{ 499 close(ef->ef_fd); 500/* if (ef->ef_fpage) 501 free(ef->ef_fpage);*/ 502 if (ef->ef_name) 503 free(ef->ef_name); 504 return 0; 505} 506