1 /*
2  * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 #include <stdio.h>
27 #include <sys/mman.h>
28 #include <dlfcn.h>
29 #include <libelf.h>
30 #include <strings.h>
31 #include <fcntl.h>
32 #include <sys/param.h>
33 #include <stdlib.h>
34 #include <thread.h>
35 #include <synch.h>
36 #include <stdarg.h>
37 #include <unistd.h>
38 
39 #define TRUE    1
40 #define FALSE   0
41 
42 /* 32/64 bit build issues. */
43 
44 #ifdef _LP64
45  #define ElfXX_Sym      Elf64_Sym
46  #define ElfXX_Ehdr     Elf64_Ehdr
47  #define ElfXX_Shdr     Elf64_Shdr
48  #define elfXX_getehdr  elf64_getehdr
49  #define ElfXX_Addr     Elf64_Addr
50  #define ELFXX_ST_TYPE  ELF64_ST_TYPE
51  #define ELFXX_ST_BIND  ELF64_ST_BIND
52  #define elfXX_getshdr  elf64_getshdr
53 #else
54  #define ElfXX_Sym      Elf32_Sym
55  #define ElfXX_Ehdr     Elf32_Ehdr
56  #define ElfXX_Shdr     Elf32_Shdr
57  #define elfXX_getehdr  elf32_getehdr
58  #define ElfXX_Addr     Elf32_Addr
59  #define ELFXX_ST_TYPE  ELF32_ST_TYPE
60  #define ELFXX_ST_BIND  ELF32_ST_BIND
61  #define elfXX_getshdr  elf32_getshdr
62 #endif
63 
64 extern void *_getReturnAddr(void);
65 
66 
67 
68 typedef struct StabEntry {
69     unsigned      n_strx;
70     unsigned char n_type;
71     char          n_other;
72     short         n_desc;
73     unsigned      n_value;
74 } StabEntry;
75 
76 
77 typedef struct SymChain {
78     struct SymChain *next;
79     ElfXX_Sym *sym;
80 } SymChain;
81 
82 
83 typedef struct ObjFileList {
84     struct ObjFileList *next;
85     const char *objFileName;
86     int    nameLen;
87 } ObjFileList;
88 
89 
90 typedef struct ElfInfo {
91     const char *fullName;
92     const char *baseName;
93     FILE       *outFile;
94     int        fd;
95     Elf        *elf;
96     Elf_Data   *sectionStringData;
97     Elf_Data   *symData;
98     Elf_Data   *symStringData;
99     int        symCount;
100     SymChain   *symChainHead;
101     Elf_Data   *stabData;
102     Elf_Data   *stabStringData;
103     int        stabCount;
104     ObjFileList *objFileList;
105 } ElfInfo;
106 
107 
108 
109 #define COUNT_BUF_SIZE (16*1024*1024)
110 
111 #define ENTRY_CHAIN_BUCKETS  4999
112 
113 static int *countBuf;
114 static void *textOffset;
115 static const char *libFileName;
116 
117 
118 
fail(const char * err,...)119 static void fail(const char *err, ...)
120 {
121     va_list ap;
122     va_start(ap, err);
123     vfprintf(stderr, err, ap);
124     fflush(stderr);
125     va_end(ap);
126 }
127 
128 
129 
getSymString(ElfInfo * elfInfo,int index)130 static const char *getSymString(ElfInfo *elfInfo, int index)
131 {
132     return (const char *)elfInfo->symStringData->d_buf + index;
133 }
134 
135 
getStabString(ElfInfo * elfInfo,int index)136 static const char *getStabString(ElfInfo *elfInfo, int index)
137 {
138     return (const char *)elfInfo->stabStringData->d_buf + index;
139 }
140 
141 
getSectionString(ElfInfo * elfInfo,int index)142 static const char *getSectionString(ElfInfo *elfInfo, int index)
143 {
144     return (const char *)elfInfo->sectionStringData->d_buf + index;
145 }
146 
147 
makeObjFileList(ElfInfo * elfInfo)148 static const char *makeObjFileList(ElfInfo *elfInfo)
149 {
150     int i;
151     const char *file;
152     unsigned offset, lastOffset;
153     ObjFileList *objFileList;
154 
155     file = NULL;
156     offset = lastOffset = 0;
157     for (i = 0; i < elfInfo->stabCount; ++i) {
158         StabEntry *stab = ((StabEntry *)elfInfo->stabData->d_buf) + i;
159 
160         if (stab->n_type == 0 /* N_UNDEF */) {
161             offset = lastOffset;
162             lastOffset += stab-> n_value;
163         }
164         else if (stab->n_type == 0x38 /* N_OBJ */) {
165             file = getStabString(elfInfo, stab->n_strx + offset);
166             objFileList = (ObjFileList *)malloc(sizeof (ObjFileList));
167             objFileList->objFileName = file;
168             /*fprintf(stderr,"new obj file %s.\n", file);*/
169             objFileList->nameLen = strlen(file);
170             objFileList->next = elfInfo->objFileList;
171             elfInfo->objFileList = objFileList;
172         }
173     }
174     return NULL;
175 }
176 
177 
createElfInfo(const char * fullName)178 static ElfInfo *createElfInfo(const char *fullName)
179 {
180     ElfInfo    *elfInfo;
181     ElfXX_Ehdr *ehdr;
182     Elf_Scn    *sectionStringSection;
183     Elf_Scn    *stringSection;
184     Elf_Scn    *symSection;
185     ElfXX_Shdr *symHeader;
186     Elf_Scn    *stabSection;
187     ElfXX_Shdr *stabHeader;
188     ElfXX_Shdr *stringHeader;
189     Elf        *elf;
190     const char *p;
191 
192     /*fprintf(stderr, "# mapfile info for %s.\n", fullName);*/
193     elfInfo = (ElfInfo *)malloc(sizeof (ElfInfo));
194     memset(elfInfo, 0, sizeof (ElfInfo));
195     elfInfo->fullName = strdup(fullName);
196     p = rindex(elfInfo->fullName, '/');
197     elfInfo->baseName = (p == NULL) ? elfInfo->fullName : p + 1;
198 
199     /* Open the ELF file. Get section headers. */
200 
201     elf_version(EV_CURRENT);
202     elfInfo->fd = open(fullName, O_RDONLY);
203     if (elfInfo->fd < 0)
204         fail("Unable to open ELF file %s.\n", fullName);
205     elf = elf_begin(elfInfo->fd, ELF_C_READ, (Elf *)0);
206     if (elf == NULL)
207         fail("elf_begin failed.\n");
208     ehdr = elfXX_getehdr(elf);
209     sectionStringSection = elf_getscn(elf, ehdr->e_shstrndx);
210     elfInfo->sectionStringData = elf_getdata(sectionStringSection, NULL);
211 
212     /* Find the symbol table section. */
213 
214     symSection = NULL;
215     while ((symSection = elf_nextscn(elf, symSection)) != NULL) {
216         symHeader = elfXX_getshdr(symSection);
217         p = getSectionString(elfInfo, symHeader->sh_name);
218         if (strcmp(p, ".symtab") == 0)
219             break;
220     }
221     if (symSection == NULL)
222         fail("Unable to find symbol table.\n");
223 
224     elfInfo->symData = elf_getdata(symSection, NULL);
225     elfInfo->symCount = elfInfo->symData->d_size / sizeof (ElfXX_Sym);
226 
227     /* Find the string section. */
228 
229     stringSection = NULL;
230     while ((stringSection = elf_nextscn(elf, stringSection)) != NULL) {
231         stringHeader = elfXX_getshdr(stringSection);
232         p = getSectionString(elfInfo, stringHeader->sh_name);
233         if (strcmp(p, ".strtab") == 0)
234             break;
235     }
236     if (stringSection == NULL)
237         fail("Unable to find string table.\n");
238 
239     elfInfo->symStringData = elf_getdata(stringSection, NULL);
240     elfInfo->symChainHead = NULL;
241 
242     /* Find the stab section. */
243 
244     stabSection = NULL;
245     while ((stabSection = elf_nextscn(elf, stabSection)) != NULL) {
246         stabHeader = elfXX_getshdr(stabSection);
247         p = getSectionString(elfInfo, stabHeader->sh_name);
248         if (strcmp(p, ".stab.index") == 0)
249             break;
250     }
251     if (stabSection == NULL)
252         fail("Unable to find .stab.index.\n");
253 
254     elfInfo->stabData = elf_getdata(stabSection, NULL);
255     elfInfo->stabCount = elfInfo->stabData->d_size / sizeof (StabEntry);
256 
257     /* Find the string section. */
258 
259     stringSection = NULL;
260     while ((stringSection = elf_nextscn(elf, stringSection)) != NULL) {
261         stringHeader = elfXX_getshdr(stringSection);
262         p = getSectionString(elfInfo, stringHeader->sh_name);
263         if (strcmp(p, ".stab.indexstr") == 0)
264             break;
265     }
266     if (stringSection == NULL)
267         fail("Unable to find .stab.indexstr table.\n");
268 
269     elfInfo->stabStringData = elf_getdata(stringSection, NULL);
270     makeObjFileList(elfInfo);
271 
272     return elfInfo;
273 }
274 
275 
identifyFile(ElfInfo * elfInfo,const char * name)276 static const char *identifyFile(ElfInfo *elfInfo, const char *name)
277 {
278     int i;
279     const char *file;
280     const char *sourceFile;
281     unsigned offset, lastOffset;
282     const char *lastOptions;
283     char *buf;
284 
285     file = NULL;
286     lastOptions = NULL;
287     offset = lastOffset = 0;
288     for (i = 0; i < elfInfo->stabCount; ++i) {
289         StabEntry *stab = ((StabEntry *)elfInfo->stabData->d_buf) + i;
290 
291         if (stab->n_type == 0 /* N_UNDEF */) {
292             offset = lastOffset;
293             lastOffset += stab-> n_value;
294             file = NULL;   /* C++ output files seem not to have N_OBJ fields.*/
295             lastOptions = NULL;
296             sourceFile = getStabString(elfInfo, stab->n_strx + offset);
297         }
298         else if (stab->n_type == 0x24 /* N_FUN */) {
299             const char *stabName;
300             char *p1, *p2;
301 
302             stabName = getStabString(elfInfo, stab->n_strx + offset);
303             if (strcmp (stabName, name) == 0) {
304 
305                 if (file != NULL)
306                     return file;
307 
308                 if (lastOptions == NULL)
309                     return NULL;
310 
311                 p1 = strstr(lastOptions, ";ptr");
312                 if (p1 == NULL)
313                     return NULL;
314                 p1 += 4;
315                 p2 = index(p1, ';');
316                 if (p2 == NULL)
317                     return NULL;
318 
319                 buf = (char *)malloc(p2 - p1 + strlen(sourceFile) + 10);
320                 strncpy(buf, p1, p2 - p1);
321                 buf[p2-p1] = '/';
322                 strcpy(buf + (p2 - p1) + 1, sourceFile);
323                 p1 = rindex(buf, '.');
324                 if (p1 == NULL)
325                     return NULL;
326                 p1[1] = 'o';
327                 p1[2] = '\0';
328                 return buf;
329             }
330         }
331         else if (stab->n_type == 0x3c /* N_OPT */) {
332             lastOptions =  getStabString(elfInfo, stab->n_strx + offset);
333         }
334         else if (stab->n_type == 0x38 /* N_OBJ */) {
335             file = getStabString(elfInfo, stab->n_strx + offset);
336         }
337     }
338     return NULL;
339 }
340 
341 
checkObjFileList(ElfInfo * elfInfo,const char * file)342 static const char *checkObjFileList(ElfInfo *elfInfo, const char *file) {
343     ObjFileList *objFileList;
344     int len = strlen(file);
345     int nameLen;
346     const char *objFileName;
347 
348     /*fprintf(stderr, "checkObjFileList(%s).\n", file);*/
349     for (objFileList = elfInfo->objFileList; objFileList != NULL;
350          objFileList = objFileList->next) {
351 
352         objFileName = objFileList->objFileName;
353         nameLen = objFileList->nameLen;
354         if (strcmp(objFileName +nameLen - len, file) != 0)
355             continue;
356 
357         if (len == nameLen)
358             return file;
359 
360         if (len > nameLen)
361             continue;
362 
363         if (*(objFileName + nameLen - len - 1) == '/')
364             return objFileName;
365     }
366     return file;
367 }
368 
369 
identifySymbol(ElfInfo * elfInfo,ElfXX_Addr value,int count)370 static void identifySymbol(ElfInfo *elfInfo, ElfXX_Addr value, int count)
371 {
372     int i;
373     ElfXX_Sym *bestFunc = NULL;
374     ElfXX_Sym *bestFile = NULL;
375     ElfXX_Sym *lastFile = NULL;
376     char fileName[MAXPATHLEN];
377     char buf[4096];
378     const char *file;
379     SymChain *chain;
380     const char *format;
381 
382     for (i = 0; i < elfInfo->symCount; ++i) {
383         ElfXX_Sym *sym = ((ElfXX_Sym *)elfInfo->symData->d_buf) + i;
384         if (ELFXX_ST_TYPE(sym->st_info) == STT_FUNC) {
385 
386             if (sym->st_shndx == SHN_UNDEF)
387                 continue;
388 
389             if (sym->st_value > value)
390                 continue;
391 
392             if (bestFunc != NULL) {
393 
394                 if (sym->st_value < bestFunc->st_value)
395                     continue;
396 
397                 /*
398                  * If we have two symbols of equal value, we have a problem -
399                  * we must pick the "right" one, which is the one the compiler
400                  * used to generate the section name with -xF.
401                  *
402                  * The compiler has the nasty habit of generating two
403                  * mangled names for some C++ functions.
404                  *
405                  * Try - picking the shortest name.
406                  */
407 
408                 if (sym->st_value == bestFunc->st_value) {
409                     if (strlen(getSymString(elfInfo, bestFunc->st_name)) <
410                         strlen(getSymString(elfInfo, sym->st_name)))
411                         continue;
412                 }
413 
414             }
415             bestFunc = sym;
416             bestFile = lastFile;
417         }
418         else if (ELFXX_ST_TYPE(sym->st_info) == STT_FILE) {
419             lastFile = sym;
420         }
421     }
422 
423     if (bestFunc == NULL)
424         fail("Unable to find symbol for address 0x%x.\n", value);
425 
426     for (chain = elfInfo->symChainHead; chain != NULL; chain = chain->next) {
427         if (chain->sym == bestFunc)
428             return;
429     }
430     chain = (SymChain *)malloc(sizeof (SymChain));
431     chain->sym = bestFunc;
432     chain->next = elfInfo->symChainHead;
433     elfInfo->symChainHead = chain;
434 
435 
436     if (ELFXX_ST_BIND(bestFunc->st_info) == STB_GLOBAL)
437         file = "";
438     else {
439         const char *name = getSymString(elfInfo, bestFunc->st_name);
440         file = identifyFile(elfInfo, name);
441         if (file == NULL) {
442             if (bestFile == NULL) {
443                 file = "notFound";
444                 fail("Failed to identify %s.\n", name);
445             } else {
446                 char *suffix;
447                 fileName[0] = ':';
448                 fileName[1] = ' ';
449                 file = getSymString(elfInfo, bestFile->st_name);
450                 strncpy(fileName+2, file, MAXPATHLEN-3);
451                 suffix = rindex(fileName, '.');
452                 if (suffix == NULL)
453                     fail("no file name suffix?");
454                 suffix[1] = 'o';
455                 suffix[2] = '\0';
456 
457                 file = checkObjFileList(elfInfo, fileName+2);
458                 if (file != fileName+2)
459                     strncpy(fileName+2, file, MAXPATHLEN-3);
460 
461                 file = fileName;
462             }
463         } else {
464             fileName[0] = ':';
465             fileName[1] = ' ';
466             strncpy(fileName + 2, file, MAXPATHLEN-3);
467             file = fileName;
468         }
469     }
470     format = "text: .text%%%s%s;\n";
471     i = snprintf(buf, sizeof buf, format,
472             bestFunc ? getSymString(elfInfo, bestFunc->st_name) : "notFound",
473             file);
474     write(2, buf, i);
475 }
476 
477 
478 static mutex_t mutex;
479 static int orderByCount = FALSE;
480 
481 
init_mcount(void)482 static void init_mcount(void)
483 {
484     mutex_init(&mutex, USYNC_THREAD, NULL);
485 }
486 
487 #pragma init(init_mcount)
488 
489 
490 typedef struct CountAddrPair {
491     int          count;
492     unsigned int addr;
493 } CountAddrPair;
494 
495 
compareCounts(const void * a,const void * b)496 static int compareCounts(const void *a, const void *b) {
497     return ((CountAddrPair *)b)->count - ((CountAddrPair *)a)->count;
498 }
499 
compareCountsReverse(const void * a,const void * b)500 static int compareCountsReverse(const void *a, const void *b) {
501     return ((CountAddrPair *)a)->count - ((CountAddrPair *)b)->count;
502 }
503 
504 
doCounts(void)505 static void doCounts(void) {
506     unsigned int i;
507     int n;
508     int nMethods;
509     int nMethods2;
510     CountAddrPair *pairs;
511     ElfInfo *elfInfo;
512 
513     elfInfo = createElfInfo(libFileName);
514 
515     nMethods = 0;
516     for (i = 0; i < COUNT_BUF_SIZE >> 2; ++i) {
517         n = countBuf[i];
518         if (n > 0)
519             ++nMethods;
520     }
521     pairs = (CountAddrPair *)malloc(sizeof(CountAddrPair) * nMethods);
522     nMethods2 = 0;
523     for (i = 0; i < COUNT_BUF_SIZE >> 2; ++i) {
524         n = countBuf[i];
525         if (n > 0) {
526             pairs[nMethods2].count = n;
527             pairs[nMethods2].addr = i << 2;
528             ++nMethods2;
529             if (nMethods2 > nMethods) {
530                 fprintf(stderr, "Number of methods detected increased after"
531                         " the atexit call.\n");
532                 break;
533             }
534         }
535     }
536     if (orderByCount) {
537         qsort(pairs, nMethods, sizeof pairs[0], &compareCounts);
538         for (i = 0; i < nMethods; ++i) {
539             identifySymbol(elfInfo, pairs[i].addr, pairs[i].count);
540         }
541     }
542     else {
543         qsort(pairs, nMethods, sizeof pairs[0], &compareCountsReverse);
544         for (i = 0; i < nMethods; ++i) {
545             identifySymbol(elfInfo, pairs[i].addr, 0);
546         }
547     }
548 }
549 
550 
__mcount(void * i0)551 static void __mcount(void *i0)
552 {
553     Dl_info info;
554     unsigned int offset;
555     int *p;
556     static int callsCounted = 0;
557     static int initialized = FALSE;
558 
559     if (!initialized) {
560         dladdr(i0, &info);
561         libFileName = info.dli_fname;
562 #if 0
563         fprintf(stderr, "Profiling %s\n", libFileName);
564 #endif
565         textOffset = info.dli_fbase;
566         if (getenv("MCOUNT_ORDER_BY_COUNT") != NULL) {
567             orderByCount = TRUE;
568         }
569         countBuf = (int *)malloc(COUNT_BUF_SIZE);
570         memset(countBuf, 0, COUNT_BUF_SIZE);
571         atexit(&doCounts);
572         initialized = TRUE;
573     }
574 
575     if ((uintptr_t)i0 < (uintptr_t)textOffset) {
576         fprintf(stderr, "mcount: function being profiled out of range????\n");
577         fprintf(stderr, "        profiling more than one library at once????\n");
578 #if 0
579         dladdr(i0, &info);
580         fprintf(stderr, "Problem with %s in %s ???\n",
581                 info.dli_sname, info.dli_fname);
582 #endif
583         fflush(stderr);
584         exit(666);
585     }
586     offset = ((uintptr_t)i0) - ((uintptr_t)textOffset);
587     if (offset > COUNT_BUF_SIZE) {
588         fprintf(stderr, "mcount: internal buffer too small for test.\n");
589         fprintf(stderr, "     or function being profiled out of range????\n");
590         fprintf(stderr, "     or profiling more than one library at once????\n");
591 #if 0
592         dladdr(i0, &info);
593         fprintf(stderr, "Problem with %s in %s ???\n",
594                 info.dli_sname, info.dli_fname);
595 #endif
596         fflush(stderr);
597         exit(666);
598     }
599 
600     p = &countBuf[offset >>2];
601     if (orderByCount) {
602         ++*p;
603     }
604     else {
605         if (*p == 0) {
606             *p = ++callsCounted;
607         }
608     }
609 }
610 
611 
_mcount(void)612 void _mcount(void)
613 {
614     __mcount(_getReturnAddr());
615 }
616 
617 
mcount(void)618 void mcount(void)
619 {
620     __mcount(_getReturnAddr());
621 }
622