xref: /aosp_15_r20/external/bcc/src/cc/bcc_elf.c (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
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 #ifdef HAVE_LIBLZMA
28 #include <lzma.h>
29 #endif
30 #ifdef HAVE_LIBDEBUGINFOD
31 #include <elfutils/debuginfod.h>
32 #endif
33 
34 #include <gelf.h>
35 #include "bcc_elf.h"
36 #include "bcc_proc.h"
37 #include "bcc_syms.h"
38 #include "bcc_zip.h"
39 
40 #define NT_STAPSDT 3
41 #define ELF_ST_TYPE(x) (((uint32_t) x) & 0xf)
42 
openelf_fd(int fd,Elf ** elf_out)43 static int openelf_fd(int fd, Elf **elf_out) {
44   if (elf_version(EV_CURRENT) == EV_NONE)
45     return -1;
46 
47   *elf_out = elf_begin(fd, ELF_C_READ, 0);
48   if (*elf_out == NULL)
49     return -1;
50 
51   return 0;
52 }
53 
openelf_mem(void * buf,size_t buf_len,Elf ** elf_out)54 static int openelf_mem(void *buf, size_t buf_len, Elf **elf_out) {
55   if (elf_version(EV_CURRENT) == EV_NONE)
56     return -1;
57 
58   *elf_out = elf_memory(buf, buf_len);
59   if (*elf_out == NULL)
60     return -1;
61 
62   return 0;
63 }
64 
65 // Type of bcc_elf_file.
66 enum bcc_elf_file_type {
67   // Empty bcc_elf_file, not associated with any Elf or resources.
68   // None of the union fields should be read and elf pointer is NULL.
69   BCC_ELF_FILE_TYPE_NONE = 0,
70 
71   // bcc_elf_file owning a file descriptor and providing access to
72   // an Elf file backed by data from that file.
73   // fd field of the impl union stores the file descriptor and elf
74   // pointer is not NULL.
75   BCC_ELF_FILE_TYPE_FD,
76 
77   // bcc_elf_file owning a memory buffer and providing access to an
78   // Elf file backed by data in that buffer.
79   // buf field of the impl union stores the address of the buffer
80   // and elf pointer is not NULL.
81   BCC_ELF_FILE_TYPE_BUF,
82 
83   // bcc_elf_file owning a bcc_zip_archive and providing access to
84   // an Elf file backed by data from one of the zip entries.
85   // archive field of the impl union stores the address of the
86   // zip archive and elf pointer is not NULL.
87   BCC_ELF_FILE_TYPE_ARCHIVE
88 };
89 
90 // Provides access to an Elf structure in an uniform way,
91 // independently from its source (file, memory buffer, zip archive).
92 struct bcc_elf_file {
93   Elf *elf;
94   enum bcc_elf_file_type type;
95 
96   union {
97     int fd;
98     void *buf;
99     struct bcc_zip_archive *archive;
100   };
101 };
102 
103 // Initializes bcc_elf_file as not pointing to any elf file and not
104 // owning any resources. After returning the provided elf_file can be
105 // safely passed to bcc_elf_file_close.
bcc_elf_file_init(struct bcc_elf_file * elf_file)106 static void bcc_elf_file_init(struct bcc_elf_file *elf_file) {
107   memset(elf_file, 0, sizeof(struct bcc_elf_file));
108 }
109 
110 #ifdef HAVE_LIBLZMA
bcc_elf_file_open_buf(void * buf,size_t buf_len,struct bcc_elf_file * out)111 static int bcc_elf_file_open_buf(void *buf, size_t buf_len,
112                                  struct bcc_elf_file *out) {
113   Elf *elf = NULL;
114 
115   if (openelf_mem(buf, buf_len, &elf)) {
116     return -1;
117   }
118 
119   out->elf = elf;
120   out->type = BCC_ELF_FILE_TYPE_BUF;
121   out->buf = buf;
122   return 0;
123 }
124 #endif
125 
bcc_elf_file_open_fd(int fd,struct bcc_elf_file * out)126 static int bcc_elf_file_open_fd(int fd, struct bcc_elf_file *out) {
127   Elf *elf = NULL;
128 
129   if (openelf_fd(fd, &elf)) {
130     return -1;
131   }
132 
133   out->elf = elf;
134   out->type = BCC_ELF_FILE_TYPE_FD;
135   out->fd = fd;
136   return 0;
137 }
138 
bcc_elf_file_open(const char * path,struct bcc_elf_file * out)139 static int bcc_elf_file_open(const char *path, struct bcc_elf_file *out) {
140   struct bcc_zip_archive *archive = NULL;
141   struct bcc_zip_entry entry;
142   int fd = -1;
143 
144   fd = open(path, O_RDONLY);
145   if (fd >= 0) {
146     if (bcc_elf_file_open_fd(fd, out)) {
147       close(fd);
148       return -1;
149     }
150 
151     return 0;
152   }
153 
154   archive = bcc_zip_archive_open_and_find(path, &entry);
155   if (archive) {
156     if (entry.compression ||
157         openelf_mem((void *)entry.data, entry.data_length, &out->elf)) {
158       bcc_zip_archive_close(archive);
159       return -1;
160     }
161 
162     out->type = BCC_ELF_FILE_TYPE_ARCHIVE;
163     out->archive = archive;
164     return 0;
165   }
166 
167   return -1;
168 }
169 
bcc_elf_file_close(struct bcc_elf_file * elf_file)170 static void bcc_elf_file_close(struct bcc_elf_file *elf_file) {
171   if (elf_file->elf) {
172     elf_end(elf_file->elf);
173   }
174 
175   switch (elf_file->type) {
176   case BCC_ELF_FILE_TYPE_FD:
177     close(elf_file->fd);
178     break;
179 
180   case BCC_ELF_FILE_TYPE_BUF:
181     free(elf_file->buf);
182     break;
183 
184   case BCC_ELF_FILE_TYPE_ARCHIVE:
185     bcc_zip_archive_close(elf_file->archive);
186     break;
187 
188   default:
189     break;
190   }
191 
192   bcc_elf_file_init(elf_file);
193 }
194 
parse_stapsdt_note(struct bcc_elf_usdt * probe,GElf_Shdr * probes_shdr,const char * desc,int elf_class)195 static const char *parse_stapsdt_note(struct bcc_elf_usdt *probe,
196                                       GElf_Shdr *probes_shdr,
197                                       const char *desc, int elf_class) {
198   if (elf_class == ELFCLASS32) {
199     probe->pc = *((uint32_t *)(desc));
200     probe->base_addr = *((uint32_t *)(desc + 4));
201     probe->semaphore = *((uint32_t *)(desc + 8));
202     desc = desc + 12;
203   } else {
204     probe->pc = *((uint64_t *)(desc));
205     probe->base_addr = *((uint64_t *)(desc + 8));
206     probe->semaphore = *((uint64_t *)(desc + 16));
207     desc = desc + 24;
208   }
209 
210   // Offset from start of file
211   if (probe->semaphore && probes_shdr)
212     probe->semaphore_offset =
213       probe->semaphore - probes_shdr->sh_addr + probes_shdr->sh_offset;
214   else
215     probe->semaphore_offset = 0;
216 
217   probe->provider = desc;
218   desc += strlen(desc) + 1;
219 
220   probe->name = desc;
221   desc += strlen(desc) + 1;
222 
223   probe->arg_fmt = desc;
224   desc += strlen(desc) + 1;
225 
226   return desc;
227 }
228 
do_note_segment(Elf_Scn * section,GElf_Shdr * probes_shdr,int elf_class,bcc_elf_probecb callback,const char * binpath,uint64_t first_inst_offset,void * payload)229 static int do_note_segment(Elf_Scn *section, GElf_Shdr *probes_shdr, int elf_class,
230                            bcc_elf_probecb callback, const char *binpath,
231                            uint64_t first_inst_offset, void *payload) {
232   Elf_Data *data = NULL;
233 
234   while ((data = elf_getdata(section, data)) != 0) {
235     size_t offset = 0;
236     GElf_Nhdr hdr;
237     size_t name_off, desc_off;
238 
239     while ((offset = gelf_getnote(data, offset, &hdr, &name_off, &desc_off)) !=
240            0) {
241       const char *desc, *desc_end;
242       struct bcc_elf_usdt probe;
243 
244       if (hdr.n_type != NT_STAPSDT)
245         continue;
246 
247       if (hdr.n_namesz != 8)
248         continue;
249 
250       if (memcmp((const char *)data->d_buf + name_off, "stapsdt", 8) != 0)
251         continue;
252 
253       desc = (const char *)data->d_buf + desc_off;
254       desc_end = desc + hdr.n_descsz;
255 
256       if (parse_stapsdt_note(&probe, probes_shdr, desc, elf_class) == desc_end) {
257         if (probe.pc < first_inst_offset)
258           fprintf(stderr,
259                   "WARNING: invalid address 0x%lx for probe (%s,%s) in binary %s\n",
260                   probe.pc, probe.provider, probe.name, binpath);
261         else
262           callback(binpath, &probe, payload);
263       }
264     }
265   }
266   return 0;
267 }
268 
listprobes(Elf * e,bcc_elf_probecb callback,const char * binpath,void * payload)269 static int listprobes(Elf *e, bcc_elf_probecb callback, const char *binpath,
270                       void *payload) {
271   Elf_Scn *section = NULL;
272   bool found_probes_shdr;
273   size_t stridx;
274   int elf_class = gelf_getclass(e);
275   uint64_t first_inst_offset = 0;
276   GElf_Shdr probes_shdr = {};
277 
278   if (elf_getshdrstrndx(e, &stridx) != 0)
279     return -1;
280 
281   // Get the offset to the first instruction
282   while ((section = elf_nextscn(e, section)) != 0) {
283     GElf_Shdr header;
284 
285     if (!gelf_getshdr(section, &header))
286       continue;
287 
288     // The elf file section layout is based on increasing virtual address,
289     // getting the first section with SHF_EXECINSTR is enough.
290     if (header.sh_flags & SHF_EXECINSTR) {
291       first_inst_offset = header.sh_addr;
292       break;
293     }
294   }
295 
296   found_probes_shdr = false;
297   while ((section = elf_nextscn(e, section)) != 0) {
298     if (!gelf_getshdr(section, &probes_shdr))
299       continue;
300 
301     char *name = elf_strptr(e, stridx, probes_shdr.sh_name);
302     if (name && !strcmp(name, ".probes")) {
303       found_probes_shdr = true;
304       break;
305     }
306   }
307 
308   while ((section = elf_nextscn(e, section)) != 0) {
309     GElf_Shdr header;
310     char *name;
311 
312     if (!gelf_getshdr(section, &header))
313       continue;
314 
315     if (header.sh_type != SHT_NOTE)
316       continue;
317 
318     name = elf_strptr(e, stridx, header.sh_name);
319     if (name && !strcmp(name, ".note.stapsdt")) {
320       GElf_Shdr *shdr_ptr = found_probes_shdr ? &probes_shdr : NULL;
321       if (do_note_segment(section, shdr_ptr, elf_class, callback, binpath,
322                           first_inst_offset, payload) < 0)
323         return -1;
324     }
325   }
326 
327   return 0;
328 }
329 
bcc_elf_foreach_usdt(const char * path,bcc_elf_probecb callback,void * payload)330 int bcc_elf_foreach_usdt(const char *path, bcc_elf_probecb callback,
331                          void *payload) {
332   int res;
333   struct bcc_elf_file elf_file;
334   bcc_elf_file_init(&elf_file);
335 
336   if (bcc_elf_file_open(path, &elf_file) < 0)
337     return -1;
338 
339   res = listprobes(elf_file.elf, callback, path, payload);
340 
341   bcc_elf_file_close(&elf_file);
342   return res;
343 }
344 
get_section(Elf * e,const char * section_name,GElf_Shdr * section_hdr,size_t * section_idx)345 static Elf_Scn * get_section(Elf *e, const char *section_name,
346                              GElf_Shdr *section_hdr, size_t *section_idx) {
347   Elf_Scn *section = NULL;
348   GElf_Shdr header;
349   char *name;
350 
351   size_t stridx;
352   if (elf_getshdrstrndx(e, &stridx) != 0)
353     return NULL;
354 
355   size_t index;
356   for (index = 1; (section = elf_nextscn(e, section)) != 0; index++) {
357     if (!gelf_getshdr(section, &header))
358       continue;
359 
360     name = elf_strptr(e, stridx, header.sh_name);
361     if (name && !strcmp(name, section_name)) {
362       if (section_hdr)
363         *section_hdr = header;
364       if (section_idx)
365         *section_idx = index;
366       return section;
367     }
368   }
369 
370   return NULL;
371 }
372 
list_in_scn(Elf * e,Elf_Scn * section,size_t stridx,size_t symsize,struct bcc_symbol_option * option,bcc_elf_symcb callback,bcc_elf_symcb_lazy callback_lazy,void * payload,bool debugfile)373 static int list_in_scn(Elf *e, Elf_Scn *section, size_t stridx, size_t symsize,
374                        struct bcc_symbol_option *option,
375                        bcc_elf_symcb callback, bcc_elf_symcb_lazy callback_lazy,
376                        void *payload, bool debugfile) {
377   Elf_Data *data = NULL;
378 
379 #if defined(__powerpc64__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
380   size_t opdidx = 0;
381   Elf_Scn *opdsec = NULL;
382   GElf_Shdr opdshdr = {};
383   Elf_Data *opddata = NULL;
384 
385   opdsec = get_section(e, ".opd", &opdshdr, &opdidx);
386   if (opdsec && opdshdr.sh_type == SHT_PROGBITS)
387     opddata = elf_getdata(opdsec, NULL);
388 #endif
389 
390   while ((data = elf_getdata(section, data)) != 0) {
391     size_t i, symcount = data->d_size / symsize;
392 
393     if (data->d_size % symsize)
394       return -1;
395 
396     for (i = 0; i < symcount; ++i) {
397       GElf_Sym sym;
398       const char *name;
399       size_t name_len;
400 
401       if (!gelf_getsym(data, (int)i, &sym))
402         continue;
403 
404       if ((name = elf_strptr(e, stridx, sym.st_name)) == NULL)
405         continue;
406       if (name[0] == 0)
407         continue;
408       name_len = strlen(name);
409 
410       if (sym.st_value == 0)
411         continue;
412 
413       uint32_t st_type = ELF_ST_TYPE(sym.st_info);
414       if (!(option->use_symbol_type & (1 << st_type)))
415         continue;
416 
417 #ifdef __powerpc64__
418 #if defined(_CALL_ELF) && _CALL_ELF == 2
419       if (option->use_symbol_type & (1 << STT_PPC64_ELFV2_SYM_LEP)) {
420         /*
421          * The PowerPC 64-bit ELF v2 ABI says that the 3 most significant bits
422          * in the st_other field of the symbol table specifies the number of
423          * instructions between a function's Global Entry Point (GEP) and Local
424          * Entry Point (LEP).
425          */
426         switch (sym.st_other >> 5) {
427           /* GEP and LEP are the same for 0 or 1, usage is reserved for 7 */
428           /* If 2, LEP is 1 instruction past the GEP */
429           case 2: sym.st_value += 4; break;
430           /* If 3, LEP is 2 instructions past the GEP */
431           case 3: sym.st_value += 8; break;
432           /* If 4, LEP is 4 instructions past the GEP */
433           case 4: sym.st_value += 16; break;
434           /* If 5, LEP is 8 instructions past the GEP */
435           case 5: sym.st_value += 32; break;
436           /* If 6, LEP is 16 instructions past the GEP */
437           case 6: sym.st_value += 64; break;
438         }
439       }
440 #else
441       if (opddata && sym.st_shndx == opdidx) {
442         size_t offset = sym.st_value - opdshdr.sh_addr;
443         /* Find the function descriptor */
444         uint64_t *descr = opddata->d_buf + offset;
445         /* Read the actual entry point address from the descriptor */
446         sym.st_value = *descr;
447       }
448 #endif
449 #endif
450 
451       int ret;
452       if (option->lazy_symbolize)
453         ret = callback_lazy(stridx, sym.st_name, name_len, sym.st_value,
454                             sym.st_size, debugfile, payload);
455       else
456         ret = callback(name, sym.st_value, sym.st_size, payload);
457       if (ret < 0)
458         return 1;      // signal termination to caller
459     }
460   }
461 
462   return 0;
463 }
464 
listsymbols(Elf * e,bcc_elf_symcb callback,bcc_elf_symcb_lazy callback_lazy,void * payload,struct bcc_symbol_option * option,bool debugfile)465 static int listsymbols(Elf *e, bcc_elf_symcb callback,
466                        bcc_elf_symcb_lazy callback_lazy, void *payload,
467                        struct bcc_symbol_option *option, bool debugfile) {
468   Elf_Scn *section = NULL;
469 
470   while ((section = elf_nextscn(e, section)) != 0) {
471     GElf_Shdr header;
472 
473     if (!gelf_getshdr(section, &header))
474       continue;
475 
476     if (header.sh_type != SHT_SYMTAB && header.sh_type != SHT_DYNSYM)
477       continue;
478 
479     int rc = list_in_scn(e, section, header.sh_link, header.sh_entsize,
480                          option, callback, callback_lazy, payload, debugfile);
481     if (rc == 1)
482       break;    // callback signaled termination
483 
484     if (rc < 0)
485       return rc;
486   }
487 
488   return 0;
489 }
490 
get_section_elf_data(Elf * e,const char * section_name)491 static Elf_Data * get_section_elf_data(Elf *e, const char *section_name) {
492   Elf_Scn *section = get_section(e, section_name, NULL, NULL);
493   if (section)
494     return elf_getdata(section, NULL);
495   return NULL;
496 }
497 
find_debuglink(Elf * e,char ** debug_file,unsigned int * crc)498 static int find_debuglink(Elf *e, char **debug_file, unsigned int *crc) {
499   Elf_Data *data = NULL;
500 
501   *debug_file = NULL;
502   *crc = 0;
503 
504   data = get_section_elf_data(e, ".gnu_debuglink");
505   if (!data || data->d_size <= 5)
506     return 0;
507 
508   *debug_file = (char *)data->d_buf;
509   *crc = *(unsigned int*)((char *)data->d_buf + data->d_size - 4);
510 
511   return *debug_file ? 1 : 0;
512 }
513 
find_buildid(Elf * e,char * buildid)514 static int find_buildid(Elf *e, char *buildid) {
515   Elf_Data *data = get_section_elf_data(e, ".note.gnu.build-id");
516   if (!data || data->d_size <= 16 || strcmp((char *)data->d_buf + 12, "GNU"))
517     return 0;
518 
519   char *buf = (char *)data->d_buf + 16;
520   size_t length = data->d_size - 16;
521   size_t i = 0;
522   for (i = 0; i < length; ++i) {
523     sprintf(buildid + (i * 2), "%02hhx", buf[i]);
524   }
525 
526   return 1;
527 }
528 
529 // The CRC algorithm used by GNU debuglink. Taken from:
530 //    https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
gnu_debuglink_crc32(unsigned int crc,char * buf,size_t len)531 static unsigned int gnu_debuglink_crc32(unsigned int crc,
532                                         char *buf, size_t len) {
533   static const unsigned int crc32_table[256] =
534   {
535     0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
536     0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4,
537     0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07,
538     0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
539     0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856,
540     0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
541     0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
542     0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
543     0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
544     0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a,
545     0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599,
546     0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
547     0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190,
548     0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
549     0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e,
550     0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
551     0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed,
552     0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
553     0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3,
554     0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
555     0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
556     0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5,
557     0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010,
558     0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
559     0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17,
560     0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6,
561     0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
562     0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
563     0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344,
564     0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
565     0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a,
566     0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
567     0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1,
568     0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c,
569     0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
570     0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
571     0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe,
572     0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31,
573     0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c,
574     0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
575     0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b,
576     0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
577     0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1,
578     0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
579     0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
580     0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7,
581     0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66,
582     0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
583     0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
584     0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8,
585     0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b,
586     0x2d02ef8d
587   };
588   char *end;
589 
590   crc = ~crc & 0xffffffff;
591   for (end = buf + len; buf < end; ++buf)
592     crc = crc32_table[(crc ^ *buf) & 0xff] ^ (crc >> 8);
593   return ~crc & 0xffffffff;
594 }
595 
verify_checksum(const char * file,unsigned int crc)596 static int verify_checksum(const char *file, unsigned int crc) {
597   struct stat st;
598   int fd;
599   void *buf;
600   unsigned int actual;
601 
602   fd = open(file, O_RDONLY);
603   if (fd < 0)
604     return 0;
605 
606   if (fstat(fd, &st) < 0) {
607     close(fd);
608     return 0;
609   }
610 
611   buf = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
612   if (!buf) {
613     close(fd);
614     return 0;
615   }
616 
617   actual = gnu_debuglink_crc32(0, buf, st.st_size);
618 
619   munmap(buf, st.st_size);
620   close(fd);
621   return actual == crc;
622 }
623 
624 // Check if two filenames point to the same file, including hard or soft links.
same_file(char * a,const char * b)625 static bool same_file(char *a, const char *b)
626 {
627 	struct stat stat_a, stat_b;
628 
629 	if (stat(a, &stat_a) || stat(b, &stat_b))
630 		return false;
631 
632 	if ((stat_a.st_dev == stat_b.st_dev) &&
633 	    (stat_a.st_ino == stat_b.st_ino))
634 		return true;
635 	else
636 		return false;
637 }
638 
try_open_debuglink_candidate(const char * path,int check_crc,int crc,struct bcc_elf_file * out)639 static int try_open_debuglink_candidate(const char *path, int check_crc,
640                                         int crc, struct bcc_elf_file *out) {
641   if (access(path, F_OK)) {
642     return -1;
643   }
644 
645   if (check_crc && !verify_checksum(path, crc)) {
646     return -1;
647   }
648 
649   return bcc_elf_file_open(path, out);
650 }
651 
652 // Returns 0 on success, otherwise nonzero.
653 // If successfull, 'out' param is a valid bcc_elf_file.
654 // Caller is responsible for calling bcc_elf_file_close when done using it.
655 // See https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
find_debug_via_debuglink(Elf * e,const char * binpath,int check_crc,struct bcc_elf_file * out)656 static int find_debug_via_debuglink(Elf *e, const char *binpath, int check_crc,
657                                     struct bcc_elf_file *out) {
658   char fullpath[PATH_MAX];
659   char tmppath[PATH_MAX];
660   char *bindir = NULL;
661   unsigned int crc;
662   char *name;  // the name of the debuginfo file
663 
664   if (!find_debuglink(e, &name, &crc))
665     return -1;
666 
667   strncpy(tmppath, binpath, PATH_MAX);
668   tmppath[PATH_MAX - 1] = 0;
669   bindir = dirname(tmppath);
670 
671   // Search for the file in 'binpath', but ignore the file we find if it
672   // matches the binary itself: the binary will always be probed later on,
673   // and it might contain poorer symbols (e.g. stripped or partial symbols)
674   // than the external debuginfo that might be available elsewhere.
675   snprintf(fullpath, sizeof(fullpath),"%s/%s", bindir, name);
676   if (same_file(fullpath, binpath) != true &&
677       try_open_debuglink_candidate(fullpath, check_crc, crc, out) == 0)
678     return 0;
679 
680   // Search for the file in 'binpath'/.debug
681   snprintf(fullpath, sizeof(fullpath), "%s/.debug/%s", bindir, name);
682   if (try_open_debuglink_candidate(fullpath, check_crc, crc, out) == 0)
683     return 0;
684 
685   // Search for the file in the global debug directory /usr/lib/debug/'binpath'
686   snprintf(fullpath, sizeof(fullpath), "/usr/lib/debug%s/%s", bindir, name);
687   if (try_open_debuglink_candidate(fullpath, check_crc, crc, out) == 0)
688     return 0;
689 
690   return -1;
691 }
692 
693 // Returns 0 on success, otherwise nonzero.
694 // If successfull, 'out' param is a valid bcc_elf_file.
695 // Caller is responsible for calling bcc_elf_file_close when done using it.
696 // See https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
find_debug_via_buildid(Elf * e,struct bcc_elf_file * out)697 static int find_debug_via_buildid(Elf *e, struct bcc_elf_file *out) {
698   char fullpath[PATH_MAX];
699   char buildid[128];  // currently 40 seems to be default, let's be safe
700 
701   if (!find_buildid(e, buildid))
702     return -1;
703 
704   // Search for the file in the global debug directory with a sub-path:
705   //    mm/nnnnnn...nnnn.debug
706   // Where mm are the first two characters of the buildid, and nnnn are the
707   // rest of the build id, followed by .debug.
708   const char *bcc_debuginfo_root = getenv("BCC_DEBUGINFO_ROOT");
709   if (bcc_debuginfo_root == NULL)
710     bcc_debuginfo_root = "/usr/lib/debug";
711   snprintf(fullpath, sizeof(fullpath), "%s/.build-id/%c%c/%s.debug",
712            bcc_debuginfo_root, buildid[0], buildid[1], buildid + 2);
713   return bcc_elf_file_open(fullpath, out);
714 }
715 
716 // Returns 0 on success, otherwise nonzero.
717 // If successfull, 'out' param is a valid bcc_elf_file.
718 // Caller is responsible for calling bcc_elf_file_close when done using it.
719 // See
720 // https://github.com/torvalds/linux/blob/v5.2/tools/perf/Documentation/perf-report.txt#L325
find_debug_via_symfs(Elf * e,const char * path,struct bcc_elf_file * out)721 static int find_debug_via_symfs(Elf *e, const char *path,
722                                 struct bcc_elf_file *out) {
723   char fullpath[PATH_MAX];
724   char buildid[128];
725   char symfs_buildid[128];
726   int check_build_id;
727   char *symfs;
728   struct bcc_elf_file symfs_elf_file;
729   bcc_elf_file_init(&symfs_elf_file);
730 
731   symfs = getenv("BCC_SYMFS");
732   if (!symfs || !*symfs)
733     goto fail;
734 
735   check_build_id = find_buildid(e, buildid);
736 
737   int ns_prefix_length = 0;
738   sscanf(path, "/proc/%*u/root/%n", &ns_prefix_length);
739   path += ns_prefix_length;
740 
741   snprintf(fullpath, sizeof(fullpath), "%s/%s", symfs, path);
742   if (access(fullpath, F_OK) == -1)
743     goto fail;
744 
745   if (bcc_elf_file_open(fullpath, &symfs_elf_file) < 0) {
746     goto fail;
747   }
748 
749   if (check_build_id) {
750     if (!find_buildid(symfs_elf_file.elf, symfs_buildid))
751       goto fail;
752 
753     if (strncmp(buildid, symfs_buildid, sizeof(buildid)))
754       goto fail;
755   }
756 
757   *out = symfs_elf_file;
758   return 0;
759 
760 fail:
761   bcc_elf_file_close(&symfs_elf_file);
762   return -1;
763 }
764 
765 #ifdef HAVE_LIBLZMA
766 
767 #define LZMA_MIN_BUFFER_SIZE 4096
768 #define LZMA_MEMLIMIT (128 * 1024 * 1024)
open_mini_debug_info_file(void * gnu_debugdata,size_t gnu_debugdata_size,struct bcc_elf_file * out)769 static int open_mini_debug_info_file(void *gnu_debugdata,
770                                      size_t gnu_debugdata_size,
771                                      struct bcc_elf_file *out) {
772   void *decompressed = NULL;
773   void *new_decompressed = NULL;
774   size_t decompressed_data_size = 0;
775   size_t decompressed_buffer_size = 0;
776   lzma_stream stream = LZMA_STREAM_INIT;
777   lzma_ret ret;
778 
779   ret = lzma_stream_decoder(&stream, LZMA_MEMLIMIT, 0);
780   if (ret != LZMA_OK)
781     return -1;
782 
783   stream.next_in = gnu_debugdata;
784   stream.avail_in = gnu_debugdata_size;
785   stream.avail_out = 0;
786 
787   while (ret == LZMA_OK && stream.avail_in > 0) {
788     if (stream.avail_out < LZMA_MIN_BUFFER_SIZE) {
789       decompressed_buffer_size += LZMA_MIN_BUFFER_SIZE;
790       new_decompressed = realloc(decompressed, decompressed_buffer_size);
791       if (new_decompressed == NULL) {
792         ret = LZMA_MEM_ERROR;
793         break;
794       }
795 
796       decompressed = new_decompressed;
797       stream.avail_out += LZMA_MIN_BUFFER_SIZE;
798       stream.next_out = decompressed + decompressed_data_size;
799     }
800     ret = lzma_code(&stream, LZMA_FINISH);
801     decompressed_data_size = decompressed_buffer_size - stream.avail_out;
802   }
803   lzma_end(&stream);
804 
805   if (ret != LZMA_STREAM_END ||
806       bcc_elf_file_open_buf(decompressed, decompressed_data_size, out)) {
807     free(decompressed);
808     return -1;
809   }
810 
811   return 0;
812 }
813 
814 // Returns 0 on success, otherwise nonzero.
815 // If successfull, 'out' param is a valid bcc_elf_file.
816 // Caller is responsible for calling bcc_elf_file_close when done using it.
817 // See https://sourceware.org/gdb/onlinedocs/gdb/MiniDebugInfo.html
find_debug_via_mini_debug_info(Elf * elf,struct bcc_elf_file * out)818 static int find_debug_via_mini_debug_info(Elf *elf, struct bcc_elf_file *out) {
819   Elf_Data *gnu_debugdata;
820 
821   gnu_debugdata = get_section_elf_data(elf, ".gnu_debugdata");
822   if (gnu_debugdata == NULL)
823     return -1;
824 
825   return open_mini_debug_info_file(gnu_debugdata->d_buf, gnu_debugdata->d_size,
826                                    out);
827 }
828 #endif
829 
830 #ifdef HAVE_LIBDEBUGINFOD
831 
832 // Returns 0 on success, otherwise nonzero.
833 // If successfull, 'out' param is a valid bcc_elf_file.
834 // Caller is responsible for calling bcc_elf_file_close when done using it.
835 // See https://sourceware.org/elfutils/Debuginfod.html
find_debug_via_debuginfod(Elf * e,struct bcc_elf_file * out)836 static int find_debug_via_debuginfod(Elf *e, struct bcc_elf_file *out) {
837   char buildid[128];
838   int fd = -1;
839 
840   if (!find_buildid(e, buildid))
841     return -1;
842 
843   debuginfod_client *client = debuginfod_begin();
844   if (!client)
845     return -1;
846 
847   // In case of an error, the function returns a negative error code.
848   fd = debuginfod_find_debuginfo(client, (const unsigned char *)buildid, 0,
849                                  NULL);
850   if (fd >= 0) {
851     if (bcc_elf_file_open_fd(fd, out)) {
852       close(fd);
853       fd = -1;
854     }
855   }
856 
857   debuginfod_end(client);
858   return fd >= 0 ? 0 : -1;
859 }
860 #endif
861 
862 // Returns 0 on success, otherwise nonzero.
863 // If successfull, 'out' param is a valid bcc_elf_file.
864 // Caller is responsible for calling bcc_elf_file_close when done using it.
find_debug_file(Elf * e,const char * path,int check_crc,struct bcc_elf_file * out)865 static int find_debug_file(Elf *e, const char *path, int check_crc,
866                            struct bcc_elf_file *out) {
867   if (find_debug_via_symfs(e, path, out) == 0)
868     return 0;
869 
870   if (find_debug_via_buildid(e, out) == 0)
871     return 0;
872 
873   if (find_debug_via_debuglink(e, path, check_crc, out) == 0)
874     return 0;
875 
876 #ifdef HAVE_LIBLZMA
877   if (find_debug_via_mini_debug_info(e, out) == 0)
878     return 0;
879 #endif
880 
881 #ifdef HAVE_LIBDEBUGINFOD
882   if (find_debug_via_debuginfod(e, out) == 0)
883     return 0;
884 #endif
885 
886   return -1;
887 }
888 
foreach_sym_core(const char * path,bcc_elf_symcb callback,bcc_elf_symcb_lazy callback_lazy,struct bcc_symbol_option * option,void * payload)889 static int foreach_sym_core(const char *path, bcc_elf_symcb callback,
890                             bcc_elf_symcb_lazy callback_lazy,
891                             struct bcc_symbol_option *option, void *payload) {
892   struct bcc_elf_file elf_file;
893   bcc_elf_file_init(&elf_file);
894   struct bcc_elf_file debug_elf_file;
895   bcc_elf_file_init(&debug_elf_file);
896   int res;
897 
898   if (!option)
899     return -1;
900 
901   if (bcc_elf_file_open(path, &elf_file) < 0)
902     return -1;
903 
904   if (option->use_debug_file) {
905     if (find_debug_file(elf_file.elf, path, option->check_debug_file_crc,
906                         &debug_elf_file) == 0) {
907       listsymbols(debug_elf_file.elf, callback, callback_lazy, payload, option,
908                   1);
909       bcc_elf_file_close(&debug_elf_file);
910     }
911   }
912 
913   res = listsymbols(elf_file.elf, callback, callback_lazy, payload, option, 0);
914   bcc_elf_file_close(&elf_file);
915   return res;
916 }
917 
bcc_elf_foreach_sym(const char * path,bcc_elf_symcb callback,void * option,void * payload)918 int bcc_elf_foreach_sym(const char *path, bcc_elf_symcb callback,
919                         void *option, void *payload) {
920   struct bcc_symbol_option *o = option;
921   o->lazy_symbolize = 0;
922   return foreach_sym_core(path, callback, NULL, o, payload);
923 }
924 
bcc_elf_foreach_sym_lazy(const char * path,bcc_elf_symcb_lazy callback,void * option,void * payload)925 int bcc_elf_foreach_sym_lazy(const char *path, bcc_elf_symcb_lazy callback,
926                         void *option, void *payload) {
927   struct bcc_symbol_option *o = option;
928   o->lazy_symbolize = 1;
929   return foreach_sym_core(path, NULL, callback, o, payload);
930 }
931 
bcc_elf_get_text_scn_info(const char * path,uint64_t * addr,uint64_t * offset)932 int bcc_elf_get_text_scn_info(const char *path, uint64_t *addr,
933                               uint64_t *offset) {
934   int err;
935   Elf_Scn *section = NULL;
936   GElf_Shdr header;
937   size_t stridx;
938   char *name;
939   struct bcc_elf_file elf_file;
940   bcc_elf_file_init(&elf_file);
941 
942   if ((err = bcc_elf_file_open(path, &elf_file)) < 0 ||
943       (err = elf_getshdrstrndx(elf_file.elf, &stridx)) < 0)
944     goto exit;
945 
946   err = -1;
947   while ((section = elf_nextscn(elf_file.elf, section)) != 0) {
948     if (!gelf_getshdr(section, &header))
949       continue;
950 
951     name = elf_strptr(elf_file.elf, stridx, header.sh_name);
952     if (name && !strcmp(name, ".text")) {
953       *addr = (uint64_t)header.sh_addr;
954       *offset = (uint64_t)header.sh_offset;
955       err = 0;
956       break;
957     }
958   }
959 
960 exit:
961   bcc_elf_file_close(&elf_file);
962   return err;
963 }
964 
bcc_elf_foreach_load_section(const char * path,bcc_elf_load_sectioncb callback,void * payload)965 int bcc_elf_foreach_load_section(const char *path,
966                                  bcc_elf_load_sectioncb callback,
967                                  void *payload) {
968   int err = -1, res;
969   size_t nhdrs, i;
970   struct bcc_elf_file elf_file;
971   bcc_elf_file_init(&elf_file);
972 
973   if (bcc_elf_file_open(path, &elf_file) < 0)
974     goto exit;
975 
976   if (elf_getphdrnum(elf_file.elf, &nhdrs) != 0)
977     goto exit;
978 
979   GElf_Phdr header;
980   for (i = 0; i < nhdrs; i++) {
981     if (!gelf_getphdr(elf_file.elf, (int)i, &header))
982       continue;
983     if (header.p_type != PT_LOAD || !(header.p_flags & PF_X))
984       continue;
985     res = callback(header.p_vaddr, header.p_memsz, header.p_offset, payload);
986     if (res < 0) {
987       err = 1;
988       goto exit;
989     }
990   }
991   err = 0;
992 
993 exit:
994   bcc_elf_file_close(&elf_file);
995   return err;
996 }
997 
bcc_elf_get_type(const char * path)998 int bcc_elf_get_type(const char *path) {
999   GElf_Ehdr hdr;
1000   void* res = NULL;
1001   struct bcc_elf_file elf_file;
1002   bcc_elf_file_init(&elf_file);
1003 
1004   if (bcc_elf_file_open(path, &elf_file) < 0)
1005     return -1;
1006 
1007   res = (void *)gelf_getehdr(elf_file.elf, &hdr);
1008   bcc_elf_file_close(&elf_file);
1009 
1010   if (!res)
1011     return -1;
1012   else
1013     return hdr.e_type;
1014 }
1015 
bcc_elf_is_exe(const char * path)1016 int bcc_elf_is_exe(const char *path) {
1017   return (bcc_elf_get_type(path) != -1) && (access(path, X_OK) == 0);
1018 }
1019 
bcc_elf_is_shared_obj(const char * path)1020 int bcc_elf_is_shared_obj(const char *path) {
1021   return bcc_elf_get_type(path) == ET_DYN;
1022 }
1023 
bcc_elf_is_vdso(const char * name)1024 int bcc_elf_is_vdso(const char *name) {
1025   return strcmp(name, "[vdso]") == 0;
1026 }
1027 
1028 // -2: Failed
1029 // -1: Not initialized
1030 // >0: Initialized
1031 static int vdso_image_fd = -1;
1032 
find_vdso(struct mod_info * info,int enter_ns,void * payload)1033 static int find_vdso(struct mod_info *info, int enter_ns, void *payload) {
1034   int fd;
1035   char tmpfile[128];
1036   if (!bcc_elf_is_vdso(info->name))
1037     return 0;
1038 
1039   uint64_t sz = info->end_addr - info->start_addr;
1040   void *image = malloc(sz);
1041   if (!image)
1042     goto on_error;
1043   memcpy(image, (void *)info->start_addr, sz);
1044 
1045   snprintf(tmpfile, sizeof(tmpfile), "/tmp/bcc_%d_vdso_image_XXXXXX", getpid());
1046   fd = mkostemp(tmpfile, O_CLOEXEC);
1047   if (fd < 0) {
1048     fprintf(stderr, "Unable to create temp file: %s\n", strerror(errno));
1049     goto on_error;
1050   }
1051   // Unlink the file to avoid leaking
1052   if (unlink(tmpfile) == -1)
1053     fprintf(stderr, "Unlink %s failed: %s\n", tmpfile, strerror(errno));
1054 
1055   if (write(fd, image, sz) == -1) {
1056     fprintf(stderr, "Failed to write to vDSO image: %s\n", strerror(errno));
1057     close(fd);
1058     goto on_error;
1059   }
1060   vdso_image_fd = fd;
1061 
1062 on_error:
1063   if (image)
1064     free(image);
1065   // Always stop the iteration
1066   return -1;
1067 }
1068 
bcc_elf_foreach_vdso_sym(bcc_elf_symcb callback,void * payload)1069 int bcc_elf_foreach_vdso_sym(bcc_elf_symcb callback, void *payload) {
1070   Elf *elf;
1071   static struct bcc_symbol_option default_option = {
1072     .use_debug_file = 0,
1073     .check_debug_file_crc = 0,
1074     .use_symbol_type = (1 << STT_FUNC) | (1 << STT_GNU_IFUNC)
1075   };
1076 
1077   if (vdso_image_fd == -1) {
1078     vdso_image_fd = -2;
1079     bcc_procutils_each_module(getpid(), &find_vdso, NULL);
1080   }
1081   if (vdso_image_fd == -2)
1082     return -1;
1083 
1084   if (openelf_fd(vdso_image_fd, &elf) == -1)
1085     return -1;
1086 
1087   int rc = listsymbols(elf, callback, NULL, payload, &default_option, 0);
1088   elf_end(elf);
1089   return rc;
1090 }
1091 
1092 // return value: 0   : success
1093 //               < 0 : error and no bcc lib found
1094 //               > 0 : error and bcc lib found
bcc_free_memory_with_file(const char * path)1095 static int bcc_free_memory_with_file(const char *path) {
1096   unsigned long sym_addr = 0, sym_shndx;
1097   Elf_Scn *section = NULL;
1098   int err;
1099   GElf_Shdr header;
1100   struct bcc_elf_file elf_file;
1101   bcc_elf_file_init(&elf_file);
1102 
1103   if ((err = bcc_elf_file_open(path, &elf_file)) < 0)
1104     goto exit;
1105 
1106   // get symbol address of "bcc_free_memory", which
1107   // will be used to calculate runtime .text address
1108   // range, esp. for shared libraries.
1109   err = -1;
1110   while ((section = elf_nextscn(elf_file.elf, section)) != 0) {
1111     Elf_Data *data = NULL;
1112     size_t symsize;
1113 
1114     if (!gelf_getshdr(section, &header))
1115       continue;
1116 
1117     if (header.sh_type != SHT_SYMTAB && header.sh_type != SHT_DYNSYM)
1118       continue;
1119 
1120     /* iterate all symbols */
1121     symsize = header.sh_entsize;
1122     while ((data = elf_getdata(section, data)) != 0) {
1123       size_t i, symcount = data->d_size / symsize;
1124 
1125       for (i = 0; i < symcount; ++i) {
1126         GElf_Sym sym;
1127 
1128         if (!gelf_getsym(data, (int)i, &sym))
1129           continue;
1130 
1131         if (GELF_ST_TYPE(sym.st_info) != STT_FUNC)
1132           continue;
1133 
1134         const char *name;
1135         if ((name = elf_strptr(elf_file.elf, header.sh_link, sym.st_name)) ==
1136             NULL)
1137           continue;
1138 
1139         if (strcmp(name, "bcc_free_memory") == 0) {
1140           sym_addr = sym.st_value;
1141           sym_shndx = sym.st_shndx;
1142           break;
1143         }
1144       }
1145     }
1146   }
1147 
1148   // Didn't find bcc_free_memory in the ELF file.
1149   if (sym_addr == 0)
1150     goto exit;
1151 
1152   int sh_idx = 0;
1153   section = NULL;
1154   err = 1;
1155   while ((section = elf_nextscn(elf_file.elf, section)) != 0) {
1156     sh_idx++;
1157     if (!gelf_getshdr(section, &header))
1158       continue;
1159 
1160     if (sh_idx == sym_shndx) {
1161       unsigned long saddr, saddr_n, eaddr;
1162       long page_size = sysconf(_SC_PAGESIZE);
1163 
1164       saddr = (unsigned long)bcc_free_memory - sym_addr + header.sh_addr;
1165       eaddr = saddr + header.sh_size;
1166 
1167       // adjust saddr and eaddr, start addr needs to be page aligned
1168       saddr_n = (saddr + page_size - 1) & ~(page_size - 1);
1169       eaddr -= saddr_n - saddr;
1170 
1171       if (madvise((void *)saddr_n, eaddr - saddr_n, MADV_DONTNEED)) {
1172         fprintf(stderr, "madvise failed, saddr %lx, eaddr %lx\n", saddr, eaddr);
1173         goto exit;
1174       }
1175 
1176       err = 0;
1177       break;
1178     }
1179   }
1180 
1181 exit:
1182   bcc_elf_file_close(&elf_file);
1183   return err;
1184 }
1185 
1186 // Free bcc mmemory
1187 //
1188 // The main purpose of this function is to free llvm/clang text memory
1189 // through madvise MADV_DONTNEED.
1190 //
1191 // bcc could be linked statically or dynamically into the application.
1192 // If it is static linking, there is no easy way to know which region
1193 // inside .text section belongs to llvm/clang, so the whole .text section
1194 // is freed. Otherwise, the process map is searched to find libbcc.so
1195 // library and the whole .text section for that shared library is
1196 // freed.
1197 //
1198 // Note that the text memory used by bcc (mainly llvm/clang) is reclaimable
1199 // in the kernel as it is file backed. But the reclaim process
1200 // may take some time if no memory pressure. So this API is mostly
1201 // used for application who needs to immediately lowers its RssFile
1202 // metric right after loading BPF program.
bcc_free_memory()1203 int bcc_free_memory() {
1204   int err;
1205 
1206   // First try whether bcc is statically linked or not
1207   err = bcc_free_memory_with_file("/proc/self/exe");
1208   if (err >= 0)
1209     return -err;
1210 
1211   // Not statically linked, let us find the libbcc.so
1212   FILE *maps = fopen("/proc/self/maps", "r");
1213   if (!maps)
1214     return -1;
1215 
1216   char *line = NULL;
1217   size_t size;
1218   while (getline(&line, &size, maps) > 0) {
1219     char *libbcc = strstr(line, "libbcc.so");
1220     if (!libbcc)
1221       continue;
1222 
1223     // Parse the line and get the full libbcc.so path
1224     unsigned long addr_start, addr_end, offset, inode;
1225     int path_start = 0, path_end = 0;
1226     unsigned int devmajor, devminor;
1227     char perms[8];
1228     if (sscanf(line, "%lx-%lx %7s %lx %x:%x %lu %n%*[^\n]%n",
1229                &addr_start, &addr_end, perms, &offset,
1230                &devmajor, &devminor, &inode,
1231                &path_start, &path_end) < 7)
1232        break;
1233 
1234     // Free the text in the bcc dynamic library.
1235     char libbcc_path[4096];
1236     memcpy(libbcc_path, line + path_start, path_end - path_start);
1237     libbcc_path[path_end - path_start] = '\0';
1238     err = bcc_free_memory_with_file(libbcc_path);
1239     err = (err <= 0) ? err : -err;
1240   }
1241 
1242   fclose(maps);
1243   free(line);
1244   return err;
1245 }
1246 
bcc_elf_get_buildid(const char * path,char * buildid)1247 int bcc_elf_get_buildid(const char *path, char *buildid) {
1248   int rc = -1;
1249   struct bcc_elf_file elf_file;
1250   bcc_elf_file_init(&elf_file);
1251 
1252   if (bcc_elf_file_open(path, &elf_file) < 0)
1253     return -1;
1254 
1255   if (!find_buildid(elf_file.elf, buildid))
1256     goto exit;
1257 
1258   rc = 0;
1259 exit:
1260   bcc_elf_file_close(&elf_file);
1261   return rc;
1262 }
1263 
bcc_elf_symbol_str(const char * path,size_t section_idx,size_t str_table_idx,char * out,size_t len,int debugfile)1264 int bcc_elf_symbol_str(const char *path, size_t section_idx,
1265                        size_t str_table_idx, char *out, size_t len,
1266                        int debugfile) {
1267   int err = 0;
1268   const char *name;
1269   struct bcc_elf_file elf_file;
1270   bcc_elf_file_init(&elf_file);
1271   struct bcc_elf_file debug_elf_file;
1272   bcc_elf_file_init(&debug_elf_file);
1273 
1274   if (!out)
1275     return -1;
1276 
1277   if (bcc_elf_file_open(path, &elf_file) < 0)
1278     return -1;
1279 
1280   if (debugfile) {
1281     if (find_debug_file(elf_file.elf, path, 0, &debug_elf_file)) {
1282       err = -1;
1283       goto exit;
1284     }
1285 
1286     if ((name = elf_strptr(debug_elf_file.elf, section_idx, str_table_idx)) ==
1287         NULL) {
1288       err = -1;
1289       goto exit;
1290     }
1291   } else {
1292     if ((name = elf_strptr(elf_file.elf, section_idx, str_table_idx)) == NULL) {
1293       err = -1;
1294       goto exit;
1295     }
1296   }
1297 
1298   strncpy(out, name, len);
1299 
1300 exit:
1301   bcc_elf_file_close(&debug_elf_file);
1302   bcc_elf_file_close(&elf_file);
1303   return err;
1304 }
1305 
1306 #if 0
1307 #include <stdio.h>
1308 
1309 int main(int argc, char *argv[])
1310 {
1311   uint64_t addr;
1312   if (bcc_elf_findsym(argv[1], argv[2], -1, STT_FUNC, &addr) < 0)
1313     return -1;
1314 
1315   printf("%s: %p\n", argv[2], (void *)addr);
1316   return 0;
1317 }
1318 #endif
1319