#include <strings.h>
#include <libctf.h>
#include "check-common.h"
typedef struct ctftests_lookup_cb {
ctf_file_t *clc_fp;
ctf_id_t clc_id;
const char *clc_name;
} ctftests_lookup_cb_t;
typedef struct ctftest_member_cb {
ctf_file_t *cmc_fp;
const check_member_t *cmc_members;
const char *cmc_name;
} ctftest_member_cb_t;
static int
ctftest_lookup_type_cb(ctf_id_t id, boolean_t root, void *arg)
{
char buf[2048];
ctftests_lookup_cb_t *clc = arg;
if (ctf_type_name(clc->clc_fp, id, buf, sizeof (buf)) == NULL)
return (0);
if (strcmp(buf, clc->clc_name) != 0)
return (0);
clc->clc_id = id;
return (1);
}
static ctf_id_t
ctftest_lookup_type(ctf_file_t *fp, const char *name)
{
ctftests_lookup_cb_t clc;
clc.clc_fp = fp;
clc.clc_id = CTF_ERR;
clc.clc_name = name;
(void) ctf_type_iter(fp, B_TRUE, ctftest_lookup_type_cb, &clc);
return (clc.clc_id);
}
static int
ctftest_lookup_object_cb(const char *obj, ctf_id_t type, ulong_t idx, void *arg)
{
ctftests_lookup_cb_t *clc = arg;
if (strcmp(obj, clc->clc_name) == 0) {
clc->clc_id = type;
return (1);
}
return (0);
}
static ctf_id_t
ctftest_lookup_symbol(ctf_file_t *fp, const char *name)
{
ctftests_lookup_cb_t clc;
clc.clc_fp = fp;
clc.clc_id = CTF_ERR;
clc.clc_name = name;
(void) ctf_object_iter(fp, ctftest_lookup_object_cb, &clc);
return (clc.clc_id);
}
typedef struct ctf_function_cb {
const char *cfc_name;
ulong_t *cfc_symp;
ctf_funcinfo_t *cfc_fip;
} ctf_function_cb_t;
static int
ctftest_lookup_function_cb(const char *name, ulong_t symidx,
ctf_funcinfo_t *fip, void *arg)
{
ctf_function_cb_t *cfc = arg;
if (strcmp(name, cfc->cfc_name) != 0)
return (0);
*cfc->cfc_symp = symidx;
*cfc->cfc_fip = *fip;
return (1);
}
static boolean_t
ctftest_lookup_function(ctf_file_t *fp, const char *name, ulong_t *symp,
ctf_funcinfo_t *fip)
{
ctf_function_cb_t cfc;
*symp = 0;
cfc.cfc_name = name;
cfc.cfc_symp = symp;
cfc.cfc_fip = fip;
(void) ctf_function_iter(fp, ctftest_lookup_function_cb, &cfc);
return (*symp == 0 ? B_FALSE : B_TRUE);
}
boolean_t
ctftest_check_numbers(ctf_file_t *fp, const check_number_t *tests)
{
uint_t i;
boolean_t ret = B_TRUE;
for (i = 0; tests[i].cn_tname != NULL; i++) {
ctf_id_t id;
ctf_encoding_t enc;
if (ctftest_skip(tests[i].cn_skips)) {
warnx("skipping check numbers test %s due to known "
"compiler issue", tests[i].cn_tname);
continue;
}
id = ctftest_lookup_type(fp, tests[i].cn_tname);
if (id == CTF_ERR) {
warnx("failed to look up %s", tests[i].cn_tname);
ret = B_FALSE;
continue;
}
if (ctf_type_kind(fp, id) != tests[i].cn_kind) {
warnx("type kind mismatch for %s: got %u, expected %u",
tests[i].cn_tname, ctf_type_kind(fp, id),
tests[i].cn_kind);
ret = B_FALSE;
continue;
}
if (ctf_type_encoding(fp, id, &enc) == CTF_ERR) {
warnx("failed to get type encoding for %s: %s",
tests[i].cn_tname, ctf_errmsg(ctf_errno(fp)));
ret = B_FALSE;
continue;
}
if (enc.cte_format != tests[i].cn_flags) {
warnx("encoding flags mismatch for %s: got 0x%x, "
"expected 0x%x", tests[i].cn_tname, enc.cte_format,
tests[i].cn_flags);
ret = B_FALSE;
continue;
}
if (enc.cte_offset != tests[i].cn_offset) {
warnx("encoding offset mismatch for %s: got 0x%x, "
"expected 0x%x", tests[i].cn_tname, enc.cte_offset,
tests[i].cn_offset);
ret = B_FALSE;
continue;
}
if (enc.cte_bits != tests[i].cn_size) {
warnx("encoding size mismatch for %s: got 0x%x, "
"expected 0x%x", tests[i].cn_tname, enc.cte_bits,
tests[i].cn_size);
ret = B_FALSE;
continue;
}
}
return (ret);
}
typedef struct ctftests_symbol_cb {
ctf_file_t *csc_fp;
boolean_t csc_ret;
const check_symbol_t *csc_tests;
} ctftest_symbol_cb_t;
static int
ctftest_check_symbol_cb(const char *obj, ctf_id_t type, ulong_t idx, void *arg)
{
ctftest_symbol_cb_t *cb = arg;
const check_symbol_t *tests = cb->csc_tests;
ctf_file_t *fp = cb->csc_fp;
uint_t i;
for (i = 0; tests[i].cs_symbol != NULL; i++) {
ctf_id_t id;
if (strcmp(obj, tests[i].cs_symbol) != 0)
continue;
id = ctftest_lookup_type(fp, tests[i].cs_type);
if (id == CTF_ERR) {
warnx("failed to lookup type %s for symbol %s",
tests[i].cs_type, tests[i].cs_symbol);
cb->csc_ret = B_FALSE;
return (0);
}
if (id != type) {
warnx("type mismatch for symbol %s, has type id %ld"
", but specified type %s has id %ld",
tests[i].cs_symbol, type, tests[i].cs_type, id);
cb->csc_ret = B_FALSE;
return (0);
}
}
return (0);
}
boolean_t
ctftest_check_symbols(ctf_file_t *fp, const check_symbol_t *tests)
{
ctftest_symbol_cb_t cb;
cb.csc_fp = fp;
cb.csc_ret = B_TRUE;
cb.csc_tests = tests;
if (ctf_object_iter(fp, ctftest_check_symbol_cb, &cb) != 0)
return (B_FALSE);
return (cb.csc_ret);
}
boolean_t
ctftest_check_descent(const char *symbol, ctf_file_t *fp,
const check_descent_t *tests, boolean_t quiet)
{
ctf_id_t base;
uint_t layer = 0;
base = ctftest_lookup_symbol(fp, symbol);
if (base == CTF_ERR) {
warnx("failed to lookup type for symbol %s", symbol);
return (B_FALSE);
}
while (tests->cd_tname != NULL) {
ctf_id_t tid;
int kind;
ctf_arinfo_t ari;
if (base == CTF_ERR) {
if (!quiet) {
warnx("encountered non-reference type at layer "
"%u while still expecting type %s for "
"symbol %s", layer,
tests->cd_tname, symbol);
}
return (B_FALSE);
}
tid = ctftest_lookup_type(fp, tests->cd_tname);
if (tid == CTF_ERR) {
if (!quiet) {
warnx("failed to lookup type %s",
tests->cd_tname);
}
return (B_FALSE);
}
if (tid != base) {
if (!quiet) {
warnx("type mismatch at layer %u: found "
"id %ld, but expecting type id %ld for "
"type %s, symbol %s", layer, base, tid,
tests->cd_tname, symbol);
}
return (B_FALSE);
}
kind = ctf_type_kind(fp, base);
if (kind != tests->cd_kind) {
if (!quiet) {
warnx("type kind mismatch at layer %u: found "
"kind %u, but expected kind %u for %s, "
"symbol %s", layer, kind, tests->cd_kind,
tests->cd_tname, symbol);
}
return (B_FALSE);
}
switch (kind) {
case CTF_K_ARRAY:
if (ctf_array_info(fp, base, &ari) == CTF_ERR) {
if (!quiet) {
warnx("failed to lookup array info at "
"layer %ld for type %s, "
"symbol %s: %s", base,
tests->cd_tname, symbol,
ctf_errmsg(ctf_errno(fp)));
}
return (B_FALSE);
}
if (tests->cd_nents != ari.ctr_nelems) {
if (!quiet) {
warnx("array element mismatch at layer "
"%u for type %s, symbol %s: found "
"%u, expected %u", layer,
tests->cd_tname, symbol,
ari.ctr_nelems, tests->cd_nents);
}
return (B_FALSE);
}
tid = ctftest_lookup_type(fp, tests->cd_contents);
if (tid == CTF_ERR) {
if (!quiet) {
warnx("failed to look up type %s",
tests->cd_contents);
}
return (B_FALSE);
}
if (ari.ctr_contents != tid) {
if (!quiet) {
warnx("array contents mismatch at "
"layer %u for type %s, symbol %s: "
"found %ld, expected %s/%ld",
layer, tests->cd_tname,
symbol, ari.ctr_contents,
tests->cd_contents, tid);
}
return (B_FALSE);
}
base = ari.ctr_contents;
break;
default:
base = ctf_type_reference(fp, base);
break;
}
tests++;
layer++;
}
if (base != CTF_ERR) {
if (!quiet) {
warnx("found additional type %ld in chain, "
"but expected no more", base);
}
return (B_FALSE);
}
return (B_TRUE);
}
int
ctftest_check_enum_count(const char *name, int value, void *arg)
{
uint_t *u = arg;
*u = *u + 1;
return (0);
}
int
ctftest_check_enum_value(const char *name, int value, void *arg)
{
uint_t i;
const check_enum_t *enums = arg;
for (i = 0; enums[i].ce_name != NULL; i++) {
if (strcmp(enums[i].ce_name, name) != 0)
continue;
if (enums[i].ce_value == (int64_t)value)
return (0);
warnx("enum %s value mismatch: found %d, expected %" PRId64,
name, value, enums[i].ce_value);
return (1);
}
warnx("found no matching entry for enum member %s", name);
return (1);
}
boolean_t
ctftest_check_enum(const char *type, ctf_file_t *fp, const check_enum_t *enums)
{
int ret;
uint_t tcount, ecount;
ctf_id_t base;
if ((base = ctftest_lookup_type(fp, type)) == CTF_ERR) {
warnx("Failed to look up type %s", type);
return (B_FALSE);
}
if (ctf_type_kind(fp, base) != CTF_K_ENUM) {
warnx("%s is not an enum", type);
return (B_FALSE);
}
tcount = 0;
while (enums[tcount].ce_name != NULL) {
tcount++;
}
ecount = 0;
if (ctf_enum_iter(fp, base, ctftest_check_enum_count, &ecount) != 0) {
warnx("failed to walk enum %s: %s", type,
ctf_errmsg(ctf_errno(fp)));
return (B_FALSE);
}
if (tcount != ecount) {
warnx("enum value mismatch: expected %u values, but found %u",
tcount, ecount);
return (B_FALSE);
}
if ((ret = ctf_enum_iter(fp, base, ctftest_check_enum_value,
(void *)enums)) != 0) {
if (ret == -1) {
warnx("failed to walk enum %s: %s", type,
ctf_errmsg(ctf_errno(fp)));
}
return (B_FALSE);
}
return (B_TRUE);
}
int
ctftest_check_member_count(const char *mname, ctf_id_t mtype, ulong_t bitoff,
void *arg)
{
uint_t *countp = arg;
*countp = *countp + 1;
return (0);
}
int
ctftest_check_members_cb(const char *mname, ctf_id_t mtype, ulong_t bitoff,
void *arg)
{
uint_t i;
const ctftest_member_cb_t *cmc = arg;
const check_member_t *members = cmc->cmc_members;
ctf_file_t *fp = cmc->cmc_fp;
for (i = 0; members[i].cm_name != NULL; i++) {
boolean_t bad = B_FALSE;
char buf[2048];
if (strcmp(mname, members[i].cm_name) != 0)
continue;
if (bitoff != members[i].cm_offset) {
warnx("member %s of type %s has mismatched bit offset: "
"found %lu, expected %lu", mname, cmc->cmc_name,
bitoff, members[i].cm_offset);
bad = B_TRUE;
}
if (ctf_type_name(fp, mtype, buf, sizeof (buf)) == NULL) {
warnx("failed to obtain type name for member %s: %s",
mname, ctf_errmsg(ctf_errno(fp)));
bad = B_TRUE;
} else if (strcmp(buf, members[i].cm_type) != 0) {
warnx("member %s has bad type, found %s, expected %s",
mname, buf, members[i].cm_type);
bad = B_TRUE;
}
return (bad ? 1 : 0);
}
warnx("found no matching entry for member %s of type %s", mname,
cmc->cmc_name);
return (1);
}
boolean_t
ctftest_check_members(const char *type, ctf_file_t *fp, int kind,
size_t size, const check_member_t *members)
{
int ret;
uint_t tcount, mcount;
ctf_id_t base;
ctftest_member_cb_t cmc;
if ((base = ctftest_lookup_type(fp, type)) == CTF_ERR) {
warnx("failed to look up type %s", type);
return (B_FALSE);
}
if (ctf_type_kind(fp, base) != kind) {
warnx("%s has kind %s, expected %s", type,
ctf_kind_name(fp, ctf_type_kind(fp, base)),
ctf_kind_name(fp, kind));
return (B_FALSE);
}
if (size != ctf_type_size(fp, base)) {
warnx("%s has bad size, expected %zu, found %zd",
type, size, ctf_type_size(fp, base));
return (B_FALSE);
}
tcount = 0;
while (members[tcount].cm_name != NULL) {
tcount++;
}
mcount = 0;
if (ctf_member_iter(fp, base, ctftest_check_member_count, &mcount) !=
0) {
warnx("failed to walk members of %s: %s", type,
ctf_errmsg(ctf_errno(fp)));
return (B_FALSE);
}
if (tcount != mcount) {
warnx("type member mismatch: expected %u values, but found %u",
tcount, mcount);
return (B_FALSE);
}
cmc.cmc_fp = fp;
cmc.cmc_members = members;
cmc.cmc_name = type;
if ((ret = ctf_member_iter(fp, base, ctftest_check_members_cb,
&cmc)) != 0) {
if (ret == -1) {
warnx("failed to walk type %s: %s", type,
ctf_errmsg(ctf_errno(fp)));
}
return (B_FALSE);
}
return (B_TRUE);
}
boolean_t
ctftest_check_member_info(const char *type, ctf_file_t *fp, int kind,
size_t size, const check_member_t *members)
{
boolean_t ret = B_TRUE;
ctf_id_t base;
if ((base = ctftest_lookup_type(fp, type)) == CTF_ERR) {
warnx("failed to look up type %s", type);
return (B_FALSE);
}
if (ctf_type_kind(fp, base) != kind) {
warnx("%s has kind %s, expected %s", type,
ctf_kind_name(fp, ctf_type_kind(fp, base)),
ctf_kind_name(fp, kind));
return (B_FALSE);
}
if (size != ctf_type_size(fp, base)) {
warnx("%s has bad size, expected %zu, found %zd",
type, size, ctf_type_size(fp, base));
ret = B_FALSE;
}
for (size_t i = 0; members[i].cm_name != NULL; i++) {
ctf_membinfo_t mi;
char buf[2048];
if (ctf_member_info(fp, base, members[i].cm_name, &mi) ==
CTF_ERR) {
warnx("failed to look up member %s in %s: %s",
members[i].cm_name, type,
ctf_errmsg(ctf_errno(fp)));
}
if (mi.ctm_offset != members[i].cm_offset) {
warnx("member %s of type %s has mismatched bit offset: "
"found %lu, expected %lu", members[i].cm_name, type,
mi.ctm_offset, members[i].cm_offset);
ret = B_FALSE;
}
if (ctf_type_name(fp, mi.ctm_type, buf, sizeof (buf)) == NULL) {
warnx("failed to obtain type name for member %s in %s: "
"%s", members[i].cm_name, type,
ctf_errmsg(ctf_errno(fp)));
ret = B_FALSE;
} else if (strcmp(buf, members[i].cm_type) != 0) {
warnx("member %s in %s has bad type, found %s, "
"expected %s", members[i].cm_name, type, buf,
members[i].cm_type);
ret = B_FALSE;
}
}
return (ret);
}
boolean_t
ctftest_check_function(const char *symbol, ctf_file_t *fp, const char *rtype,
uint_t nargs, uint_t flags, const char **argv)
{
ulong_t sym;
ctf_funcinfo_t fi;
uint_t i;
boolean_t ret = B_TRUE;
ctf_id_t *args;
char buf[2048];
if (!ctftest_lookup_function(fp, symbol, &sym, &fi)) {
warnx("failed to look up function %s", symbol);
return (B_FALSE);
}
if (ctf_type_name(fp, fi.ctc_return, buf, sizeof (buf)) == NULL) {
warnx("failed to lookup return type name for function %s",
symbol);
ret = B_FALSE;
} else if (strcmp(rtype, buf) != 0) {
warnx("return type has wrong type: found %s, expected %s",
buf, rtype);
ret = B_FALSE;
}
if (nargs != fi.ctc_argc) {
warnx("function argument mismatch: found %u, expected %u",
fi.ctc_argc, nargs);
ret = B_FALSE;
}
if (flags != fi.ctc_flags) {
warnx("function flags mismatch, found 0x%x, expected 0x%x",
fi.ctc_flags, flags);
ret = B_FALSE;
}
if (!ret || fi.ctc_argc == 0) {
return (ret);
}
if ((args = calloc(fi.ctc_argc, sizeof (ctf_id_t))) == NULL) {
warnx("failed to allocate memory for function arguments");
return (B_FALSE);
}
if (ctf_func_args(fp, sym, fi.ctc_argc, args) != 0) {
warnx("failed to get function information: %s",
ctf_errmsg(ctf_errno(fp)));
free(args);
return (B_FALSE);
}
for (i = 0; i < fi.ctc_argc; i++) {
if (ctf_type_name(fp, args[i], buf, sizeof (buf)) == NULL) {
warnx("failed to obtain type name for argument %u: %s",
i, ctf_errmsg(ctf_errno(fp)));
ret = B_FALSE;
break;
}
if (strcmp(buf, argv[i]) != 0) {
warnx("argument %u has wrong type: found %s, "
"expected %s", i, buf, argv[i]);
ret = B_FALSE;
break;
}
}
free(args);
return (ret);
}
boolean_t
ctftest_check_fptr(const char *type, ctf_file_t *fp, const char *rtype,
uint_t nargs, uint_t flags, const char **argv)
{
ctf_id_t tid;
ctf_funcinfo_t fi;
uint_t i;
boolean_t ret = B_TRUE;
ctf_id_t *args;
char buf[2048];
if ((tid = ctf_lookup_by_name(fp, type)) == CTF_ERR) {
warnx("failed to look up type %s: %s", type,
ctf_errmsg(ctf_errno(fp)));
return (B_FALSE);
}
if ((tid = ctf_type_resolve(fp, tid)) == CTF_ERR) {
warnx("failed to convert type %s to base type: %s", type,
ctf_errmsg(ctf_errno(fp)));
return (B_FALSE);
}
if (ctf_type_kind(fp, tid) == CTF_K_POINTER &&
(tid = ctf_type_reference(fp, tid)) == CTF_ERR) {
warnx("failed to convert type %s to base type: %s", type,
ctf_errmsg(ctf_errno(fp)));
return (B_FALSE);
}
if (ctf_func_info_by_id(fp, tid, &fi) != 0) {
warnx("failed to get function information for type %s: %s",
type, ctf_errmsg(ctf_errno(fp)));
return (B_FALSE);
}
if (ctf_type_name(fp, fi.ctc_return, buf, sizeof (buf)) == NULL) {
warnx("failed to lookup return type name for function %s",
type);
ret = B_FALSE;
} else if (strcmp(rtype, buf) != 0) {
warnx("return type has wrong type: found %s, expected %s",
buf, rtype);
ret = B_FALSE;
}
if (nargs != fi.ctc_argc) {
warnx("function argument mismatch: found %u, expected %u",
fi.ctc_argc, nargs);
ret = B_FALSE;
}
if (flags != fi.ctc_flags) {
warnx("function flags mismatch, found 0x%x, expected 0x%x",
fi.ctc_flags, flags);
ret = B_FALSE;
}
if (!ret || fi.ctc_argc == 0) {
return (ret);
}
if ((args = calloc(fi.ctc_argc, sizeof (ctf_id_t))) == NULL) {
warnx("failed to allocate memory for function arguments");
return (B_FALSE);
}
if (ctf_func_args_by_id(fp, tid, fi.ctc_argc, args) != 0) {
warnx("failed to get function information: %s",
ctf_errmsg(ctf_errno(fp)));
free(args);
return (B_FALSE);
}
for (i = 0; i < fi.ctc_argc; i++) {
if (ctf_type_name(fp, args[i], buf, sizeof (buf)) == NULL) {
warnx("failed to obtain type name for argument %u: %s",
i, ctf_errmsg(ctf_errno(fp)));
ret = B_FALSE;
break;
}
if (strcmp(buf, argv[i]) != 0) {
warnx("argument %u has wrong type: found %s, "
"expected %s", i, buf, argv[i]);
ret = B_FALSE;
break;
}
}
free(args);
return (ret);
}
boolean_t
ctftest_check_size(const char *type, ctf_file_t *fp, size_t size)
{
ctf_id_t base;
if ((base = ctftest_lookup_type(fp, type)) == CTF_ERR) {
warnx("Failed to look up type %s", type);
return (B_FALSE);
}
if (size != ctf_type_size(fp, base)) {
warnx("%s has bad size, expected %zu, found %zd",
type, size, ctf_type_size(fp, base));
return (B_FALSE);
}
return (B_TRUE);
}
typedef struct ctftest_duplicates {
ctf_file_t *ctd_fp;
char **ctd_names;
size_t ctd_len;
size_t ctd_curent;
boolean_t ctd_ret;
} ctftest_duplicates_t;
static int
ctftest_duplicates_cb(ctf_id_t id, boolean_t root, void *arg)
{
char buf[2048];
ctftest_duplicates_t *dup = arg;
size_t i;
if (ctf_type_name(dup->ctd_fp, id, buf, sizeof (buf)) == NULL) {
warnx("failed to lookup name for id %ld", id);
dup->ctd_ret = B_FALSE;
return (1);
}
for (i = 0; i < dup->ctd_curent; i++) {
if (strcmp(buf, dup->ctd_names[i]) == 0) {
warnx("encountered duplicate type '%s'", buf);
dup->ctd_ret = B_FALSE;
return (0);
}
}
if (dup->ctd_curent == dup->ctd_len) {
char **n;
size_t newlen = dup->ctd_len * 2;
n = recallocarray(dup->ctd_names, dup->ctd_len, newlen,
sizeof (char *));
if (n == NULL) {
warnx("failed to resize type name array");
dup->ctd_ret = B_FALSE;
return (1);
}
dup->ctd_names = n;
dup->ctd_len = newlen;
}
dup->ctd_names[dup->ctd_curent] = strdup(buf);
if (dup->ctd_names[dup->ctd_curent] == NULL) {
warn("failed to duplicate type name");
dup->ctd_ret = B_FALSE;
return (1);
}
dup->ctd_curent++;
return (0);
}
boolean_t
ctftest_duplicates(ctf_file_t *fp)
{
size_t i;
ctftest_duplicates_t d;
bzero(&d, sizeof (d));
d.ctd_fp = fp;
d.ctd_len = 4;
d.ctd_ret = B_TRUE;
d.ctd_names = recallocarray(NULL, 0, d.ctd_len, sizeof (char *));
if (d.ctd_names == NULL) {
warnx("failed to allocate duplicate name storage");
return (B_FALSE);
}
(void) ctf_type_iter(fp, B_TRUE, ctftest_duplicates_cb, &d);
for (i = 0; i < d.ctd_curent; i++) {
free(d.ctd_names[i]);
}
free(d.ctd_names);
return (d.ctd_ret);
}
boolean_t
ctftest_skip(check_skip_t skip)
{
const char *compiler;
if (skip == 0) {
return (B_FALSE);
}
compiler = getenv("ctf_cc_type");
if (compiler == NULL) {
return (B_FALSE);
}
if ((skip & SKIP_CLANG) != 0 && strcmp(compiler, "clang") == 0)
return (B_TRUE);
return (B_FALSE);
}