1 /* Copyright 2013-2019 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 33 typedef Elf64_External_Phdr Elf_External_Phdr; 34 typedef Elf64_External_Ehdr Elf_External_Ehdr; 35 typedef Elf64_External_Shdr Elf_External_Shdr; 36 typedef Elf64_External_Sym Elf_External_Sym; 37 typedef uint64_t Elf_Addr; 38 39 #elif defined TARGET_ILP32 40 41 typedef Elf32_External_Phdr Elf_External_Phdr; 42 typedef Elf32_External_Ehdr Elf_External_Ehdr; 43 typedef Elf32_External_Shdr Elf_External_Shdr; 44 typedef Elf32_External_Sym Elf_External_Sym; 45 typedef uint32_t Elf_Addr; 46 47 #endif 48 49 #define GET(hdr, field) (\ 50 sizeof ((hdr)->field) == 1 ? (uint64_t) (hdr)->field[0] : \ 51 sizeof ((hdr)->field) == 2 ? (uint64_t) *(uint16_t *) (hdr)->field : \ 52 sizeof ((hdr)->field) == 4 ? (uint64_t) *(uint32_t *) (hdr)->field : \ 53 sizeof ((hdr)->field) == 8 ? *(uint64_t *) (hdr)->field : \ 54 *(uint64_t *) NULL) 55 56 #define GETADDR(hdr, field) (\ 57 sizeof ((hdr)->field) == sizeof (Elf_Addr) ? *(Elf_Addr *) (hdr)->field : \ 58 *(Elf_Addr *) NULL) 59 60 struct segment 61 { 62 uint8_t *mapped_addr; 63 size_t mapped_size; 64 Elf_External_Phdr *phdr; 65 struct segment *next; 66 }; 67 68 struct library 69 { 70 int fd; 71 Elf_External_Ehdr *ehdr; 72 struct segment *segments; 73 }; 74 75 static Elf_External_Shdr *find_shdr (Elf_External_Ehdr *ehdr, 76 const char *section); 77 static int translate_offset (uint64_t file_offset, struct segment *seg, 78 void **addr); 79 80 #ifdef TARGET_LP64 81 82 uint8_t 83 elf_st_type (uint8_t st_info) 84 { 85 return ELF64_ST_TYPE (st_info); 86 } 87 88 #elif defined TARGET_ILP32 89 90 uint8_t 91 elf_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 100 static struct segment * 101 load (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 151 static const char * 152 get_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 182 static void 183 unload (struct segment *seg) 184 { 185 munmap (seg->mapped_addr, seg->mapped_size); 186 free (seg); 187 } 188 189 void 190 unload_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 207 struct library * 208 load_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 321 int 322 get_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 340 Elf_External_Shdr * 341 find_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 348 const char * 349 find_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 369 const char * 370 find_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 399 static Elf_External_Shdr * 400 find_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 427 static Elf_External_Sym * 428 find_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 447 static int 448 translate_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 478 int 479 lookup_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