/* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright (c) 2019, Joyent, Inc. */ /* * Verify that various type information for static symbols is accurate for the * file in question. To run this test, there's a global and static version of a * symbol and function that exists on a per-file basis. These will all have been * reproduced in the output file. As such, we need to iterate the symbol table * to know which version should be which and use that to drive things. */ #include #include #include #include #include #include #include #include "check-common.h" typedef struct check_map { const char *map_file; const char *map_type; } check_map_t; static const char *global_type = "int"; static check_map_t map[] = { { "test-a.c", "uint8_t" }, { "test-b.c", "uint16_t" }, { "test-c.c", "uint32_t" }, { "test-d.c", "uint64_t" }, { NULL } }; static const char * check_file_to_type(GElf_Sym *symp, const char *file, const char *name) { uint_t i; if (ELF32_ST_BIND(symp->st_info) == STB_GLOBAL) { return (global_type); } if (file == NULL) { warnx("encountered non-global symbol without STT_FILE info: %s", name); return (NULL); } for (i = 0; map[i].map_file != NULL; i++) { if (strcmp(map[i].map_file, file) == 0) { return (map[i].map_type); } } warnx("failed to find type mapping for symbol %s, file %s", name, file); return (NULL); } static int check_global(ctf_file_t *fp, GElf_Sym *symp, int symid, const char *file, const char *name) { const char *type; ctf_id_t tid; char buf[2048]; if ((type = check_file_to_type(symp, file, name)) == NULL) { return (EXIT_FAILURE); } if ((tid = ctf_lookup_by_symbol(fp, symid)) == CTF_ERR) { warnx("failed to get type for symbol %s (%d): %s", name, symid, ctf_errmsg(ctf_errno(fp))); return (EXIT_FAILURE); } if (ctf_type_name(fp, tid, buf, sizeof (buf)) == NULL) { warnx("failed to get type name for symbol %s (%d): %s", name, symid, ctf_errmsg(ctf_errno(fp))); return (EXIT_FAILURE); } if (strcmp(buf, type) != 0) { warnx("type mismatch for symbol %s (%d): found %s, expected %s", name, symid, buf, type); return (EXIT_FAILURE); } return (0); } static int check_mumble(ctf_file_t *fp, GElf_Sym *symp, int symid, const char *file, const char *name) { const char *type; ctf_funcinfo_t fi; ctf_id_t id, args; if ((type = check_file_to_type(symp, file, name)) == NULL) { return (EXIT_FAILURE); } if ((id = ctf_lookup_by_name(fp, type)) == CTF_ERR) { warnx("failed to lookup type id for %s: %s", type, ctf_errmsg(ctf_errno(fp))); return (EXIT_FAILURE); } if (ctf_func_info(fp, symid, &fi) != 0) { warnx("failed to get function information for %s (%d): %s", name, symid, ctf_errmsg(ctf_errno(fp))); return (EXIT_FAILURE); } if (fi.ctc_argc != 1) { warnx("argument count mismatch for symbol %s (%d): found %u, " "expected %d", name, symid, fi.ctc_argc, 1); return (EXIT_FAILURE); } if (fi.ctc_flags != 0) { warnx("function flags mismatch for symbol %s (%d): found %u, " "expected %d", name, symid, fi.ctc_flags, 0); return (EXIT_FAILURE); } if (fi.ctc_return != id) { warnx("return value mismatch for symbol %s (%d): found %ld, " "expected %ld", name, symid, fi.ctc_return, id); return (EXIT_FAILURE); } if (ctf_func_args(fp, symid, 1, &args) != 0) { warnx("failed to get function arguments for symbol %s (%d): %s", name, symid, ctf_errmsg(ctf_errno(fp))); return (EXIT_FAILURE); } if (args != id) { warnx("argument mismatch for symbol %s (%d): found %ld, " "expected %ld", name, symid, args, id); return (EXIT_FAILURE); } return (0); } static int check_merge_static(const char *file, ctf_file_t *fp, Elf *elf) { Elf_Scn *scn = NULL, *symscn = NULL; Elf_Data *symdata = NULL; GElf_Shdr symhdr; ulong_t nsyms; int i; const char *curfile = NULL; int ret = 0; while ((scn = elf_nextscn(elf, scn)) != NULL) { if (gelf_getshdr(scn, &symhdr) == NULL) { warnx("failed to get section header: %s", elf_errmsg(elf_errno())); return (EXIT_FAILURE); } if (symhdr.sh_type == SHT_SYMTAB) { symscn = scn; break; } } if (symscn == NULL) { warnx("failed to find symbol table for %s", file); return (EXIT_FAILURE); } if ((symdata = elf_getdata(symscn, NULL)) == NULL) { warnx("failed to get data for symbol table %s: %s", file, elf_errmsg(elf_errno())); return (EXIT_FAILURE); } if (symhdr.sh_link == SHN_XINDEX) { warnx("test does not support extended ELF sections!"); return (EXIT_FAILURE); } nsyms = symhdr.sh_size / symhdr.sh_entsize; if (nsyms > INT_MAX) { warnx("file contains more symbols than libelf can iterate"); return (EXIT_FAILURE); } for (i = 1; i < (int)nsyms; i++) { GElf_Sym sym; const char *name; uint_t type; if (gelf_getsym(symdata, i, &sym) == NULL) { warnx("failed to get data about symbol %d", i); return (EXIT_FAILURE); } if ((name = elf_strptr(elf, symhdr.sh_link, sym.st_name)) == NULL) { warnx("failed to get name for symbol %d", i); return (EXIT_FAILURE); } type = GELF_ST_TYPE(sym.st_info); if (type == STT_FILE) { curfile = name; continue; } if (strcmp(name, "global") == 0) { ret |= check_global(fp, &sym, i, curfile, name); } else if (strcmp(name, "mumble") == 0) { ret |= check_mumble(fp, &sym, i, curfile, name); } } return (ret); } int main(int argc, char *argv[]) { int i, ret = 0; if (argc < 2) { errx(EXIT_FAILURE, "missing test files"); } if (elf_version(EV_CURRENT) == EV_NONE) { errx(EXIT_FAILURE, "failed to initialize libelf"); } for (i = 1; i < argc; i++) { int fd; ctf_file_t *fp; Elf *elf; if ((fd = open(argv[i], O_RDONLY)) < 0) { warn("failed to open %s", argv[i]); ret = EXIT_FAILURE; continue; } if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { warnx("failed to open libelf handle to %s", argv[i]); ret = EXIT_FAILURE; (void) close(fd); continue; } if ((fp = ctf_open(argv[i], &ret)) == NULL) { warnx("failed to open %s: %s", argv[i], ctf_errmsg(ret)); ret = EXIT_FAILURE; (void) close(fd); (void) elf_end(elf); continue; } if (check_merge_static(argv[i], fp, elf) != 0) { ret = EXIT_FAILURE; } ctf_close(fp); (void) close(fd); (void) elf_end(elf); } return (ret); }