1) Cmake support for PPC64LE builds
[dyninst.git] / symlite / src / SymLite-elf.C
1 /*
2  * See the dyninst/COPYRIGHT file for copyright information.
3  * 
4  * We provide the Paradyn Tools (below described as "Paradyn")
5  * on an AS IS basis, and do not warrant its validity or performance.
6  * We reserve the right to update, modify, or discontinue this
7  * software at any time.  We shall have no obligation to supply such
8  * updates or modifications or any other form of support to you.
9  * 
10  * By your use of Paradyn, you understand and agree that we (or any
11  * other person or entity with proprietary rights in Paradyn) are
12  * under no obligation to provide either maintenance services,
13  * update services, notices of latent defects, or correction of
14  * defects for Paradyn.
15  * 
16  * This library is free software; you can redistribute it and/or
17  * modify it under the terms of the GNU Lesser General Public
18  * License as published by the Free Software Foundation; either
19  * version 2.1 of the License, or (at your option) any later version.
20  * 
21  * This library is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
24  * Lesser General Public License for more details.
25  * 
26  * You should have received a copy of the GNU Lesser General Public
27  * License along with this library; if not, write to the Free Software
28  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
29  */
30
31 #include "SymLite-elf.h"
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35 #include <unistd.h>
36 #include <assert.h>
37 #include <iostream> 
38
39 using namespace std;
40 using namespace Dyninst;
41
42 SymElf::SymElf(std::string file_) :
43    elf(NULL),
44    fd(-1),
45    need_odp(false),
46    odp_section(NULL),
47    file(file_),
48    buffer(NULL),
49    buffer_size(0),
50    cache(NULL),
51    cache_size(0),
52    sym_sections(NULL),
53    sym_sections_size(0),
54    ref_count(0),
55    construction_error(false)
56 {
57    fd = open(file_.c_str(), O_RDONLY);
58    if (fd == -1) {
59       construction_error = true;
60       return;
61    }
62    elf = Elf_X::newElf_X(fd, ELF_C_READ, NULL, file_);
63    if (!elf->isValid()) {
64       construction_error = true;
65       close(fd);
66       fd = -1;
67       return;
68    }
69    init();
70 }
71
72 SymElf::SymElf(const char *buffer_, unsigned long buffer_size_) :
73    elf(NULL),
74    fd(-1),
75    need_odp(false),
76    odp_section(NULL),
77    file(),
78    buffer(buffer_),
79    buffer_size(buffer_size_),
80    cache(NULL),
81    cache_size(0),
82    sym_sections(NULL),
83    sym_sections_size(0),
84    ref_count(0),
85    construction_error(false)
86 {
87    elf = Elf_X::newElf_X(const_cast<char *>(buffer_), (size_t) buffer_size);
88    if (!elf->isValid()) {
89       construction_error = true;
90       return;
91    }
92    init();
93 }
94
95 SymElf::~SymElf()
96 {
97    if (!elf) return;
98    if (elf->isValid())
99       elf->end();
100    if (fd != -1) {
101       close(fd);
102       fd = -1;
103    }
104    if (cache) {
105       free(cache);
106       cache = NULL;
107       cache_size = 0;
108    }
109    if (sym_sections) {
110       free(sym_sections);
111       sym_sections = NULL;
112       sym_sections_size = 0;
113    }
114 }
115
116 void SymElf::init()
117 {
118    if (elf->e_machine() == EM_PPC64) {
119       unsigned short stridx = elf->e_shstrndx();
120       Elf_X_Shdr strshdr = elf->get_shdr(stridx);
121       Elf_X_Data strdata = strshdr.get_data();
122       const char *names = (const char *) strdata.d_buf();
123       
124       for (unsigned i=0; i < elf->e_shnum(); i++) {
125          Elf_X_Shdr &shdr = elf->get_shdr(i);
126          if (strcmp(names + shdr.sh_name(), ".opd") != 0)
127             continue;
128          odp_section = & shdr;
129          need_odp = true;
130          break;
131       }
132    }
133 }
134
135 #define INVALID_SYM_CODE ((int) 0xffffffff)
136 #define UNSET_INDEX_CODE ((int) 0xfffffffe)
137
138 #define FOR_EACH_SYMBOL(shdr, symbols, str_buffer, idx) \
139    Elf_X_Data sym_data = shdr.get_data(); \
140    Elf_X_Sym symbols = sym_data.get_sym(); \
141    int str_index = shdr.sh_link(); \
142    Elf_X_Shdr str_shdr = elf->get_shdr(str_index); \
143    if (!str_shdr.isValid()) { \
144       continue; \
145    } \
146    Elf_X_Data str_data = str_shdr.get_data(); \
147    const char *str_buffer = (const char *) str_data.d_buf(); \
148    unsigned sym_count = symbols.count(); \
149    for (unsigned idx=0; idx<sym_count; idx++)
150
151 #define MAKE_SYMBOL(name, idx, shdr, sym) \
152    sym.v1 = (void *) (const_cast<char *>(name)); \
153    sym.v2 = (void *) shdr.getScn(); \
154    sym.i1 = (int) idx; \
155    sym.i2 = UNSET_INDEX_CODE;
156
157 #define SET_SYM_CACHEINDEX(sym, idx) \
158    sym.i2 = idx
159
160 #define GET_SYMBOL(sym, shdr, symbols, name, idx) \
161    assert(sym.i2 != INVALID_SYM_CODE); \
162    const char *name = (const char *) sym.v1; \
163    Elf_X_Shdr shdr = Elf_X_Shdr(elf->wordSize() == 8, (Elf_Scn *) sym.v2); \
164    unsigned idx = (unsigned) sym.i1; \
165    Elf_X_Data sym_data = shdr.get_data(); \
166    Elf_X_Sym symbols = sym_data.get_sym();
167    
168 #define GET_INVALID_SYMBOL(sym) \
169    sym.v1 = sym.v2 = NULL; \
170    sym.i1 = 0; sym.i2 = INVALID_SYM_CODE;
171
172 Symbol_t SymElf::getSymbolByName(std::string symname)
173 {
174    Symbol_t ret;
175    for (unsigned i=0; i < elf->e_shnum(); i++) 
176    {
177       Elf_X_Shdr shdr = elf->get_shdr(i);
178       if (shdr.sh_type() != SHT_SYMTAB && shdr.sh_type() != SHT_DYNSYM) {
179          continue;
180       } 
181
182       FOR_EACH_SYMBOL(shdr, symbol, str_buffer, idx) 
183       {
184          unsigned str_loc = symbol.st_name(idx);
185          if (strcmp(str_buffer+str_loc, symname.c_str()) != 0)
186             continue;
187          if (symbol.st_shndx(idx) == 0) {
188            continue;
189          }
190
191          MAKE_SYMBOL(str_buffer+str_loc, idx, shdr, ret);
192          return ret;
193       }
194    }
195    GET_INVALID_SYMBOL(ret);
196    return ret;
197 }
198
199 Symbol_t SymElf::getContainingSymbol(Dyninst::Offset offset)
200 {
201 #if 1
202    if (!cache) {
203       createSymCache();
204    }
205    return lookupCachedSymbol(offset);
206
207 #else
208    Dyninst::Offset nearest = 0;
209    bool has_nearest = false;
210    Symbol_t nearest_sym;
211
212    for (unsigned i=0; i < elf->e_shnum(); i++) 
213    {
214       Elf_X_Shdr shdr = elf->get_shdr(i);
215       if (shdr.sh_type() != SHT_SYMTAB && shdr.sh_type() != SHT_DYNSYM) {
216          continue;
217       } 
218
219       FOR_EACH_SYMBOL(shdr, symbol, str_buffer, idx) 
220       {
221          Dyninst::Offset sym_offset = getSymOffset(symbol, idx);
222          if (sym_offset <= offset && (!has_nearest || sym_offset > nearest)) {
223             unsigned str_loc = symbol.st_name(idx);
224             MAKE_SYMBOL(str_buffer+str_loc, idx, shdr, nearest_sym);
225             has_nearest = true;
226             nearest = sym_offset;
227          }
228       }
229    }
230    if (!has_nearest) {
231       GET_INVALID_SYMBOL(nearest_sym);
232    }
233
234    return nearest_sym;
235 #endif
236 }
237
238 std::string SymElf::getInterpreterName()
239 {
240    for (unsigned i=0; i < elf->e_phnum(); i++)
241    {
242       Elf_X_Phdr phdr = elf->get_phdr(i);
243       if (phdr.p_type() != PT_INTERP)
244          continue;
245       Dyninst::Offset off = (Dyninst::Offset) phdr.p_offset();
246       
247       if (fd != -1) {
248          char interp_buffer[4096];
249          ssize_t result;
250          do {
251             result = pread(fd, interp_buffer, 4096, off);
252          } while (result == -1 && errno == EINTR);
253          if (result != -1) {
254             return std::string(interp_buffer);
255          }
256       }
257       else if (buffer) {
258          return std::string(buffer + off);
259       }
260
261       //rawfile is expensive
262       size_t filesize;
263       const char *whole_file = elf->e_rawfile(filesize);
264       if (filesize < off) {
265          return std::string();
266       }
267       return std::string(whole_file + off);
268    }
269    return std::string();
270 }
271
272 unsigned SymElf::numSegments()
273 {
274    return elf->e_phnum();
275 }
276
277 bool SymElf::getSegment(unsigned num, SymSegment &seg)
278 {
279    if (num >= elf->e_phnum())
280       return false;
281
282    Elf_X_Phdr &phdr = elf->get_phdr(num);
283    seg.file_offset = phdr.p_offset();
284    seg.mem_addr = phdr.p_vaddr();
285    seg.file_size = phdr.p_filesz();
286    seg.mem_size = phdr.p_memsz();
287    seg.type = phdr.p_type();
288    seg.perms = phdr.p_flags() & 0x7;
289    return true;
290 }
291
292 unsigned SymElf::getAddressWidth()
293 {
294    return elf->wordSize();
295 }
296
297 int SymElf::getABIVersion() const
298 {
299    return (elf->e_machine() == EM_PPC64 && elf->e_flags() == 0x2) ? 2 : 1;
300 }
301
302 bool SymElf::isBigEndianDataEncoding() const
303 {
304    return (elf->e_endian() != 0);
305 }
306
307 unsigned long SymElf::getSymbolSize(const Symbol_t &sym)
308 {
309    GET_SYMBOL(sym, shdr, symbol, name, idx);
310    (void)name; //Silence warnings
311    unsigned long size = symbol.st_size(idx);
312    return size;
313 }
314
315 Dyninst::Offset SymElf::getSymbolOffset(const Symbol_t &sym)
316 {
317    assert(sym.i2 != INVALID_SYM_CODE);
318    if (sym.i2 != UNSET_INDEX_CODE) {
319       int cache_index = sym.i2;
320       return cache[cache_index].symaddress;
321    }
322
323    GET_SYMBOL(sym, shdr, symbols, name, idx);
324    (void)name; //Silence warnings
325    return getSymOffset(symbols, idx);
326 }
327
328 Dyninst::Offset SymElf::getSymbolTOC(const Symbol_t &sym)
329 {
330    GET_SYMBOL(sym, shdr, symbols, name, idx);
331    (void)name; //Silence warnings
332    return getSymTOC(symbols, idx);
333 }
334
335 std::string SymElf::getSymbolName(const Symbol_t &sym)
336 {
337    GET_SYMBOL(sym, shdr, symbols, name, idx);
338    (void)idx; (void)symbols; //Silence warnings
339    return std::string(name);
340 }
341
342 std::string SymElf::getDemangledName(const Symbol_t &sym)
343 {
344    assert(sym.i2 != INVALID_SYM_CODE);
345    int cache_index = -1;
346    const char *name = NULL;
347    if (sym.i2 != UNSET_INDEX_CODE) {
348       cache_index = sym.i2;
349       name = (const char *) sym.v1;
350    }
351    else {
352       assert(0); //TODO: Lookup in cache
353    }
354
355    if (cache[cache_index].demangled_name)
356       return std::string(cache[cache_index].demangled_name);
357    char *res = P_cplus_demangle(name, false, true);
358    if (!res) {
359       //Try native demangler
360       res = P_cplus_demangle(name, true, true);
361    }
362
363    cache[cache_index].demangled_name = res ? res : name;
364    return cache[cache_index].demangled_name;
365 }
366
367 bool SymElf::isValidSymbol(const Symbol_t &sym)
368 {
369    return (sym.i2 != INVALID_SYM_CODE);
370 }
371
372 static int symcache_cmp(const void *a, const void *b)
373 {
374    const SymCacheEntry *aa = (const SymCacheEntry *) a;
375    const SymCacheEntry *bb = (const SymCacheEntry *) b;
376    if (aa->symaddress < bb->symaddress) return -1;
377    else if (aa->symaddress > bb->symaddress) return 1;
378    else return 0;
379 }
380
381 unsigned long SymElf::getSymOffset(const Elf_X_Sym &symbol, unsigned idx)
382 {
383    if (need_odp && symbol.ST_TYPE(idx) == STT_FUNC) {
384       unsigned long odp_addr = odp_section->sh_addr();
385       unsigned long odp_size = odp_section->sh_size();
386       const char *odp_data = (const char *) odp_section->get_data().d_buf();
387       
388       unsigned long sym_offset = symbol.st_value(idx);
389       while (sym_offset >= odp_addr && sym_offset < odp_addr + odp_size)
390          sym_offset = *((const unsigned long *) (odp_data + sym_offset - odp_addr));
391       return sym_offset;
392    }
393
394    return symbol.st_value(idx);
395 }
396
397 unsigned long SymElf::getSymTOC(const Elf_X_Sym &symbol, unsigned idx)
398 {
399    if (need_odp && symbol.ST_TYPE(idx) == STT_FUNC) {
400       unsigned long odp_addr = odp_section->sh_addr();
401       unsigned long odp_size = odp_section->sh_size();
402       const char *odp_data = (const char *) odp_section->get_data().d_buf();
403       unsigned long sym_offset = symbol.st_value(idx);
404
405       if (sym_offset < odp_addr || (sym_offset >= odp_addr + odp_size)) 
406          return 0;
407
408       unsigned long toc = *((const unsigned long *) (odp_data + (sym_offset - odp_addr + sizeof(long))));
409       return toc;
410    }
411
412    return 0;
413 }
414
415 void SymElf::createSymCache()
416 {
417    unsigned long sym_count = 0, cur_sym = 0, cur_sec = 0;
418    
419    if (!cache && sym_sections)
420       return;
421
422    assert(!cache);
423    assert(!sym_sections);
424    for (unsigned i=0; i < elf->e_shnum(); i++) 
425    {
426       Elf_X_Shdr shdr = elf->get_shdr(i);
427       if (shdr.sh_type() != SHT_SYMTAB && shdr.sh_type() != SHT_DYNSYM) {
428          continue;
429       }
430       Elf_X_Data sym_data = shdr.get_data();
431       Elf_X_Sym symbols = sym_data.get_sym();
432       sym_count += symbols.count();
433       sym_sections_size++;
434    }
435
436    sym_sections = (Elf_X_Shdr *) malloc(sym_sections_size * sizeof(Elf_X_Shdr));
437    if (sym_count)
438       cache = (SymCacheEntry *) malloc(sym_count * sizeof(SymCacheEntry));
439    
440    for (unsigned i=0; i < elf->e_shnum(); i++) 
441    {
442       Elf_X_Shdr shdr = elf->get_shdr(i);
443       if (shdr.sh_type() != SHT_SYMTAB && shdr.sh_type() != SHT_DYNSYM) {
444          continue;
445       }
446
447       sym_sections[cur_sec] = shdr;
448       cur_sec++;
449
450       FOR_EACH_SYMBOL(shdr, symbols, str_buffer, idx)
451       {
452          (void)str_buffer; //Disable warnings
453          unsigned char symtype = symbols.ST_TYPE(idx);
454          if (symtype != STT_FUNC)
455             continue;
456          if (!symbols.st_value(idx))
457             continue;
458          cache[cur_sym].symaddress = getSymOffset(symbols, idx);
459          cache[cur_sym].symloc = symbols.st_symptr(idx);
460          cache[cur_sym].demangled_name = NULL;
461          cur_sym++;
462       }
463    }
464    cache_size = cur_sym;
465    if (cache)
466       cache = (SymCacheEntry *) realloc(cache, cur_sym  * sizeof(SymCacheEntry)); //Size reduction
467    if (cache)
468       qsort(cache, cache_size, sizeof(SymCacheEntry), symcache_cmp);
469 }
470
471 Symbol_t SymElf::lookupCachedSymbol(Dyninst::Offset off)
472 {
473    unsigned min = 0;
474    unsigned max = cache_size;
475    unsigned cur = cache_size / 2;
476    Symbol_t ret;
477    
478    if (!cache) {
479       ret.i2 = INVALID_SYM_CODE;
480       return ret;
481    }
482
483    for (;;) {
484       if (max == min || min+1 == max)
485          break;
486       Dyninst::Offset cur_off = cache[cur].symaddress;
487       if (cur_off < off) {
488          min = cur;
489       }
490       else if (cur_off > off) {
491          max = cur;
492       }
493       else {
494          break;
495       }
496       cur = (min + max) / 2;
497    }
498    void *sym_ptr = cache[cur].symloc;
499
500    for (unsigned i=0; i<sym_sections_size; i++) {
501       Elf_X_Shdr &shdr = sym_sections[i];
502       Elf_X_Data data = shdr.get_data();
503       
504       void *data_start = data.d_buf();
505       signed long sym_offset = ((unsigned char *) sym_ptr) - ((unsigned char *) data_start);
506       if (sym_offset < 0 || sym_offset >= (signed long) data.d_size())
507          continue;
508
509       //Calculate symbol index
510       Elf_X_Sym syms = data.get_sym();
511       unsigned sym_idx = sym_offset / syms.st_entsize();
512       
513       //Lookup symbol name
514       unsigned int str_index = shdr.sh_link();
515       Elf_X_Shdr str_shdr = elf->get_shdr(str_index);
516       Elf_X_Data str_data = str_shdr.get_data();
517       const char *str_buffer = (const char *) str_data.d_buf();
518       const char *name = str_buffer + syms.st_name(sym_idx);
519       
520       MAKE_SYMBOL(name, sym_idx, shdr, ret);
521       SET_SYM_CACHEINDEX(ret, cur);
522       return ret;
523    }
524    assert(0);
525
526    return ret;
527 }
528
529 Section_t SymElf::getSectionByName(std::string name)
530 {
531    unsigned short stridx = elf->e_shstrndx();
532    Elf_X_Shdr strshdr = elf->get_shdr(stridx);
533    Elf_X_Data strdata = strshdr.get_data();
534    const char *names = (const char *) strdata.d_buf();
535    Section_t ret;
536    ret.i1 = -1;
537
538    for (unsigned i=0; i < elf->e_shnum(); i++) 
539    {
540       Elf_X_Shdr shdr = elf->get_shdr(i);
541       const char *sname = names + shdr.sh_name();
542       if (name == sname) {
543          ret.i1 = i;
544          break;
545       }
546    }
547    
548    return ret;
549 }
550
551 Section_t SymElf::getSectionByAddress(Dyninst::Address addr)
552 {
553    Section_t ret;
554    ret.i1 = -1;
555
556    for (unsigned i=0; i < elf->e_shnum(); i++) 
557    {
558       Elf_X_Shdr shdr = elf->get_shdr(i);
559       Dyninst::Address mem_start = shdr.sh_addr();
560       unsigned long mem_size = shdr.sh_size();
561       if (addr >= mem_start && addr < mem_start + mem_size) {
562          ret.i1 = i;
563          break;
564       }
565    }
566    return ret;
567 }
568
569 Dyninst::Address SymElf::getSectionAddress(Section_t sec)
570 {
571    assert(isValidSection(sec));
572    Elf_X_Shdr shdr = elf->get_shdr(sec.i1);
573    
574    return shdr.sh_addr();
575 }
576
577 std::string SymElf::getSectionName(Section_t sec)
578 {
579    assert(isValidSection(sec));
580    Elf_X_Shdr shdr = elf->get_shdr(sec.i1);
581
582    unsigned short stridx = elf->e_shstrndx();
583    Elf_X_Shdr strshdr = elf->get_shdr(stridx);
584    Elf_X_Data strdata = strshdr.get_data();
585    const char *names = (const char *) strdata.d_buf();
586
587    return std::string(names + shdr.sh_name());
588 }
589
590 bool SymElf::isValidSection(Section_t sec)
591 {
592    return (sec.i1 != -1);
593 }
594
595 Dyninst::Offset SymElf::imageOffset() 
596
597    assert(0); return 0;
598 };
599
600 Dyninst::Offset SymElf::dataOffset() 
601
602    assert(0); return 0; 
603 }
604
605 void *SymElf::getElfHandle() {
606    return (void *) elf;
607 }
608
609 namespace Dyninst {
610 extern map<string, SymElf *> *getSymelfCache();
611 }
612
613 SymElfFactory::SymElfFactory()
614 {
615    open_symelfs = Dyninst::getSymelfCache();
616    assert(open_symelfs);
617 }
618
619 SymElfFactory::~SymElfFactory()
620 {
621 }
622
623 SymReader *SymElfFactory::openSymbolReader(std::string pathname)
624 {
625    SymElf *se = NULL;
626    std::map<std::string, SymElf *>::iterator i = open_symelfs->find(pathname);
627    if (i == open_symelfs->end()) {
628       se = new SymElf(pathname);
629       if (se->construction_error) {
630          delete se;
631          return NULL;
632       }
633       se->ref_count = 1;
634       (*open_symelfs)[pathname] = se;
635    }
636    else {
637       se = i->second;
638       se->ref_count++;
639    }
640    return static_cast<SymReader *>(se);
641 }
642
643 SymReader *SymElfFactory::openSymbolReader(const char *buffer, unsigned long size)
644 {
645    SymElf *se = new SymElf(buffer, size);
646    if (se->construction_error) {
647       delete se;
648       return NULL;
649    }
650    se->ref_count = 1;
651    return static_cast<SymReader *>(se);
652 }
653
654 bool SymElfFactory::closeSymbolReader(SymReader *sr)
655 {
656    SymElf *ser = static_cast<SymElf *>(sr);
657    std::map<std::string, SymElf *>::iterator i = open_symelfs->find(ser->file);
658    if (i == open_symelfs->end()) {
659       delete ser;
660       return true;
661    }
662
663    ser->ref_count--;
664    if (ser->ref_count == 0) {
665       open_symelfs->erase(i);
666       delete ser;
667    }
668    return true;
669 }
670
671 int SymElf::getFD()
672 {
673   return fd;
674   
675 }