#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <libelf.h>
#include <gelf.h>
#include <limits.h>
#include <strings.h>
#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);
}