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.
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.
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.
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
23 2011-12-15 Jaewon Lim <jaewon81.lim@samsung.com> add hashing for symbols
25 2011-12-08 Jaewon Lim <jaewon81.lim@samsung.com> get symbol data from binary's symtab when dladdr cannot resolve symbol
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
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
41 #include "private_link.h" // for link_map, ElfW
43 #include "dacollection.h"
45 #if __ELF_NATIVE_CLASS == 32
48 /* We assyme 64bits. */
49 # define WORD_WIDTH 16
52 #define FILEPATH_MAX 1024
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)
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)
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)
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)))
81 // start of implementation by Jaewon Lim
90 typedef struct _symdata symdata_t;
92 // get symbol data from file binary
93 static symdata_t* _get_symboldata(char* filepath)
100 // first find in glist
101 pdata = (symdata_t*)find_glist(filepath);
107 fd = open(filepath, O_RDONLY | O_CLOEXEC);
113 if(fstat(fd, &st) == -1)
119 contents = (char*)mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
120 if(likely(contents != NULL))
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;
126 for (i = 0; i < elf_hdr->e_shnum; ++i)
128 if(unlikely(sec_hdr[i].sh_type == SHT_SYMTAB))
135 if(symtab_idx != -1) //there is symbol table
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
140 pdata = (symdata_t*)malloc(sizeof(symdata_t) +
141 sec_hdr[symtab_idx].sh_size +
142 sec_hdr[strtab_idx].sh_size);
144 if(likely(pdata != NULL))
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);
153 if(add_to_glist(filepath, (void*)pdata) == 0) // fail to add
162 munmap((void*)contents, st.st_size);
170 int get_map_address(void* symbol, void** map_start, void** map_end)
174 struct link_map* map = NULL;
176 status = dladdr1(symbol, &info, (void**)&map, RTLD_DL_LINKMAP);
177 if(status && map != NULL)
179 *map_start = (void*)(map->l_map_start);
180 *map_end = (void*)(map->l_map_end);
189 // end of implementation by Jaewon Lim
191 char** cached_backtrace_symbols (void* const* array, int size)
193 Dl_info info[MAX_STACK_DEPTH];
194 int status[MAX_STACK_DEPTH];
195 char* chararr[MAX_STACK_DEPTH];
201 memset(chararr, 0, MAX_STACK_DEPTH * sizeof(char*));
203 /* Fill in the information we can get from `dladdr'. */
204 for (cnt = 0; cnt < size; ++cnt)
206 struct link_map* map;
208 if(find_symbol_hash(array[cnt], &foundsym) <= 0) // not found or error
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')
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);
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;
226 total += 5 + WORD_WIDTH;
228 else // there is a entry for key
231 chararr[cnt] = foundsym;
232 if(chararr[cnt] != NULL)
233 total += (strlen(chararr[cnt]) + 1);
236 // this never happened
242 /* Allocate memory for the result. */
243 result = (char **) malloc (size * sizeof (char *) + total);
246 char *last = (char *) (result + size);
248 for (cnt = 0; cnt < size; ++cnt)
252 if(chararr[cnt] != NULL) // there is a cache
254 last += (1 + sprintf(last, "%s", chararr[cnt]));
256 else // there is no cache
259 if (status[cnt] && info[cnt].dli_fname != NULL && info[cnt].dli_fname[0] != '\0')
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;
265 if (info[cnt].dli_sname == NULL && info[cnt].dli_saddr == 0)
267 tstrlen = sprintf (last, "%s(%s) [%p]", info[cnt].dli_fname ?: "", info[cnt].dli_sname ?: "", array[cnt]);
273 if (array[cnt] >= (void *) info[cnt].dli_saddr)
276 offset = array[cnt] - info[cnt].dli_saddr;
281 offset = info[cnt].dli_saddr - array[cnt];
284 tstrlen = sprintf (last, "%s(%s%c%#tx) [%p]",
285 info[cnt].dli_fname ?: "",
286 info[cnt].dli_sname ?: "",
287 sign, offset, array[cnt]);
292 tstrlen = sprintf (last, "[%p]", array[cnt]);
296 add_symbol_hash(array[cnt], last, tstrlen);
302 assert (last <= (char *) result + size * sizeof (char *) + total);
304 else // fail to malloc
312 char** da_backtrace_symbols (void* const* array, int size)
314 Dl_info info[MAX_STACK_DEPTH];
315 int status[MAX_STACK_DEPTH];
316 char* chararr[MAX_STACK_DEPTH];
322 memset(chararr, 0, MAX_STACK_DEPTH * sizeof(char*));
324 /* Fill in the information we can get from `dladdr'. */
325 for (cnt = 0; cnt < size; ++cnt)
327 struct link_map* map;
329 if(find_symbol_hash(array[cnt], &foundsym) <= 0) // not found or error
331 status[cnt] = dladdr1 (array[cnt], &info[cnt], (void**)&map, RTLD_DL_LINKMAP);
332 if(info[cnt].dli_sname == NULL)
334 char filepath[FILEPATH_MAX]; // for file path
336 /* If this is the main program the information is incomplete. */
337 if (map->l_name[0] == '\0' && map->l_type == lt_executable)
339 strncpy(filepath, program_invocation_name, FILEPATH_MAX - 1);
346 strcpy(filepath, map->l_origin);
347 len = strlen(filepath);
348 if(len > 0 && filepath[len-1] != '/')
351 filepath[len+1] = '\0';
356 strcat(filepath, map->l_name);
359 symdata_t* pdata = _get_symboldata(filepath);
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;
366 for(i = 0; i < num_syms; ++i)
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)
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)));
382 if (status[cnt] && info[cnt].dli_fname && info[cnt].dli_fname[0] != '\0')
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);
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;
397 total += 5 + WORD_WIDTH;
399 else // there is a entry for key
401 chararr[cnt] = foundsym;
402 if(chararr[cnt] != NULL)
403 total += (strlen(chararr[cnt]) + 1);
412 /* Allocate memory for the result. */
413 result = (char **) malloc (size * sizeof (char *) + total);
416 char *last = (char *) (result + size);
418 for (cnt = 0; cnt < size; ++cnt)
422 if(chararr[cnt] != NULL) // there is a cache
424 last += (1 + sprintf(last, "%s", chararr[cnt]));
426 else // there is no cache
429 if (status[cnt] && info[cnt].dli_fname != NULL && info[cnt].dli_fname[0] != '\0')
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;
435 if (info[cnt].dli_sname == NULL && info[cnt].dli_saddr == 0)
437 tstrlen = sprintf (last, "%s(%s) [%p]", info[cnt].dli_fname ?: "", info[cnt].dli_sname ?: "", array[cnt]);
443 if (array[cnt] >= (void *) info[cnt].dli_saddr)
446 offset = array[cnt] - info[cnt].dli_saddr;
451 offset = info[cnt].dli_saddr - array[cnt];
454 tstrlen = sprintf (last, "%s(%s%c%#tx) [%p]",
455 info[cnt].dli_fname ?: "",
456 info[cnt].dli_sname ?: "",
457 sign, offset, array[cnt]);
462 tstrlen = sprintf (last, "[%p]", array[cnt]);
466 add_symbol_hash(array[cnt], last, tstrlen);
472 assert (last <= (char *) result + size * sizeof (char *) + total);
474 else // fail to malloc