|
1 // vim:ts=8:sw=2:et: |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "leaky.h" |
|
7 |
|
8 #ifdef USE_BFD |
|
9 #include <stdio.h> |
|
10 #include <string.h> |
|
11 #include <fcntl.h> |
|
12 #include <unistd.h> |
|
13 #include <libgen.h> |
|
14 #include <bfd.h> |
|
15 #include <cxxabi.h> |
|
16 |
|
17 static bfd *try_debug_file(const char *filename, unsigned long crc32) |
|
18 { |
|
19 int fd = open(filename, O_RDONLY); |
|
20 if (fd < 0) |
|
21 return nullptr; |
|
22 |
|
23 unsigned char buf[4*1024]; |
|
24 unsigned long crc = 0; |
|
25 |
|
26 while (1) { |
|
27 ssize_t count = read(fd, buf, sizeof(buf)); |
|
28 if (count <= 0) |
|
29 break; |
|
30 |
|
31 crc = bfd_calc_gnu_debuglink_crc32(crc, buf, count); |
|
32 } |
|
33 |
|
34 close(fd); |
|
35 |
|
36 if (crc != crc32) |
|
37 return nullptr; |
|
38 |
|
39 bfd *object = bfd_openr(filename, nullptr); |
|
40 if (!bfd_check_format(object, bfd_object)) { |
|
41 bfd_close(object); |
|
42 return nullptr; |
|
43 } |
|
44 |
|
45 return object; |
|
46 } |
|
47 |
|
48 static bfd *find_debug_file(bfd *lib, const char *aFileName) |
|
49 { |
|
50 // check for a separate debug file with symbols |
|
51 asection *sect = bfd_get_section_by_name(lib, ".gnu_debuglink"); |
|
52 |
|
53 if (!sect) |
|
54 return nullptr; |
|
55 |
|
56 bfd_size_type debuglinkSize = bfd_section_size (objfile->obfd, sect); |
|
57 |
|
58 char *debuglink = new char[debuglinkSize]; |
|
59 bfd_get_section_contents(lib, sect, debuglink, 0, debuglinkSize); |
|
60 |
|
61 // crc checksum is aligned to 4 bytes, and after the NUL. |
|
62 int crc_offset = (int(strlen(debuglink)) & ~3) + 4; |
|
63 unsigned long crc32 = bfd_get_32(lib, debuglink + crc_offset); |
|
64 |
|
65 // directory component |
|
66 char *dirbuf = strdup(aFileName); |
|
67 const char *dir = dirname(dirbuf); |
|
68 |
|
69 static const char debug_subdir[] = ".debug"; |
|
70 // This is gdb's default global debugging info directory, but gdb can |
|
71 // be instructed to use a different directory. |
|
72 static const char global_debug_dir[] = "/usr/lib/debug"; |
|
73 |
|
74 char *filename = |
|
75 new char[strlen(global_debug_dir) + strlen(dir) + crc_offset + 3]; |
|
76 |
|
77 // /path/debuglink |
|
78 sprintf(filename, "%s/%s", dir, debuglink); |
|
79 bfd *debugFile = try_debug_file(filename, crc32); |
|
80 if (!debugFile) { |
|
81 |
|
82 // /path/.debug/debuglink |
|
83 sprintf(filename, "%s/%s/%s", dir, debug_subdir, debuglink); |
|
84 debugFile = try_debug_file(filename, crc32); |
|
85 if (!debugFile) { |
|
86 |
|
87 // /usr/lib/debug/path/debuglink |
|
88 sprintf(filename, "%s/%s/%s", global_debug_dir, dir, debuglink); |
|
89 debugFile = try_debug_file(filename, crc32); |
|
90 } |
|
91 } |
|
92 |
|
93 delete[] filename; |
|
94 free(dirbuf); |
|
95 delete[] debuglink; |
|
96 |
|
97 return debugFile; |
|
98 } |
|
99 |
|
100 |
|
101 // Use an indirect array to avoid copying tons of objects |
|
102 Symbol ** leaky::ExtendSymbols(int num) |
|
103 { |
|
104 long n = numExternalSymbols + num; |
|
105 |
|
106 externalSymbols = (Symbol**) |
|
107 realloc(externalSymbols, |
|
108 (size_t) (sizeof(externalSymbols[0]) * n)); |
|
109 Symbol *new_array = new Symbol[n]; |
|
110 for (int i = 0; i < num; i++) { |
|
111 externalSymbols[i + numExternalSymbols] = &new_array[i]; |
|
112 } |
|
113 lastSymbol = externalSymbols + n; |
|
114 Symbol **sp = externalSymbols + numExternalSymbols; |
|
115 numExternalSymbols = n; |
|
116 return sp; |
|
117 } |
|
118 |
|
119 #define NEXT_SYMBOL do { sp++; \ |
|
120 if (sp >= lastSymbol) { \ |
|
121 sp = ExtendSymbols(16384); \ |
|
122 } \ |
|
123 } while (0) |
|
124 |
|
125 void leaky::ReadSymbols(const char *aFileName, u_long aBaseAddress) |
|
126 { |
|
127 int initialSymbols = usefulSymbols; |
|
128 if (nullptr == externalSymbols) { |
|
129 externalSymbols = (Symbol**) calloc(sizeof(Symbol*),10000); |
|
130 Symbol *new_array = new Symbol[10000]; |
|
131 for (int i = 0; i < 10000; i++) { |
|
132 externalSymbols[i] = &new_array[i]; |
|
133 } |
|
134 numExternalSymbols = 10000; |
|
135 } |
|
136 Symbol** sp = externalSymbols + usefulSymbols; |
|
137 lastSymbol = externalSymbols + numExternalSymbols; |
|
138 |
|
139 // Create a dummy symbol for the library so, if it doesn't have any |
|
140 // symbols, we show it by library. |
|
141 (*sp)->Init(aFileName, aBaseAddress); |
|
142 NEXT_SYMBOL; |
|
143 |
|
144 bfd_boolean kDynamic = (bfd_boolean) false; |
|
145 |
|
146 static int firstTime = 1; |
|
147 if (firstTime) { |
|
148 firstTime = 0; |
|
149 bfd_init (); |
|
150 } |
|
151 |
|
152 bfd* lib = bfd_openr(aFileName, nullptr); |
|
153 if (nullptr == lib) { |
|
154 return; |
|
155 } |
|
156 if (!bfd_check_format(lib, bfd_object)) { |
|
157 bfd_close(lib); |
|
158 return; |
|
159 } |
|
160 |
|
161 bfd *symbolFile = find_debug_file(lib, aFileName); |
|
162 |
|
163 // read mini symbols |
|
164 PTR minisyms; |
|
165 unsigned int size; |
|
166 long symcount = 0; |
|
167 |
|
168 if (symbolFile) { |
|
169 symcount = bfd_read_minisymbols(symbolFile, kDynamic, &minisyms, &size); |
|
170 if (symcount == 0) { |
|
171 bfd_close(symbolFile); |
|
172 } else { |
|
173 bfd_close(lib); |
|
174 } |
|
175 } |
|
176 if (symcount == 0) { |
|
177 symcount = bfd_read_minisymbols(lib, kDynamic, &minisyms, &size); |
|
178 if (symcount == 0) { |
|
179 // symtab is empty; try dynamic symbols |
|
180 kDynamic = (bfd_boolean) true; |
|
181 symcount = bfd_read_minisymbols(lib, kDynamic, &minisyms, &size); |
|
182 } |
|
183 symbolFile = lib; |
|
184 } |
|
185 |
|
186 asymbol* store; |
|
187 store = bfd_make_empty_symbol(symbolFile); |
|
188 |
|
189 // Scan symbols |
|
190 size_t demangle_buffer_size = 128; |
|
191 char *demangle_buffer = (char*) malloc(demangle_buffer_size); |
|
192 bfd_byte* from = (bfd_byte *) minisyms; |
|
193 bfd_byte* fromend = from + symcount * size; |
|
194 for (; from < fromend; from += size) { |
|
195 asymbol *sym; |
|
196 sym = bfd_minisymbol_to_symbol(symbolFile, kDynamic, (const PTR) from, store); |
|
197 |
|
198 symbol_info syminfo; |
|
199 bfd_get_symbol_info (symbolFile, sym, &syminfo); |
|
200 |
|
201 // if ((syminfo.type == 'T') || (syminfo.type == 't')) { |
|
202 const char* nm = bfd_asymbol_name(sym); |
|
203 if (nm && nm[0]) { |
|
204 char* dnm = nullptr; |
|
205 if (strncmp("__thunk", nm, 7)) { |
|
206 dnm = |
|
207 abi::__cxa_demangle(nm, demangle_buffer, &demangle_buffer_size, 0); |
|
208 if (dnm) { |
|
209 demangle_buffer = dnm; |
|
210 } |
|
211 } |
|
212 (*sp)->Init(dnm ? dnm : nm, syminfo.value + aBaseAddress); |
|
213 NEXT_SYMBOL; |
|
214 } |
|
215 // } |
|
216 } |
|
217 |
|
218 free(demangle_buffer); |
|
219 demangle_buffer = nullptr; |
|
220 |
|
221 bfd_close(symbolFile); |
|
222 |
|
223 int interesting = sp - externalSymbols; |
|
224 if (!quiet) { |
|
225 printf("%s provided %d symbols\n", aFileName, |
|
226 interesting - initialSymbols); |
|
227 } |
|
228 usefulSymbols = interesting; |
|
229 } |
|
230 |
|
231 #endif /* USE_BFD */ |