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