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