Home | History | Annotate | Download | only in cc
      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 
     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 
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    594 int bcc_elf_is_exe(const char *path) {
    595   return (bcc_elf_get_type(path) != -1) && (access(path, X_OK) == 0);
    596 }
    597 
    598 int bcc_elf_is_shared_obj(const char *path) {
    599   return bcc_elf_get_type(path) == ET_DYN;
    600 }
    601 
    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 
    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 
    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