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