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