ef.c revision 87551
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 87551 2001-12-09 07:22:26Z mikeh $ 33 */ 34 35#include <sys/param.h> 36#include <sys/exec.h> 37#include <sys/queue.h> 38#include <sys/kernel.h> 39#include <sys/reboot.h> 40#include <sys/linker.h> 41#include <string.h> 42#include <machine/bootinfo.h> 43#include <machine/elf.h> 44#include <stand.h> 45#define FREEBSD_ELF 46#include <link.h> 47 48#include <err.h> 49 50#include "ef.h" 51 52static void ef_print_phdr(Elf_Phdr *); 53static u_long ef_get_offset(elf_file_t, Elf_Off); 54static int ef_parse_dynamic(elf_file_t); 55 56void 57ef_print_phdr(Elf_Phdr *phdr) 58{ 59 60 if ((phdr->p_flags & PF_W) == 0) { 61 printf("text=0x%lx ", (long)phdr->p_filesz); 62 } else { 63 printf("data=0x%lx", (long)phdr->p_filesz); 64 if (phdr->p_filesz < phdr->p_memsz) 65 printf("+0x%lx", (long)(phdr->p_memsz - phdr->p_filesz)); 66 printf(" "); 67 } 68} 69 70u_long 71ef_get_offset(elf_file_t ef, Elf_Off off) 72{ 73 Elf_Phdr *ph; 74 int i; 75 76 for (i = 0; i < ef->ef_nsegs; i++) { 77 ph = ef->ef_segs[i]; 78 if (off >= ph->p_vaddr && off < ph->p_vaddr + ph->p_memsz) { 79 return ph->p_offset + (off - ph->p_vaddr); 80 } 81 } 82 return 0; 83} 84 85/* 86 * next three functions copied from link_elf.c 87 */ 88static unsigned long 89elf_hash(const char *name) 90{ 91 const unsigned char *p = (const unsigned char *) name; 92 unsigned long h = 0; 93 unsigned long g; 94 95 while (*p != '\0') { 96 h = (h << 4) + *p++; 97 if ((g = h & 0xf0000000) != 0) 98 h ^= g >> 24; 99 h &= ~g; 100 } 101 return h; 102} 103 104int 105ef_lookup_symbol(elf_file_t ef, const char* name, Elf_Sym** sym) 106{ 107 unsigned long symnum; 108 Elf_Sym* symp; 109 char *strp; 110 unsigned long hash; 111 112 /* First, search hashed global symbols */ 113 hash = elf_hash(name); 114 symnum = ef->ef_buckets[hash % ef->ef_nbuckets]; 115 116 while (symnum != STN_UNDEF) { 117 if (symnum >= ef->ef_nchains) { 118 warnx("ef_lookup_symbol: file %s have corrupted symbol table\n", 119 ef->ef_name); 120 return ENOENT; 121 } 122 123 symp = ef->ef_symtab + symnum; 124 if (symp->st_name == 0) { 125 warnx("ef_lookup_symbol: file %s have corrupted symbol table\n", 126 ef->ef_name); 127 return ENOENT; 128 } 129 130 strp = ef->ef_strtab + symp->st_name; 131 132 if (strcmp(name, strp) == 0) { 133 if (symp->st_shndx != SHN_UNDEF || 134 (symp->st_value != 0 && 135 ELF_ST_TYPE(symp->st_info) == STT_FUNC)) { 136 *sym = symp; 137 return 0; 138 } else 139 return ENOENT; 140 } 141 142 symnum = ef->ef_chains[symnum]; 143 } 144 145 return ENOENT; 146} 147 148int 149ef_parse_dynamic(elf_file_t ef) 150{ 151 Elf_Dyn *dp; 152 Elf_Off hashhdr[2]; 153/* int plttype = DT_REL;*/ 154 int error; 155 156 for (dp = ef->ef_dyn; dp->d_tag != DT_NULL; dp++) { 157 switch (dp->d_tag) { 158 case DT_HASH: 159 error = ef_read(ef, ef_get_offset(ef, dp->d_un.d_ptr), 160 sizeof(hashhdr), hashhdr); 161 if (error) { 162 warnx("can't read hash header (%lx)", 163 ef_get_offset(ef, dp->d_un.d_ptr)); 164 return error; 165 } 166 ef->ef_nbuckets = hashhdr[0]; 167 ef->ef_nchains = hashhdr[1]; 168 error = ef_read_entry(ef, -1, 169 (hashhdr[0] + hashhdr[1]) * sizeof(Elf_Off), 170 (void**)&ef->ef_hashtab); 171 if (error) { 172 warnx("can't read hash table"); 173 return error; 174 } 175 ef->ef_buckets = ef->ef_hashtab; 176 ef->ef_chains = ef->ef_buckets + ef->ef_nbuckets; 177 break; 178 case DT_STRTAB: 179 ef->ef_stroff = dp->d_un.d_ptr; 180 break; 181 case DT_STRSZ: 182 ef->ef_strsz = dp->d_un.d_val; 183 break; 184 case DT_SYMTAB: 185 ef->ef_symoff = dp->d_un.d_ptr; 186 break; 187 case DT_SYMENT: 188 if (dp->d_un.d_val != sizeof(Elf_Sym)) 189 return EFTYPE; 190 break; 191 } 192 } 193 if (ef->ef_symoff == 0) { 194 warnx("%s: no .dynsym section found\n", ef->ef_name); 195 return EFTYPE; 196 } 197 if (ef->ef_stroff == 0) { 198 warnx("%s: no .dynstr section found\n", ef->ef_name); 199 return EFTYPE; 200 } 201 if (ef_read_entry(ef, ef_get_offset(ef, ef->ef_symoff), 202 ef->ef_nchains * sizeof(Elf_Sym), 203 (void**)&ef->ef_symtab) != 0) { 204 if (ef->ef_verbose) 205 warnx("%s: can't load .dynsym section (0x%lx)", 206 ef->ef_name, (long)ef->ef_symoff); 207 return EIO; 208 } 209 if (ef_read_entry(ef, ef_get_offset(ef, ef->ef_stroff), ef->ef_strsz, 210 (void**)&ef->ef_strtab) != 0) { 211 warnx("can't load .dynstr section"); 212 return EIO; 213 } 214 return 0; 215} 216 217int 218ef_read(elf_file_t ef, Elf_Off offset, size_t len, void*dest) 219{ 220 ssize_t r; 221 222 if (offset != (Elf_Off)-1) { 223 if (lseek(ef->ef_fd, offset, SEEK_SET) == -1) 224 return EIO; 225 } 226 227 r = read(ef->ef_fd, dest, len); 228 if (r != -1 && (size_t)r == len) 229 return 0; 230 else 231 return EIO; 232} 233 234int 235ef_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void**ptr) 236{ 237 int error; 238 239 *ptr = malloc(len); 240 if (*ptr == NULL) 241 return ENOMEM; 242 error = ef_read(ef, offset, len, *ptr); 243 if (error) 244 free(*ptr); 245 return error; 246} 247 248int 249ef_seg_read(elf_file_t ef, Elf_Off offset, size_t len, void*dest) 250{ 251 u_long ofs = ef_get_offset(ef, offset); 252 253 if (ofs == 0) { 254 if (ef->ef_verbose) 255 warnx("ef_seg_read(%s): zero offset (%lx:%ld)", 256 ef->ef_name, (long)offset, ofs); 257 return EFAULT; 258 } 259 return ef_read(ef, ofs, len, dest); 260} 261 262int 263ef_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void**ptr) 264{ 265 int error; 266 267 *ptr = malloc(len); 268 if (*ptr == NULL) 269 return ENOMEM; 270 error = ef_seg_read(ef, offset, len, *ptr); 271 if (error) 272 free(*ptr); 273 return error; 274} 275 276int 277ef_open(const char *filename, elf_file_t ef, int verbose) 278{ 279 Elf_Ehdr *hdr; 280 int fd; 281 int error; 282 int phlen, res; 283 int nsegs; 284 Elf_Phdr *phdr, *phdyn, *phphdr, *phlimit; 285 286 bzero(ef, sizeof(*ef)); 287 if (filename == NULL) 288 return EFTYPE; 289 ef->ef_verbose = verbose; 290 if ((fd = open(filename, O_RDONLY)) == -1) 291 return errno; 292 ef->ef_fd = fd; 293 ef->ef_name = strdup(filename); 294 hdr = (Elf_Ehdr *)&ef->ef_hdr; 295 do { 296 res = read(fd, hdr, sizeof(*hdr)); 297 error = EFTYPE; 298 if (res != sizeof(*hdr)) 299 break; 300 if (!IS_ELF(*hdr)) 301 break; 302 if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || 303 hdr->e_ident[EI_DATA] != ELF_TARG_DATA || 304 hdr->e_ident[EI_VERSION] != EV_CURRENT || 305 hdr->e_version != EV_CURRENT || 306 hdr->e_machine != ELF_TARG_MACH || 307 hdr->e_phentsize != sizeof(Elf_Phdr)) 308 break; 309 phlen = hdr->e_phnum * sizeof(Elf_Phdr); 310 if (ef_read_entry(ef, hdr->e_phoff, phlen, 311 (void**)&ef->ef_ph) != 0) 312 break; 313 phdr = ef->ef_ph; 314 phlimit = phdr + hdr->e_phnum; 315 nsegs = 0; 316 phdyn = NULL; 317 phphdr = NULL; 318 while (phdr < phlimit) { 319 if (verbose > 1) 320 ef_print_phdr(phdr); 321 switch (phdr->p_type) { 322 case PT_LOAD: 323 if (nsegs == 2) { 324 warnx("%s: too many sections", 325 filename); 326 break; 327 } 328 ef->ef_segs[nsegs++] = phdr; 329 break; 330 case PT_PHDR: 331 phphdr = phdr; 332 break; 333 case PT_DYNAMIC: 334 phdyn = phdr; 335 break; 336 } 337 phdr++; 338 } 339 if (verbose > 1) 340 printf("\n"); 341 ef->ef_nsegs = nsegs; 342 if (phdyn == NULL) { 343 warnx("file isn't dynamically-linked"); 344 break; 345 } 346 if (ef_read_entry(ef, phdyn->p_offset, 347 phdyn->p_filesz, (void**)&ef->ef_dyn) != 0) { 348 printf("ef_read_entry failed\n"); 349 break; 350 } 351 error = ef_parse_dynamic(ef); 352 if (error) 353 break; 354 if (hdr->e_type == ET_DYN) { 355 ef->ef_type = EFT_KLD; 356/* pad = (u_int)dest & PAGE_MASK; 357 if (pad) 358 dest += PAGE_SIZE - pad;*/ 359 error = 0; 360 } else if (hdr->e_type == ET_EXEC) { 361/* dest = hdr->e_entry; 362 if (dest == 0) 363 break;*/ 364 ef->ef_type = EFT_KERNEL; 365 error = 0; 366 } else 367 break; 368 } while(0); 369 if (error) { 370 ef_close(ef); 371 if (ef->ef_verbose) 372 warnc(error, "elf_open(%s)", filename); 373 } 374 return error; 375} 376 377int 378ef_close(elf_file_t ef) 379{ 380 close(ef->ef_fd); 381/* if (ef->ef_fpage) 382 free(ef->ef_fpage);*/ 383 if (ef->ef_name) 384 free(ef->ef_name); 385 return 0; 386} 387