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 (c) 2019, Joyent, Inc.
  14  */
  15 
  16 /*
  17  * Verify that various type information for static symbols is accurate for the
  18  * file in question. To run this test, there's a global and static version of a
  19  * symbol and function that exists on a per-file basis. These will all have been
  20  * reproduced in the output file. As such, we need to iterate the symbol table
  21  * to know which version should be which and use that to drive things.
  22  */
  23 
  24 #include <sys/types.h>
  25 #include <sys/stat.h>
  26 #include <fcntl.h>
  27 #include <libelf.h>
  28 #include <gelf.h>
  29 #include <limits.h>
  30 #include <strings.h>
  31 
  32 #include "check-common.h"
  33 
  34 typedef struct check_map {
  35         const char *map_file;
  36         const char *map_type;
  37 } check_map_t;
  38 
  39 static const char *global_type = "int";
  40 static check_map_t map[] = {
  41         { "test-a.c", "uint8_t" },
  42         { "test-b.c", "uint16_t" },
  43         { "test-c.c", "uint32_t" },
  44         { "test-d.c", "uint64_t" },
  45         { NULL }
  46 };
  47 
  48 static const char *
  49 check_file_to_type(GElf_Sym *symp, const char *file, const char *name)
  50 {
  51         uint_t i;
  52 
  53         if (ELF32_ST_BIND(symp->st_info) == STB_GLOBAL) {
  54                 return (global_type);
  55         }
  56 
  57         if (file == NULL) {
  58                 warnx("encountered non-global symbol without STT_FILE info: %s",
  59                     name);
  60                 return (NULL);
  61         }
  62 
  63         for (i = 0; map[i].map_file != NULL; i++) {
  64                 if (strcmp(map[i].map_file, file) == 0) {
  65                         return (map[i].map_type);
  66                 }
  67         }
  68 
  69         warnx("failed to find type mapping for symbol %s, file %s", name, file);
  70         return (NULL);
  71 }
  72 
  73 static int
  74 check_global(ctf_file_t *fp, GElf_Sym *symp, int symid, const char *file,
  75     const char *name)
  76 {
  77         const char *type;
  78         ctf_id_t tid;
  79         char buf[2048];
  80 
  81         if ((type = check_file_to_type(symp, file, name)) == NULL) {
  82                 return (EXIT_FAILURE);
  83         }
  84 
  85         if ((tid = ctf_lookup_by_symbol(fp, symid)) == CTF_ERR) {
  86                 warnx("failed to get type for symbol %s (%d): %s", name, symid,
  87                     ctf_errmsg(ctf_errno(fp)));
  88                 return (EXIT_FAILURE);
  89         }
  90 
  91         if (ctf_type_name(fp, tid, buf, sizeof (buf)) == NULL) {
  92                 warnx("failed to get type name for symbol %s (%d): %s",
  93                     name, symid, ctf_errmsg(ctf_errno(fp)));
  94                 return (EXIT_FAILURE);
  95         }
  96 
  97         if (strcmp(buf, type) != 0) {
  98                 warnx("type mismatch for symbol %s (%d): found %s, expected %s",
  99                     name, symid, buf, type);
 100                 return (EXIT_FAILURE);
 101         }
 102 
 103         return (0);
 104 }
 105 
 106 static int
 107 check_mumble(ctf_file_t *fp, GElf_Sym *symp, int symid, const char *file,
 108     const char *name)
 109 {
 110         const char *type;
 111         ctf_funcinfo_t fi;
 112         ctf_id_t id, args;
 113 
 114         if ((type = check_file_to_type(symp, file, name)) == NULL) {
 115                 return (EXIT_FAILURE);
 116         }
 117 
 118         if ((id = ctf_lookup_by_name(fp, type)) == CTF_ERR) {
 119                 warnx("failed to lookup type id for %s: %s", type,
 120                     ctf_errmsg(ctf_errno(fp)));
 121                 return (EXIT_FAILURE);
 122         }
 123 
 124         if (ctf_func_info(fp, symid, &fi) != 0) {
 125                 warnx("failed to get function information for %s (%d): %s",
 126                     name, symid, ctf_errmsg(ctf_errno(fp)));
 127                 return (EXIT_FAILURE);
 128         }
 129 
 130         if (fi.ctc_argc != 1) {
 131                 warnx("argument count mismatch for symbol %s (%d): found %u, "
 132                     "expected %d", name, symid, fi.ctc_argc, 1);
 133                 return (EXIT_FAILURE);
 134         }
 135 
 136         if (fi.ctc_flags != 0) {
 137                 warnx("function flags mismatch for symbol %s (%d): found %u, "
 138                     "expected %d", name, symid, fi.ctc_flags, 0);
 139                 return (EXIT_FAILURE);
 140         }
 141 
 142         if (fi.ctc_return != id) {
 143                 warnx("return value mismatch for symbol %s (%d): found %ld, "
 144                     "expected %ld", name, symid, fi.ctc_return, id);
 145                 return (EXIT_FAILURE);
 146         }
 147 
 148         if (ctf_func_args(fp, symid, 1, &args) != 0) {
 149                 warnx("failed to get function arguments for symbol %s (%d): %s",
 150                     name, symid, ctf_errmsg(ctf_errno(fp)));
 151                 return (EXIT_FAILURE);
 152         }
 153 
 154         if (args != id) {
 155                 warnx("argument mismatch for symbol %s (%d): found %ld, "
 156                     "expected %ld", name, symid, args, id);
 157                 return (EXIT_FAILURE);
 158         }
 159 
 160         return (0);
 161 }
 162 
 163 static int
 164 check_merge_static(const char *file, ctf_file_t *fp, Elf *elf)
 165 {
 166         Elf_Scn *scn = NULL, *symscn = NULL;
 167         Elf_Data *symdata = NULL;
 168         GElf_Shdr symhdr;
 169         ulong_t nsyms;
 170         int i;
 171         const char *curfile = NULL;
 172         int ret = 0;
 173 
 174         while ((scn = elf_nextscn(elf, scn)) != NULL) {
 175                 if (gelf_getshdr(scn, &symhdr) == NULL) {
 176                         warnx("failed to get section header: %s",
 177                             elf_errmsg(elf_errno()));
 178                         return (EXIT_FAILURE);
 179                 }
 180 
 181                 if (symhdr.sh_type == SHT_SYMTAB) {
 182                         symscn = scn;
 183                         break;
 184                 }
 185         }
 186 
 187         if (symscn == NULL) {
 188                 warnx("failed to find symbol table for %s", file);
 189                 return (EXIT_FAILURE);
 190         }
 191 
 192         if ((symdata = elf_getdata(symscn, NULL)) == NULL) {
 193                 warnx("failed to get data for symbol table %s: %s", file,
 194                     elf_errmsg(elf_errno()));
 195                 return (EXIT_FAILURE);
 196         }
 197 
 198         if (symhdr.sh_link == SHN_XINDEX) {
 199                 warnx("test does not support extended ELF sections!");
 200                 return (EXIT_FAILURE);
 201         }
 202         nsyms = symhdr.sh_size / symhdr.sh_entsize;
 203         if (nsyms > INT_MAX) {
 204                 warnx("file contains more symbols than libelf can iterate");
 205                 return (EXIT_FAILURE);
 206         }
 207 
 208         for (i = 1; i < (int)nsyms; i++) {
 209                 GElf_Sym sym;
 210                 const char *name;
 211                 uint_t type;
 212 
 213                 if (gelf_getsym(symdata, i, &sym) == NULL) {
 214                         warnx("failed to get data about symbol %d", i);
 215                         return (EXIT_FAILURE);
 216                 }
 217 
 218                 if ((name = elf_strptr(elf, symhdr.sh_link, sym.st_name)) ==
 219                     NULL) {
 220                         warnx("failed to get name for symbol %d", i);
 221                         return (EXIT_FAILURE);
 222                 }
 223 
 224                 type = GELF_ST_TYPE(sym.st_info);
 225                 if (type == STT_FILE) {
 226                         curfile = name;
 227                         continue;
 228                 }
 229 
 230                 if (strcmp(name, "global") == 0) {
 231                         ret |= check_global(fp, &sym, i, curfile, name);
 232                 } else if (strcmp(name, "mumble") == 0) {
 233                         ret |= check_mumble(fp, &sym, i, curfile, name);
 234                 }
 235         }
 236 
 237         return (ret);
 238 }
 239 
 240 int
 241 main(int argc, char *argv[])
 242 {
 243         int i, ret = 0;
 244 
 245         if (argc < 2) {
 246                 errx(EXIT_FAILURE, "missing test files");
 247         }
 248 
 249         if (elf_version(EV_CURRENT) == EV_NONE) {
 250                 errx(EXIT_FAILURE, "failed to initialize libelf");
 251         }
 252 
 253         for (i = 1; i < argc; i++) {
 254                 int fd;
 255                 ctf_file_t *fp;
 256                 Elf *elf;
 257 
 258                 if ((fd = open(argv[i], O_RDONLY)) < 0) {
 259                         warn("failed to open %s", argv[i]);
 260                         ret = EXIT_FAILURE;
 261                         continue;
 262                 }
 263 
 264                 if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
 265                         warnx("failed to open libelf handle to %s", argv[i]);
 266                         ret = EXIT_FAILURE;
 267                         (void) close(fd);
 268                         continue;
 269                 }
 270 
 271                 if ((fp = ctf_open(argv[i], &ret)) == NULL) {
 272                         warnx("failed to open %s: %s", argv[i],
 273                             ctf_errmsg(ret));
 274                         ret = EXIT_FAILURE;
 275                         (void) close(fd);
 276                         (void) elf_end(elf);
 277                         continue;
 278                 }
 279 
 280                 if (check_merge_static(argv[i], fp, elf) != 0) {
 281                         ret = EXIT_FAILURE;
 282                 }
 283 
 284                 ctf_close(fp);
 285                 (void) close(fd);
 286                 (void) elf_end(elf);
 287         }
 288 
 289         return (ret);
 290 }