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.
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.
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/>.
24 #include "sym-file-loader.h"
28 #include <elf/common.h>
29 #include <elf/external.h>
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
;
39 #elif defined TARGET_ILP32
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
;
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 : \
56 #define GETADDR(hdr, field) (\
57 sizeof ((hdr)->field) == sizeof (Elf_Addr) ? *(Elf_Addr *) (hdr)->field : \
64 Elf_External_Phdr
*phdr
;
71 Elf_External_Ehdr
*ehdr
;
72 struct segment
*segments
;
75 static Elf_External_Shdr
*find_shdr (Elf_External_Ehdr
*ehdr
,
77 static int translate_offset (uint64_t file_offset
, struct segment
*seg
,
83 elf_st_type (uint8_t st_info
)
85 return ELF64_ST_TYPE (st_info
);
88 #elif defined TARGET_ILP32
91 elf_st_type (uint8_t st_info
)
93 return ELF32_ST_TYPE (st_info
);
98 /* Load a program segment. */
100 static struct segment
*
101 load (uint8_t *addr
, Elf_External_Phdr
*phdr
, struct segment
*tail_seg
)
103 struct segment
*seg
= NULL
;
104 uint8_t *mapped_addr
= NULL
;
105 size_t mapped_size
= 0;
109 /* For the sake of simplicity all operations are permitted. */
110 unsigned perm
= PROT_READ
| PROT_WRITE
| PROT_EXEC
;
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
);
117 from
= (void *) (addr
+ GET (phdr
, p_offset
));
118 to
= (void *) mapped_addr
;
120 memcpy (to
, from
, GET (phdr
, p_filesz
));
122 seg
= (struct segment
*) malloc (sizeof (struct segment
));
127 seg
->mapped_addr
= mapped_addr
;
128 seg
->mapped_size
= mapped_size
;
133 tail_seg
->next
= seg
;
139 # define SELF_LINK "/proc/self/exe"
141 # define SELF_LINK "/proc/curproc/exe"
142 #elif defined __OpenBSD__ || defined __FreeBSD__ || defined __DragonFly__
143 # define SELF_LINK "/proc/curproc/file"
145 # define SELF_LINK "/proc/self/path/a.out"
148 /* Like RPATH=$ORIGIN, return the dirname of the current
154 static char self_path
[PATH_MAX
];
155 static ssize_t self_path_len
;
157 if (self_path_len
== 0)
160 self_path_len
= readlink (SELF_LINK
, self_path
, PATH_MAX
- 1);
161 if (self_path_len
!= -1)
165 self_path
[self_path_len
] = '\0';
166 dirsep
= strrchr (self_path
, '/');
174 if (self_path_len
== -1)
180 /* Unload/unmap a segment. */
183 unload (struct segment
*seg
)
185 munmap (seg
->mapped_addr
, seg
->mapped_size
);
190 unload_shlib (struct library
*lib
)
192 struct segment
*seg
, *next_seg
;
194 for (seg
= lib
->segments
; seg
!= NULL
; seg
= next_seg
)
196 next_seg
= seg
->next
;
204 /* Mini shared library loader. No reallocation
205 is performed for the sake of simplicity. */
208 load_shlib (const char *file
)
215 Elf_External_Ehdr
*ehdr
;
216 Elf_External_Phdr
*phdr
;
217 struct segment
*head_seg
= NULL
;
218 struct segment
*tail_seg
= NULL
;
222 /* Map the lib in memory for reading.
224 If the file name is relative, try looking it up relative to the
225 main executable's path. I.e., emulate RPATH=$ORIGIN. */
228 origin
= get_origin ();
231 fprintf (stderr
, "get_origin not implemented.");
235 path
= alloca (strlen (origin
) + 1 + strlen (file
) + 1);
236 sprintf (path
, "%s/%s", origin
, file
);
237 fd
= open (path
, O_RDONLY
);
241 fd
= open (file
, O_RDONLY
);
245 perror ("fopen failed.");
249 fsize
= lseek (fd
, 0, SEEK_END
);
253 perror ("lseek failed.");
257 addr
= (uint8_t *) mmap (NULL
, fsize
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
258 if (addr
== (uint8_t *) -1)
260 perror ("mmap failed.");
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
)
271 printf ("Not an ELF file: %x\n", ehdr
->e_ident
[EI_MAG0
]);
275 if (ehdr
->e_ident
[EI_CLASS
] == ELFCLASS32
)
277 if (sizeof (void *) != 4)
279 printf ("Architecture mismatch.");
283 else if (ehdr
->e_ident
[EI_CLASS
] == ELFCLASS64
)
285 if (sizeof (void *) != 8)
287 printf ("Architecture mismatch.");
292 lib
= malloc (sizeof (struct library
));
295 printf ("malloc failed.");
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
++)
306 if (GET (phdr
, p_type
) == PT_LOAD
)
308 struct segment
*next_seg
= load (addr
, phdr
, tail_seg
);
317 lib
->segments
= head_seg
;
322 get_text_addr (struct library
*lib
, void **text_addr
)
324 Elf_External_Shdr
*text
;
326 /* Get the text section. */
327 text
= find_shdr (lib
->ehdr
, ".text");
331 if (translate_offset (GET (text
, sh_offset
), lib
->segments
, text_addr
)
338 /* Return the section-header table. */
341 find_shdrtab (Elf_External_Ehdr
*ehdr
)
343 return (Elf_External_Shdr
*) (((uint8_t *) ehdr
) + GET (ehdr
, e_shoff
));
346 /* Return the string table of the section headers. */
349 find_shstrtab (Elf_External_Ehdr
*ehdr
, uint64_t *size
)
351 const Elf_External_Shdr
*shdr
;
352 const Elf_External_Shdr
*shstr
;
354 if (GET (ehdr
, e_shnum
) <= GET (ehdr
, e_shstrndx
))
356 printf ("The index of the string table is corrupt.");
360 shdr
= find_shdrtab (ehdr
);
362 shstr
= &shdr
[GET (ehdr
, e_shstrndx
)];
363 *size
= GET (shstr
, sh_size
);
364 return ((const char *) ehdr
) + GET (shstr
, sh_offset
);
367 /* Return the string table named SECTION. */
370 find_strtab (Elf_External_Ehdr
*ehdr
,
371 const char *section
, uint64_t *strtab_size
)
373 uint64_t shstrtab_size
= 0;
374 const char *shstrtab
;
376 const Elf_External_Shdr
*shdr
= find_shdrtab (ehdr
);
378 /* Get the string table of the section headers. */
379 shstrtab
= find_shstrtab (ehdr
, &shstrtab_size
);
380 if (shstrtab
== NULL
)
383 for (i
= 0; i
< GET (ehdr
, e_shnum
); i
++)
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)
389 *strtab_size
= GET (shdr
+ i
, sh_size
);
390 return ((const char *) ehdr
) + GET (shdr
+ i
, sh_offset
);
397 /* Return the section header named SECTION. */
399 static Elf_External_Shdr
*
400 find_shdr (Elf_External_Ehdr
*ehdr
, const char *section
)
402 uint64_t shstrtab_size
= 0;
403 const char *shstrtab
;
406 /* Get the string table of the section headers. */
407 shstrtab
= find_shstrtab (ehdr
, &shstrtab_size
);
408 if (shstrtab
== NULL
)
411 Elf_External_Shdr
*shdr
= find_shdrtab (ehdr
);
412 for (i
= 0; i
< GET (ehdr
, e_shnum
); i
++)
414 uint64_t name
= GET (shdr
+ i
, sh_name
);
415 if (name
<= shstrtab_size
)
417 if (strcmp ((const char *) &shstrtab
[name
], section
) == 0)
425 /* Return the symbol table. */
427 static Elf_External_Sym
*
428 find_symtab (Elf_External_Ehdr
*ehdr
, uint64_t *symtab_size
)
431 const Elf_External_Shdr
*shdr
= find_shdrtab (ehdr
);
433 for (i
= 0; i
< GET (ehdr
, e_shnum
); i
++)
435 if (GET (shdr
+ i
, sh_type
) == SHT_SYMTAB
)
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
));
445 /* Translate a file offset to an address in a loaded segment. */
448 translate_offset (uint64_t file_offset
, struct segment
*seg
, void **addr
)
452 uint64_t p_from
, p_to
;
454 Elf_External_Phdr
*phdr
= seg
->phdr
;
462 p_from
= GET (phdr
, p_offset
);
463 p_to
= p_from
+ GET (phdr
, p_filesz
);
465 if (p_from
<= file_offset
&& file_offset
< p_to
)
467 *addr
= (void *) (seg
->mapped_addr
+ (file_offset
- p_from
));
476 /* Lookup the address of FUNC. */
479 lookup_function (struct library
*lib
, const char *func
, void **addr
)
482 uint64_t strtab_size
= 0;
483 Elf_External_Sym
*symtab
;
484 uint64_t symtab_size
= 0;
486 Elf_External_Ehdr
*ehdr
= lib
->ehdr
;
487 struct segment
*seg
= lib
->segments
;
489 /* Get the string table for the symbols. */
490 strtab
= find_strtab (ehdr
, ".strtab", &strtab_size
);
493 printf (".strtab not found.");
497 /* Get the symbol table. */
498 symtab
= find_symtab (ehdr
, &symtab_size
);
501 printf ("symbol table not found.");
505 for (i
= 0; i
< symtab_size
; i
++)
507 Elf_External_Sym
*sym
= &symtab
[i
];
509 if (elf_st_type (GET (sym
, st_info
)) != STT_FUNC
)
512 if (GET (sym
, st_name
) < strtab_size
)
514 const char *name
= &strtab
[GET (sym
, st_name
)];
515 if (strcmp (name
, func
) == 0)
518 uint64_t offset
= GET (sym
, st_value
);
519 return translate_offset (offset
, seg
, addr
);