doc: Add serial to list of ci file reserved words
[charm.git] / src / conv-core / loadsym.c
1 /* refun.c - replace libc functions in calling process
2
3     Copyright (C) 2001 Victor Zandy <zandy@cs.wisc.edu>
4
5     This library is free software; you can redistribute it and/or
6     modify it under the terms of the GNU Lesser General Public
7     License as published by the Free Software Foundation; either
8     version 2.1 of the License, or (at your option) any later version.
9
10     This file exports one function:
11
12       int replace_function(char *from, void *to);
13
14           replace_function modifies the current process so that all
15           subsequent calls to FROM, the name of a function in libc,
16           are redirected to TO, a pointer to any function.
17
18           Returns 0 on success, -1 on failure.
19
20     It (currently) only works on x86 Linux.
21
22     Compile it with no special options to produce a .o that can be
23     linked with a library or program, e.g. "cc -c refun.c".
24
25     Send mail regarding this file to zandy@cs.wisc.edu.
26 */
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <ctype.h>
31 #include <errno.h>
32 #include <unistd.h>
33 #include <sys/mman.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37
38 #if CMK_HAS_ELF_H
39 #include <elf.h>
40
41 #include "converse.h"
42
43 #if CMK_AMD64 || CMK_CRAYXT
44 typedef Elf64_Addr    ELFXX_TYPE_Addr;
45 typedef Elf64_Dyn     ELFXX_TYPE_Dyn;
46 typedef Elf64_Rela    ELFXX_TYPE_Rel;
47 typedef Elf64_Sym     ELFXX_TYPE_Sym;
48 typedef Elf64_Shdr    ELFXX_TYPE_Shdr;
49 typedef Elf64_Ehdr    ELFXX_TYPE_Ehdr;
50 #define ELFXX_R_TYPE   ELF64_R_TYPE
51 #define ELFXX_R_SYM    ELF64_R_SYM
52 #define ELFXX_ST_TYPE  ELF64_ST_TYPE
53 #define CMK_DT_REL     DT_RELA
54 #define CMK_DT_RELSZ   DT_RELASZ
55 #define is_elf_global(x)  ((x) == R_X86_64_GLOB_DAT)
56 #elif CMK_IA64
57 #error "NOT SUPPORTED"
58 #else
59 typedef Elf32_Addr    ELFXX_TYPE_Addr;
60 typedef Elf32_Dyn     ELFXX_TYPE_Dyn;
61 typedef Elf32_Rel     ELFXX_TYPE_Rel;
62 typedef Elf32_Sym     ELFXX_TYPE_Sym;
63 typedef Elf32_Shdr    ELFXX_TYPE_Shdr;
64 typedef Elf32_Ehdr    ELFXX_TYPE_Ehdr;
65 #define ELFXX_R_TYPE   ELF32_R_TYPE
66 #define ELFXX_R_SYM    ELF32_R_SYM
67 #define ELFXX_ST_TYPE  ELF32_ST_TYPE
68 #define CMK_DT_REL     DT_REL
69 #define CMK_DT_RELSZ   DT_RELSZ
70 #define is_elf_global(x)  ((x) == R_386_GLOB_DAT)
71 #endif
72
73 /* symbol table */
74 typedef struct symtab *symtab_t;
75 struct symlist {
76         ELFXX_TYPE_Sym *sym;       /* symbols */
77         char *str;            /* symbol strings */
78         unsigned num;         /* number of symbols */
79 };
80 struct symtab {
81         struct symlist *st;    /* "static" symbols */
82         struct symlist *dyn;   /* dynamic symbols */
83 };
84
85
86 /* memory map for libraries */
87 #define MAX_NAME_LEN 256
88 #define MEMORY_ONLY  "[memory]"
89 struct mm {
90         char name[MAX_NAME_LEN];
91         unsigned long start, end;
92 };
93 static struct mm mm[50];
94 static int nmm;
95
96 /* FIXME: Why is this missing from unistd.h? */
97 extern ssize_t pread(int fd, void *buf, size_t count, off_t offset);
98
99 static struct symlist *
100 get_syms(int fd, ELFXX_TYPE_Shdr *symh, ELFXX_TYPE_Shdr *strh)
101 {
102         struct symlist *sl, *ret;
103         int rv;
104
105         ret = NULL;
106         sl = (struct symlist *) malloc(sizeof(struct symlist));
107         if (!sl) {
108                 fprintf(stderr, "Out of memory\n");
109                 goto out;
110         }
111         sl->str = NULL;
112         sl->sym = NULL;
113
114         /* sanity */
115         if (symh->sh_size % sizeof(ELFXX_TYPE_Sym)) { 
116                 fprintf(stderr, "elf_error\n");
117                 goto out;
118         }
119
120         /* symbol table */
121         sl->num = symh->sh_size / sizeof(ELFXX_TYPE_Sym);
122         sl->sym = (ELFXX_TYPE_Sym *) malloc(symh->sh_size);
123         if (!sl->sym) {
124                 fprintf(stderr, "Out of memory\n");
125                 goto out;
126         }
127         rv = pread(fd, sl->sym, symh->sh_size, symh->sh_offset);
128         if (0 > rv) {
129                 perror("read");
130                 goto out;
131         }
132         if (rv != symh->sh_size) {
133                 fprintf(stderr, "elf error\n");
134                 goto out;
135         }
136
137         /* string table */
138         sl->str = (char *) malloc(strh->sh_size);
139         if (!sl->str) {
140                 fprintf(stderr, "Out of memory\n");
141                 goto out;
142         }
143         rv = pread(fd, sl->str, strh->sh_size, strh->sh_offset);
144         if (0 > rv) {
145                 perror("read");
146                 goto out;
147         }
148         if (rv != strh->sh_size) {
149                 fprintf(stderr, "elf error");
150                 goto out;
151         }
152
153         ret = sl;
154 out:
155         return ret;
156 }
157
158 static int
159 do_load(int fd, symtab_t symtab)
160 {
161         int rv;
162         size_t size;
163         ELFXX_TYPE_Ehdr ehdr;
164         ELFXX_TYPE_Shdr *shdr = NULL, *p;
165         ELFXX_TYPE_Shdr *dynsymh, *dynstrh;
166         ELFXX_TYPE_Shdr *symh, *strh;
167         char *shstrtab = NULL;
168         int i;
169         int ret = -1;
170         
171         /* elf header */
172         rv = read(fd, &ehdr, sizeof(ehdr));
173         if (0 > rv) {
174                 perror("read");
175                 goto out;
176         }
177         if (rv != sizeof(ehdr)) {
178                 fprintf(stderr, "elf error\n");
179                 goto out;
180         }
181         if (strncmp(ELFMAG, ehdr.e_ident, SELFMAG)) { /* sanity */
182                 fprintf(stderr, "not an elf\n");
183                 goto out;
184         }
185         if (sizeof(ELFXX_TYPE_Shdr) != ehdr.e_shentsize) { /* sanity */
186                 fprintf(stderr, "elf error\n");
187                 goto out;
188         }
189
190         /* section header table */
191         size = ehdr.e_shentsize * ehdr.e_shnum;
192         shdr = (ELFXX_TYPE_Shdr *) malloc(size);
193         if (!shdr) {
194                 fprintf(stderr, "Out of memory\n");
195                 goto out;
196         }
197         rv = pread(fd, shdr, size, ehdr.e_shoff);
198         if (0 > rv) {
199                 perror("read");
200                 goto out;
201         }
202         if (rv != size) {
203                 fprintf(stderr, "elf error");
204                 goto out;
205         }
206         
207         /* section header string table */
208         size = shdr[ehdr.e_shstrndx].sh_size;
209         shstrtab = (char *) malloc(size);
210         if (!shstrtab) {
211                 fprintf(stderr, "Out of memory\n");
212                 goto out;
213         }
214         rv = pread(fd, shstrtab, size, shdr[ehdr.e_shstrndx].sh_offset);
215         if (0 > rv) {
216                 perror("read");
217                 goto out;
218         }
219         if (rv != size) {
220                 fprintf(stderr, "elf error\n");
221                 goto out;
222         }
223
224         /* symbol table headers */
225         symh = dynsymh = NULL;
226         strh = dynstrh = NULL;
227         for (i = 0, p = shdr; i < ehdr.e_shnum; i++, p++)
228                 if (SHT_SYMTAB == p->sh_type) {
229                         if (symh) {
230                                 fprintf(stderr, "too many symbol tables\n");
231                                 goto out;
232                         }
233                         symh = p;
234                 } else if (SHT_DYNSYM == p->sh_type) {
235                         if (dynsymh) {
236                                 fprintf(stderr, "too many symbol tables\n");
237                                 goto out;
238                         }
239                         dynsymh = p;
240                 } else if (SHT_STRTAB == p->sh_type
241                            && !strncmp(shstrtab+p->sh_name, ".strtab", 7)) {
242                         if (strh) {
243                                 fprintf(stderr, "too many string tables\n");
244                                 goto out;
245                         }
246                         strh = p;
247                 } else if (SHT_STRTAB == p->sh_type
248                            && !strncmp(shstrtab+p->sh_name, ".dynstr", 7)) {
249                         if (dynstrh) {
250                                 fprintf(stderr, "too many string tables\n");
251                                 goto out;
252                         }
253                         dynstrh = p;
254                 }
255         /* sanity checks */
256         if ((!dynsymh && dynstrh) || (dynsymh && !dynstrh)) {
257                 fprintf(stderr, "bad dynamic symbol table");
258                 goto out;
259         }
260         if ((!symh && strh) || (symh && !strh)) {
261                 fprintf(stderr, "bad symbol table");
262                 goto out;
263         }
264         if (!dynsymh && !symh) {
265                 fprintf(stderr, "no symbol table");
266                 goto out;
267         }
268
269         /* symbol tables */
270         if (dynsymh)
271                 symtab->dyn = get_syms(fd, dynsymh, dynstrh);
272         if (symh)
273                 symtab->st = get_syms(fd, symh, strh);
274         ret = 0;
275 out:
276         free(shstrtab);
277         free(shdr);
278         return ret;
279 }
280
281 static symtab_t
282 load_symtab(char *filename)
283 {
284         int fd;
285         symtab_t symtab;
286
287         symtab = (symtab_t) malloc(sizeof(*symtab));
288         if (!symtab) {
289                 fprintf(stderr, "Out of memory\n");
290                 return NULL;
291         }
292         bzero(symtab, sizeof(*symtab));
293
294         fd = open(filename, O_RDONLY);
295         if (0 > fd) {
296                 perror("open");
297                 return NULL;
298         }
299         if (0 > do_load(fd, symtab)) {
300                 fprintf(stderr, "Error ELF parsing %s\n", filename);
301                 free(symtab);
302                 symtab = NULL;
303         }
304         close(fd);
305         return symtab;
306 }
307
308 #define MYMAX(a,b)  (a>b?a:b)
309
310 static int
311 lookup2(struct symlist *sl, unsigned char type,
312         char *name, unsigned long *val, unsigned int *size)
313 {
314         ELFXX_TYPE_Sym *p;
315         int len;
316         int i;
317
318         len = strlen(name);
319         for (i = 0, p = sl->sym; i < sl->num; i++, p++) {
320                 int mlen = MYMAX(len, strlen(sl->str+p->st_name));       
321                 if (!strncmp(sl->str+p->st_name, name, mlen)
322                     && ELFXX_ST_TYPE(p->st_info) == type) {
323                         *val = p->st_value;
324                         *size = p->st_size;
325                         return 0;
326                 }
327         }
328         return -1;
329 }
330
331 static int
332 lookup_sym(symtab_t s, unsigned char type,
333            char *name, unsigned long *val, int *size)
334 {
335         if (s->dyn && !lookup2(s->dyn, type, name, val, size))
336                 return 0;
337         if (s->st && !lookup2(s->st, type, name, val, size))
338                 return 0;
339         return -1;
340 }
341
342 static int
343 load_memmap()
344 {
345         char raw[10000];
346         char name[MAX_NAME_LEN];
347         char *p;
348         unsigned long start, end;
349         struct mm *m;
350         int fd, rv;
351         int i;
352
353         fd = open("/proc/self/maps", O_RDONLY);
354         if (0 > fd) {
355                 fprintf(stderr, "Can't open /proc/self/maps for reading\n");
356                 return -1;
357         }
358
359         /* Zero to ensure data is null terminated */
360         bzero(raw, sizeof(raw));
361         rv = read(fd, raw, sizeof(raw));
362         if (0 > rv) {
363                 perror("read");
364                 return -1;
365         }
366         if (rv >= sizeof(raw)) {
367                 fprintf(stderr, "Too many memory mapping\n");
368                 return -1;
369         }
370         close(fd);
371
372         p = strtok(raw, "\n");
373         m = mm;
374         while (p) {
375                 /* parse current map line */
376                 rv = sscanf(p, "%08lx-%08lx %*s %*s %*s %*s %s\n",
377                             &start, &end, name);
378                 p = strtok(NULL, "\n");
379
380                 if (rv == 2) {
381                         m = &mm[nmm++];
382                         m->start = start;
383                         m->end = end;
384                         strcpy(m->name, MEMORY_ONLY);
385                         continue;
386                 }
387
388                 /* search backward for other mapping with same name */
389                 for (i = nmm-1; i >= 0; i--) {
390                         m = &mm[i];
391                         if (!strcmp(m->name, name))
392                                 break;
393                 }
394
395                 if (i >= 0) {
396                         if (start < m->start)
397                                 m->start = start;
398                         if (end > m->end)
399                                 m->end = end;
400                 } else {
401                         /* new entry */
402                         m = &mm[nmm++];
403                         m->start = start;
404                         m->end = end;
405                         strcpy(m->name, name);
406                 }
407         }
408         return 0;
409 }
410
411 /* Return non-zero iff NAME is the absolute pathname of the C library.
412    This is a crude test and could stand to be sharpened. */
413 static int
414 match_libc(const char *name)
415 {
416         char *p;
417
418         p = strrchr(name, '/');
419         if (!p)
420                 return 0;
421         p++;
422         if (strncmp("libc", p, 4))
423                 return 0;
424         p += 4;
425
426         /* here comes our crude test -> 'libc.so' or 'libc-[0-9]' */
427         if (!strncmp(".so", p, 3) || (p[0] == '-' && isdigit(p[1])))
428                 return 1;
429         return 0;
430 }
431
432 /* Find libc in calling process, storing no more than LEN-1 chars of
433    its name in NAME and set START to its starting address.  If libc
434    cannot be found return -1 and leave NAME and START untouched.
435    Otherwise return 0 and null-terminated NAME. */
436 static int
437 find_my_libc(char *name, int len, unsigned long *start)
438 {
439         int i;
440         struct mm *m;
441
442         if (!nmm && 0 > load_memmap()) {
443                 fprintf(stderr, "cannot read my memory map\n");
444                 return -1;
445         }
446         
447         for (i = 0, m = mm; i < nmm; i++, m++) {
448                 if (!strcmp(m->name, MEMORY_ONLY))
449                         continue;
450                 if (match_libc(m->name))
451                         break; /* found it */
452         }
453         if (i >= nmm)
454                 /* not found */
455                 return -1;
456
457         *start = m->start;
458         strncpy(name, m->name, len);
459         if (strlen(m->name) >= len)
460                 name[len-1] = '\0';
461         return 0;
462 }
463
464 static int
465 patch(unsigned long from, unsigned long to)
466 {
467         unsigned char *p;
468         int *q;
469         size_t pgsize;
470
471         /* twiddle protection */
472         pgsize = getpagesize();
473         if (0 > mprotect((void *) (from & ~(pgsize - 1)), pgsize,
474                          PROT_READ|PROT_WRITE|PROT_EXEC))
475                 return -1;
476
477         /* opcode */
478         p = (unsigned char *) from;
479         *p++ = 0xe9;
480
481         /* displacement */
482         q = (int *) p;
483         *q = to - (from + 5);
484
485         /* FIXME: restore protection */
486         return 0;
487 }
488
489 /* user-visible entry */
490
491 static symtab_t symtab;     /* libc symbol table */
492 static unsigned long libc;  /* libc start address */
493
494 int
495 lookup_obj_sym(char *name, unsigned long *val, int *size)
496 {
497         if(-1==lookup_sym(symtab, STT_OBJECT, name, val, size) 
498          &&-1==lookup_sym(symtab, STT_NOTYPE, name, val, size))
499                 return -1;
500         else 
501                 return 0;
502 }
503
504 int
505 init_symtab(char *exename)
506 {
507         char libcname[128];
508
509 /*
510         if (0 > find_my_libc(libcname, sizeof(libcname), &libc))
511                 return -1;
512 */
513         symtab = load_symtab(exename);
514         /*symtab = load_symtab("cpi"); */
515         if (!symtab)
516                 return -1;
517         return 0;
518 }
519
520
521 #endif