Home | History | Annotate | Line # | Download | only in gdb.base
sym-file-loader.c revision 1.1.1.2
      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 
     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