• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2016 GitHub, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include <sys/mman.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <unistd.h>
22 #include <string.h>
23 #include <libgen.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <limits.h>
27 
28 #include <gelf.h>
29 #include "bcc_elf.h"
30 #include "bcc_proc.h"
31 #include "bcc_syms.h"
32 
33 #define NT_STAPSDT 3
34 #define ELF_ST_TYPE(x) (((uint32_t) x) & 0xf)
35 
openelf_fd(int fd,Elf ** elf_out)36 static int openelf_fd(int fd, Elf **elf_out) {
37   if (elf_version(EV_CURRENT) == EV_NONE)
38     return -1;
39 
40   *elf_out = elf_begin(fd, ELF_C_READ, 0);
41   if (*elf_out == NULL)
42     return -1;
43 
44   return 0;
45 }
46 
openelf(const char * path,Elf ** elf_out,int * fd_out)47 static int openelf(const char *path, Elf **elf_out, int *fd_out) {
48   *fd_out = open(path, O_RDONLY);
49   if (*fd_out < 0)
50     return -1;
51 
52   if (openelf_fd(*fd_out, elf_out) == -1) {
53     close(*fd_out);
54     return -1;
55   }
56 
57   return 0;
58 }
59 
parse_stapsdt_note(struct bcc_elf_usdt * probe,const char * desc,int elf_class)60 static const char *parse_stapsdt_note(struct bcc_elf_usdt *probe,
61                                       const char *desc, int elf_class) {
62   if (elf_class == ELFCLASS32) {
63     probe->pc = *((uint32_t *)(desc));
64     probe->base_addr = *((uint32_t *)(desc + 4));
65     probe->semaphore = *((uint32_t *)(desc + 8));
66     desc = desc + 12;
67   } else {
68     probe->pc = *((uint64_t *)(desc));
69     probe->base_addr = *((uint64_t *)(desc + 8));
70     probe->semaphore = *((uint64_t *)(desc + 16));
71     desc = desc + 24;
72   }
73 
74   probe->provider = desc;
75   desc += strlen(desc) + 1;
76 
77   probe->name = desc;
78   desc += strlen(desc) + 1;
79 
80   probe->arg_fmt = desc;
81   desc += strlen(desc) + 1;
82 
83   return desc;
84 }
85 
do_note_segment(Elf_Scn * section,int elf_class,bcc_elf_probecb callback,const char * binpath,uint64_t first_inst_offset,void * payload)86 static int do_note_segment(Elf_Scn *section, int elf_class,
87                            bcc_elf_probecb callback, const char *binpath,
88                            uint64_t first_inst_offset, void *payload) {
89   Elf_Data *data = NULL;
90 
91   while ((data = elf_getdata(section, data)) != 0) {
92     size_t offset = 0;
93     GElf_Nhdr hdr;
94     size_t name_off, desc_off;
95 
96     while ((offset = gelf_getnote(data, offset, &hdr, &name_off, &desc_off)) !=
97            0) {
98       const char *desc, *desc_end;
99       struct bcc_elf_usdt probe;
100 
101       if (hdr.n_type != NT_STAPSDT)
102         continue;
103 
104       if (hdr.n_namesz != 8)
105         continue;
106 
107       if (memcmp((const char *)data->d_buf + name_off, "stapsdt", 8) != 0)
108         continue;
109 
110       desc = (const char *)data->d_buf + desc_off;
111       desc_end = desc + hdr.n_descsz;
112 
113       if (parse_stapsdt_note(&probe, desc, elf_class) == desc_end) {
114         if (probe.pc < first_inst_offset)
115           fprintf(stderr,
116                   "WARNING: invalid address 0x%lx for probe (%s,%s) in binary %s\n",
117                   probe.pc, probe.provider, probe.name, binpath);
118         else
119           callback(binpath, &probe, payload);
120       }
121     }
122   }
123   return 0;
124 }
125 
listprobes(Elf * e,bcc_elf_probecb callback,const char * binpath,void * payload)126 static int listprobes(Elf *e, bcc_elf_probecb callback, const char *binpath,
127                       void *payload) {
128   Elf_Scn *section = NULL;
129   size_t stridx;
130   int elf_class = gelf_getclass(e);
131   uint64_t first_inst_offset = 0;
132 
133   if (elf_getshdrstrndx(e, &stridx) != 0)
134     return -1;
135 
136   // Get the offset to the first instruction
137   while ((section = elf_nextscn(e, section)) != 0) {
138     GElf_Shdr header;
139 
140     if (!gelf_getshdr(section, &header))
141       continue;
142 
143     // The elf file section layout is based on increasing virtual address,
144     // getting the first section with SHF_EXECINSTR is enough.
145     if (header.sh_flags & SHF_EXECINSTR) {
146       first_inst_offset = header.sh_addr;
147       break;
148     }
149   }
150 
151   while ((section = elf_nextscn(e, section)) != 0) {
152     GElf_Shdr header;
153     char *name;
154 
155     if (!gelf_getshdr(section, &header))
156       continue;
157 
158     if (header.sh_type != SHT_NOTE)
159       continue;
160 
161     name = elf_strptr(e, stridx, header.sh_name);
162     if (name && !strcmp(name, ".note.stapsdt")) {
163       if (do_note_segment(section, elf_class, callback, binpath,
164                           first_inst_offset, payload) < 0)
165         return -1;
166     }
167   }
168 
169   return 0;
170 }
171 
bcc_elf_foreach_usdt(const char * path,bcc_elf_probecb callback,void * payload)172 int bcc_elf_foreach_usdt(const char *path, bcc_elf_probecb callback,
173                          void *payload) {
174   Elf *e;
175   int fd, res;
176 
177   if (openelf(path, &e, &fd) < 0)
178     return -1;
179 
180   res = listprobes(e, callback, path, payload);
181   elf_end(e);
182   close(fd);
183 
184   return res;
185 }
186 
list_in_scn(Elf * e,Elf_Scn * section,size_t stridx,size_t symsize,struct bcc_symbol_option * option,bcc_elf_symcb callback,void * payload)187 static int list_in_scn(Elf *e, Elf_Scn *section, size_t stridx, size_t symsize,
188                        struct bcc_symbol_option *option,
189                        bcc_elf_symcb callback, void *payload) {
190   Elf_Data *data = NULL;
191 
192   while ((data = elf_getdata(section, data)) != 0) {
193     size_t i, symcount = data->d_size / symsize;
194 
195     if (data->d_size % symsize)
196       return -1;
197 
198     for (i = 0; i < symcount; ++i) {
199       GElf_Sym sym;
200       const char *name;
201 
202       if (!gelf_getsym(data, (int)i, &sym))
203         continue;
204 
205       if ((name = elf_strptr(e, stridx, sym.st_name)) == NULL)
206         continue;
207       if (name[0] == 0)
208         continue;
209 
210       if (sym.st_value == 0)
211         continue;
212 
213       uint32_t st_type = ELF_ST_TYPE(sym.st_info);
214       if (!(option->use_symbol_type & (1 << st_type)))
215         continue;
216 
217       if (callback(name, sym.st_value, sym.st_size, payload) < 0)
218         return 1;      // signal termination to caller
219     }
220   }
221 
222   return 0;
223 }
224 
listsymbols(Elf * e,bcc_elf_symcb callback,void * payload,struct bcc_symbol_option * option)225 static int listsymbols(Elf *e, bcc_elf_symcb callback, void *payload,
226                        struct bcc_symbol_option *option) {
227   Elf_Scn *section = NULL;
228 
229   while ((section = elf_nextscn(e, section)) != 0) {
230     GElf_Shdr header;
231 
232     if (!gelf_getshdr(section, &header))
233       continue;
234 
235     if (header.sh_type != SHT_SYMTAB && header.sh_type != SHT_DYNSYM)
236       continue;
237 
238     int rc = list_in_scn(e, section, header.sh_link, header.sh_entsize,
239                          option, callback, payload);
240     if (rc == 1)
241       break;    // callback signaled termination
242 
243     if (rc < 0)
244       return rc;
245   }
246 
247   return 0;
248 }
249 
get_section_elf_data(Elf * e,const char * section_name)250 static Elf_Data * get_section_elf_data(Elf *e, const char *section_name) {
251   Elf_Scn *section = NULL;
252   GElf_Shdr header;
253   char *name;
254 
255   size_t stridx;
256   if (elf_getshdrstrndx(e, &stridx) != 0)
257     return NULL;
258 
259   while ((section = elf_nextscn(e, section)) != 0) {
260     if (!gelf_getshdr(section, &header))
261       continue;
262 
263     name = elf_strptr(e, stridx, header.sh_name);
264     if (name && !strcmp(name, section_name)) {
265       return elf_getdata(section, NULL);
266     }
267   }
268 
269   return NULL;
270 }
271 
find_debuglink(Elf * e,char ** debug_file,unsigned int * crc)272 static int find_debuglink(Elf *e, char **debug_file, unsigned int *crc) {
273   Elf_Data *data = NULL;
274 
275   *debug_file = NULL;
276   *crc = 0;
277 
278   data = get_section_elf_data(e, ".gnu_debuglink");
279   if (!data || data->d_size <= 5)
280     return 0;
281 
282   *debug_file = (char *)data->d_buf;
283   *crc = *(unsigned int*)((char *)data->d_buf + data->d_size - 4);
284 
285   return *debug_file ? 1 : 0;
286 }
287 
find_buildid(Elf * e,char * buildid)288 static int find_buildid(Elf *e, char *buildid) {
289   Elf_Data *data = get_section_elf_data(e, ".note.gnu.build-id");
290   if (!data || data->d_size <= 16 || strcmp((char *)data->d_buf + 12, "GNU"))
291     return 0;
292 
293   char *buf = (char *)data->d_buf + 16;
294   size_t length = data->d_size - 16;
295   size_t i = 0;
296   for (i = 0; i < length; ++i) {
297     sprintf(buildid + (i * 2), "%02hhx", buf[i]);
298   }
299 
300   return 1;
301 }
302 
303 // The CRC algorithm used by GNU debuglink. Taken from:
304 //    https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
gnu_debuglink_crc32(unsigned int crc,char * buf,size_t len)305 static unsigned int gnu_debuglink_crc32(unsigned int crc,
306                                         char *buf, size_t len) {
307   static const unsigned int crc32_table[256] =
308   {
309     0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
310     0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4,
311     0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07,
312     0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
313     0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856,
314     0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
315     0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
316     0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
317     0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
318     0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a,
319     0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599,
320     0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
321     0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190,
322     0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
323     0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e,
324     0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
325     0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed,
326     0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
327     0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3,
328     0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
329     0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
330     0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5,
331     0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010,
332     0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
333     0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17,
334     0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6,
335     0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
336     0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
337     0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344,
338     0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
339     0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a,
340     0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
341     0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1,
342     0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c,
343     0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
344     0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
345     0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe,
346     0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31,
347     0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c,
348     0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
349     0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b,
350     0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
351     0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1,
352     0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
353     0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
354     0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7,
355     0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66,
356     0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
357     0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
358     0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8,
359     0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b,
360     0x2d02ef8d
361   };
362   char *end;
363 
364   crc = ~crc & 0xffffffff;
365   for (end = buf + len; buf < end; ++buf)
366     crc = crc32_table[(crc ^ *buf) & 0xff] ^ (crc >> 8);
367   return ~crc & 0xffffffff;
368 }
369 
verify_checksum(const char * file,unsigned int crc)370 static int verify_checksum(const char *file, unsigned int crc) {
371   struct stat st;
372   int fd;
373   void *buf;
374   unsigned int actual;
375 
376   fd = open(file, O_RDONLY);
377   if (fd < 0)
378     return 0;
379 
380   if (fstat(fd, &st) < 0) {
381     close(fd);
382     return 0;
383   }
384 
385   buf = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
386   if (!buf) {
387     close(fd);
388     return 0;
389   }
390 
391   actual = gnu_debuglink_crc32(0, buf, st.st_size);
392 
393   munmap(buf, st.st_size);
394   close(fd);
395   return actual == crc;
396 }
397 
find_debug_via_debuglink(Elf * e,const char * binpath,int check_crc)398 static char *find_debug_via_debuglink(Elf *e, const char *binpath,
399                                       int check_crc) {
400   char fullpath[PATH_MAX];
401   char *bindir = NULL;
402   char *res = NULL;
403   unsigned int crc;
404   char *name;  // the name of the debuginfo file
405 
406   if (!find_debuglink(e, &name, &crc))
407     return NULL;
408 
409   bindir = strdup(binpath);
410   bindir = dirname(bindir);
411 
412   // Search for the file in 'binpath', but ignore the file we find if it
413   // matches the binary itself: the binary will always be probed later on,
414   // and it might contain poorer symbols (e.g. stripped or partial symbols)
415   // than the external debuginfo that might be available elsewhere.
416   snprintf(fullpath, sizeof(fullpath),"%s/%s", bindir, name);
417   if (strcmp(fullpath, binpath) != 0 && access(fullpath, F_OK) != -1) {
418     res = strdup(fullpath);
419     goto DONE;
420   }
421 
422   // Search for the file in 'binpath'/.debug
423   snprintf(fullpath, sizeof(fullpath), "%s/.debug/%s", bindir, name);
424   if (access(fullpath, F_OK) != -1) {
425     res = strdup(fullpath);
426     goto DONE;
427   }
428 
429   // Search for the file in the global debug directory /usr/lib/debug/'binpath'
430   snprintf(fullpath, sizeof(fullpath), "/usr/lib/debug%s/%s", bindir, name);
431   if (access(fullpath, F_OK) != -1) {
432     res = strdup(fullpath);
433     goto DONE;
434   }
435 
436 DONE:
437   free(bindir);
438   if (res && check_crc && !verify_checksum(res, crc))
439     return NULL;
440   return res;
441 }
442 
find_debug_via_buildid(Elf * e)443 static char *find_debug_via_buildid(Elf *e) {
444   char fullpath[PATH_MAX];
445   char buildid[128];  // currently 40 seems to be default, let's be safe
446 
447   if (!find_buildid(e, buildid))
448     return NULL;
449 
450   // Search for the file in the global debug directory with a sub-path:
451   //    mm/nnnnnn...nnnn.debug
452   // Where mm are the first two characters of the buildid, and nnnn are the
453   // rest of the build id, followed by .debug.
454   snprintf(fullpath, sizeof(fullpath), "/usr/lib/debug/.build-id/%c%c/%s.debug",
455           buildid[0], buildid[1], buildid + 2);
456   if (access(fullpath, F_OK) != -1) {
457     return strdup(fullpath);
458   }
459 
460   return NULL;
461 }
462 
foreach_sym_core(const char * path,bcc_elf_symcb callback,struct bcc_symbol_option * option,void * payload,int is_debug_file)463 static int foreach_sym_core(const char *path, bcc_elf_symcb callback,
464                             struct bcc_symbol_option *option, void *payload,
465                             int is_debug_file) {
466   Elf *e;
467   int fd, res;
468   char *debug_file;
469 
470   if (!option)
471     return -1;
472 
473   if (openelf(path, &e, &fd) < 0)
474     return -1;
475 
476   // If there is a separate debuginfo file, try to locate and read it, first
477   // using the build-id section, then using the debuglink section. These are
478   // also the rules that GDB folows.
479   // See: https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
480   if (option->use_debug_file && !is_debug_file) {
481     // The is_debug_file argument helps avoid infinitely resolving debuginfo
482     // files for debuginfo files and so on.
483     debug_file = find_debug_via_buildid(e);
484     if (!debug_file)
485       debug_file = find_debug_via_debuglink(e, path,
486                                             option->check_debug_file_crc);
487     if (debug_file) {
488       foreach_sym_core(debug_file, callback, option, payload, 1);
489       free(debug_file);
490     }
491   }
492 
493   res = listsymbols(e, callback, payload, option);
494   elf_end(e);
495   close(fd);
496   return res;
497 }
498 
bcc_elf_foreach_sym(const char * path,bcc_elf_symcb callback,void * option,void * payload)499 int bcc_elf_foreach_sym(const char *path, bcc_elf_symcb callback,
500                         void *option, void *payload) {
501   return foreach_sym_core(
502       path, callback, (struct bcc_symbol_option*)option, payload, 0);
503 }
504 
bcc_elf_get_text_scn_info(const char * path,uint64_t * addr,uint64_t * offset)505 int bcc_elf_get_text_scn_info(const char *path, uint64_t *addr,
506 				   uint64_t *offset) {
507   Elf *e = NULL;
508   int fd = -1, err;
509   Elf_Scn *section = NULL;
510   GElf_Shdr header;
511   size_t stridx;
512   char *name;
513 
514   if ((err = openelf(path, &e, &fd)) < 0 ||
515       (err = elf_getshdrstrndx(e, &stridx)) < 0)
516     goto exit;
517 
518   err = -1;
519   while ((section = elf_nextscn(e, section)) != 0) {
520     if (!gelf_getshdr(section, &header))
521       continue;
522 
523     name = elf_strptr(e, stridx, header.sh_name);
524     if (name && !strcmp(name, ".text")) {
525       *addr = (uint64_t)header.sh_addr;
526       *offset = (uint64_t)header.sh_offset;
527       err = 0;
528       break;
529     }
530   }
531 
532 exit:
533   if (e)
534     elf_end(e);
535   if (fd >= 0)
536     close(fd);
537   return err;
538 }
539 
bcc_elf_foreach_load_section(const char * path,bcc_elf_load_sectioncb callback,void * payload)540 int bcc_elf_foreach_load_section(const char *path,
541                                  bcc_elf_load_sectioncb callback,
542                                  void *payload) {
543   Elf *e = NULL;
544   int fd = -1, err = -1, res;
545   size_t nhdrs, i;
546 
547   if (openelf(path, &e, &fd) < 0)
548     goto exit;
549 
550   if (elf_getphdrnum(e, &nhdrs) != 0)
551     goto exit;
552 
553   GElf_Phdr header;
554   for (i = 0; i < nhdrs; i++) {
555     if (!gelf_getphdr(e, (int)i, &header))
556       continue;
557     if (header.p_type != PT_LOAD || !(header.p_flags & PF_X))
558       continue;
559     res = callback(header.p_vaddr, header.p_memsz, header.p_offset, payload);
560     if (res < 0) {
561       err = 1;
562       goto exit;
563     }
564   }
565   err = 0;
566 
567 exit:
568   if (e)
569     elf_end(e);
570   if (fd >= 0)
571     close(fd);
572   return err;
573 }
574 
bcc_elf_get_type(const char * path)575 int bcc_elf_get_type(const char *path) {
576   Elf *e;
577   GElf_Ehdr hdr;
578   int fd;
579   void* res = NULL;
580 
581   if (openelf(path, &e, &fd) < 0)
582     return -1;
583 
584   res = (void*)gelf_getehdr(e, &hdr);
585   elf_end(e);
586   close(fd);
587 
588   if (!res)
589     return -1;
590   else
591     return hdr.e_type;
592 }
593 
bcc_elf_is_exe(const char * path)594 int bcc_elf_is_exe(const char *path) {
595   return (bcc_elf_get_type(path) != -1) && (access(path, X_OK) == 0);
596 }
597 
bcc_elf_is_shared_obj(const char * path)598 int bcc_elf_is_shared_obj(const char *path) {
599   return bcc_elf_get_type(path) == ET_DYN;
600 }
601 
bcc_elf_is_vdso(const char * name)602 int bcc_elf_is_vdso(const char *name) {
603   return strcmp(name, "[vdso]") == 0;
604 }
605 
606 // -2: Failed
607 // -1: Not initialized
608 // >0: Initialized
609 static int vdso_image_fd = -1;
610 
find_vdso(const char * name,uint64_t st,uint64_t en,uint64_t offset,bool enter_ns,void * payload)611 static int find_vdso(const char *name, uint64_t st, uint64_t en,
612                      uint64_t offset, bool enter_ns, void *payload) {
613   int fd;
614   char tmpfile[128];
615   if (!bcc_elf_is_vdso(name))
616     return 0;
617 
618   void *image = malloc(en - st);
619   if (!image)
620     goto on_error;
621   memcpy(image, (void *)st, en - st);
622 
623   snprintf(tmpfile, sizeof(tmpfile), "/tmp/bcc_%d_vdso_image_XXXXXX", getpid());
624   fd = mkostemp(tmpfile, O_CLOEXEC);
625   if (fd < 0) {
626     fprintf(stderr, "Unable to create temp file: %s\n", strerror(errno));
627     goto on_error;
628   }
629   // Unlink the file to avoid leaking
630   if (unlink(tmpfile) == -1)
631     fprintf(stderr, "Unlink %s failed: %s\n", tmpfile, strerror(errno));
632 
633   if (write(fd, image, en - st) == -1) {
634     fprintf(stderr, "Failed to write to vDSO image: %s\n", strerror(errno));
635     close(fd);
636     goto on_error;
637   }
638   vdso_image_fd = fd;
639 
640 on_error:
641   if (image)
642     free(image);
643   // Always stop the iteration
644   return -1;
645 }
646 
bcc_elf_foreach_vdso_sym(bcc_elf_symcb callback,void * payload)647 int bcc_elf_foreach_vdso_sym(bcc_elf_symcb callback, void *payload) {
648   Elf *elf;
649   static struct bcc_symbol_option default_option = {
650     .use_debug_file = 0,
651     .check_debug_file_crc = 0,
652     .use_symbol_type = (1 << STT_FUNC) | (1 << STT_GNU_IFUNC)
653   };
654 
655   if (vdso_image_fd == -1) {
656     vdso_image_fd = -2;
657     bcc_procutils_each_module(getpid(), &find_vdso, NULL);
658   }
659   if (vdso_image_fd == -2)
660     return -1;
661 
662   if (openelf_fd(vdso_image_fd, &elf) == -1)
663     return -1;
664 
665   return listsymbols(elf, callback, payload, &default_option);
666 }
667 
668 #if 0
669 #include <stdio.h>
670 
671 int main(int argc, char *argv[])
672 {
673   uint64_t addr;
674   if (bcc_elf_findsym(argv[1], argv[2], -1, STT_FUNC, &addr) < 0)
675     return -1;
676 
677   printf("%s: %p\n", argv[2], (void *)addr);
678   return 0;
679 }
680 #endif
681