2 A hacky replacement for backtrace_symbols in glibc
4 backtrace_symbols in glibc looks up symbols using dladdr which is limited in
5 the symbols that it sees. libbacktracesymbols opens the executable and shared
6 libraries using libbfd and will look up backtrace information using the symbol
7 table and the dwarf line information.
9 It may make more sense for this program to use libelf instead of libbfd.
10 However, I have not investigated that yet.
12 Derived from addr2line.c from GNU Binutils by Jeff Muizelaar
14 Copyright 2007 Jeff Muizelaar
17 /* addr2line.c -- convert addresses to line number and function name
18 Copyright 1997, 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
19 Contributed by Ulrich Lauther <Ulrich.Lauther@mchp.siemens.de>
21 This file was part of GNU Binutils.
23 This program is free software; you can redistribute it and/or modify
24 it under the terms of the GNU General Public License as published by
25 the Free Software Foundation; either version 2, or (at your option)
28 This program is distributed in the hope that it will be useful,
29 but WITHOUT ANY WARRANTY; without even the implied warranty of
30 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 GNU General Public License for more details.
33 You should have received a copy of the GNU General Public License
34 along with this program; if not, write to the Free Software
35 Foundation, 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. */
37 #define fatal(a, b) exit(1)
38 #define bfd_fatal(a) exit(1)
39 #define bfd_nonfatal(a) exit(1)
40 #define list_matching_formats(a) exit(1)
42 /* 2 characters for each byte, plus 1 each for 0, x, and NULL */
43 #define PTRSTR_LEN (sizeof(void *) * 2 + 3)
53 #include <libiberty.h>
57 static asymbol
**syms
; /* Symbol table. */
59 /* 150 isn't special; it's just an arbitrary non-ASCII char value. */
60 #define OPTION_DEMANGLER (150)
62 static void slurp_symtab(bfd
* abfd
);
63 static void find_address_in_section(bfd
*abfd
, asection
*section
, void *data
);
65 /* Read in the symbol table. */
67 static void slurp_symtab(bfd
* abfd
)
72 if ((bfd_get_file_flags(abfd
) & HAS_SYMS
) == 0)
75 symcount
= bfd_read_minisymbols(abfd
, false, (PTR
) & syms
, &size
);
77 symcount
= bfd_read_minisymbols(abfd
, true /* dynamic */ ,
81 bfd_fatal(bfd_get_filename(abfd
));
84 /* These global variables are used to pass information between
85 translate_addresses and find_address_in_section. */
88 static const char *filename
;
89 static const char *functionname
;
90 static unsigned int line
;
93 /* Look for an address in a section. This is called via
94 bfd_map_over_sections. */
96 static void find_address_in_section(bfd
*abfd
, asection
*section
, void *data
__attribute__ ((__unused__
)) )
104 if ((bfd_get_section_flags(abfd
, section
) & SEC_ALLOC
) == 0)
107 vma
= bfd_get_section_vma(abfd
, section
);
111 size
= bfd_section_size(abfd
, section
);
112 if (pc
>= vma
+ size
)
115 found
= bfd_find_nearest_line(abfd
, section
, syms
, pc
- vma
,
116 &filename
, &functionname
, &line
);
119 static char** translate_addresses_buf(bfd
* abfd
, bfd_vma
*addr
, int naddr
)
121 int naddr_orig
= naddr
;
124 enum { Count
, Print
} state
;
127 char **ret_buf
= NULL
;
128 /* iterate over the formating twice.
129 * the first time we count how much space we need
130 * the second time we do the actual printing */
131 for (state
=Count
; state
<=Print
; state
++) {
132 if (state
== Print
) {
133 ret_buf
= malloc(total
+ sizeof(char*)*naddr
);
134 buf
= (char*)(ret_buf
+ naddr
);
139 ret_buf
[naddr
-1] = buf
;
143 bfd_map_over_sections(abfd
, find_address_in_section
,
147 total
+= snprintf(buf
, len
, "[0x%llx] \?\?() \?\?:0",(long long unsigned int) addr
[naddr
-1]) + 1;
152 if (name
== NULL
|| *name
== '\0')
154 if (filename
!= NULL
) {
157 h
= strrchr(filename
, '/');
161 total
+= snprintf(buf
, len
, "%s/%u: %s()", filename
? filename
: "??",
165 if (state
== Print
) {
166 /* set buf just past the end of string */
167 buf
= buf
+ total
+ 1;
175 /* Process a file. */
177 static char **process_file(const char *file_name
, bfd_vma
*addr
, int naddr
)
183 abfd
= bfd_openr(file_name
, NULL
);
186 bfd_fatal(file_name
);
188 if (bfd_check_format(abfd
, bfd_archive
))
189 fatal("%s: can not get addresses from archive", file_name
);
191 if (!bfd_check_format_matches(abfd
, bfd_object
, &matching
)) {
192 bfd_nonfatal(bfd_get_filename(abfd
));
193 if (bfd_get_error() ==
194 bfd_error_file_ambiguously_recognized
) {
195 list_matching_formats(matching
);
203 ret_buf
= translate_addresses_buf(abfd
, addr
, naddr
);
223 static int find_matching_file(struct dl_phdr_info
*info
,
224 size_t size
, void *data
)
226 struct file_match
*match
= data
;
227 /* This code is modeled from Gfind_proc_info-lsb.c:callback() from libunwind */
229 const ElfW(Phdr
) *phdr
;
230 ElfW(Addr
) load_base
= info
->dlpi_addr
;
231 phdr
= info
->dlpi_phdr
;
232 for (n
= info
->dlpi_phnum
; --n
>= 0; phdr
++) {
233 if (phdr
->p_type
== PT_LOAD
) {
234 ElfW(Addr
) vaddr
= phdr
->p_vaddr
+ load_base
;
235 if ((size_t)match
->address
>= vaddr
&& (size_t)match
->address
< vaddr
+ phdr
->p_memsz
) {
236 /* we found a match */
237 match
->file
= info
->dlpi_name
;
238 match
->base
= (void*)info
->dlpi_addr
;
245 char **backtrace_symbols(void *const *buffer
, int size
)
247 int stack_depth
= size
- 1;
249 /* discard calling function */
256 locations
= malloc(sizeof(char**) * (stack_depth
+1));
259 for(x
=stack_depth
, y
=0; x
>=0; x
--, y
++){
260 struct file_match match
= { .address
= buffer
[x
] };
263 dl_iterate_phdr(find_matching_file
, &match
);
264 addr
= buffer
[x
] - match
.base
;
265 if (match
.file
&& strlen(match
.file
))
266 ret_buf
= process_file(match
.file
, &addr
, 1);
268 ret_buf
= process_file("/proc/self/exe", &addr
, 1);
269 locations
[x
] = ret_buf
;
270 total
+= strlen(ret_buf
[0]) + 1;
273 /* allocate the array of char* we are going to return and extra space for
274 * all of the strings */
275 final
= malloc(total
+ (stack_depth
+ 1) * sizeof(char*));
276 /* get a pointer to the extra space */
277 f_strings
= (char*)(final
+ stack_depth
+ 1);
279 /* fill in all of strings and pointers */
280 for(x
=stack_depth
; x
>=0; x
--){
281 strcpy(f_strings
, locations
[x
][0]);
283 final
[x
] = f_strings
;
284 f_strings
+= strlen(f_strings
) + 1;