1 /* Copyright 2013-2020 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/>.
25 #include "sym-file-loader.h"
29 #include <elf/common.h>
30 #include <elf/external.h>
34 typedef Elf64_External_Phdr Elf_External_Phdr
;
35 typedef Elf64_External_Ehdr Elf_External_Ehdr
;
36 typedef Elf64_External_Shdr Elf_External_Shdr
;
37 typedef Elf64_External_Sym Elf_External_Sym
;
38 typedef uint64_t Elf_Addr
;
40 #elif defined TARGET_ILP32
42 typedef Elf32_External_Phdr Elf_External_Phdr
;
43 typedef Elf32_External_Ehdr Elf_External_Ehdr
;
44 typedef Elf32_External_Shdr Elf_External_Shdr
;
45 typedef Elf32_External_Sym Elf_External_Sym
;
46 typedef uint32_t Elf_Addr
;
50 #define GET(hdr, field) (\
51 sizeof ((hdr)->field) == 1 ? (uint64_t) (hdr)->field[0] : \
52 sizeof ((hdr)->field) == 2 ? (uint64_t) *(uint16_t *) (hdr)->field : \
53 sizeof ((hdr)->field) == 4 ? (uint64_t) *(uint32_t *) (hdr)->field : \
54 sizeof ((hdr)->field) == 8 ? *(uint64_t *) (hdr)->field : \
57 #define GETADDR(hdr, field) (\
58 sizeof ((hdr)->field) == sizeof (Elf_Addr) ? *(Elf_Addr *) (hdr)->field : \
65 Elf_External_Phdr
*phdr
;
72 Elf_External_Ehdr
*ehdr
;
73 struct segment
*segments
;
76 static Elf_External_Shdr
*find_shdr (Elf_External_Ehdr
*ehdr
,
78 static int translate_offset (uint64_t file_offset
, struct segment
*seg
,
84 elf_st_type (uint8_t st_info
)
86 return ELF64_ST_TYPE (st_info
);
89 #elif defined TARGET_ILP32
92 elf_st_type (uint8_t st_info
)
94 return ELF32_ST_TYPE (st_info
);
99 /* Load a program segment. */
101 static struct segment
*
102 load (uint8_t *addr
, Elf_External_Phdr
*phdr
, struct segment
*tail_seg
)
104 struct segment
*seg
= NULL
;
105 uint8_t *mapped_addr
= NULL
;
106 size_t mapped_size
= 0;
110 /* For the sake of simplicity all operations are permitted. */
111 unsigned perm
= PROT_READ
| PROT_WRITE
| PROT_EXEC
;
113 mapped_addr
= (uint8_t *) mmap ((void *) GETADDR (phdr
, p_vaddr
),
114 GET (phdr
, p_memsz
), perm
,
115 MAP_ANONYMOUS
| MAP_PRIVATE
, -1, 0);
116 assert (mapped_addr
!= MAP_FAILED
);
118 mapped_size
= GET (phdr
, p_memsz
);
120 from
= (void *) (addr
+ GET (phdr
, p_offset
));
121 to
= (void *) mapped_addr
;
123 memcpy (to
, from
, GET (phdr
, p_filesz
));
125 seg
= (struct segment
*) malloc (sizeof (struct segment
));
130 seg
->mapped_addr
= mapped_addr
;
131 seg
->mapped_size
= mapped_size
;
136 tail_seg
->next
= seg
;
142 # define SELF_LINK "/proc/self/exe"
144 # define SELF_LINK "/proc/curproc/exe"
145 #elif defined __OpenBSD__ || defined __FreeBSD__ || defined __DragonFly__
146 # define SELF_LINK "/proc/curproc/file"
148 # define SELF_LINK "/proc/self/path/a.out"
151 /* Like RPATH=$ORIGIN, return the dirname of the current
157 static char self_path
[PATH_MAX
];
158 static ssize_t self_path_len
;
160 if (self_path_len
== 0)
163 self_path_len
= readlink (SELF_LINK
, self_path
, PATH_MAX
- 1);
164 if (self_path_len
!= -1)
168 self_path
[self_path_len
] = '\0';
169 dirsep
= strrchr (self_path
, '/');
177 if (self_path_len
== -1)
183 /* Unload/unmap a segment. */
186 unload (struct segment
*seg
)
188 munmap (seg
->mapped_addr
, seg
->mapped_size
);
193 unload_shlib (struct library
*lib
)
195 struct segment
*seg
, *next_seg
;
197 for (seg
= lib
->segments
; seg
!= NULL
; seg
= next_seg
)
199 next_seg
= seg
->next
;
207 /* Mini shared library loader. No reallocation
208 is performed for the sake of simplicity. */
211 load_shlib (const char *file
)
218 Elf_External_Ehdr
*ehdr
;
219 Elf_External_Phdr
*phdr
;
220 struct segment
*head_seg
= NULL
;
221 struct segment
*tail_seg
= NULL
;
225 /* Map the lib in memory for reading.
227 If the file name is relative, try looking it up relative to the
228 main executable's path. I.e., emulate RPATH=$ORIGIN. */
231 origin
= get_origin ();
234 fprintf (stderr
, "get_origin not implemented.");
238 path
= alloca (strlen (origin
) + 1 + strlen (file
) + 1);
239 sprintf (path
, "%s/%s", origin
, file
);
240 fd
= open (path
, O_RDONLY
);
244 fd
= open (file
, O_RDONLY
);
248 perror ("fopen failed.");
252 fsize
= lseek (fd
, 0, SEEK_END
);
256 perror ("lseek failed.");
260 addr
= (uint8_t *) mmap (NULL
, fsize
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
261 if (addr
== MAP_FAILED
)
263 perror ("mmap failed.");
267 /* Check if the lib is an ELF file. */
268 ehdr
= (Elf_External_Ehdr
*) addr
;
269 if (ehdr
->e_ident
[EI_MAG0
] != ELFMAG0
270 || ehdr
->e_ident
[EI_MAG1
] != ELFMAG1
271 || ehdr
->e_ident
[EI_MAG2
] != ELFMAG2
272 || ehdr
->e_ident
[EI_MAG3
] != ELFMAG3
)
274 printf ("Not an ELF file: %x\n", ehdr
->e_ident
[EI_MAG0
]);
278 if (ehdr
->e_ident
[EI_CLASS
] == ELFCLASS32
)
280 if (sizeof (void *) != 4)
282 printf ("Architecture mismatch.");
286 else if (ehdr
->e_ident
[EI_CLASS
] == ELFCLASS64
)
288 if (sizeof (void *) != 8)
290 printf ("Architecture mismatch.");
295 lib
= malloc (sizeof (struct library
));
298 printf ("malloc failed.");
304 /* Load the program segments. For the sake of simplicity
305 assume that no reallocation is needed. */
306 phdr
= (Elf_External_Phdr
*) (addr
+ GET (ehdr
, e_phoff
));
307 for (i
= 0; i
< GET (ehdr
, e_phnum
); i
++, phdr
++)
309 if (GET (phdr
, p_type
) == PT_LOAD
)
311 struct segment
*next_seg
= load (addr
, phdr
, tail_seg
);
320 lib
->segments
= head_seg
;
325 get_text_addr (struct library
*lib
, void **text_addr
)
327 Elf_External_Shdr
*text
;
329 /* Get the text section. */
330 text
= find_shdr (lib
->ehdr
, ".text");
334 if (translate_offset (GET (text
, sh_offset
), lib
->segments
, text_addr
)
341 /* Return the section-header table. */
344 find_shdrtab (Elf_External_Ehdr
*ehdr
)
346 return (Elf_External_Shdr
*) (((uint8_t *) ehdr
) + GET (ehdr
, e_shoff
));
349 /* Return the string table of the section headers. */
352 find_shstrtab (Elf_External_Ehdr
*ehdr
, uint64_t *size
)
354 const Elf_External_Shdr
*shdr
;
355 const Elf_External_Shdr
*shstr
;
357 if (GET (ehdr
, e_shnum
) <= GET (ehdr
, e_shstrndx
))
359 printf ("The index of the string table is corrupt.");
363 shdr
= find_shdrtab (ehdr
);
365 shstr
= &shdr
[GET (ehdr
, e_shstrndx
)];
366 *size
= GET (shstr
, sh_size
);
367 return ((const char *) ehdr
) + GET (shstr
, sh_offset
);
370 /* Return the string table named SECTION. */
373 find_strtab (Elf_External_Ehdr
*ehdr
,
374 const char *section
, uint64_t *strtab_size
)
376 uint64_t shstrtab_size
= 0;
377 const char *shstrtab
;
379 const Elf_External_Shdr
*shdr
= find_shdrtab (ehdr
);
381 /* Get the string table of the section headers. */
382 shstrtab
= find_shstrtab (ehdr
, &shstrtab_size
);
383 if (shstrtab
== NULL
)
386 for (i
= 0; i
< GET (ehdr
, e_shnum
); i
++)
388 uint64_t name
= GET (shdr
+ i
, sh_name
);
389 if (GET (shdr
+ i
, sh_type
) == SHT_STRTAB
&& name
<= shstrtab_size
390 && strcmp ((const char *) &shstrtab
[name
], section
) == 0)
392 *strtab_size
= GET (shdr
+ i
, sh_size
);
393 return ((const char *) ehdr
) + GET (shdr
+ i
, sh_offset
);
400 /* Return the section header named SECTION. */
402 static Elf_External_Shdr
*
403 find_shdr (Elf_External_Ehdr
*ehdr
, const char *section
)
405 uint64_t shstrtab_size
= 0;
406 const char *shstrtab
;
409 /* Get the string table of the section headers. */
410 shstrtab
= find_shstrtab (ehdr
, &shstrtab_size
);
411 if (shstrtab
== NULL
)
414 Elf_External_Shdr
*shdr
= find_shdrtab (ehdr
);
415 for (i
= 0; i
< GET (ehdr
, e_shnum
); i
++)
417 uint64_t name
= GET (shdr
+ i
, sh_name
);
418 if (name
<= shstrtab_size
)
420 if (strcmp ((const char *) &shstrtab
[name
], section
) == 0)
428 /* Return the symbol table. */
430 static Elf_External_Sym
*
431 find_symtab (Elf_External_Ehdr
*ehdr
, uint64_t *symtab_size
)
434 const Elf_External_Shdr
*shdr
= find_shdrtab (ehdr
);
436 for (i
= 0; i
< GET (ehdr
, e_shnum
); i
++)
438 if (GET (shdr
+ i
, sh_type
) == SHT_SYMTAB
)
440 *symtab_size
= GET (shdr
+ i
, sh_size
) / sizeof (Elf_External_Sym
);
441 return (Elf_External_Sym
*) (((const char *) ehdr
) +
442 GET (shdr
+ i
, sh_offset
));
448 /* Translate a file offset to an address in a loaded segment. */
451 translate_offset (uint64_t file_offset
, struct segment
*seg
, void **addr
)
455 uint64_t p_from
, p_to
;
457 Elf_External_Phdr
*phdr
= seg
->phdr
;
465 p_from
= GET (phdr
, p_offset
);
466 p_to
= p_from
+ GET (phdr
, p_filesz
);
468 if (p_from
<= file_offset
&& file_offset
< p_to
)
470 *addr
= (void *) (seg
->mapped_addr
+ (file_offset
- p_from
));
479 /* Lookup the address of FUNC. */
482 lookup_function (struct library
*lib
, const char *func
, void **addr
)
485 uint64_t strtab_size
= 0;
486 Elf_External_Sym
*symtab
;
487 uint64_t symtab_size
= 0;
489 Elf_External_Ehdr
*ehdr
= lib
->ehdr
;
490 struct segment
*seg
= lib
->segments
;
492 /* Get the string table for the symbols. */
493 strtab
= find_strtab (ehdr
, ".strtab", &strtab_size
);
496 printf (".strtab not found.");
500 /* Get the symbol table. */
501 symtab
= find_symtab (ehdr
, &symtab_size
);
504 printf ("symbol table not found.");
508 for (i
= 0; i
< symtab_size
; i
++)
510 Elf_External_Sym
*sym
= &symtab
[i
];
512 if (elf_st_type (GET (sym
, st_info
)) != STT_FUNC
)
515 if (GET (sym
, st_name
) < strtab_size
)
517 const char *name
= &strtab
[GET (sym
, st_name
)];
518 if (strcmp (name
, func
) == 0)
521 uint64_t offset
= GET (sym
, st_value
);
522 return translate_offset (offset
, seg
, addr
);