tizen 2.3 release
[framework/system/swap-probe.git] / helper / btsym.c
1 /* Return list with names for address in backtrace.
2    Copyright (C) 1998,1999,2000,2001,2003,2009 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; if not, write to the Free
18    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19    02111-1307 USA.  */
20
21
22 /*
23 2011-12-15 Jaewon Lim <jaewon81.lim@samsung.com> add hashing for symbols
24
25 2011-12-08 Jaewon Lim <jaewon81.lim@samsung.com> get symbol data from binary's symtab when dladdr cannot resolve symbol
26 */
27
28 #include <assert.h>             // for assert
29 #include <stdio.h>              // for printf, sprintf
30 #include <stdlib.h>             // for malloc
31 #include <string.h>             // for strlen
32 #include <stddef.h>             // for ptrdiff_t
33 #include <errno.h>              // for program_invocation_name
34
35 #include <sys/types.h>  // for open, fstat
36 #include <sys/stat.h>   // for open, fstat
37 #include <fcntl.h>              // for open
38 #include <unistd.h>             // for fstat
39 #include <sys/mman.h>   // for mmap, munmap
40
41 #include "private_link.h"               // for link_map, ElfW
42 #include "dahelper.h"
43 #include "dacollection.h"
44
45 #if __ELF_NATIVE_CLASS == 32
46 # define WORD_WIDTH 8
47 #else
48 /* We assyme 64bits.  */
49 # define WORD_WIDTH 16
50 #endif
51
52 #define FILEPATH_MAX 1024
53
54 /* We use this macro to refer to ELF types independent of the native wordsize.
55    'ElfW(TYPE)' is used in place of 'Elf32_TYPE' or 'Elf64_TYPE'.  */
56 #define ELFW(type)  _ElfW (ELF, __ELF_NATIVE_CLASS, type)
57
58 /* Result of the lookup functions and how to retrieve the base address.  */
59 typedef struct link_map *lookup_t;
60 #define LOOKUP_VALUE(map) map
61 #define LOOKUP_VALUE_ADDRESS(map) ((map) ? (map)->l_addr : 0)
62
63 /* On some architectures a pointer to a function is not just a pointer
64    to the actual code of the function but rather an architecture
65    specific descriptor. */
66 #ifndef ELF_FUNCTION_PTR_IS_SPECIAL
67 # define DL_SYMBOL_ADDRESS(map, ref) \
68         (void *) (LOOKUP_VALUE_ADDRESS (map) + ref->st_value)
69 # define DL_LOOKUP_ADDRESS(addr) ((ElfW(Addr)) (addr))
70 # define DL_DT_INIT_ADDRESS(map, start) (start)
71 # define DL_DT_FINI_ADDRESS(map, start) (start)
72 #endif
73
74 /* On some architectures dladdr can't use st_size of all symbols this way.  */
75 #define DL_ADDR_SYM_MATCH(L, SYM, ADDR) \
76         (((ADDR) >= (L)->l_addr + (SYM)->st_value)              \
77                 && ((((SYM)->st_shndx == SHN_UNDEF || (SYM)->st_size == 0)       \
78                         && ((ADDR) == (L)->l_addr + (SYM)->st_value))         \
79                         || ((ADDR) < (L)->l_addr + (SYM)->st_value + (SYM)->st_size)))
80
81 // start of implementation by Jaewon Lim
82 struct _symdata
83 {
84         ElfW(Shdr)      symhdr;
85         ElfW(Shdr)      strhdr;
86         ElfW(Sym)*      symtab;
87         char*           strtab;
88 };
89
90 typedef struct _symdata symdata_t;
91
92 // get symbol data from file binary
93 static symdata_t* _get_symboldata(char* filepath)
94 {
95         int fd;
96         struct stat st;
97         char *contents;
98         symdata_t* pdata;
99
100         // first find in glist
101         pdata = (symdata_t*)find_glist(filepath);
102         if(pdata != NULL)
103         {
104                 return pdata;
105         }
106
107         fd = open(filepath, O_RDONLY | O_CLOEXEC);
108         if(fd == -1)
109         {
110                 return pdata;
111         }
112
113         if(fstat(fd, &st) == -1)
114         {
115                 close(fd);
116                 return pdata;
117         }
118
119         contents = (char*)mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
120         if(likely(contents != NULL))
121         {
122                 ElfW(Ehdr) * elf_hdr = (ElfW(Ehdr) *)(contents);
123                 ElfW(Shdr) * sec_hdr = (ElfW(Shdr) *)(contents + elf_hdr->e_shoff);
124                 int i, symtab_idx = -1;
125
126                 for (i = 0; i < elf_hdr->e_shnum; ++i)
127                 {
128                         if(unlikely(sec_hdr[i].sh_type == SHT_SYMTAB))
129                         {
130                                 symtab_idx = i;
131                                 break;
132                         }
133                 }
134
135                 if(symtab_idx != -1)    //there is symbol table
136                 {
137                         int strtab_idx = sec_hdr[symtab_idx].sh_link;
138                         if(likely((strtab_idx != 0) && (sec_hdr[strtab_idx].sh_type == SHT_STRTAB)))    // associated string table is valid
139                         {
140                                 pdata = (symdata_t*)malloc(sizeof(symdata_t) +
141                                                 sec_hdr[symtab_idx].sh_size +
142                                                 sec_hdr[strtab_idx].sh_size);
143
144                                 if(likely(pdata != NULL))
145                                 {
146                                         memcpy(&(pdata->symhdr), &(sec_hdr[symtab_idx]), sizeof(ElfW(Shdr)));
147                                         memcpy(&(pdata->strhdr), &(sec_hdr[strtab_idx]), sizeof(ElfW(Shdr)));
148                                         pdata->symtab = (ElfW(Sym) *)(pdata + 1);
149                                         pdata->strtab = ((char*)pdata) + sizeof(symdata_t) + sec_hdr[symtab_idx].sh_size;
150                                         memcpy((void*)(pdata->symtab), (void*)(contents + sec_hdr[symtab_idx].sh_offset), sec_hdr[symtab_idx].sh_size);
151                                         memcpy((void*)(pdata->strtab), (void*)(contents + sec_hdr[strtab_idx].sh_offset), sec_hdr[strtab_idx].sh_size);
152
153                                         if(add_to_glist(filepath, (void*)pdata) == 0)   // fail to add
154                                         {
155                                                 free(pdata);
156                                                 pdata = NULL;
157                                         }
158                                 }
159                         }
160                 }
161
162                 munmap((void*)contents, st.st_size);
163         }
164
165         close(fd);
166
167         return pdata;
168 }
169
170 int get_map_address(void* symbol, void** map_start, void** map_end)
171 {
172         Dl_info info;
173         int status, ret = 0;
174         struct link_map* map = NULL;
175
176         status = dladdr1(symbol, &info, (void**)&map, RTLD_DL_LINKMAP);
177         if(status && map != NULL)
178         {
179                 *map_start = (void*)(map->l_map_start);
180                 *map_end = (void*)(map->l_map_end);
181                 ret = 1;
182         }
183         else
184                 ret = 0;
185
186         return ret;
187 }
188
189 // end of implementation by Jaewon Lim
190
191 char** cached_backtrace_symbols (void* const* array, int size)
192 {
193         Dl_info info[MAX_STACK_DEPTH];
194         int status[MAX_STACK_DEPTH];
195         char* chararr[MAX_STACK_DEPTH];
196         int cnt;
197         size_t total = 0;
198         char **result;
199         char* foundsym;
200
201         memset(chararr, 0, MAX_STACK_DEPTH * sizeof(char*));
202
203         /* Fill in the information we can get from `dladdr'.  */
204         for (cnt = 0; cnt < size; ++cnt)
205         {
206                 struct link_map* map;
207
208                 if(find_symbol_hash(array[cnt], &foundsym) <= 0)        // not found or error
209                 {
210                         status[cnt] = dladdr1 (array[cnt], &info[cnt], (void**)&map, RTLD_DL_LINKMAP);
211                         if (status[cnt] && info[cnt].dli_fname && info[cnt].dli_fname[0] != '\0')
212                         {
213                                 /* We have some info, compute the length of the string which will be
214                                 "<file-name>(<sym-name>+offset) [address].  */
215                                 total += (strlen (info[cnt].dli_fname ?: "")
216                                                 + strlen (info[cnt].dli_sname ?: "")
217                                                 + 3 + WORD_WIDTH + 3 + WORD_WIDTH + 5);
218
219                                 /* The load bias is more useful to the user than the load
220                                 address.  The use of these addresses is to calculate an
221                                 address in the ELF file, so its prelinked bias is not
222                                 something we want to subtract out.  */
223                                 info[cnt].dli_fbase = (void *) map->l_addr;
224                         }
225                         else
226                                 total += 5 + WORD_WIDTH;
227                 }
228                 else            // there is a entry for key
229                 {
230                         status[cnt] = 0;
231                         chararr[cnt] = foundsym;
232                         if(chararr[cnt] != NULL)
233                                 total += (strlen(chararr[cnt]) + 1);
234                         else
235                         {
236                                 // this never happened
237                                 total += 100;
238                         }
239                 }
240         }
241
242         /* Allocate memory for the result.  */
243         result = (char **) malloc (size * sizeof (char *) + total);
244         if (result != NULL)
245         {
246                 char *last = (char *) (result + size);
247
248                 for (cnt = 0; cnt < size; ++cnt)
249                 {
250                         result[cnt] = last;
251
252                         if(chararr[cnt] != NULL)                // there is a cache
253                         {
254                                 last += (1 + sprintf(last, "%s", chararr[cnt]));
255                         }
256                         else            // there is no cache
257                         {
258                                 int tstrlen;
259                                 if (status[cnt] && info[cnt].dli_fname != NULL && info[cnt].dli_fname[0] != '\0')
260                                 {
261                                         // We found no symbol name to use, so describe it as relative to the file.
262                                         if (info[cnt].dli_sname == NULL)
263                                                 info[cnt].dli_saddr = info[cnt].dli_fbase;
264
265                                         if (info[cnt].dli_sname == NULL && info[cnt].dli_saddr == 0)
266                                         {
267                                                 tstrlen = sprintf (last, "%s(%s) [%p]", info[cnt].dli_fname ?: "", info[cnt].dli_sname ?: "", array[cnt]);
268                                         }
269                                         else
270                                         {
271                                                 char sign;
272                                                 ptrdiff_t offset;
273                                                 if (array[cnt] >= (void *) info[cnt].dli_saddr)
274                                                 {
275                                                         sign = '+';
276                                                         offset = array[cnt] - info[cnt].dli_saddr;
277                                                 }
278                                                 else
279                                                 {
280                                                         sign = '-';
281                                                         offset = info[cnt].dli_saddr - array[cnt];
282                                                 }
283
284                                                 tstrlen = sprintf (last, "%s(%s%c%#tx) [%p]",
285                                                                 info[cnt].dli_fname ?: "",
286                                                                 info[cnt].dli_sname ?: "",
287                                                                 sign, offset, array[cnt]);
288                                         }
289                                 }
290                                 else
291                                 {
292                                         tstrlen = sprintf (last, "[%p]", array[cnt]);
293                                 }
294                                 tstrlen++;
295
296                                 add_symbol_hash(array[cnt], last, tstrlen);
297
298                                 last += tstrlen;
299                         }
300                 }
301
302                 assert (last <= (char *) result + size * sizeof (char *) + total);
303         }
304         else            // fail to malloc
305         {
306                 // do nothing
307         }
308
309         return result;
310 }
311
312 char** da_backtrace_symbols (void* const* array, int size)
313 {
314         Dl_info info[MAX_STACK_DEPTH];
315         int status[MAX_STACK_DEPTH];
316         char* chararr[MAX_STACK_DEPTH];
317         int cnt;
318         size_t total = 0;
319         char **result;
320         char* foundsym;
321
322         memset(chararr, 0, MAX_STACK_DEPTH * sizeof(char*));
323
324         /* Fill in the information we can get from `dladdr'.  */
325         for (cnt = 0; cnt < size; ++cnt)
326         {
327                 struct link_map* map;
328
329                 if(find_symbol_hash(array[cnt], &foundsym) <= 0)        // not found or error
330                 {
331                         status[cnt] = dladdr1 (array[cnt], &info[cnt], (void**)&map, RTLD_DL_LINKMAP);
332                         if(info[cnt].dli_sname == NULL)
333                         {
334                                 char filepath[FILEPATH_MAX];    // for file path
335
336                                 /* If this is the main program the information is incomplete.  */
337                                 if (map->l_name[0] == '\0' && map->l_type == lt_executable)
338                                 {
339                                         strncpy(filepath, program_invocation_name, FILEPATH_MAX - 1);
340                                 }
341                                 else
342                                 {
343                                         int len;
344                                         if(map->l_origin)
345                                         {
346                                                 strcpy(filepath, map->l_origin);
347                                                 len = strlen(filepath);
348                                                 if(len > 0 && filepath[len-1] != '/')
349                                                 {
350                                                         filepath[len] = '/';
351                                                         filepath[len+1] = '\0';
352                                                 }
353                                         }
354                                         else
355                                                 filepath[0] = '\0';
356                                         strcat(filepath, map->l_name);
357                                 }
358
359                                 symdata_t* pdata = _get_symboldata(filepath);
360                                 if(pdata != NULL)
361                                 {
362                                         ElfW(Sym) * sym_ent = pdata->symtab;
363                                         char* strtab = pdata->strtab;
364                                         int i, num_syms = pdata->symhdr.sh_size / pdata->symhdr.sh_entsize;
365
366                                         for(i = 0; i < num_syms; ++i)
367                                         {
368                                                 if (ELFW(ST_TYPE) (sym_ent[i].st_info) != STT_TLS
369                                                                 && (sym_ent[i].st_shndx != SHN_UNDEF || sym_ent[i].st_value != 0)
370                                                                 && DL_ADDR_SYM_MATCH (map, &(sym_ent[i]), DL_LOOKUP_ADDRESS (array[cnt]))
371                                                                 && sym_ent[i].st_name < pdata->strhdr.sh_size)
372                                                 {
373                                                         // We found a symbol close by.  Fill in its name and exact address.
374                                                         info[cnt].dli_sname = strtab + ((ElfW(Sym) *)(sym_ent + i))->st_name;
375                                                         info[cnt].dli_saddr = DL_SYMBOL_ADDRESS (map, ((ElfW(Sym) *)(sym_ent + i)));
376                                                         break;
377                                                 }
378                                         }
379                                 }
380                         }
381
382                         if (status[cnt] && info[cnt].dli_fname && info[cnt].dli_fname[0] != '\0')
383                         {
384                                 /* We have some info, compute the length of the string which will be
385                                 "<file-name>(<sym-name>+offset) [address].  */
386                                 total += (strlen (info[cnt].dli_fname ?: "")
387                                                 + strlen (info[cnt].dli_sname ?: "")
388                                                 + 3 + WORD_WIDTH + 3 + WORD_WIDTH + 5);
389
390                                 /* The load bias is more useful to the user than the load
391                                 address.  The use of these addresses is to calculate an
392                                 address in the ELF file, so its prelinked bias is not
393                                 something we want to subtract out.  */
394 //                              info[cnt].dli_fbase = (void *) map->l_addr;
395                         }
396                         else
397                                 total += 5 + WORD_WIDTH;
398                 }
399                 else            // there is a entry for key
400                 {
401                         chararr[cnt] = foundsym;
402                         if(chararr[cnt] != NULL)
403                                 total += (strlen(chararr[cnt]) + 1);
404                         else
405                         {
406                                 assert(false);
407                                 total += 100;
408                         }
409                 }
410         }
411
412         /* Allocate memory for the result.  */
413         result = (char **) malloc (size * sizeof (char *) + total);
414         if (result != NULL)
415         {
416                 char *last = (char *) (result + size);
417
418                 for (cnt = 0; cnt < size; ++cnt)
419                 {
420                         result[cnt] = last;
421
422                         if(chararr[cnt] != NULL)                // there is a cache
423                         {
424                                 last += (1 + sprintf(last, "%s", chararr[cnt]));
425                         }
426                         else            // there is no cache
427                         {
428                                 int tstrlen;
429                                 if (status[cnt] && info[cnt].dli_fname != NULL && info[cnt].dli_fname[0] != '\0')
430                                 {
431                                         // We found no symbol name to use, so describe it as relative to the file.
432                                         if (info[cnt].dli_sname == NULL)
433                                                 info[cnt].dli_saddr = info[cnt].dli_fbase;
434
435                                         if (info[cnt].dli_sname == NULL && info[cnt].dli_saddr == 0)
436                                         {
437                                                 tstrlen = sprintf (last, "%s(%s) [%p]", info[cnt].dli_fname ?: "", info[cnt].dli_sname ?: "", array[cnt]);
438                                         }
439                                         else
440                                         {
441                                                 char sign;
442                                                 ptrdiff_t offset;
443                                                 if (array[cnt] >= (void *) info[cnt].dli_saddr)
444                                                 {
445                                                         sign = '+';
446                                                         offset = array[cnt] - info[cnt].dli_saddr;
447                                                 }
448                                                 else
449                                                 {
450                                                         sign = '-';
451                                                         offset = info[cnt].dli_saddr - array[cnt];
452                                                 }
453
454                                                 tstrlen = sprintf (last, "%s(%s%c%#tx) [%p]",
455                                                                 info[cnt].dli_fname ?: "",
456                                                                 info[cnt].dli_sname ?: "",
457                                                                 sign, offset, array[cnt]);
458                                         }
459                                 }
460                                 else
461                                 {
462                                         tstrlen = sprintf (last, "[%p]", array[cnt]);
463                                 }
464                                 tstrlen++;
465
466                                 add_symbol_hash(array[cnt], last, tstrlen);
467
468                                 last += tstrlen;
469                         }
470                 }
471
472                 assert (last <= (char *) result + size * sizeof (char *) + total);
473         }
474         else            // fail to malloc
475         {
476                 // do nothing
477         }
478
479         return result;
480 }
481