#include <strings.h>
#include <stdio.h>
#include <ctype.h>
#include <sys/machelf.h>
#include <sys/elf.h>
#include <sys/auxv_SPARC.h>
#include <sys/auxv_386.h>
#include <elfcap.h>
#define STRDESC(_str) { _str, sizeof (_str) - 1 }
#define RESERVED_ELFCAP_DESC { 0, { NULL, 0 }, { NULL, 0 }, { NULL, 0 } }
static const elfcap_str_t format[] = {
STRDESC(" "),
STRDESC(" "),
STRDESC(" | ")
};
#define FORMAT_NELTS (sizeof (format) / sizeof (format[0]))
static const elfcap_desc_t sf1[ELFCAP_NUM_SF1] = {
{
SF1_SUNW_FPKNWN, STRDESC("SF1_SUNW_FPKNWN"),
STRDESC("FPKNWN"), STRDESC("fpknwn")
},
{
SF1_SUNW_FPUSED, STRDESC("SF1_SUNW_FPUSED"),
STRDESC("FPUSED"), STRDESC("fpused"),
},
{
SF1_SUNW_ADDR32, STRDESC("SF1_SUNW_ADDR32"),
STRDESC("ADDR32"), STRDESC("addr32"),
}
};
static const elfcap_desc_t hw1_sparc[ELFCAP_NUM_HW1_SPARC] = {
{
AV_SPARC_MUL32, STRDESC("AV_SPARC_MUL32"),
STRDESC("MUL32"), STRDESC("mul32"),
},
{
AV_SPARC_DIV32, STRDESC("AV_SPARC_DIV32"),
STRDESC("DIV32"), STRDESC("div32"),
},
{
AV_SPARC_FSMULD, STRDESC("AV_SPARC_FSMULD"),
STRDESC("FSMULD"), STRDESC("fsmuld"),
},
{
AV_SPARC_V8PLUS, STRDESC("AV_SPARC_V8PLUS"),
STRDESC("V8PLUS"), STRDESC("v8plus"),
},
{
AV_SPARC_POPC, STRDESC("AV_SPARC_POPC"),
STRDESC("POPC"), STRDESC("popc"),
},
{
AV_SPARC_VIS, STRDESC("AV_SPARC_VIS"),
STRDESC("VIS"), STRDESC("vis"),
},
{
AV_SPARC_VIS2, STRDESC("AV_SPARC_VIS2"),
STRDESC("VIS2"), STRDESC("vis2"),
},
{
AV_SPARC_ASI_BLK_INIT, STRDESC("AV_SPARC_ASI_BLK_INIT"),
STRDESC("ASI_BLK_INIT"), STRDESC("asi_blk_init"),
},
{
AV_SPARC_FMAF, STRDESC("AV_SPARC_FMAF"),
STRDESC("FMAF"), STRDESC("fmaf"),
},
RESERVED_ELFCAP_DESC,
{
AV_SPARC_VIS3, STRDESC("AV_SPARC_VIS3"),
STRDESC("VIS3"), STRDESC("vis3"),
},
{
AV_SPARC_HPC, STRDESC("AV_SPARC_HPC"),
STRDESC("HPC"), STRDESC("hpc"),
},
{
AV_SPARC_RANDOM, STRDESC("AV_SPARC_RANDOM"),
STRDESC("RANDOM"), STRDESC("random"),
},
{
AV_SPARC_TRANS, STRDESC("AV_SPARC_TRANS"),
STRDESC("TRANS"), STRDESC("trans"),
},
{
AV_SPARC_FJFMAU, STRDESC("AV_SPARC_FJFMAU"),
STRDESC("FJFMAU"), STRDESC("fjfmau"),
},
{
AV_SPARC_IMA, STRDESC("AV_SPARC_IMA"),
STRDESC("IMA"), STRDESC("ima"),
},
{
AV_SPARC_ASI_CACHE_SPARING,
STRDESC("AV_SPARC_ASI_CACHE_SPARING"),
STRDESC("CSPARE"), STRDESC("cspare"),
},
{
AV_SPARC_PAUSE,
STRDESC("AV_SPARC_PAUSE"),
STRDESC("PAUSE"), STRDESC("pause"),
},
{
AV_SPARC_CBCOND,
STRDESC("AV_SPARC_CBCOND"),
STRDESC("CBCOND"), STRDESC("cbcond"),
},
{
AV_SPARC_AES,
STRDESC("AV_SPARC_AES"),
STRDESC("AES"), STRDESC("aes"),
},
{
AV_SPARC_DES,
STRDESC("AV_SPARC_DES"),
STRDESC("DES"), STRDESC("des"),
},
{
AV_SPARC_KASUMI,
STRDESC("AV_SPARC_KASUMI"),
STRDESC("KASUMI"), STRDESC("kasumi"),
},
{
AV_SPARC_CAMELLIA,
STRDESC("AV_SPARC_CAMELLIA"),
STRDESC("CAMELLIA"), STRDESC("camellia"),
},
{
AV_SPARC_MD5,
STRDESC("AV_SPARC_MD5"),
STRDESC("MD5"), STRDESC("md5"),
},
{
AV_SPARC_SHA1,
STRDESC("AV_SPARC_SHA1"),
STRDESC("SHA1"), STRDESC("sha1"),
},
{
AV_SPARC_SHA256,
STRDESC("AV_SPARC_SHA256"),
STRDESC("SHA256"), STRDESC("sha256"),
},
{
AV_SPARC_SHA512,
STRDESC("AV_SPARC_SHA512"),
STRDESC("SHA512"), STRDESC("sha512"),
},
{
AV_SPARC_MPMUL,
STRDESC("AV_SPARC_MPMUL"),
STRDESC("MPMUL"), STRDESC("mpmul"),
},
{
AV_SPARC_MONT,
STRDESC("AV_SPARC_MONT"),
STRDESC("MONT"), STRDESC("mont"),
},
{
AV_SPARC_CRC32C,
STRDESC("AV_SPARC_CRC32C"),
STRDESC("CRC32C"), STRDESC("crc32c"),
}
};
static const elfcap_desc_t hw1_386[ELFCAP_NUM_HW1_386] = {
{
AV_386_FPU, STRDESC("AV_386_FPU"),
STRDESC("FPU"), STRDESC("fpu"),
},
{
AV_386_TSC, STRDESC("AV_386_TSC"),
STRDESC("TSC"), STRDESC("tsc"),
},
{
AV_386_CX8, STRDESC("AV_386_CX8"),
STRDESC("CX8"), STRDESC("cx8"),
},
{
AV_386_SEP, STRDESC("AV_386_SEP"),
STRDESC("SEP"), STRDESC("sep"),
},
{
AV_386_AMD_SYSC, STRDESC("AV_386_AMD_SYSC"),
STRDESC("AMD_SYSC"), STRDESC("amd_sysc"),
},
{
AV_386_CMOV, STRDESC("AV_386_CMOV"),
STRDESC("CMOV"), STRDESC("cmov"),
},
{
AV_386_MMX, STRDESC("AV_386_MMX"),
STRDESC("MMX"), STRDESC("mmx"),
},
{
AV_386_AMD_MMX, STRDESC("AV_386_AMD_MMX"),
STRDESC("AMD_MMX"), STRDESC("amd_mmx"),
},
{
AV_386_AMD_3DNow, STRDESC("AV_386_AMD_3DNow"),
STRDESC("AMD_3DNow"), STRDESC("amd_3dnow"),
},
{
AV_386_AMD_3DNowx, STRDESC("AV_386_AMD_3DNowx"),
STRDESC("AMD_3DNowx"), STRDESC("amd_3dnowx"),
},
{
AV_386_FXSR, STRDESC("AV_386_FXSR"),
STRDESC("FXSR"), STRDESC("fxsr"),
},
{
AV_386_SSE, STRDESC("AV_386_SSE"),
STRDESC("SSE"), STRDESC("sse"),
},
{
AV_386_SSE2, STRDESC("AV_386_SSE2"),
STRDESC("SSE2"), STRDESC("sse2"),
},
{
AV_386_SSE3, STRDESC("AV_386_SSE3"),
STRDESC("SSE3"), STRDESC("sse3"),
},
{
AV_386_CX16, STRDESC("AV_386_CX16"),
STRDESC("CX16"), STRDESC("cx16"),
},
{
AV_386_AHF, STRDESC("AV_386_AHF"),
STRDESC("AHF"), STRDESC("ahf"),
},
{
AV_386_TSCP, STRDESC("AV_386_TSCP"),
STRDESC("TSCP"), STRDESC("tscp"),
},
{
AV_386_AMD_SSE4A, STRDESC("AV_386_AMD_SSE4A"),
STRDESC("AMD_SSE4A"), STRDESC("amd_sse4a"),
},
{
AV_386_POPCNT, STRDESC("AV_386_POPCNT"),
STRDESC("POPCNT"), STRDESC("popcnt"),
},
{
AV_386_AMD_LZCNT, STRDESC("AV_386_AMD_LZCNT"),
STRDESC("AMD_LZCNT"), STRDESC("amd_lzcnt"),
},
{
AV_386_SSSE3, STRDESC("AV_386_SSSE3"),
STRDESC("SSSE3"), STRDESC("ssse3"),
},
{
AV_386_SSE4_1, STRDESC("AV_386_SSE4_1"),
STRDESC("SSE4.1"), STRDESC("sse4.1"),
},
{
AV_386_SSE4_2, STRDESC("AV_386_SSE4_2"),
STRDESC("SSE4.2"), STRDESC("sse4.2"),
},
{
AV_386_MOVBE, STRDESC("AV_386_MOVBE"),
STRDESC("MOVBE"), STRDESC("movbe"),
},
{
AV_386_AES, STRDESC("AV_386_AES"),
STRDESC("AES"), STRDESC("aes"),
},
{
AV_386_PCLMULQDQ, STRDESC("AV_386_PCLMULQDQ"),
STRDESC("PCLMULQDQ"), STRDESC("pclmulqdq"),
},
{
AV_386_XSAVE, STRDESC("AV_386_XSAVE"),
STRDESC("XSAVE"), STRDESC("xsave"),
},
{
AV_386_AVX, STRDESC("AV_386_AVX"),
STRDESC("AVX"), STRDESC("avx"),
},
{
AV_386_VMX, STRDESC("AV_386_VMX"),
STRDESC("VMX"), STRDESC("vmx"),
},
{
AV_386_AMD_SVM, STRDESC("AV_386_AMD_SVM"),
STRDESC("AMD_SVM"), STRDESC("amd_svm"),
}
};
static const elfcap_desc_t hw2_386[ELFCAP_NUM_HW2_386] = {
{
AV_386_2_F16C, STRDESC("AV_386_2_F16C"),
STRDESC("F16C"), STRDESC("f16c"),
},
{
AV_386_2_RDRAND, STRDESC("AV_386_2_RDRAND"),
STRDESC("RDRAND"), STRDESC("rdrand"),
},
{
AV_386_2_BMI1, STRDESC("AV_386_2_BMI1"),
STRDESC("BMI1"), STRDESC("bmi1"),
},
{
AV_386_2_BMI2, STRDESC("AV_386_2_BMI2"),
STRDESC("BMI2"), STRDESC("bmi2"),
},
{
AV_386_2_FMA, STRDESC("AV_386_2_FMA"),
STRDESC("FMA"), STRDESC("fma"),
},
{
AV_386_2_AVX2, STRDESC("AV_386_2_AVX2"),
STRDESC("AVX2"), STRDESC("avx2"),
},
{
AV_386_2_ADX, STRDESC("AV_386_2_ADX"),
STRDESC("ADX"), STRDESC("adx"),
},
{
AV_386_2_RDSEED, STRDESC("AV_386_2_RDSEED"),
STRDESC("RDSEED"), STRDESC("rdseed"),
},
{
AV_386_2_AVX512F, STRDESC("AV_386_2_AVX512F"),
STRDESC("AVX512F"), STRDESC("avx512f"),
},
{
AV_386_2_AVX512DQ, STRDESC("AV_386_2_AVX512DQ"),
STRDESC("AVX512DQ"), STRDESC("avx512dq"),
},
{
AV_386_2_AVX512IFMA, STRDESC("AV_386_2_AVX512IFMA"),
STRDESC("AVX512IFMA"), STRDESC("avx512ifma"),
},
{
AV_386_2_AVX512PF, STRDESC("AV_386_2_AVX512PF"),
STRDESC("AVX512PF"), STRDESC("avx512pf"),
},
{
AV_386_2_AVX512ER, STRDESC("AV_386_2_AVX512ER"),
STRDESC("AVX512ER"), STRDESC("avx512er"),
},
{
AV_386_2_AVX512CD, STRDESC("AV_386_2_AVX512CD"),
STRDESC("AVX512CD"), STRDESC("avx512cd"),
},
{
AV_386_2_AVX512BW, STRDESC("AV_386_2_AVX512BW"),
STRDESC("AVX512BW"), STRDESC("avx512bw"),
},
{
AV_386_2_AVX512VL, STRDESC("AV_386_2_AVX512VL"),
STRDESC("AVX512VL"), STRDESC("avx512vl"),
},
{
AV_386_2_AVX512VBMI, STRDESC("AV_386_2_AVX512VBMI"),
STRDESC("AVX512VBMI"), STRDESC("avx512vbmi"),
},
{
AV_386_2_AVX512VPOPCDQ, STRDESC("AV_386_2_AVX512_VPOPCDQ"),
STRDESC("AVX512_VPOPCDQ"), STRDESC("avx512_vpopcntdq"),
},
{
AV_386_2_AVX512_4NNIW, STRDESC("AV_386_2_AVX512_4NNIW"),
STRDESC("AVX512_4NNIW"), STRDESC("avx512_4nniw"),
},
{
AV_386_2_AVX512_4FMAPS, STRDESC("AV_386_2_AVX512_4FMAPS"),
STRDESC("AVX512_4FMAPS"), STRDESC("avx512_4fmaps"),
},
{
AV_386_2_SHA, STRDESC("AV_386_2_SHA"),
STRDESC("SHA"), STRDESC("sha"),
},
{
AV_386_2_FSGSBASE, STRDESC("AV_386_2_FSGSBASE"),
STRDESC("FSGSBASE"), STRDESC("fsgsbase")
},
{
AV_386_2_CLFLUSHOPT, STRDESC("AV_386_2_CLFLUSHOPT"),
STRDESC("CLFLUSHOPT"), STRDESC("clflushopt")
},
{
AV_386_2_CLWB, STRDESC("AV_386_2_CLWB"),
STRDESC("CLWB"), STRDESC("clwb")
},
{
AV_386_2_MONITORX, STRDESC("AV_386_2_MONITORX"),
STRDESC("MONITORX"), STRDESC("monitorx")
},
{
AV_386_2_CLZERO, STRDESC("AV_386_2_CLZERO"),
STRDESC("CLZERO"), STRDESC("clzero")
},
{
AV_386_2_AVX512_VNNI, STRDESC("AV_386_2_AVX512_VNNI"),
STRDESC("AVX512_VNNI"), STRDESC("avx512_vnni")
},
{
AV_386_2_VPCLMULQDQ, STRDESC("AV_386_2_VPCLMULQDQ"),
STRDESC("VPCLMULQDQ"), STRDESC("vpclmulqdq")
},
{
AV_386_2_VAES, STRDESC("AV_386_2_VAES"),
STRDESC("VAES"), STRDESC("vaes")
},
{
AV_386_2_GFNI, STRDESC("AV_386_2_GFNI"),
STRDESC("GFNI"), STRDESC("gfni")
},
{
AV_386_2_AVX512_VP2INT, STRDESC("AV_386_2_AVX512_VP2INT"),
STRDESC("AVX512_VP2INT"), STRDESC("avx512_vp2int")
},
{
AV_386_2_AVX512_BITALG, STRDESC("AV_386_2_AVX512_BITALG"),
STRDESC("AVX512_BITALG"), STRDESC("avx512_bitalg")
}
};
static const elfcap_desc_t hw3_386[ELFCAP_NUM_HW3_386] = {
{
AV_386_3_AVX512_VBMI2, STRDESC("AV_386_3_AVX512_VBMI2"),
STRDESC("AVX512_VBMI2"), STRDESC("avx512_vbmi2")
},
{
AV_386_3_AVX512_BF16, STRDESC("AV_386_3_AVX512_BF16"),
STRDESC("AVX512_BF16"), STRDESC("avx512_bf16")
}
};
static elfcap_err_t
token(char **ostr, size_t *olen, const elfcap_str_t *nstr)
{
if (*olen < nstr->s_len)
return (ELFCAP_ERR_BUFOVFL);
(void) strcat(*ostr, nstr->s_str);
*ostr += nstr->s_len;
*olen -= nstr->s_len;
return (ELFCAP_ERR_NONE);
}
static elfcap_err_t
get_str_desc(elfcap_style_t style, const elfcap_desc_t *cdp,
const elfcap_str_t **ret_str)
{
switch (ELFCAP_STYLE_MASK(style)) {
case ELFCAP_STYLE_FULL:
*ret_str = &cdp->c_full;
break;
case ELFCAP_STYLE_UC:
*ret_str = &cdp->c_uc;
break;
case ELFCAP_STYLE_LC:
*ret_str = &cdp->c_lc;
break;
default:
return (ELFCAP_ERR_INVSTYLE);
}
return (ELFCAP_ERR_NONE);
}
static elfcap_err_t
expand(elfcap_style_t style, elfcap_mask_t val, const elfcap_desc_t *cdp,
uint_t cnum, char *str, size_t slen, elfcap_fmt_t fmt)
{
uint_t cnt;
int follow = 0, err;
const elfcap_str_t *nstr;
if (val == 0)
return (ELFCAP_ERR_NONE);
for (cnt = cnum; cnt > 0; cnt--) {
uint_t mask = cdp[cnt - 1].c_val;
if ((val & mask) != 0) {
if (follow++ && ((err = token(&str, &slen,
&format[fmt])) != ELFCAP_ERR_NONE))
return (err);
err = get_str_desc(style, &cdp[cnt - 1], &nstr);
if (err != ELFCAP_ERR_NONE)
return (err);
if ((err = token(&str, &slen, nstr)) != ELFCAP_ERR_NONE)
return (err);
val = val & ~mask;
}
}
if (val) {
if (follow && ((err = token(&str, &slen, &format[fmt])) !=
ELFCAP_ERR_NONE))
return (err);
(void) snprintf(str, slen, "0x%x", val);
}
return (ELFCAP_ERR_NONE);
}
elfcap_err_t
elfcap_hw1_to_str(elfcap_style_t style, elfcap_mask_t val, char *str,
size_t len, elfcap_fmt_t fmt, ushort_t mach)
{
*str = '\0';
if ((fmt < 0) || (fmt >= FORMAT_NELTS))
return (ELFCAP_ERR_INVFMT);
if ((mach == EM_386) || (mach == EM_IA_64) || (mach == EM_AMD64))
return (expand(style, val, &hw1_386[0], ELFCAP_NUM_HW1_386,
str, len, fmt));
if ((mach == EM_SPARC) || (mach == EM_SPARC32PLUS) ||
(mach == EM_SPARCV9))
return (expand(style, val, hw1_sparc, ELFCAP_NUM_HW1_SPARC,
str, len, fmt));
return (ELFCAP_ERR_UNKMACH);
}
elfcap_err_t
elfcap_hw2_to_str(elfcap_style_t style, elfcap_mask_t val, char *str,
size_t len, elfcap_fmt_t fmt, ushort_t mach)
{
*str = '\0';
if ((fmt < 0) || (fmt >= FORMAT_NELTS))
return (ELFCAP_ERR_INVFMT);
if ((mach == EM_386) || (mach == EM_IA_64) || (mach == EM_AMD64))
return (expand(style, val, &hw2_386[0], ELFCAP_NUM_HW2_386,
str, len, fmt));
return (expand(style, val, NULL, 0, str, len, fmt));
}
elfcap_err_t
elfcap_hw3_to_str(elfcap_style_t style, elfcap_mask_t val, char *str,
size_t len, elfcap_fmt_t fmt, ushort_t mach)
{
*str = '\0';
if ((fmt < 0) || (fmt >= FORMAT_NELTS))
return (ELFCAP_ERR_INVFMT);
if ((mach == EM_386) || (mach == EM_IA_64) || (mach == EM_AMD64))
return (expand(style, val, &hw3_386[0], ELFCAP_NUM_HW3_386,
str, len, fmt));
return (expand(style, val, NULL, 0, str, len, fmt));
}
elfcap_err_t
elfcap_sf1_to_str(elfcap_style_t style, elfcap_mask_t val, char *str,
size_t len, elfcap_fmt_t fmt, ushort_t mach)
{
*str = '\0';
if ((fmt < 0) || (fmt >= FORMAT_NELTS))
return (ELFCAP_ERR_INVFMT);
return (expand(style, val, &sf1[0], ELFCAP_NUM_SF1, str, len, fmt));
}
elfcap_err_t
elfcap_tag_to_str(elfcap_style_t style, uint64_t tag, elfcap_mask_t val,
char *str, size_t len, elfcap_fmt_t fmt, ushort_t mach)
{
switch (tag) {
case CA_SUNW_HW_1:
return (elfcap_hw1_to_str(style, val, str, len, fmt, mach));
case CA_SUNW_SF_1:
return (elfcap_sf1_to_str(style, val, str, len, fmt, mach));
case CA_SUNW_HW_2:
return (elfcap_hw2_to_str(style, val, str, len, fmt, mach));
case CA_SUNW_HW_3:
return (elfcap_hw3_to_str(style, val, str, len, fmt, mach));
}
return (ELFCAP_ERR_UNKTAG);
}
static elfcap_mask_t
value(elfcap_style_t style, const char *str, const elfcap_desc_t *cdp,
uint_t cnum)
{
const elfcap_str_t *nstr;
uint_t num;
int err;
for (num = 0; num < cnum; num++) {
if (cdp[num].c_val == 0)
continue;
if ((err = get_str_desc(style, &cdp[num], &nstr)) != 0)
return (err);
if (style & ELFCAP_STYLE_F_ICMP) {
if (strcasecmp(str, nstr->s_str) == 0)
return (cdp[num].c_val);
} else {
if (strcmp(str, nstr->s_str) == 0)
return (cdp[num].c_val);
}
}
return (0);
}
elfcap_mask_t
elfcap_sf1_from_str(elfcap_style_t style, const char *str, ushort_t mach)
{
return (value(style, str, &sf1[0], ELFCAP_NUM_SF1));
}
elfcap_mask_t
elfcap_hw1_from_str(elfcap_style_t style, const char *str, ushort_t mach)
{
if ((mach == EM_386) || (mach == EM_IA_64) || (mach == EM_AMD64))
return (value(style, str, &hw1_386[0], ELFCAP_NUM_HW1_386));
if ((mach == EM_SPARC) || (mach == EM_SPARC32PLUS) ||
(mach == EM_SPARCV9))
return (value(style, str, hw1_sparc, ELFCAP_NUM_HW1_SPARC));
return (0);
}
elfcap_mask_t
elfcap_hw2_from_str(elfcap_style_t style, const char *str, ushort_t mach)
{
if ((mach == EM_386) || (mach == EM_IA_64) || (mach == EM_AMD64))
return (value(style, str, &hw2_386[0], ELFCAP_NUM_HW2_386));
return (0);
}
elfcap_mask_t
elfcap_hw3_from_str(elfcap_style_t style, const char *str, ushort_t mach)
{
if ((mach == EM_386) || (mach == EM_IA_64) || (mach == EM_AMD64))
return (value(style, str, &hw3_386[0], ELFCAP_NUM_HW3_386));
return (0);
}
elfcap_mask_t
elfcap_tag_from_str(elfcap_style_t style, uint64_t tag, const char *str,
ushort_t mach)
{
switch (tag) {
case CA_SUNW_HW_1:
return (elfcap_hw1_from_str(style, str, mach));
case CA_SUNW_SF_1:
return (elfcap_sf1_from_str(style, str, mach));
case CA_SUNW_HW_2:
return (elfcap_hw2_from_str(style, str, mach));
case CA_SUNW_HW_3:
return (elfcap_hw3_from_str(style, str, mach));
}
return (0);
}
const elfcap_str_t *
elfcap_getdesc_formats(void)
{
return (format);
}
const elfcap_desc_t *
elfcap_getdesc_hw1_sparc(void)
{
return (hw1_sparc);
}
const elfcap_desc_t *
elfcap_getdesc_hw1_386(void)
{
return (hw1_386);
}
const elfcap_desc_t *
elfcap_getdesc_sf1(void)
{
return (sf1);
}
const elfcap_desc_t *
elfcap_getdesc_hw2_386(void)
{
return (hw2_386);
}
const elfcap_desc_t *
elfcap_getdesc_hw3_386(void)
{
return (hw3_386);
}