sym-file-loader.c revision 1.3
1/* Copyright 2013-2015 Free Software Foundation, Inc. 2 This program is free software; you can redistribute it and/or modify 3 it under the terms of the GNU General Public License as published by 4 the Free Software Foundation; either version 3 of the License, or 5 (at your option) any later version. 6 7 This program is distributed in the hope that it will be useful, 8 but WITHOUT ANY WARRANTY; without even the implied warranty of 9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 GNU General Public License for more details. 11 12 You should have received a copy of the GNU General Public License 13 along with this program. If not, see <http://www.gnu.org/licenses/>. 14*/ 15 16#include <unistd.h> 17#include <fcntl.h> 18#include <limits.h> 19#include <stdio.h> 20#include <stdlib.h> 21#include <string.h> 22#include <sys/mman.h> 23 24#include "sym-file-loader.h" 25 26#include <inttypes.h> 27#include <ansidecl.h> 28#include <elf/common.h> 29#include <elf/external.h> 30 31#ifdef TARGET_LP64 32 33typedef Elf64_External_Phdr Elf_External_Phdr; 34typedef Elf64_External_Ehdr Elf_External_Ehdr; 35typedef Elf64_External_Shdr Elf_External_Shdr; 36typedef Elf64_External_Sym Elf_External_Sym; 37typedef uint64_t Elf_Addr; 38 39#elif defined TARGET_ILP32 40 41typedef Elf32_External_Phdr Elf_External_Phdr; 42typedef Elf32_External_Ehdr Elf_External_Ehdr; 43typedef Elf32_External_Shdr Elf_External_Shdr; 44typedef Elf32_External_Sym Elf_External_Sym; 45typedef uint32_t Elf_Addr; 46 47#endif 48 49#define GET(hdr, field) (\ 50sizeof ((hdr)->field) == 1 ? (uint64_t) (hdr)->field[0] : \ 51sizeof ((hdr)->field) == 2 ? (uint64_t) *(uint16_t *) (hdr)->field : \ 52sizeof ((hdr)->field) == 4 ? (uint64_t) *(uint32_t *) (hdr)->field : \ 53sizeof ((hdr)->field) == 8 ? *(uint64_t *) (hdr)->field : \ 54*(uint64_t *) NULL) 55 56#define GETADDR(hdr, field) (\ 57sizeof ((hdr)->field) == sizeof (Elf_Addr) ? *(Elf_Addr *) (hdr)->field : \ 58*(Elf_Addr *) NULL) 59 60struct segment 61{ 62 uint8_t *mapped_addr; 63 size_t mapped_size; 64 Elf_External_Phdr *phdr; 65 struct segment *next; 66}; 67 68struct library 69{ 70 int fd; 71 Elf_External_Ehdr *ehdr; 72 struct segment *segments; 73}; 74 75static Elf_External_Shdr *find_shdr (Elf_External_Ehdr *ehdr, 76 const char *section); 77static int translate_offset (uint64_t file_offset, struct segment *seg, 78 void **addr); 79 80#ifdef TARGET_LP64 81 82uint8_t 83elf_st_type (uint8_t st_info) 84{ 85 return ELF64_ST_TYPE (st_info); 86} 87 88#elif defined TARGET_ILP32 89 90uint8_t 91elf_st_type (uint8_t st_info) 92{ 93 return ELF32_ST_TYPE (st_info); 94} 95 96#endif 97 98/* Load a program segment. */ 99 100static struct segment * 101load (uint8_t *addr, Elf_External_Phdr *phdr, struct segment *tail_seg) 102{ 103 struct segment *seg = NULL; 104 uint8_t *mapped_addr = NULL; 105 size_t mapped_size = 0; 106 void *from = NULL; 107 void *to = NULL; 108 109 /* For the sake of simplicity all operations are permitted. */ 110 unsigned perm = PROT_READ | PROT_WRITE | PROT_EXEC; 111 112 mapped_addr = (uint8_t *) mmap ((void *) GETADDR (phdr, p_vaddr), 113 GET (phdr, p_memsz), perm, 114 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 115 mapped_size = GET (phdr, p_memsz); 116 117 from = (void *) (addr + GET (phdr, p_offset)); 118 to = (void *) mapped_addr; 119 120 memcpy (to, from, GET (phdr, p_filesz)); 121 122 seg = (struct segment *) malloc (sizeof (struct segment)); 123 124 if (seg == 0) 125 return 0; 126 127 seg->mapped_addr = mapped_addr; 128 seg->mapped_size = mapped_size; 129 seg->phdr = phdr; 130 seg->next = 0; 131 132 if (tail_seg != 0) 133 tail_seg->next = seg; 134 135 return seg; 136} 137 138#ifdef __linux__ 139# define SELF_LINK "/proc/self/exe" 140#elif defined NETBSD 141# define SELF_LINK "/proc/curproc/exe" 142#elif defined __OpenBSD__ || defined __FreeBSD__ || defined __DragonFly__ 143# define SELF_LINK "/proc/curproc/file" 144#elif defined SunOS 145# define SELF_LINK "/proc/self/path/a.out" 146#endif 147 148/* Like RPATH=$ORIGIN, return the dirname of the current 149 executable. */ 150 151static const char * 152get_origin (void) 153{ 154 static char self_path[PATH_MAX]; 155 static ssize_t self_path_len; 156 157 if (self_path_len == 0) 158 { 159#ifdef SELF_LINK 160 self_path_len = readlink (SELF_LINK, self_path, PATH_MAX - 1); 161 if (self_path_len != -1) 162 { 163 char *dirsep; 164 165 self_path[self_path_len] = '\0'; 166 dirsep = strrchr (self_path, '/'); 167 *dirsep = '\0'; 168 } 169#else 170 self_path_len = -1; 171#endif 172 } 173 174 if (self_path_len == -1) 175 return NULL; 176 else 177 return self_path; 178} 179 180/* Unload/unmap a segment. */ 181 182static void 183unload (struct segment *seg) 184{ 185 munmap (seg->mapped_addr, seg->mapped_size); 186 free (seg); 187} 188 189void 190unload_shlib (struct library *lib) 191{ 192 struct segment *seg, *next_seg; 193 194 for (seg = lib->segments; seg != NULL; seg = next_seg) 195 { 196 next_seg = seg->next; 197 unload (seg); 198 } 199 200 close (lib->fd); 201 free (lib); 202} 203 204/* Mini shared library loader. No reallocation 205 is performed for the sake of simplicity. */ 206 207struct library * 208load_shlib (const char *file) 209{ 210 struct library *lib; 211 uint64_t i; 212 int fd = -1; 213 off_t fsize; 214 uint8_t *addr; 215 Elf_External_Ehdr *ehdr; 216 Elf_External_Phdr *phdr; 217 struct segment *head_seg = NULL; 218 struct segment *tail_seg = NULL; 219 const char *origin; 220 char *path; 221 222 /* Map the lib in memory for reading. 223 224 If the file name is relative, try looking it up relative to the 225 main executable's path. I.e., emulate RPATH=$ORIGIN. */ 226 if (file[0] != '/') 227 { 228 origin = get_origin (); 229 if (origin == NULL) 230 { 231 fprintf (stderr, "get_origin not implemented."); 232 return NULL; 233 } 234 235 path = alloca (strlen (origin) + 1 + strlen (file) + 1); 236 sprintf (path, "%s/%s", origin, file); 237 fd = open (path, O_RDONLY); 238 } 239 240 if (fd < 0) 241 fd = open (file, O_RDONLY); 242 243 if (fd < 0) 244 { 245 perror ("fopen failed."); 246 return NULL; 247 } 248 249 fsize = lseek (fd, 0, SEEK_END); 250 251 if (fsize < 0) 252 { 253 perror ("lseek failed."); 254 return NULL; 255 } 256 257 addr = (uint8_t *) mmap (NULL, fsize, PROT_READ, MAP_PRIVATE, fd, 0); 258 if (addr == (uint8_t *) -1) 259 { 260 perror ("mmap failed."); 261 return NULL; 262 } 263 264 /* Check if the lib is an ELF file. */ 265 ehdr = (Elf_External_Ehdr *) addr; 266 if (ehdr->e_ident[EI_MAG0] != ELFMAG0 267 || ehdr->e_ident[EI_MAG1] != ELFMAG1 268 || ehdr->e_ident[EI_MAG2] != ELFMAG2 269 || ehdr->e_ident[EI_MAG3] != ELFMAG3) 270 { 271 printf ("Not an ELF file: %x\n", ehdr->e_ident[EI_MAG0]); 272 return NULL; 273 } 274 275 if (ehdr->e_ident[EI_CLASS] == ELFCLASS32) 276 { 277 if (sizeof (void *) != 4) 278 { 279 printf ("Architecture mismatch."); 280 return NULL; 281 } 282 } 283 else if (ehdr->e_ident[EI_CLASS] == ELFCLASS64) 284 { 285 if (sizeof (void *) != 8) 286 { 287 printf ("Architecture mismatch."); 288 return NULL; 289 } 290 } 291 292 lib = malloc (sizeof (struct library)); 293 if (lib == NULL) 294 { 295 printf ("malloc failed."); 296 return NULL; 297 } 298 299 lib->fd = fd; 300 301 /* Load the program segments. For the sake of simplicity 302 assume that no reallocation is needed. */ 303 phdr = (Elf_External_Phdr *) (addr + GET (ehdr, e_phoff)); 304 for (i = 0; i < GET (ehdr, e_phnum); i++, phdr++) 305 { 306 if (GET (phdr, p_type) == PT_LOAD) 307 { 308 struct segment *next_seg = load (addr, phdr, tail_seg); 309 if (next_seg == 0) 310 continue; 311 tail_seg = next_seg; 312 if (head_seg == 0) 313 head_seg = next_seg; 314 } 315 } 316 lib->ehdr = ehdr; 317 lib->segments = head_seg; 318 return lib; 319} 320 321int 322get_text_addr (struct library *lib, void **text_addr) 323{ 324 Elf_External_Shdr *text; 325 326 /* Get the text section. */ 327 text = find_shdr (lib->ehdr, ".text"); 328 if (text == NULL) 329 return -1; 330 331 if (translate_offset (GET (text, sh_offset), lib->segments, text_addr) 332 != 0) 333 return -1; 334 335 return 0; 336} 337 338/* Return the section-header table. */ 339 340Elf_External_Shdr * 341find_shdrtab (Elf_External_Ehdr *ehdr) 342{ 343 return (Elf_External_Shdr *) (((uint8_t *) ehdr) + GET (ehdr, e_shoff)); 344} 345 346/* Return the string table of the section headers. */ 347 348const char * 349find_shstrtab (Elf_External_Ehdr *ehdr, uint64_t *size) 350{ 351 const Elf_External_Shdr *shdr; 352 const Elf_External_Shdr *shstr; 353 354 if (GET (ehdr, e_shnum) <= GET (ehdr, e_shstrndx)) 355 { 356 printf ("The index of the string table is corrupt."); 357 return NULL; 358 } 359 360 shdr = find_shdrtab (ehdr); 361 362 shstr = &shdr[GET (ehdr, e_shstrndx)]; 363 *size = GET (shstr, sh_size); 364 return ((const char *) ehdr) + GET (shstr, sh_offset); 365} 366 367/* Return the string table named SECTION. */ 368 369const char * 370find_strtab (Elf_External_Ehdr *ehdr, 371 const char *section, uint64_t *strtab_size) 372{ 373 uint64_t shstrtab_size = 0; 374 const char *shstrtab; 375 uint64_t i; 376 const Elf_External_Shdr *shdr = find_shdrtab (ehdr); 377 378 /* Get the string table of the section headers. */ 379 shstrtab = find_shstrtab (ehdr, &shstrtab_size); 380 if (shstrtab == NULL) 381 return NULL; 382 383 for (i = 0; i < GET (ehdr, e_shnum); i++) 384 { 385 uint64_t name = GET (shdr + i, sh_name); 386 if (GET (shdr + i, sh_type) == SHT_STRTAB && name <= shstrtab_size 387 && strcmp ((const char *) &shstrtab[name], section) == 0) 388 { 389 *strtab_size = GET (shdr + i, sh_size); 390 return ((const char *) ehdr) + GET (shdr + i, sh_offset); 391 } 392 393 } 394 return NULL; 395} 396 397/* Return the section header named SECTION. */ 398 399static Elf_External_Shdr * 400find_shdr (Elf_External_Ehdr *ehdr, const char *section) 401{ 402 uint64_t shstrtab_size = 0; 403 const char *shstrtab; 404 uint64_t i; 405 406 /* Get the string table of the section headers. */ 407 shstrtab = find_shstrtab (ehdr, &shstrtab_size); 408 if (shstrtab == NULL) 409 return NULL; 410 411 Elf_External_Shdr *shdr = find_shdrtab (ehdr); 412 for (i = 0; i < GET (ehdr, e_shnum); i++) 413 { 414 uint64_t name = GET (shdr + i, sh_name); 415 if (name <= shstrtab_size) 416 { 417 if (strcmp ((const char *) &shstrtab[name], section) == 0) 418 return &shdr[i]; 419 } 420 421 } 422 return NULL; 423} 424 425/* Return the symbol table. */ 426 427static Elf_External_Sym * 428find_symtab (Elf_External_Ehdr *ehdr, uint64_t *symtab_size) 429{ 430 uint64_t i; 431 const Elf_External_Shdr *shdr = find_shdrtab (ehdr); 432 433 for (i = 0; i < GET (ehdr, e_shnum); i++) 434 { 435 if (GET (shdr + i, sh_type) == SHT_SYMTAB) 436 { 437 *symtab_size = GET (shdr + i, sh_size) / sizeof (Elf_External_Sym); 438 return (Elf_External_Sym *) (((const char *) ehdr) + 439 GET (shdr + i, sh_offset)); 440 } 441 } 442 return NULL; 443} 444 445/* Translate a file offset to an address in a loaded segment. */ 446 447static int 448translate_offset (uint64_t file_offset, struct segment *seg, void **addr) 449{ 450 while (seg) 451 { 452 uint64_t p_from, p_to; 453 454 Elf_External_Phdr *phdr = seg->phdr; 455 456 if (phdr == NULL) 457 { 458 seg = seg->next; 459 continue; 460 } 461 462 p_from = GET (phdr, p_offset); 463 p_to = p_from + GET (phdr, p_filesz); 464 465 if (p_from <= file_offset && file_offset < p_to) 466 { 467 *addr = (void *) (seg->mapped_addr + (file_offset - p_from)); 468 return 0; 469 } 470 seg = seg->next; 471 } 472 473 return -1; 474} 475 476/* Lookup the address of FUNC. */ 477 478int 479lookup_function (struct library *lib, const char *func, void **addr) 480{ 481 const char *strtab; 482 uint64_t strtab_size = 0; 483 Elf_External_Sym *symtab; 484 uint64_t symtab_size = 0; 485 uint64_t i; 486 Elf_External_Ehdr *ehdr = lib->ehdr; 487 struct segment *seg = lib->segments; 488 489 /* Get the string table for the symbols. */ 490 strtab = find_strtab (ehdr, ".strtab", &strtab_size); 491 if (strtab == NULL) 492 { 493 printf (".strtab not found."); 494 return -1; 495 } 496 497 /* Get the symbol table. */ 498 symtab = find_symtab (ehdr, &symtab_size); 499 if (symtab == NULL) 500 { 501 printf ("symbol table not found."); 502 return -1; 503 } 504 505 for (i = 0; i < symtab_size; i++) 506 { 507 Elf_External_Sym *sym = &symtab[i]; 508 509 if (elf_st_type (GET (sym, st_info)) != STT_FUNC) 510 continue; 511 512 if (GET (sym, st_name) < strtab_size) 513 { 514 const char *name = &strtab[GET (sym, st_name)]; 515 if (strcmp (name, func) == 0) 516 { 517 518 uint64_t offset = GET (sym, st_value); 519 return translate_offset (offset, seg, addr); 520 } 521 } 522 } 523 524 return -1; 525} 526