#include <libctf.h>
#include <ctf_impl.h>
#include <sys/debug.h>
typedef struct ctf_diff_func {
const char *cdf_name;
ulong_t cdf_symidx;
ulong_t cdf_matchidx;
} ctf_diff_func_t;
typedef struct ctf_diff_obj {
const char *cdo_name;
ulong_t cdo_symidx;
ctf_id_t cdo_id;
ulong_t cdo_matchidx;
} ctf_diff_obj_t;
typedef struct ctf_diff_guess {
struct ctf_diff_guess *cdg_next;
ctf_id_t cdg_iid;
ctf_id_t cdg_oid;
} ctf_diff_guess_t;
struct ctf_diff {
uint_t cds_flags;
boolean_t cds_tvalid;
ctf_file_t *cds_ifp;
ctf_file_t *cds_ofp;
ctf_id_t *cds_forward;
ctf_id_t *cds_reverse;
size_t cds_fsize;
size_t cds_rsize;
ctf_diff_type_f cds_func;
ctf_diff_guess_t *cds_guess;
void *cds_arg;
uint_t cds_nifuncs;
uint_t cds_nofuncs;
uint_t cds_nextifunc;
uint_t cds_nextofunc;
ctf_diff_func_t *cds_ifuncs;
ctf_diff_func_t *cds_ofuncs;
boolean_t cds_ffillip;
boolean_t cds_fvalid;
uint_t cds_niobj;
uint_t cds_noobj;
uint_t cds_nextiobj;
uint_t cds_nextoobj;
ctf_diff_obj_t *cds_iobj;
ctf_diff_obj_t *cds_oobj;
boolean_t cds_ofillip;
boolean_t cds_ovalid;
};
#define TINDEX(tid) (tid - 1)
static int ctf_diff_type(ctf_diff_t *, ctf_file_t *, ctf_id_t, ctf_file_t *,
ctf_id_t);
static int
ctf_diff_name(ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp, ctf_id_t oid)
{
const char *iname, *oname;
const ctf_type_t *itp, *otp;
if ((itp = ctf_lookup_by_id(&ifp, iid)) == NULL)
return (CTF_ERR);
if ((otp = ctf_lookup_by_id(&ofp, oid)) == NULL)
return (ctf_set_errno(ifp, iid));
iname = ctf_strptr(ifp, itp->ctt_name);
oname = ctf_strptr(ofp, otp->ctt_name);
if ((iname == NULL || oname == NULL) && (iname != oname))
return (B_TRUE);
if (iname == NULL && oname == NULL)
return (B_FALSE);
return (strcmp(iname, oname) == 0 ? B_FALSE: B_TRUE);
}
static int
ctf_diff_number(ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp, ctf_id_t oid)
{
ctf_encoding_t ien, den;
if (ctf_type_encoding(ifp, iid, &ien) != 0)
return (CTF_ERR);
if (ctf_type_encoding(ofp, oid, &den) != 0)
return (ctf_set_errno(ifp, iid));
if (bcmp(&ien, &den, sizeof (ctf_encoding_t)) != 0)
return (B_TRUE);
return (B_FALSE);
}
static int
ctf_diff_typedef(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid,
ctf_file_t *ofp, ctf_id_t oid)
{
ctf_id_t iref = CTF_ERR, oref = CTF_ERR;
while (ctf_type_kind(ifp, iid) == CTF_K_TYPEDEF) {
iref = ctf_type_reference(ifp, iid);
if (iref == CTF_ERR)
return (CTF_ERR);
iid = iref;
}
while (ctf_type_kind(ofp, oid) == CTF_K_TYPEDEF) {
oref = ctf_type_reference(ofp, oid);
if (oref == CTF_ERR)
return (CTF_ERR);
oid = oref;
}
VERIFY(iref != CTF_ERR && oref != CTF_ERR);
return (ctf_diff_type(cds, ifp, iref, ofp, oref));
}
static int
ctf_diff_qualifier(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid,
ctf_file_t *ofp, ctf_id_t oid)
{
ctf_id_t iref, oref;
iref = ctf_type_reference(ifp, iid);
if (iref == CTF_ERR)
return (CTF_ERR);
oref = ctf_type_reference(ofp, oid);
if (oref == CTF_ERR)
return (ctf_set_errno(ifp, ctf_errno(ofp)));
return (ctf_diff_type(cds, ifp, iref, ofp, oref));
}
static int
ctf_diff_array(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp,
ctf_id_t oid)
{
int ret;
ctf_arinfo_t iar, oar;
if (ctf_array_info(ifp, iid, &iar) == CTF_ERR)
return (CTF_ERR);
if (ctf_array_info(ofp, oid, &oar) == CTF_ERR)
return (ctf_set_errno(ifp, ctf_errno(ofp)));
ret = ctf_diff_type(cds, ifp, iar.ctr_contents, ofp, oar.ctr_contents);
if (ret != B_FALSE)
return (ret);
if (iar.ctr_nelems != oar.ctr_nelems)
return (B_TRUE);
if ((cds->cds_flags & CTF_DIFF_F_IGNORE_INTNAMES) == 0) {
ret = ctf_diff_type(cds, ifp, iar.ctr_index, ofp,
oar.ctr_index);
if (ret != B_FALSE)
return (ret);
}
return (B_FALSE);
}
static int
ctf_diff_fptr(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp,
ctf_id_t oid)
{
int ret, i;
ctf_funcinfo_t ifunc, ofunc;
ctf_id_t *iids, *oids;
if (ctf_func_info_by_id(ifp, iid, &ifunc) == CTF_ERR)
return (CTF_ERR);
if (ctf_func_info_by_id(ofp, oid, &ofunc) == CTF_ERR)
return (ctf_set_errno(ifp, ctf_errno(ofp)));
if (ifunc.ctc_argc != ofunc.ctc_argc)
return (B_TRUE);
if (ifunc.ctc_flags != ofunc.ctc_flags)
return (B_TRUE);
ret = ctf_diff_type(cds, ifp, ifunc.ctc_return, ofp, ofunc.ctc_return);
if (ret != B_FALSE)
return (ret);
iids = ctf_alloc(sizeof (ctf_id_t) * ifunc.ctc_argc);
if (iids == NULL)
return (ctf_set_errno(ifp, ENOMEM));
oids = ctf_alloc(sizeof (ctf_id_t) * ifunc.ctc_argc);
if (oids == NULL) {
ctf_free(iids, sizeof (ctf_id_t) * ifunc.ctc_argc);
return (ctf_set_errno(ifp, ENOMEM));
}
if (ctf_func_args_by_id(ifp, iid, ifunc.ctc_argc, iids) == CTF_ERR) {
ret = CTF_ERR;
goto out;
}
if (ctf_func_args_by_id(ofp, oid, ofunc.ctc_argc, oids) == CTF_ERR) {
ret = ctf_set_errno(ifp, ctf_errno(ofp));
goto out;
}
ret = B_TRUE;
for (i = 0; i < ifunc.ctc_argc; i++) {
ret = ctf_diff_type(cds, ifp, iids[i], ofp, oids[i]);
if (ret != B_FALSE)
goto out;
}
ret = B_FALSE;
out:
ctf_free(iids, sizeof (ctf_id_t) * ifunc.ctc_argc);
ctf_free(oids, sizeof (ctf_id_t) * ofunc.ctc_argc);
return (ret);
}
static int
ctf_diff_struct(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp,
ctf_id_t oid)
{
ctf_file_t *oifp;
const ctf_type_t *itp, *otp;
ssize_t isize, iincr, osize, oincr;
const ctf_member_t *imp, *omp;
const ctf_lmember_t *ilmp, *olmp;
int n;
ctf_diff_guess_t *cdg;
oifp = ifp;
if ((itp = ctf_lookup_by_id(&ifp, iid)) == NULL)
return (CTF_ERR);
if ((otp = ctf_lookup_by_id(&ofp, oid)) == NULL)
return (ctf_set_errno(oifp, ctf_errno(ofp)));
if (ctf_type_size(ifp, iid) != ctf_type_size(ofp, oid))
return (B_TRUE);
if (LCTF_INFO_VLEN(ifp, itp->ctt_info) !=
LCTF_INFO_VLEN(ofp, otp->ctt_info))
return (B_TRUE);
(void) ctf_get_ctt_size(ifp, itp, &isize, &iincr);
(void) ctf_get_ctt_size(ofp, otp, &osize, &oincr);
if (ifp->ctf_version == CTF_VERSION_1 || isize < CTF_LSTRUCT_THRESH) {
imp = (const ctf_member_t *)((uintptr_t)itp + iincr);
ilmp = NULL;
} else {
imp = NULL;
ilmp = (const ctf_lmember_t *)((uintptr_t)itp + iincr);
}
if (ofp->ctf_version == CTF_VERSION_1 || osize < CTF_LSTRUCT_THRESH) {
omp = (const ctf_member_t *)((uintptr_t)otp + oincr);
olmp = NULL;
} else {
omp = NULL;
olmp = (const ctf_lmember_t *)((uintptr_t)otp + oincr);
}
cdg = ctf_alloc(sizeof (ctf_diff_guess_t));
if (cdg == NULL)
return (ctf_set_errno(ifp, ENOMEM));
cdg->cdg_iid = iid;
cdg->cdg_oid = oid;
cdg->cdg_next = cds->cds_guess;
cds->cds_guess = cdg;
cds->cds_forward[TINDEX(iid)] = oid;
cds->cds_reverse[TINDEX(oid)] = iid;
for (n = LCTF_INFO_VLEN(ifp, itp->ctt_info); n != 0; n--) {
const char *iname, *oname;
ulong_t ioff, ooff;
ctf_id_t itype, otype;
int ret;
if (imp != NULL) {
iname = ctf_strptr(ifp, imp->ctm_name);
ioff = imp->ctm_offset;
itype = imp->ctm_type;
} else {
iname = ctf_strptr(ifp, ilmp->ctlm_name);
ioff = CTF_LMEM_OFFSET(ilmp);
itype = ilmp->ctlm_type;
}
if (omp != NULL) {
oname = ctf_strptr(ofp, omp->ctm_name);
ooff = omp->ctm_offset;
otype = omp->ctm_type;
} else {
oname = ctf_strptr(ofp, olmp->ctlm_name);
ooff = CTF_LMEM_OFFSET(olmp);
otype = olmp->ctlm_type;
}
if (ioff != ooff) {
return (B_TRUE);
}
if (strcmp(iname, oname) != 0) {
return (B_TRUE);
}
ret = ctf_diff_type(cds, ifp, itype, ofp, otype);
if (ret != B_FALSE) {
return (ret);
}
if (imp != NULL)
imp++;
if (ilmp != NULL)
ilmp++;
if (omp != NULL)
omp++;
if (olmp != NULL)
olmp++;
}
return (B_FALSE);
}
typedef struct ctf_diff_union_member {
ctf_diff_t *cdum_cds;
ctf_file_t *cdum_fp;
ctf_file_t *cdum_iterfp;
const char *cdum_name;
ctf_id_t cdum_type;
int cdum_ret;
} ctf_diff_union_member_t;
typedef struct ctf_diff_union_fp {
ctf_diff_t *cduf_cds;
ctf_file_t *cduf_curfp;
ctf_file_t *cduf_altfp;
ctf_id_t cduf_type;
int cduf_ret;
} ctf_diff_union_fp_t;
static int
ctf_diff_union_check_member(const char *name, ctf_id_t id, ulong_t off,
void *arg)
{
int ret;
ctf_diff_union_member_t *cdump = arg;
if (strcmp(name, cdump->cdum_name) != 0)
return (0);
ret = ctf_diff_type(cdump->cdum_cds, cdump->cdum_fp, cdump->cdum_type,
cdump->cdum_iterfp, id);
if (ret == CTF_ERR) {
cdump->cdum_ret = CTF_ERR;
return (1);
}
if (ret == B_FALSE) {
cdump->cdum_ret = B_FALSE;
return (1);
}
return (0);
}
static int
ctf_diff_union_check_fp(const char *name, ctf_id_t id, ulong_t off, void *arg)
{
int ret;
ctf_diff_union_member_t cdum;
ctf_diff_union_fp_t *cdufp = arg;
cdum.cdum_cds = cdufp->cduf_cds;
cdum.cdum_fp = cdufp->cduf_curfp;
cdum.cdum_iterfp = cdufp->cduf_altfp;
cdum.cdum_name = name;
cdum.cdum_type = id;
cdum.cdum_ret = B_TRUE;
ret = ctf_member_iter(cdum.cdum_iterfp, cdufp->cduf_type,
ctf_diff_union_check_member, &cdum);
if (ret == 0 || cdum.cdum_ret == CTF_ERR) {
cdufp->cduf_ret = cdum.cdum_ret;
return (1);
} else if (ret == CTF_ERR) {
(void) ctf_set_errno(cdum.cdum_fp, ctf_errno(cdum.cdum_iterfp));
cdufp->cduf_ret = CTF_ERR;
return (1);
} else {
ASSERT(cdum.cdum_ret == B_FALSE);
cdufp->cduf_ret = cdum.cdum_ret;
return (0);
}
}
static int
ctf_diff_union(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp,
ctf_id_t oid)
{
ctf_file_t *oifp;
const ctf_type_t *itp, *otp;
ctf_diff_union_fp_t cduf;
ctf_diff_guess_t *cdg;
int ret;
oifp = ifp;
if ((itp = ctf_lookup_by_id(&ifp, iid)) == NULL)
return (CTF_ERR);
if ((otp = ctf_lookup_by_id(&ofp, oid)) == NULL)
return (ctf_set_errno(oifp, ctf_errno(ofp)));
if (LCTF_INFO_VLEN(ifp, itp->ctt_info) !=
LCTF_INFO_VLEN(ofp, otp->ctt_info))
return (B_TRUE);
cdg = ctf_alloc(sizeof (ctf_diff_guess_t));
if (cdg == NULL)
return (ctf_set_errno(ifp, ENOMEM));
cdg->cdg_iid = iid;
cdg->cdg_oid = oid;
cdg->cdg_next = cds->cds_guess;
cds->cds_guess = cdg;
cds->cds_forward[TINDEX(iid)] = oid;
cds->cds_reverse[TINDEX(oid)] = iid;
cduf.cduf_cds = cds;
cduf.cduf_curfp = ifp;
cduf.cduf_altfp = ofp;
cduf.cduf_type = oid;
cduf.cduf_ret = B_TRUE;
ret = ctf_member_iter(ifp, iid, ctf_diff_union_check_fp, &cduf);
if (ret != CTF_ERR)
ret = cduf.cduf_ret;
return (ret);
}
static int
ctf_diff_enum(ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp, ctf_id_t oid)
{
ctf_file_t *oifp;
const ctf_type_t *itp, *otp;
ssize_t iincr, oincr;
const ctf_enum_t *iep, *oep;
int n;
oifp = ifp;
if ((itp = ctf_lookup_by_id(&ifp, iid)) == NULL)
return (CTF_ERR);
if ((otp = ctf_lookup_by_id(&ofp, oid)) == NULL)
return (ctf_set_errno(oifp, ctf_errno(ofp)));
if (LCTF_INFO_VLEN(ifp, itp->ctt_info) !=
LCTF_INFO_VLEN(ofp, otp->ctt_info))
return (B_TRUE);
(void) ctf_get_ctt_size(ifp, itp, NULL, &iincr);
(void) ctf_get_ctt_size(ofp, otp, NULL, &oincr);
iep = (const ctf_enum_t *)((uintptr_t)itp + iincr);
oep = (const ctf_enum_t *)((uintptr_t)otp + oincr);
for (n = LCTF_INFO_VLEN(ifp, itp->ctt_info); n != 0;
n--, iep++, oep++) {
if (strcmp(ctf_strptr(ifp, iep->cte_name),
ctf_strptr(ofp, oep->cte_name)) != 0)
return (B_TRUE);
if (iep->cte_value != oep->cte_value)
return (B_TRUE);
}
return (B_FALSE);
}
static int
ctf_diff_forward(ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp, ctf_id_t oid)
{
int ikind, okind;
ikind = ctf_type_kind(ifp, iid);
okind = ctf_type_kind(ofp, oid);
if (ikind == okind) {
ASSERT(ikind == CTF_K_FORWARD);
return (B_FALSE);
} else if (ikind == CTF_K_FORWARD) {
return (okind != CTF_K_UNION && okind != CTF_K_STRUCT);
} else {
return (ikind != CTF_K_UNION && ikind != CTF_K_STRUCT);
}
}
int
ctf_diff_type(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp,
ctf_id_t oid)
{
int ret, ikind, okind;
if (ifp == ofp && iid == oid)
return (B_FALSE);
if (cds->cds_forward[TINDEX(iid)] == oid)
return (B_FALSE);
if (cds->cds_forward[TINDEX(iid)] != 0)
return (B_TRUE);
if (cds->cds_reverse[TINDEX(oid)] == iid)
return (B_FALSE);
if ((cds->cds_flags & CTF_DIFF_F_IGNORE_INTNAMES) == 0 &&
cds->cds_reverse[TINDEX(oid)] != 0)
return (B_TRUE);
ikind = ctf_type_kind(ifp, iid);
okind = ctf_type_kind(ofp, oid);
if (ikind != okind &&
ikind != CTF_K_FORWARD && okind != CTF_K_FORWARD)
return (B_TRUE);
if ((ret = ctf_diff_name(ifp, iid, ofp, oid)) != B_FALSE) {
if (ikind != okind || ikind != CTF_K_INTEGER ||
(cds->cds_flags & CTF_DIFF_F_IGNORE_INTNAMES) == 0)
return (ret);
}
if (ikind == CTF_K_FORWARD || okind == CTF_K_FORWARD)
return (ctf_diff_forward(ifp, iid, ofp, oid));
switch (ikind) {
case CTF_K_INTEGER:
case CTF_K_FLOAT:
ret = ctf_diff_number(ifp, iid, ofp, oid);
break;
case CTF_K_ARRAY:
ret = ctf_diff_array(cds, ifp, iid, ofp, oid);
break;
case CTF_K_FUNCTION:
ret = ctf_diff_fptr(cds, ifp, iid, ofp, oid);
break;
case CTF_K_STRUCT:
ret = ctf_diff_struct(cds, ifp, iid, ofp, oid);
break;
case CTF_K_UNION:
ret = ctf_diff_union(cds, ifp, iid, ofp, oid);
break;
case CTF_K_ENUM:
ret = ctf_diff_enum(ifp, iid, ofp, oid);
break;
case CTF_K_FORWARD:
ret = ctf_diff_forward(ifp, iid, ofp, oid);
break;
case CTF_K_TYPEDEF:
ret = ctf_diff_typedef(cds, ifp, iid, ofp, oid);
break;
case CTF_K_POINTER:
case CTF_K_VOLATILE:
case CTF_K_CONST:
case CTF_K_RESTRICT:
ret = ctf_diff_qualifier(cds, ifp, iid, ofp, oid);
break;
case CTF_K_UNKNOWN:
ret = B_TRUE;
break;
default:
abort();
}
return (ret);
}
static int
ctf_diff_pass1(ctf_diff_t *cds, boolean_t self)
{
int i, j, diff;
int istart, iend, jstart, jend;
istart = 1;
iend = cds->cds_ifp->ctf_typemax;
if (cds->cds_ifp->ctf_flags & LCTF_CHILD) {
istart += CTF_CHILD_START;
iend += CTF_CHILD_START;
}
jstart = 1;
jend = cds->cds_ofp->ctf_typemax;
if (cds->cds_ofp->ctf_flags & LCTF_CHILD) {
jstart += CTF_CHILD_START;
jend += CTF_CHILD_START;
}
for (i = istart; i <= iend; i++) {
diff = B_TRUE;
if (self == B_TRUE) {
jstart = istart;
jend = i - 1;
}
for (j = jstart; j <= jend; j++) {
ctf_diff_guess_t *cdg, *tofree;
ASSERT(cds->cds_guess == NULL);
diff = ctf_diff_type(cds, cds->cds_ifp, i,
cds->cds_ofp, j);
if (diff == CTF_ERR)
return (CTF_ERR);
cdg = cds->cds_guess;
cds->cds_guess = NULL;
while (cdg != NULL) {
if (diff == B_TRUE) {
cds->cds_forward[TINDEX(cdg->cdg_iid)] =
0;
cds->cds_reverse[TINDEX(cdg->cdg_oid)] =
0;
}
tofree = cdg;
cdg = cdg->cdg_next;
ctf_free(tofree, sizeof (ctf_diff_guess_t));
}
if (diff == B_FALSE) {
cds->cds_forward[TINDEX(i)] = j;
if (cds->cds_reverse[TINDEX(j)] == 0)
cds->cds_reverse[TINDEX(j)] = i;
break;
}
}
if (diff == B_TRUE) {
cds->cds_forward[TINDEX(i)] = CTF_ERR;
cds->cds_func(cds->cds_ifp, i, B_FALSE, NULL, CTF_ERR,
cds->cds_arg);
} else {
cds->cds_func(cds->cds_ifp, i, B_TRUE, cds->cds_ofp, j,
cds->cds_arg);
}
}
return (0);
}
static int
ctf_diff_pass2(ctf_diff_t *cds)
{
int i, start, end;
start = 0x1;
end = cds->cds_ofp->ctf_typemax;
if (cds->cds_ofp->ctf_flags & LCTF_CHILD) {
start += CTF_CHILD_START;
end += CTF_CHILD_START;
}
for (i = start; i <= end; i++) {
if (cds->cds_reverse[TINDEX(i)] != 0)
continue;
cds->cds_func(cds->cds_ofp, i, B_FALSE, NULL, CTF_ERR,
cds->cds_arg);
}
return (0);
}
int
ctf_diff_init(ctf_file_t *ifp, ctf_file_t *ofp, ctf_diff_t **cdsp)
{
ctf_diff_t *cds;
size_t fsize, rsize;
cds = ctf_alloc(sizeof (ctf_diff_t));
if (cds == NULL)
return (ctf_set_errno(ifp, ENOMEM));
bzero(cds, sizeof (ctf_diff_t));
cds->cds_ifp = ifp;
cds->cds_ofp = ofp;
fsize = sizeof (ctf_id_t) * ifp->ctf_typemax;
rsize = sizeof (ctf_id_t) * ofp->ctf_typemax;
if (ifp->ctf_flags & LCTF_CHILD)
fsize += CTF_CHILD_START * sizeof (ctf_id_t);
if (ofp->ctf_flags & LCTF_CHILD)
rsize += CTF_CHILD_START * sizeof (ctf_id_t);
cds->cds_forward = ctf_alloc(fsize);
if (cds->cds_forward == NULL) {
ctf_free(cds, sizeof (ctf_diff_t));
return (ctf_set_errno(ifp, ENOMEM));
}
cds->cds_fsize = fsize;
cds->cds_reverse = ctf_alloc(rsize);
if (cds->cds_reverse == NULL) {
ctf_free(cds->cds_forward, fsize);
ctf_free(cds, sizeof (ctf_diff_t));
return (ctf_set_errno(ifp, ENOMEM));
}
cds->cds_rsize = rsize;
bzero(cds->cds_forward, fsize);
bzero(cds->cds_reverse, rsize);
cds->cds_ifp->ctf_refcnt++;
cds->cds_ofp->ctf_refcnt++;
*cdsp = cds;
return (0);
}
int
ctf_diff_types(ctf_diff_t *cds, ctf_diff_type_f cb, void *arg)
{
int ret;
cds->cds_func = cb;
cds->cds_arg = arg;
ret = ctf_diff_pass1(cds, B_FALSE);
if (ret == 0)
ret = ctf_diff_pass2(cds);
cds->cds_func = NULL;
cds->cds_arg = NULL;
cds->cds_tvalid = B_TRUE;
return (ret);
}
int
ctf_diff_self(ctf_diff_t *cds, ctf_diff_type_f cb, void *arg)
{
if (cds->cds_ifp != cds->cds_ofp)
return (EINVAL);
cds->cds_func = cb;
cds->cds_arg = arg;
return (ctf_diff_pass1(cds, B_TRUE));
}
void
ctf_diff_fini(ctf_diff_t *cds)
{
ctf_diff_guess_t *cdg;
size_t fsize, rsize;
if (cds == NULL)
return;
cds->cds_ifp->ctf_refcnt--;
cds->cds_ofp->ctf_refcnt--;
fsize = sizeof (ctf_id_t) * cds->cds_ifp->ctf_typemax;
rsize = sizeof (ctf_id_t) * cds->cds_ofp->ctf_typemax;
if (cds->cds_ifp->ctf_flags & LCTF_CHILD)
fsize += CTF_CHILD_START * sizeof (ctf_id_t);
if (cds->cds_ofp->ctf_flags & LCTF_CHILD)
rsize += CTF_CHILD_START * sizeof (ctf_id_t);
if (cds->cds_ifuncs != NULL)
ctf_free(cds->cds_ifuncs,
sizeof (ctf_diff_func_t) * cds->cds_nifuncs);
if (cds->cds_ofuncs != NULL)
ctf_free(cds->cds_ofuncs,
sizeof (ctf_diff_func_t) * cds->cds_nofuncs);
if (cds->cds_iobj != NULL)
ctf_free(cds->cds_iobj,
sizeof (ctf_diff_obj_t) * cds->cds_niobj);
if (cds->cds_oobj != NULL)
ctf_free(cds->cds_oobj,
sizeof (ctf_diff_obj_t) * cds->cds_noobj);
cdg = cds->cds_guess;
while (cdg != NULL) {
ctf_diff_guess_t *tofree = cdg;
cdg = cdg->cdg_next;
ctf_free(tofree, sizeof (ctf_diff_guess_t));
}
if (cds->cds_forward != NULL)
ctf_free(cds->cds_forward, cds->cds_fsize);
if (cds->cds_reverse != NULL)
ctf_free(cds->cds_reverse, cds->cds_rsize);
ctf_free(cds, sizeof (ctf_diff_t));
}
uint_t
ctf_diff_getflags(ctf_diff_t *cds)
{
return (cds->cds_flags);
}
int
ctf_diff_setflags(ctf_diff_t *cds, uint_t flags)
{
if ((flags & ~CTF_DIFF_F_IGNORE_INTNAMES) != 0)
return (ctf_set_errno(cds->cds_ifp, EINVAL));
cds->cds_flags = flags;
return (0);
}
static boolean_t
ctf_diff_symid(ctf_diff_t *cds, ctf_id_t iid, ctf_id_t oid)
{
ctf_file_t *ifp, *ofp;
ifp = cds->cds_ifp;
ofp = cds->cds_ofp;
if (iid == 0 || oid == 0)
return (iid == oid ? B_FALSE: B_TRUE);
if (!(ifp->ctf_flags & LCTF_CHILD) && !(ofp->ctf_flags & LCTF_CHILD)) {
if (cds->cds_forward[TINDEX(iid)] != oid)
return (B_TRUE);
return (B_FALSE);
}
return (ctf_diff_type(cds, ifp, iid, ofp, oid));
}
static void
ctf_diff_void_cb(ctf_file_t *ifp, ctf_id_t iid, boolean_t same, ctf_file_t *ofp,
ctf_id_t oid, void *arg)
{
}
static int
ctf_diff_func_count(const char *name, ulong_t symidx, ctf_funcinfo_t *fip,
void *arg)
{
uint32_t *ip = arg;
*ip = *ip + 1;
return (0);
}
static int
ctf_diff_func_fill_cb(const char *name, ulong_t symidx, ctf_funcinfo_t *fip,
void *arg)
{
uint_t *next, max;
ctf_diff_func_t *funcptr;
ctf_diff_t *cds = arg;
if (cds->cds_ffillip == B_TRUE) {
max = cds->cds_nifuncs;
next = &cds->cds_nextifunc;
funcptr = cds->cds_ifuncs + *next;
} else {
max = cds->cds_nofuncs;
next = &cds->cds_nextofunc;
funcptr = cds->cds_ofuncs + *next;
}
VERIFY(*next < max);
funcptr->cdf_name = name;
funcptr->cdf_symidx = symidx;
funcptr->cdf_matchidx = ULONG_MAX;
*next = *next + 1;
return (0);
}
int
ctf_diff_func_fill(ctf_diff_t *cds)
{
int ret;
uint32_t ifcount, ofcount, idcnt, cti;
ulong_t i, j;
ctf_id_t *iids, *oids;
ifcount = 0;
ofcount = 0;
idcnt = 0;
iids = NULL;
oids = NULL;
ret = ctf_function_iter(cds->cds_ifp, ctf_diff_func_count, &ifcount);
if (ret != 0)
return (ret);
ret = ctf_function_iter(cds->cds_ofp, ctf_diff_func_count, &ofcount);
if (ret != 0)
return (ret);
cds->cds_ifuncs = ctf_alloc(sizeof (ctf_diff_func_t) * ifcount);
if (cds->cds_ifuncs == NULL)
return (ctf_set_errno(cds->cds_ifp, ENOMEM));
cds->cds_nifuncs = ifcount;
cds->cds_nextifunc = 0;
cds->cds_ofuncs = ctf_alloc(sizeof (ctf_diff_func_t) * ofcount);
if (cds->cds_ofuncs == NULL)
return (ctf_set_errno(cds->cds_ifp, ENOMEM));
cds->cds_nofuncs = ofcount;
cds->cds_nextofunc = 0;
cds->cds_ffillip = B_TRUE;
if ((ret = ctf_function_iter(cds->cds_ifp, ctf_diff_func_fill_cb,
cds)) != 0)
return (ret);
cds->cds_ffillip = B_FALSE;
if ((ret = ctf_function_iter(cds->cds_ofp, ctf_diff_func_fill_cb,
cds)) != 0)
return (ret);
for (i = 0; i < cds->cds_nifuncs; i++) {
for (j = 0; j < cds->cds_nofuncs; j++) {
ctf_diff_func_t *ifd, *ofd;
ctf_funcinfo_t ifip, ofip;
boolean_t match;
ifd = &cds->cds_ifuncs[i];
ofd = &cds->cds_ofuncs[j];
if (strcmp(ifd->cdf_name, ofd->cdf_name) != 0)
continue;
ret = ctf_func_info(cds->cds_ifp, ifd->cdf_symidx,
&ifip);
if (ret != 0)
goto out;
ret = ctf_func_info(cds->cds_ofp, ofd->cdf_symidx,
&ofip);
if (ret != 0) {
ret = ctf_set_errno(cds->cds_ifp,
ctf_errno(cds->cds_ofp));
goto out;
}
if (ifip.ctc_argc != ofip.ctc_argc &&
ifip.ctc_flags != ofip.ctc_flags)
continue;
if (ctf_diff_symid(cds, ifip.ctc_return,
ofip.ctc_return))
continue;
if (ifip.ctc_argc > idcnt) {
if (iids != NULL)
ctf_free(iids,
sizeof (ctf_id_t) * idcnt);
if (oids != NULL)
ctf_free(oids,
sizeof (ctf_id_t) * idcnt);
iids = oids = NULL;
idcnt = ifip.ctc_argc;
iids = ctf_alloc(sizeof (ctf_id_t) * idcnt);
if (iids == NULL) {
ret = ctf_set_errno(cds->cds_ifp,
ENOMEM);
goto out;
}
oids = ctf_alloc(sizeof (ctf_id_t) * idcnt);
if (iids == NULL) {
ret = ctf_set_errno(cds->cds_ifp,
ENOMEM);
goto out;
}
}
if ((ret = ctf_func_args(cds->cds_ifp, ifd->cdf_symidx,
ifip.ctc_argc, iids)) != 0)
goto out;
if ((ret = ctf_func_args(cds->cds_ofp, ofd->cdf_symidx,
ofip.ctc_argc, oids)) != 0)
goto out;
match = B_TRUE;
for (cti = 0; cti < ifip.ctc_argc; cti++) {
if (ctf_diff_symid(cds, iids[cti], oids[cti])) {
match = B_FALSE;
break;
}
}
if (match == B_FALSE)
continue;
ifd->cdf_matchidx = j;
ofd->cdf_matchidx = i;
break;
}
}
ret = 0;
out:
if (iids != NULL)
ctf_free(iids, sizeof (ctf_id_t) * idcnt);
if (oids != NULL)
ctf_free(oids, sizeof (ctf_id_t) * idcnt);
return (ret);
}
int
ctf_diff_functions(ctf_diff_t *cds, ctf_diff_func_f cb, void *arg)
{
int ret;
ulong_t i;
if (cds->cds_tvalid == B_FALSE) {
if ((ret = ctf_diff_types(cds, ctf_diff_void_cb, NULL)) != 0)
return (ret);
}
if (cds->cds_fvalid == B_FALSE) {
if ((ret = ctf_diff_func_fill(cds)) != 0)
return (ret);
cds->cds_fvalid = B_TRUE;
}
for (i = 0; i < cds->cds_nifuncs; i++) {
if (cds->cds_ifuncs[i].cdf_matchidx == ULONG_MAX) {
cb(cds->cds_ifp, cds->cds_ifuncs[i].cdf_symidx,
B_FALSE, NULL, ULONG_MAX, arg);
} else {
ulong_t idx = cds->cds_ifuncs[i].cdf_matchidx;
cb(cds->cds_ifp, cds->cds_ifuncs[i].cdf_symidx, B_TRUE,
cds->cds_ofp, cds->cds_ofuncs[idx].cdf_symidx, arg);
}
}
for (i = 0; i < cds->cds_nofuncs; i++) {
if (cds->cds_ofuncs[i].cdf_matchidx != ULONG_MAX)
continue;
cb(cds->cds_ofp, cds->cds_ofuncs[i].cdf_symidx, B_FALSE,
NULL, ULONG_MAX, arg);
}
return (0);
}
static int
ctf_diff_obj_fill_cb(const char *name, ctf_id_t id, ulong_t symidx, void *arg)
{
uint_t *next, max;
ctf_diff_obj_t *objptr;
ctf_diff_t *cds = arg;
if (cds->cds_ofillip == B_TRUE) {
max = cds->cds_niobj;
next = &cds->cds_nextiobj;
objptr = cds->cds_iobj + *next;
} else {
max = cds->cds_noobj;
next = &cds->cds_nextoobj;
objptr = cds->cds_oobj+ *next;
}
VERIFY(*next < max);
objptr->cdo_name = name;
objptr->cdo_symidx = symidx;
objptr->cdo_id = id;
objptr->cdo_matchidx = ULONG_MAX;
*next = *next + 1;
return (0);
}
static int
ctf_diff_obj_count(const char *name, ctf_id_t id, ulong_t symidx, void *arg)
{
uint32_t *count = arg;
*count = *count + 1;
return (0);
}
static int
ctf_diff_obj_fill(ctf_diff_t *cds)
{
int ret;
uint32_t iocount, oocount;
ulong_t i, j;
iocount = 0;
oocount = 0;
ret = ctf_object_iter(cds->cds_ifp, ctf_diff_obj_count, &iocount);
if (ret != 0)
return (ret);
ret = ctf_object_iter(cds->cds_ofp, ctf_diff_obj_count, &oocount);
if (ret != 0)
return (ret);
cds->cds_iobj = ctf_alloc(sizeof (ctf_diff_obj_t) * iocount);
if (cds->cds_iobj == NULL)
return (ctf_set_errno(cds->cds_ifp, ENOMEM));
cds->cds_niobj = iocount;
cds->cds_nextiobj = 0;
cds->cds_oobj = ctf_alloc(sizeof (ctf_diff_obj_t) * oocount);
if (cds->cds_oobj == NULL)
return (ctf_set_errno(cds->cds_ifp, ENOMEM));
cds->cds_noobj = oocount;
cds->cds_nextoobj = 0;
cds->cds_ofillip = B_TRUE;
if ((ret = ctf_object_iter(cds->cds_ifp, ctf_diff_obj_fill_cb,
cds)) != 0)
return (ret);
cds->cds_ofillip = B_FALSE;
if ((ret = ctf_object_iter(cds->cds_ofp, ctf_diff_obj_fill_cb,
cds)) != 0)
return (ret);
for (i = 0; i < cds->cds_niobj; i++) {
for (j = 0; j < cds->cds_noobj; j++) {
ctf_diff_obj_t *id, *od;
id = &cds->cds_iobj[i];
od = &cds->cds_oobj[j];
if (id->cdo_name == NULL || od->cdo_name == NULL)
continue;
if (strcmp(id->cdo_name, od->cdo_name) != 0)
continue;
if (ctf_diff_symid(cds, id->cdo_id, od->cdo_id)) {
continue;
}
id->cdo_matchidx = j;
od->cdo_matchidx = i;
break;
}
}
return (0);
}
int
ctf_diff_objects(ctf_diff_t *cds, ctf_diff_obj_f cb, void *arg)
{
int ret;
ulong_t i;
if (cds->cds_tvalid == B_FALSE) {
if ((ret = ctf_diff_types(cds, ctf_diff_void_cb, NULL)) != 0)
return (ret);
}
if (cds->cds_ovalid == B_FALSE) {
if ((ret = ctf_diff_obj_fill(cds)) != 0)
return (ret);
cds->cds_ovalid = B_TRUE;
}
for (i = 0; i < cds->cds_niobj; i++) {
ctf_diff_obj_t *o = &cds->cds_iobj[i];
if (cds->cds_iobj[i].cdo_matchidx == ULONG_MAX) {
cb(cds->cds_ifp, o->cdo_symidx, o->cdo_id, B_FALSE,
NULL, ULONG_MAX, CTF_ERR, arg);
} else {
ctf_diff_obj_t *alt = &cds->cds_oobj[o->cdo_matchidx];
cb(cds->cds_ifp, o->cdo_symidx, o->cdo_id, B_TRUE,
cds->cds_ofp, alt->cdo_symidx, alt->cdo_id, arg);
}
}
for (i = 0; i < cds->cds_noobj; i++) {
ctf_diff_obj_t *o = &cds->cds_oobj[i];
if (o->cdo_matchidx != ULONG_MAX)
continue;
cb(cds->cds_ofp, o->cdo_symidx, o->cdo_id, B_FALSE, NULL,
ULONG_MAX, CTF_ERR, arg);
}
return (0);
}