sym-file-loader.c revision 1.1
1/* Copyright 2013-2014 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 <stdio.h> 19#include <stdlib.h> 20#include <string.h> 21#include <sys/mman.h> 22 23#include "sym-file-loader.h" 24 25#ifdef TARGET_LP64 26 27uint8_t 28elf_st_type (uint8_t st_info) 29{ 30 return ELF64_ST_TYPE (st_info); 31} 32 33#elif defined TARGET_ILP32 34 35uint8_t 36elf_st_type (uint8_t st_info) 37{ 38 return ELF32_ST_TYPE (st_info); 39} 40 41#endif 42 43/* Load a program segment. */ 44 45static struct segment * 46load (uint8_t *addr, Elf_External_Phdr *phdr, struct segment *tail_seg) 47{ 48 struct segment *seg = NULL; 49 uint8_t *mapped_addr = NULL; 50 void *from = NULL; 51 void *to = NULL; 52 53 /* For the sake of simplicity all operations are permitted. */ 54 unsigned perm = PROT_READ | PROT_WRITE | PROT_EXEC; 55 56 mapped_addr = (uint8_t *) mmap ((void *) GETADDR (phdr, p_vaddr), 57 GET (phdr, p_memsz), perm, 58 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 59 60 from = (void *) (addr + GET (phdr, p_offset)); 61 to = (void *) mapped_addr; 62 63 memcpy (to, from, GET (phdr, p_filesz)); 64 65 seg = (struct segment *) malloc (sizeof (struct segment)); 66 67 if (seg == 0) 68 return 0; 69 70 seg->mapped_addr = mapped_addr; 71 seg->phdr = phdr; 72 seg->next = 0; 73 74 if (tail_seg != 0) 75 tail_seg->next = seg; 76 77 return seg; 78} 79 80/* Mini shared library loader. No reallocation 81 is performed for the sake of simplicity. */ 82 83int 84load_shlib (const char *file, Elf_External_Ehdr **ehdr_out, 85 struct segment **seg_out) 86{ 87 uint64_t i; 88 int fd; 89 off_t fsize; 90 uint8_t *addr; 91 Elf_External_Ehdr *ehdr; 92 Elf_External_Phdr *phdr; 93 struct segment *head_seg = NULL; 94 struct segment *tail_seg = NULL; 95 96 /* Map the lib in memory for reading. */ 97 fd = open (file, O_RDONLY); 98 if (fd < 0) 99 { 100 perror ("fopen failed."); 101 return -1; 102 } 103 104 fsize = lseek (fd, 0, SEEK_END); 105 106 if (fsize < 0) 107 { 108 perror ("lseek failed."); 109 return -1; 110 } 111 112 addr = (uint8_t *) mmap (NULL, fsize, PROT_READ, MAP_PRIVATE, fd, 0); 113 if (addr == (uint8_t *) -1) 114 { 115 perror ("mmap failed."); 116 return -1; 117 } 118 119 /* Check if the lib is an ELF file. */ 120 ehdr = (Elf_External_Ehdr *) addr; 121 if (ehdr->e_ident[EI_MAG0] != ELFMAG0 122 || ehdr->e_ident[EI_MAG1] != ELFMAG1 123 || ehdr->e_ident[EI_MAG2] != ELFMAG2 124 || ehdr->e_ident[EI_MAG3] != ELFMAG3) 125 { 126 printf ("Not an ELF file: %x\n", ehdr->e_ident[EI_MAG0]); 127 return -1; 128 } 129 130 if (ehdr->e_ident[EI_CLASS] == ELFCLASS32) 131 { 132 if (sizeof (void *) != 4) 133 { 134 printf ("Architecture mismatch."); 135 return -1; 136 } 137 } 138 else if (ehdr->e_ident[EI_CLASS] == ELFCLASS64) 139 { 140 if (sizeof (void *) != 8) 141 { 142 printf ("Architecture mismatch."); 143 return -1; 144 } 145 } 146 147 /* Load the program segments. For the sake of simplicity 148 assume that no reallocation is needed. */ 149 phdr = (Elf_External_Phdr *) (addr + GET (ehdr, e_phoff)); 150 for (i = 0; i < GET (ehdr, e_phnum); i++, phdr++) 151 { 152 if (GET (phdr, p_type) == PT_LOAD) 153 { 154 struct segment *next_seg = load (addr, phdr, tail_seg); 155 if (next_seg == 0) 156 continue; 157 tail_seg = next_seg; 158 if (head_seg == 0) 159 head_seg = next_seg; 160 } 161 } 162 *ehdr_out = ehdr; 163 *seg_out = head_seg; 164 return 0; 165} 166 167/* Return the section-header table. */ 168 169Elf_External_Shdr * 170find_shdrtab (Elf_External_Ehdr *ehdr) 171{ 172 return (Elf_External_Shdr *) (((uint8_t *) ehdr) + GET (ehdr, e_shoff)); 173} 174 175/* Return the string table of the section headers. */ 176 177const char * 178find_shstrtab (Elf_External_Ehdr *ehdr, uint64_t *size) 179{ 180 const Elf_External_Shdr *shdr; 181 const Elf_External_Shdr *shstr; 182 183 if (GET (ehdr, e_shnum) <= GET (ehdr, e_shstrndx)) 184 { 185 printf ("The index of the string table is corrupt."); 186 return NULL; 187 } 188 189 shdr = find_shdrtab (ehdr); 190 191 shstr = &shdr[GET (ehdr, e_shstrndx)]; 192 *size = GET (shstr, sh_size); 193 return ((const char *) ehdr) + GET (shstr, sh_offset); 194} 195 196/* Return the string table named SECTION. */ 197 198const char * 199find_strtab (Elf_External_Ehdr *ehdr, 200 const char *section, uint64_t *strtab_size) 201{ 202 uint64_t shstrtab_size = 0; 203 const char *shstrtab; 204 uint64_t i; 205 const Elf_External_Shdr *shdr = find_shdrtab (ehdr); 206 207 /* Get the string table of the section headers. */ 208 shstrtab = find_shstrtab (ehdr, &shstrtab_size); 209 if (shstrtab == NULL) 210 return NULL; 211 212 for (i = 0; i < GET (ehdr, e_shnum); i++) 213 { 214 uint64_t name = GET (shdr + i, sh_name); 215 if (GET (shdr + i, sh_type) == SHT_STRTAB && name <= shstrtab_size 216 && strcmp ((const char *) &shstrtab[name], section) == 0) 217 { 218 *strtab_size = GET (shdr + i, sh_size); 219 return ((const char *) ehdr) + GET (shdr + i, sh_offset); 220 } 221 222 } 223 return NULL; 224} 225 226/* Return the section header named SECTION. */ 227 228Elf_External_Shdr * 229find_shdr (Elf_External_Ehdr *ehdr, const char *section) 230{ 231 uint64_t shstrtab_size = 0; 232 const char *shstrtab; 233 uint64_t i; 234 235 /* Get the string table of the section headers. */ 236 shstrtab = find_shstrtab (ehdr, &shstrtab_size); 237 if (shstrtab == NULL) 238 return NULL; 239 240 Elf_External_Shdr *shdr = find_shdrtab (ehdr); 241 for (i = 0; i < GET (ehdr, e_shnum); i++) 242 { 243 uint64_t name = GET (shdr + i, sh_name); 244 if (name <= shstrtab_size) 245 { 246 if (strcmp ((const char *) &shstrtab[name], section) == 0) 247 return &shdr[i]; 248 } 249 250 } 251 return NULL; 252} 253 254/* Return the symbol table. */ 255 256Elf_External_Sym * 257find_symtab (Elf_External_Ehdr *ehdr, uint64_t *symtab_size) 258{ 259 uint64_t i; 260 const Elf_External_Shdr *shdr = find_shdrtab (ehdr); 261 262 for (i = 0; i < GET (ehdr, e_shnum); i++) 263 { 264 if (GET (shdr + i, sh_type) == SHT_SYMTAB) 265 { 266 *symtab_size = GET (shdr + i, sh_size) / sizeof (Elf_External_Sym); 267 return (Elf_External_Sym *) (((const char *) ehdr) + 268 GET (shdr + i, sh_offset)); 269 } 270 } 271 return NULL; 272} 273 274/* Translate a file offset to an address in a loaded segment. */ 275 276int 277translate_offset (uint64_t file_offset, struct segment *seg, void **addr) 278{ 279 while (seg) 280 { 281 uint64_t p_from, p_to; 282 283 Elf_External_Phdr *phdr = seg->phdr; 284 285 if (phdr == NULL) 286 { 287 seg = seg->next; 288 continue; 289 } 290 291 p_from = GET (phdr, p_offset); 292 p_to = p_from + GET (phdr, p_filesz); 293 294 if (p_from <= file_offset && file_offset < p_to) 295 { 296 *addr = (void *) (seg->mapped_addr + (file_offset - p_from)); 297 return 0; 298 } 299 seg = seg->next; 300 } 301 302 return -1; 303} 304 305/* Lookup the address of FUNC. */ 306 307int 308lookup_function (const char *func, 309 Elf_External_Ehdr *ehdr, struct segment *seg, void **addr) 310{ 311 const char *strtab; 312 uint64_t strtab_size = 0; 313 Elf_External_Sym *symtab; 314 uint64_t symtab_size = 0; 315 uint64_t i; 316 317 /* Get the string table for the symbols. */ 318 strtab = find_strtab (ehdr, ".strtab", &strtab_size); 319 if (strtab == NULL) 320 { 321 printf (".strtab not found."); 322 return -1; 323 } 324 325 /* Get the symbol table. */ 326 symtab = find_symtab (ehdr, &symtab_size); 327 if (symtab == NULL) 328 { 329 printf ("symbol table not found."); 330 return -1; 331 } 332 333 for (i = 0; i < symtab_size; i++) 334 { 335 Elf_External_Sym *sym = &symtab[i]; 336 337 if (elf_st_type (GET (sym, st_info)) != STT_FUNC) 338 continue; 339 340 if (GET (sym, st_name) < strtab_size) 341 { 342 const char *name = &strtab[GET (sym, st_name)]; 343 if (strcmp (name, func) == 0) 344 { 345 346 uint64_t offset = GET (sym, st_value); 347 return translate_offset (offset, seg, addr); 348 } 349 } 350 } 351 352 return -1; 353} 354