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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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>
58 void (*dbfd_init
)(void);
59 bfd_vma (*dbfd_scan_vma
)(const char *string
, const char **end
, int base
);
60 bfd
* (*dbfd_openr
)(const char *filename
, const char *target
);
61 bfd_boolean (*dbfd_check_format
)(bfd
*abfd
, bfd_format format
);
62 bfd_boolean (*dbfd_check_format_matches
)(bfd
*abfd
, bfd_format format
, char ***matching
);
63 bfd_boolean (*dbfd_close
)(bfd
*abfd
);
64 bfd_boolean (*dbfd_map_over_sections
)(bfd
*abfd
, void (*func
)(bfd
*abfd
, asection
*sect
, void *obj
),
66 #define bfd_init dbfd_init
68 static void load_funcs(void)
70 void * handle
= dlopen("libbfd.so", RTLD_NOW
);
71 dbfd_init
= dlsym(handle
, "bfd_init");
72 dbfd_scan_vma
= dlsym(handle
, "bfd_scan_vma");
73 dbfd_openr
= dlsym(handle
, "bfd_openr");
74 dbfd_check_format
= dlsym(handle
, "bfd_check_format");
75 dbfd_check_format_matches
= dlsym(handle
, "bfd_check_format_matches");
76 dbfd_close
= dlsym(handle
, "bfd_close");
77 dbfd_map_over_sections
= dlsym(handle
, "bfd_map_over_sections");
83 static asymbol
**syms
; /* Symbol table. */
85 /* 150 isn't special; it's just an arbitrary non-ASCII char value. */
86 #define OPTION_DEMANGLER (150)
88 static void slurp_symtab(bfd
* abfd
);
89 static void find_address_in_section(bfd
*abfd
, asection
*section
, void *data
);
91 /* Read in the symbol table. */
93 static void slurp_symtab(bfd
* abfd
)
98 if ((bfd_get_file_flags(abfd
) & HAS_SYMS
) == 0)
101 symcount
= bfd_read_minisymbols(abfd
, false, (PTR
) & syms
, &size
);
103 symcount
= bfd_read_minisymbols(abfd
, true /* dynamic */ ,
104 (PTR
) & syms
, &size
);
107 bfd_fatal(bfd_get_filename(abfd
));
110 /* These global variables are used to pass information between
111 translate_addresses and find_address_in_section. */
114 static const char *filename
;
115 static const char *functionname
;
116 static unsigned int line
;
119 /* Look for an address in a section. This is called via
120 bfd_map_over_sections. */
122 static void find_address_in_section(bfd
*abfd
, asection
*section
, void *data
__attribute__ ((__unused__
)) )
130 if ((bfd_get_section_flags(abfd
, section
) & SEC_ALLOC
) == 0)
133 vma
= bfd_get_section_vma(abfd
, section
);
137 size
= bfd_section_size(abfd
, section
);
138 if (pc
>= vma
+ size
)
141 found
= bfd_find_nearest_line(abfd
, section
, syms
, pc
- vma
,
142 &filename
, &functionname
, &line
);
145 /* Read hexadecimal addresses from stdin, translate into
146 file_name:line_number and optionally function name. */
148 static void translate_addresses(bfd
* abfd
, char (*addr
)[PTRSTR_LEN
], int naddr
)
151 pc
= bfd_scan_vma(addr
[naddr
-1], NULL
, 16);
154 bfd_map_over_sections(abfd
, find_address_in_section
,
158 printf("[%s] \?\?() \?\?:0\n",addr
[naddr
-1]);
163 if (name
== NULL
|| *name
== '\0')
165 if (filename
!= NULL
) {
168 h
= strrchr(filename
, '/');
173 printf("\t%s:%u\t", filename
? filename
: "??",
176 printf("%s()\n", name
);
180 /* fflush() is essential for using this command as a server
181 child process that reads addresses from a pipe and responds
182 with line number information, processing one address at a
190 static char** translate_addresses_buf(bfd
* abfd
, bfd_vma
*addr
, int naddr
)
192 int naddr_orig
= naddr
;
195 enum { Count
, Print
} state
;
198 char **ret_buf
= NULL
;
199 /* iterate over the formating twice.
200 * the first time we count how much space we need
201 * the second time we do the actual printing */
202 for (state
=Count
; state
<=Print
; state
++) {
203 if (state
== Print
) {
204 ret_buf
= malloc(total
+ sizeof(char*)*naddr
);
205 buf
= (char*)(ret_buf
+ naddr
);
210 ret_buf
[naddr
-1] = buf
;
214 bfd_map_over_sections(abfd
, find_address_in_section
,
218 total
+= snprintf(buf
, len
, "[0x%llx] \?\?() \?\?:0",(long long unsigned int) addr
[naddr
-1]) + 1;
223 if (name
== NULL
|| *name
== '\0')
225 if (filename
!= NULL
) {
228 h
= strrchr(filename
, '/');
232 total
+= snprintf(buf
, len
, "%s:%u\t%s()", filename
? filename
: "??",
236 if (state
== Print
) {
237 /* set buf just past the end of string */
238 buf
= buf
+ total
+ 1;
246 /* Process a file. */
248 static char **process_file(const char *file_name
, bfd_vma
*addr
, int naddr
)
254 abfd
= bfd_openr(file_name
, NULL
);
257 bfd_fatal(file_name
);
259 if (bfd_check_format(abfd
, bfd_archive
))
260 fatal("%s: can not get addresses from archive", file_name
);
262 if (!bfd_check_format_matches(abfd
, bfd_object
, &matching
)) {
263 bfd_nonfatal(bfd_get_filename(abfd
));
264 if (bfd_get_error() ==
265 bfd_error_file_ambiguously_recognized
) {
266 list_matching_formats(matching
);
274 ret_buf
= translate_addresses_buf(abfd
, addr
, naddr
);
294 static int find_matching_file(struct dl_phdr_info
*info
,
295 size_t size
, void *data
)
297 struct file_match
*match
= data
;
298 /* This code is modeled from Gfind_proc_info-lsb.c:callback() from libunwind */
300 const ElfW(Phdr
) *phdr
;
301 ElfW(Addr
) load_base
= info
->dlpi_addr
;
302 phdr
= info
->dlpi_phdr
;
303 for (n
= info
->dlpi_phnum
; --n
>= 0; phdr
++) {
304 if (phdr
->p_type
== PT_LOAD
) {
305 ElfW(Addr
) vaddr
= phdr
->p_vaddr
+ load_base
;
306 if (match
->address
>= vaddr
&& match
->address
< vaddr
+ phdr
->p_memsz
) {
307 /* we found a match */
308 match
->file
= info
->dlpi_name
;
309 match
->base
= info
->dlpi_addr
;
316 char **backtrace_symbols(void *const *buffer
, int size
)
318 int stack_depth
= size
- 1;
320 /* discard calling function */
327 locations
= malloc(sizeof(char**) * (stack_depth
+1));
330 for(x
=stack_depth
, y
=0; x
>=0; x
--, y
++){
331 struct file_match match
= { .address
= buffer
[x
] };
334 dl_iterate_phdr(find_matching_file
, &match
);
335 addr
= buffer
[x
] - match
.base
;
336 if (match
.file
&& strlen(match
.file
))
337 ret_buf
= process_file(match
.file
, &addr
, 1);
339 ret_buf
= process_file("/proc/self/exe", &addr
, 1);
340 locations
[x
] = ret_buf
;
341 total
+= strlen(ret_buf
[0]) + 1;
344 /* allocate the array of char* we are going to return and extra space for
345 * all of the strings */
346 final
= malloc(total
+ (stack_depth
+ 1) * sizeof(char*));
347 /* get a pointer to the extra space */
348 f_strings
= (char*)(final
+ stack_depth
+ 1);
350 /* fill in all of strings and pointers */
351 for(x
=stack_depth
; x
>=0; x
--){
352 strcpy(f_strings
, locations
[x
][0]);
354 final
[x
] = f_strings
;
355 f_strings
+= strlen(f_strings
) + 1;
364 backtrace_symbols_fd(void *const *buffer
, int size
, int fd
)
369 strings
= backtrace_symbols(buffer
, size
);
370 if (strings
== NULL
) {
371 perror("backtrace_symbols");
375 for (j
= 0; j
< size
; j
++)
376 printf("%s\n", strings
[j
]);