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 } 291