1 /*
   2  * This file and its contents are supplied under the terms of the
   3  * Common Development and Distribution License ("CDDL"), version 1.0.
   4  * You may only use this file in accordance with the terms of version
   5  * 1.0 of the CDDL.
   6  *
   7  * A full copy of the text of the CDDL should have accompanied this
   8  * source.  A copy of the CDDL is also available via the Internet at
   9  * http://www.illumos.org/license/CDDL.
  10  */
  11 
  12 /*
  13  * Copyright 2019 Joyent, Inc.
  14  */
  15 
  16 /*
  17  * Main conversion entry points. This has been designed such that there can be
  18  * any number of different conversion backends. Currently we only have one that
  19  * understands DWARFv2 (and bits of DWARFv4). Each backend should be placed in
  20  * the ctf_converters list and each will be tried in turn.
  21  */
  22 
  23 #include <libctf_impl.h>
  24 #include <assert.h>
  25 #include <gelf.h>
  26 
  27 ctf_convert_f ctf_converters[] = {
  28         ctf_dwarf_convert
  29 };
  30 
  31 #define NCONVERTS       (sizeof (ctf_converters) / sizeof (ctf_convert_f))
  32 
  33 ctf_hsc_ret_t
  34 ctf_has_c_source(Elf *elf, char *errmsg, size_t errlen)
  35 {
  36         ctf_hsc_ret_t ret = CHR_NO_C_SOURCE;
  37         Elf_Scn *scn, *strscn;
  38         Elf_Data *data, *strdata;
  39         GElf_Shdr shdr;
  40         ulong_t i;
  41 
  42         scn = NULL;
  43         while ((scn = elf_nextscn(elf, scn)) != NULL) {
  44                 if (gelf_getshdr(scn, &shdr) == NULL) {
  45                         (void) snprintf(errmsg, errlen,
  46                             "failed to get section header: %s",
  47                             elf_errmsg(elf_errno()));
  48                         return (CHR_ERROR);
  49                 }
  50 
  51                 if (shdr.sh_type == SHT_SYMTAB)
  52                         break;
  53         }
  54 
  55         if (scn == NULL)
  56                 return (CHR_NO_C_SOURCE);
  57 
  58         if ((strscn = elf_getscn(elf, shdr.sh_link)) == NULL) {
  59                 (void) snprintf(errmsg, errlen, "failed to get str section: %s",
  60                     elf_errmsg(elf_errno()));
  61                 return (CHR_ERROR);
  62         }
  63 
  64         if ((data = elf_getdata(scn, NULL)) == NULL) {
  65                 (void) snprintf(errmsg, errlen, "failed to read section: %s",
  66                     elf_errmsg(elf_errno()));
  67                 return (CHR_ERROR);
  68         }
  69 
  70         if ((strdata = elf_getdata(strscn, NULL)) == NULL) {
  71                 (void) snprintf(errmsg, errlen,
  72                     "failed to read string table: %s", elf_errmsg(elf_errno()));
  73                 return (CHR_ERROR);
  74         }
  75 
  76         for (i = 0; i < shdr.sh_size / shdr.sh_entsize; i++) {
  77                 GElf_Sym sym;
  78                 const char *file;
  79                 size_t len;
  80 
  81                 if (gelf_getsym(data, i, &sym) == NULL) {
  82                         (void) snprintf(errmsg, errlen,
  83                             "failed to read sym %lu: %s",
  84                             i, elf_errmsg(elf_errno()));
  85                         return (CHR_ERROR);
  86                 }
  87 
  88                 if (GELF_ST_TYPE(sym.st_info) != STT_FILE)
  89                         continue;
  90 
  91                 file = (const char *)((uintptr_t)strdata->d_buf + sym.st_name);
  92                 len = strlen(file);
  93                 if (len >= 2 && strncmp(".c", &file[len - 2], 2) == 0) {
  94                         ret = CHR_HAS_C_SOURCE;
  95                         break;
  96                 }
  97         }
  98 
  99         return (ret);
 100 }
 101 
 102 static ctf_file_t *
 103 ctf_elfconvert(int fd, Elf *elf, const char *label, uint_t nthrs, uint_t flags,
 104     int *errp, char *errbuf, size_t errlen)
 105 {
 106         int err, i;
 107         ctf_file_t *fp = NULL;
 108 
 109         if (errp == NULL)
 110                 errp = &err;
 111 
 112         if (elf == NULL) {
 113                 *errp = EINVAL;
 114                 return (NULL);
 115         }
 116 
 117         if (flags & ~CTF_ALLOW_MISSING_DEBUG) {
 118                 *errp = EINVAL;
 119                 return (NULL);
 120         }
 121 
 122         if (elf_kind(elf) != ELF_K_ELF) {
 123                 *errp = ECTF_FMT;
 124                 return (NULL);
 125         }
 126 
 127         switch (ctf_has_c_source(elf, errbuf, errlen)) {
 128         case CHR_ERROR:
 129                 *errp = ECTF_ELF;
 130                 return (NULL);
 131 
 132         case CHR_NO_C_SOURCE:
 133                 *errp = ECTF_CONVNOCSRC;
 134                 return (NULL);
 135 
 136         default:
 137                 break;
 138         }
 139 
 140         for (i = 0; i < NCONVERTS; i++) {
 141                 fp = NULL;
 142                 err = ctf_converters[i](fd, elf, nthrs, flags,
 143                     &fp, errbuf, errlen);
 144 
 145                 if (err != ECTF_CONVNODEBUG)
 146                         break;
 147         }
 148 
 149         if (err != 0) {
 150                 assert(fp == NULL);
 151                 *errp = err;
 152                 return (NULL);
 153         }
 154 
 155         if (label != NULL) {
 156                 if (ctf_add_label(fp, label, fp->ctf_typemax, 0) == CTF_ERR) {
 157                         *errp = ctf_errno(fp);
 158                         ctf_close(fp);
 159                         return (NULL);
 160                 }
 161                 if (ctf_update(fp) == CTF_ERR) {
 162                         *errp = ctf_errno(fp);
 163                         ctf_close(fp);
 164                         return (NULL);
 165                 }
 166         }
 167 
 168         return (fp);
 169 }
 170 
 171 ctf_file_t *
 172 ctf_fdconvert(int fd, const char *label, uint_t nthrs, uint_t flags, int *errp,
 173     char *errbuf, size_t errlen)
 174 {
 175         int err;
 176         Elf *elf;
 177         ctf_file_t *fp;
 178 
 179         if (errp == NULL)
 180                 errp = &err;
 181 
 182         elf = elf_begin(fd, ELF_C_READ, NULL);
 183         if (elf == NULL) {
 184                 *errp = ECTF_FMT;
 185                 return (NULL);
 186         }
 187 
 188         fp = ctf_elfconvert(fd, elf, label, nthrs, flags, errp, errbuf, errlen);
 189 
 190         (void) elf_end(elf);
 191         return (fp);
 192 }