1 #include "config.h"     /* see https://sourceware.org/bugzilla/show_bug.cgi?id=14243 ... sigh... */
2 /*
3   A hacky replacement for backtrace_symbols in glibc
4 
5   backtrace_symbols in glibc looks up symbols using dladdr which is limited in
6   the symbols that it sees. libbacktracesymbols opens the executable and shared
7   libraries using libbfd and will look up backtrace information using the symbol
8   table and the dwarf line information.
9 
10   It may make more sense for this program to use libelf instead of libbfd.
11   However, I have not investigated that yet.
12 
13   Derived from addr2line.c from GNU Binutils by Jeff Muizelaar
14 
15   Copyright 2007 Jeff Muizelaar
16 */
17 
18 /* addr2line.c -- convert addresses to line number and function name
19    Copyright 1997, 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
20    Contributed by Ulrich Lauther <Ulrich.Lauther@mchp.siemens.de>
21 
22    This file was part of GNU Binutils.
23 
24    This program is free software; you can redistribute it and/or modify
25    it under the terms of the GNU General Public License as published by
26    the Free Software Foundation; either version 2, or (at your option)
27    any later version.
28 
29    This program is distributed in the hope that it will be useful,
30    but WITHOUT ANY WARRANTY; without even the implied warranty of
31    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
32    GNU General Public License for more details.
33 
34    You should have received a copy of the GNU General Public License
35    along with this program; if not, write to the Free Software
36    Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
37 
38 #define fatal(a, b) exit(1)
39 #define bfd_fatal(a) exit(1)
40 #define bfd_nonfatal(a) exit(1)
41 #define list_matching_formats(a) exit(1)
42 
43 /* 2 characters for each byte, plus 1 each for 0, x, and NULL */
44 #define PTRSTR_LEN (sizeof(void *) * 2 + 3)
45 #define true 1
46 #define false 0
47 
48 #define _GNU_SOURCE
49 #include <string.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <execinfo.h>
53 #include <bfd.h>
54 #include <libiberty.h>
55 #include <dlfcn.h>
56 #include <link.h>
57 
58 
59 static asymbol **syms;        /* Symbol table.  */
60 
61 /* 150 isn't special; it's just an arbitrary non-ASCII char value.  */
62 #define OPTION_DEMANGLER      (150)
63 
64 static void slurp_symtab(bfd * abfd);
65 static void find_address_in_section(bfd *abfd, asection *section, void *data);
66 
67 /* Read in the symbol table.  */
68 
slurp_symtab(bfd * abfd)69 static void slurp_symtab(bfd * abfd)
70 {
71       long symcount;
72       unsigned int size;
73 
74       if ((bfd_get_file_flags(abfd) & HAS_SYMS) == 0)
75             return;
76 
77       symcount = bfd_read_minisymbols(abfd, false, (PTR) & syms, &size);
78       if (symcount == 0)
79             symcount = bfd_read_minisymbols(abfd, true /* dynamic */ ,
80                                     (PTR) & syms, &size);
81 
82       if (symcount < 0)
83             bfd_fatal(bfd_get_filename(abfd));
84 }
85 
86 /* These global variables are used to pass information between
87    translate_addresses and find_address_in_section.  */
88 
89 static bfd_vma pc;
90 static const char *filename;
91 static const char *functionname;
92 static unsigned int line;
93 static int found;
94 
95 /* Look for an address in a section.  This is called via
96    bfd_map_over_sections.  */
97 
find_address_in_section(bfd * abfd,asection * section,void * data)98 static void find_address_in_section(bfd *abfd, asection *section, void *data __attribute__ ((__unused__)) )
99 {
100       bfd_vma vma;
101       bfd_size_type size;
102 
103       if (found)
104             return;
105 
106       if ((bfd_get_section_flags(abfd, section) & SEC_ALLOC) == 0)
107             return;
108 
109       vma = bfd_get_section_vma(abfd, section);
110       if (pc < vma)
111             return;
112 
113       size = bfd_section_size(abfd, section);
114       if (pc >= vma + size)
115             return;
116 
117       found = bfd_find_nearest_line(abfd, section, syms, pc - vma,
118                               &filename, &functionname, &line);
119 }
120 
121 
translate_addresses_buf(bfd * abfd,bfd_vma * addr,int naddr)122 static char** translate_addresses_buf(bfd * abfd, bfd_vma *addr, int naddr)
123 {
124       int naddr_orig = naddr;
125       char b;
126       int total  = 0;
127       enum { Count, Print } state;
128       char *buf = &b;
129       int len = 0;
130       char **ret_buf = NULL;
131       /* iterate over the formating twice.
132        * the first time we count how much space we need
133        * the second time we do the actual printing */
134       for (state=Count; state<=Print; state++) {
135       if (state == Print) {
136             ret_buf = malloc(total + sizeof(char*)*naddr);
137             buf = (char*)(ret_buf + naddr);
138             len = total;
139       }
140       while (naddr) {
141             if (state == Print)
142                   ret_buf[naddr-1] = buf;
143             pc = addr[naddr-1];
144 
145             found = false;
146             bfd_map_over_sections(abfd, find_address_in_section,
147             (PTR) NULL);
148 
149             if (!found) {
150                   total += snprintf(buf, len, "[0x%llx] \?\?() \?\?:0",(long long unsigned int) addr[naddr-1]) + 1;
151             } else {
152                   const char *name;
153 
154                   name = functionname;
155                   if (name == NULL || *name == '\0')
156                         name = "??";
157                   if (filename != NULL) {
158                         char *h;
159 
160                         h = strrchr(filename, '/');
161                         if (h != NULL)
162                               filename = h + 1;
163                   }
164                   total += snprintf(buf, len, "%s:%u\t%s()", filename ? filename : "??",
165                          line, name) + 1;
166 
167             }
168             if (state == Print) {
169                   /* set buf just past the end of string */
170                   buf = buf + total + 1;
171             }
172             naddr--;
173       }
174       naddr = naddr_orig;
175       }
176       return ret_buf;
177 }
178 /* Process a file.  */
179 
process_file(const char * file_name,bfd_vma * addr,int naddr)180 static char **process_file(const char *file_name, bfd_vma *addr, int naddr)
181 {
182       bfd *abfd;
183       char **matching;
184       char **ret_buf;
185 
186       abfd = bfd_openr(file_name, NULL);
187 
188       if (abfd == NULL)
189             bfd_fatal(file_name);
190 
191       if (bfd_check_format(abfd, bfd_archive))
192             fatal("%s: can not get addresses from archive", file_name);
193 
194       if (!bfd_check_format_matches(abfd, bfd_object, &matching)) {
195             bfd_nonfatal(bfd_get_filename(abfd));
196             if (bfd_get_error() ==
197                 bfd_error_file_ambiguously_recognized) {
198                   list_matching_formats(matching);
199                   free(matching);
200             }
201             xexit(1);
202       }
203 
204       slurp_symtab(abfd);
205 
206       ret_buf = translate_addresses_buf(abfd, addr, naddr);
207 
208       if (syms != NULL) {
209             free(syms);
210             syms = NULL;
211       }
212 
213       bfd_close(abfd);
214       return ret_buf;
215 }
216 
217 #define MAX_DEPTH 16
218 
219 struct file_match {
220       const char *file;
221       void *address;
222       void *base;
223       void *hdr;
224 };
225 
find_matching_file(struct dl_phdr_info * info,size_t size,void * data)226 static int find_matching_file(struct dl_phdr_info *info,
227             size_t size, void *data)
228 {
229       struct file_match *match = data;
230       /* This code is modeled from Gfind_proc_info-lsb.c:callback() from libunwind */
231       long n;
232       const ElfW(Phdr) *phdr;
233       ElfW(Addr) load_base = info->dlpi_addr;
234       phdr = info->dlpi_phdr;
235       for (n = info->dlpi_phnum; --n >= 0; phdr++) {
236             if (phdr->p_type == PT_LOAD) {
237                   ElfW(Addr) vaddr = phdr->p_vaddr + load_base;
238                   if (   (ElfW(Addr)) (match->address) >= vaddr
239                       && (ElfW(Addr)) (match->address) < vaddr + phdr->p_memsz) {
240                         /* we found a match */
241                         match->file = info->dlpi_name;
242                         match->base = (void *) (info->dlpi_addr);
243                   }
244             }
245       }
246       return 0;
247 }
248 
backtrace_symbols(void * const * buffer,int size)249 char **backtrace_symbols(void *const *buffer, int size)
250 {
251       int stack_depth = size - 1;
252       int x,y;
253       /* discard calling function */
254       int total = 0;
255 
256       char ***locations;
257       char **final;
258       char *f_strings;
259 
260       locations = malloc(sizeof(char**) * (stack_depth+1));
261 
262       bfd_init();
263       for(x=stack_depth, y=0; x>=0; x--, y++){
264             struct file_match match = { .address = buffer[x] };
265             char **ret_buf;
266             bfd_vma addr;
267             dl_iterate_phdr(find_matching_file, &match);
268             addr = buffer[x] - match.base;
269             if (match.file && strlen(match.file))
270                   ret_buf = process_file(match.file, &addr, 1);
271             else
272                   ret_buf = process_file("/proc/self/exe", &addr, 1);
273             locations[x] = ret_buf;
274             total += strlen(ret_buf[0]) + 1;
275       }
276 
277       /* allocate the array of char* we are going to return and extra space for
278        * all of the strings */
279       final = malloc(total + (stack_depth + 1) * sizeof(char*));
280       /* get a pointer to the extra space */
281       f_strings = (char*)(final + stack_depth + 1);
282 
283       /* fill in all of strings and pointers */
284       for(x=stack_depth; x>=0; x--){
285             strcpy(f_strings, locations[x][0]);
286             free(locations[x]);
287             final[x] = f_strings;
288             f_strings += strlen(f_strings) + 1;
289       }
290 
291       free(locations);
292 
293       return final;
294 }
295 
296 void
backtrace_symbols_fd(void * const * buffer,int size,int fd)297 backtrace_symbols_fd(void *const *buffer, int size, int fd)
298 {
299         int j;
300         char **strings;
301 
302         strings = backtrace_symbols(buffer, size);
303         if (strings == NULL) {
304             perror("backtrace_symbols");
305             exit(EXIT_FAILURE);
306         }
307 
308         for (j = 0; j < size; j++)
309             printf("%s\n", strings[j]);
310 
311         free(strings);
312 }
313