#include <stdio.h>
#include <unistd.h>
#include <elfedit.h>
#include <strings.h>
#include <debug.h>
#include <conv.h>
#include <syminfo_msg.h>
typedef enum {
SYMINFO_CMD_T_DUMP = 0,
SYMINFO_CMD_T_SI_BOUNDTO = 1,
SYMINFO_CMD_T_SI_FLAGS = 2
} SYMINFO_CMD_T;
#ifndef _ELF64
const char *
_syminfo_msg(Msg mid)
{
return (gettext(MSG_ORIG(mid)));
}
#endif
static const char *
mod_i18nhdl_to_str(elfedit_i18nhdl_t hdl)
{
Msg msg = (Msg)hdl;
return (MSG_INTL(msg));
}
typedef enum {
SYMINFO_OPT_F_AND = 1,
SYMINFO_OPT_F_CMP = 2,
SYMINFO_OPT_F_NEEDED = 4,
SYMINFO_OPT_F_OR = 8,
SYMINFO_OPT_F_SYMNDX = 16
} syminfo_opt_t;
typedef struct {
elfedit_obj_state_t *obj_state;
syminfo_opt_t optmask;
int argc;
const char **argv;
struct {
elfedit_section_t *sec;
Syminfo *data;
Word n;
} syminfo;
struct {
elfedit_section_t *sec;
Sym *data;
Word n;
} sym;
struct {
elfedit_section_t *sec;
} str;
struct {
elfedit_section_t *sec;
Dyn *data;
Word n;
} dynamic;
} ARGSTATE;
static void
process_args(elfedit_obj_state_t *obj_state, int argc, const char *argv[],
SYMINFO_CMD_T cmd, ARGSTATE *argstate)
{
elfedit_getopt_state_t getopt_state;
elfedit_getopt_ret_t *getopt_ret;
bzero(argstate, sizeof (*argstate));
argstate->obj_state = obj_state;
elfedit_getopt_init(&getopt_state, &argc, &argv);
while ((getopt_ret = elfedit_getopt(&getopt_state)) != NULL)
argstate->optmask |= getopt_ret->gor_idmask;
if (((cmd == SYMINFO_CMD_T_DUMP) && (argc > 1)) ||
((cmd == SYMINFO_CMD_T_SI_BOUNDTO) && (argc > 2)))
elfedit_command_usage();
if (argc == 0)
elfedit_pager_init();
argstate->argc = argc;
argstate->argv = argv;
argstate->syminfo.sec = elfedit_sec_getsyminfo(obj_state,
&argstate->syminfo.data, &argstate->syminfo.n);
}
static void
argstate_add_sym(ARGSTATE *argstate)
{
if (argstate->sym.sec != NULL)
return;
argstate->sym.sec = elfedit_sec_getsymtab(argstate->obj_state,
1, argstate->syminfo.sec->sec_shdr->sh_link, NULL,
&argstate->sym.data, &argstate->sym.n, NULL);
}
static void
argstate_add_str(ARGSTATE *argstate)
{
if (argstate->str.sec != NULL)
return;
argstate_add_sym(argstate);
argstate->str.sec = elfedit_sec_getstr(argstate->obj_state,
argstate->sym.sec->sec_shdr->sh_link, 0);
}
static void
argstate_add_dynamic(ARGSTATE *argstate)
{
if (argstate->dynamic.sec != NULL)
return;
argstate->dynamic.sec = elfedit_sec_getdyn(argstate->obj_state,
&argstate->dynamic.data, &argstate->dynamic.n);
}
static void
dump_syminfo(ARGSTATE *argstate, Word ndx, Word cnt)
{
Syminfo *syminfo;
Sym *sym;
Dyn *dyn;
syminfo = argstate->syminfo.data + ndx;
argstate_add_sym(argstate);
sym = argstate->sym.data + ndx;
argstate_add_str(argstate);
argstate_add_dynamic(argstate);
dyn = argstate->dynamic.data;
Elf_syminfo_title(0);
for (; cnt-- > 0; ndx++, syminfo++, sym++) {
const char *needed = NULL, *name;
name = elfedit_offset_to_str(argstate->str.sec,
sym->st_name, ELFEDIT_MSG_ERR, 0);
if ((syminfo->si_boundto < SYMINFO_BT_LOWRESERVE) &&
(syminfo->si_boundto < argstate->dynamic.n) &&
((dyn[syminfo->si_boundto].d_tag == DT_NEEDED) ||
(dyn[syminfo->si_boundto].d_tag == DT_USED)))
needed = elfedit_offset_to_str(argstate->str.sec,
dyn[syminfo->si_boundto].d_un.d_val,
ELFEDIT_MSG_ERR, 0);
else
needed = MSG_ORIG(MSG_STR_EMPTY);
Elf_syminfo_entry(0, ndx, syminfo, name, needed);
}
}
static void
print_syminfo(SYMINFO_CMD_T cmd, int autoprint, ARGSTATE *argstate,
Word ndx, Word cnt)
{
elfedit_outstyle_t outstyle;
Syminfo *syminfo;
if ((autoprint && ((elfedit_flags() & ELFEDIT_F_AUTOPRINT) == 0)) ||
(cnt == 0))
return;
outstyle = (cmd == SYMINFO_CMD_T_DUMP) ?
ELFEDIT_OUTSTYLE_DEFAULT : elfedit_outstyle();
if (outstyle == ELFEDIT_OUTSTYLE_DEFAULT) {
dump_syminfo(argstate, ndx, cnt);
return;
}
syminfo = argstate->syminfo.data;
switch (cmd) {
case SYMINFO_CMD_T_SI_BOUNDTO:
if (outstyle == ELFEDIT_OUTSTYLE_SIMPLE) {
argstate_add_dynamic(argstate);
argstate_add_str(argstate);
}
for (syminfo += ndx; cnt--; syminfo++) {
Half bndto = syminfo->si_boundto;
if (outstyle == ELFEDIT_OUTSTYLE_SIMPLE) {
const char *str = NULL;
switch (bndto) {
case SYMINFO_BT_SELF:
str = elfedit_atoconst_value_to_str(
ELFEDIT_CONST_SYMINFO_BT,
SYMINFO_BT_SELF, 1);
break;
case SYMINFO_BT_PARENT:
str = elfedit_atoconst_value_to_str(
ELFEDIT_CONST_SYMINFO_BT,
SYMINFO_BT_PARENT, 1);
break;
case SYMINFO_BT_NONE:
str = elfedit_atoconst_value_to_str(
ELFEDIT_CONST_SYMINFO_BT,
SYMINFO_BT_NONE, 1);
break;
}
if ((str == NULL) &&
(bndto < SYMINFO_BT_LOWRESERVE) &&
(argstate->dynamic.sec != NULL) &&
(bndto < argstate->dynamic.n) &&
(argstate->dynamic.data[bndto].d_tag ==
DT_NEEDED))
str = elfedit_offset_to_str(
argstate->str.sec,
argstate->dynamic.data[bndto].
d_un.d_val, ELFEDIT_MSG_ERR, 0);
if (str != NULL) {
elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
str);
continue;
}
}
elfedit_printf(MSG_ORIG(MSG_FMT_WORDVALNL),
EC_WORD(bndto));
}
break;
case SYMINFO_CMD_T_SI_FLAGS:
for (syminfo += ndx; cnt--; syminfo++) {
if (outstyle == ELFEDIT_OUTSTYLE_SIMPLE) {
Conv_syminfo_flags_buf_t buf;
elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
conv_syminfo_flags(syminfo->si_flags,
CONV_FMT_NOBKT, &buf));
} else {
elfedit_printf(MSG_ORIG(MSG_FMT_HEXNUMNL),
EC_WORD(syminfo->si_flags));
}
}
break;
}
}
static Word
arg_to_symndx(ARGSTATE *argstate, const char *arg)
{
Word symndx;
if (argstate->optmask & SYMINFO_OPT_F_SYMNDX)
return (elfedit_atoui_range(arg, MSG_ORIG(MSG_STR_SYM),
0, argstate->syminfo.n - 1, NULL));
argstate_add_sym(argstate);
argstate_add_str(argstate);
(void) elfedit_name_to_symndx(argstate->sym.sec,
argstate->str.sec, arg, ELFEDIT_MSG_ERR, &symndx);
return (symndx);
}
static Half
needed_to_boundto(ARGSTATE *argstate, const char *arg)
{
Conv_inv_buf_t inv_buf;
elfedit_dyn_elt_t strpad_elt;
elfedit_dyn_elt_t null_elt;
elfedit_section_t *dynsec;
Word null_cnt;
Dyn *dyn;
Word str_offset, ndx, numdyn;
int have_string;
argstate_add_str(argstate);
argstate_add_dynamic(argstate);
dynsec = argstate->dynamic.sec;
numdyn = argstate->dynamic.n;
elfedit_dyn_elt_init(&strpad_elt);
elfedit_dyn_elt_init(&null_elt);
null_cnt = 0;
strpad_elt.dn_dyn.d_un.d_val = 0;
dyn = argstate->dynamic.data;
for (ndx = 0; ndx < numdyn; dyn++, ndx++) {
switch (dyn->d_tag) {
case DT_NULL:
null_cnt++;
if (!null_elt.dn_seen)
elfedit_dyn_elt_save(&null_elt, ndx, dyn);
break;
case DT_SUNW_STRPAD:
if (elfedit_test_osabi(argstate->obj_state,
ELFOSABI_SOLARIS, 0))
elfedit_dyn_elt_save(&strpad_elt, ndx, dyn);
break;
}
}
have_string = elfedit_sec_findstr(argstate->str.sec,
strpad_elt.dn_dyn.d_un.d_val, arg, &str_offset) != 0;
if (have_string) {
dyn = argstate->dynamic.data;
for (ndx = 0; ndx < numdyn; dyn++, ndx++) {
if (((dyn->d_tag == DT_NEEDED) ||
(dyn->d_tag == DT_USED)) &&
(dyn->d_un.d_val == str_offset))
goto done;
}
}
if (null_cnt < 2)
elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOEXTRANULL),
EC_WORD(dynsec->sec_shndx), dynsec->sec_name);
if (!have_string)
str_offset = elfedit_dynstr_insert(dynsec,
argstate->str.sec, &strpad_elt, arg);
ndx = null_elt.dn_ndx;
elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_CONVNULL),
EC_WORD(dynsec->sec_shndx), dynsec->sec_name, EC_WORD(ndx),
conv_dyn_tag(DT_NEEDED,
argstate->obj_state->os_ehdr->e_ident[EI_OSABI],
argstate->obj_state->os_ehdr->e_machine,
0, &inv_buf));
dyn = argstate->dynamic.data + ndx;
dyn->d_tag = DT_NEEDED;
dyn->d_un.d_val = str_offset;
elfedit_modified_data(dynsec);
done:
elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_FNDNEEDED),
dynsec->sec_shndx, dynsec->sec_name, ndx, arg);
return (ndx);
}
static elfedit_cmdret_t
cmd_body(SYMINFO_CMD_T cmd, elfedit_obj_state_t *obj_state,
int argc, const char *argv[])
{
ARGSTATE argstate;
Word ndx;
Syminfo *syminfo;
elfedit_cmdret_t ret = ELFEDIT_CMDRET_NONE;
process_args(obj_state, argc, argv, cmd, &argstate);
if (argstate.argc == 0) {
print_syminfo(cmd, 0, &argstate, 0, argstate.syminfo.n);
return (ELFEDIT_CMDRET_NONE);
}
ndx = arg_to_symndx(&argstate, argstate.argv[0]);
if (argstate.argc == 1) {
print_syminfo(cmd, 0, &argstate, ndx, 1);
return (ELFEDIT_CMDRET_NONE);
}
syminfo = &argstate.syminfo.data[ndx];
if (ndx == 0)
elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_CHGSYMINFO0),
EC_WORD(argstate.syminfo.sec->sec_shndx),
argstate.syminfo.sec->sec_name, EC_WORD(ndx));
switch (cmd) {
case SYMINFO_CMD_T_SI_BOUNDTO:
{
const char *name = MSG_ORIG(MSG_CMD_SI_BOUNDTO);
Half boundto;
if (argstate.optmask & SYMINFO_OPT_F_NEEDED)
boundto = needed_to_boundto(&argstate,
argstate.argv[1]);
else
boundto = elfedit_atoconst_range(
argstate.argv[1], MSG_ORIG(MSG_STR_VALUE),
0, 0xffff, ELFEDIT_CONST_SYMINFO_BT);
if (syminfo->si_boundto == boundto) {
elfedit_msg(ELFEDIT_MSG_DEBUG,
MSG_INTL(MSG_DEBUG_X_OK),
argstate.syminfo.sec->sec_shndx,
argstate.syminfo.sec->sec_name, ndx, name,
syminfo->si_boundto);
} else {
elfedit_msg(ELFEDIT_MSG_DEBUG,
MSG_INTL(MSG_DEBUG_X_CHG),
argstate.syminfo.sec->sec_shndx,
argstate.syminfo.sec->sec_name, ndx, name,
syminfo->si_boundto, boundto);
ret = ELFEDIT_CMDRET_MOD;
syminfo->si_boundto = boundto;
}
}
break;
case SYMINFO_CMD_T_SI_FLAGS:
{
Conv_syminfo_flags_buf_t flags_buf1, flags_buf2;
const char *name = MSG_ORIG(MSG_CMD_SI_FLAGS);
Half flags = 0;
int i;
for (i = 1; i < argstate.argc; i++)
flags |= (Word)
elfedit_atoconst(argstate.argv[i],
ELFEDIT_CONST_SYMINFO_FLG);
if (argstate.optmask & SYMINFO_OPT_F_CMP)
flags = ~flags;
if (argstate.optmask & SYMINFO_OPT_F_AND)
flags &= syminfo->si_flags;
else if (argstate.optmask & SYMINFO_OPT_F_OR)
flags |= syminfo->si_flags;
if (syminfo->si_flags == flags) {
elfedit_msg(ELFEDIT_MSG_DEBUG,
MSG_INTL(MSG_DEBUG_S_OK),
argstate.syminfo.sec->sec_shndx,
argstate.syminfo.sec->sec_name, ndx, name,
conv_syminfo_flags(syminfo->si_flags,
0, &flags_buf1));
} else {
elfedit_msg(ELFEDIT_MSG_DEBUG,
MSG_INTL(MSG_DEBUG_S_CHG),
argstate.syminfo.sec->sec_shndx,
argstate.syminfo.sec->sec_name, ndx, name,
conv_syminfo_flags(syminfo->si_flags,
0, &flags_buf1),
conv_syminfo_flags(flags, 0, &flags_buf2));
ret = ELFEDIT_CMDRET_MOD;
syminfo->si_flags = flags;
}
}
break;
}
if (ret == ELFEDIT_CMDRET_MOD)
elfedit_modified_data(argstate.syminfo.sec);
print_syminfo(cmd, 1, &argstate, ndx, 1);
return (ret);
}
static void
cpl_si_boundto(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
const char *argv[], int num_opt)
{
int i;
if (argc != (num_opt + 2))
return;
for (i = 0; i < num_opt; i++)
if (strcmp(argv[i], MSG_ORIG(MSG_STR_MINUS_NEEDED)) == 0)
return;
elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_SYMINFO_BT);
}
static void
cpl_si_flags(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
const char *argv[], int num_opt)
{
if (argc == (num_opt + 2))
elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_SYMINFO_FLG);
}
static elfedit_cmdret_t
cmd_dump(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
{
return (cmd_body(SYMINFO_CMD_T_DUMP, obj_state, argc, argv));
}
static elfedit_cmdret_t
cmd_si_boundto(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
{
return (cmd_body(SYMINFO_CMD_T_SI_BOUNDTO, obj_state, argc, argv));
}
static elfedit_cmdret_t
cmd_si_flags(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
{
return (cmd_body(SYMINFO_CMD_T_SI_FLAGS, obj_state, argc, argv));
}
elfedit_module_t *
elfedit_init(elfedit_module_version_t version)
{
static const char *name_dump[] = {
MSG_ORIG(MSG_CMD_DUMP),
MSG_ORIG(MSG_STR_EMPTY),
NULL
};
static elfedit_cmd_optarg_t opt_dump[] = {
{ MSG_ORIG(MSG_STR_MINUS_SYMNDX),
ELFEDIT_I18NHDL(MSG_OPTDESC_SYMNDX), 0,
SYMINFO_OPT_F_SYMNDX, 0 },
{ NULL }
};
static elfedit_cmd_optarg_t arg_dump[] = {
{ MSG_ORIG(MSG_STR_SYM),
ELFEDIT_I18NHDL(MSG_A1_SYM),
ELFEDIT_CMDOA_F_OPT },
{ NULL }
};
static const char *name_si_boundto[] = {
MSG_ORIG(MSG_CMD_SI_BOUNDTO), NULL };
static elfedit_cmd_optarg_t opt_si_boundto[] = {
{ MSG_ORIG(MSG_STR_MINUS_NEEDED),
ELFEDIT_I18NHDL(MSG_OPTDESC_NEEDED), 0,
SYMINFO_OPT_F_NEEDED, 0 },
{ ELFEDIT_STDOA_OPT_O, 0,
ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
{ MSG_ORIG(MSG_STR_MINUS_SYMNDX),
ELFEDIT_I18NHDL(MSG_OPTDESC_SYMNDX), 0,
SYMINFO_OPT_F_SYMNDX, 0 },
{ NULL }
};
static elfedit_cmd_optarg_t arg_si_boundto[] = {
{ MSG_ORIG(MSG_STR_SYM),
ELFEDIT_I18NHDL(MSG_A1_SYM),
ELFEDIT_CMDOA_F_OPT },
{ MSG_ORIG(MSG_STR_VALUE),
ELFEDIT_I18NHDL(MSG_A2_DESC_SI_BOUNDTO),
ELFEDIT_CMDOA_F_OPT },
{ NULL }
};
static const char *name_si_flags[] = {
MSG_ORIG(MSG_CMD_SI_FLAGS), NULL };
static elfedit_cmd_optarg_t opt_si_flags[] = {
{ ELFEDIT_STDOA_OPT_AND, 0, ELFEDIT_CMDOA_F_INHERIT,
SYMINFO_OPT_F_AND, SYMINFO_OPT_F_OR },
{ ELFEDIT_STDOA_OPT_CMP, 0,
ELFEDIT_CMDOA_F_INHERIT, SYMINFO_OPT_F_CMP, 0 },
{ ELFEDIT_STDOA_OPT_O, 0,
ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
{ ELFEDIT_STDOA_OPT_OR, 0, ELFEDIT_CMDOA_F_INHERIT,
SYMINFO_OPT_F_OR, SYMINFO_OPT_F_AND },
{ MSG_ORIG(MSG_STR_MINUS_SYMNDX),
ELFEDIT_I18NHDL(MSG_OPTDESC_SYMNDX), 0,
SYMINFO_OPT_F_SYMNDX, 0 },
{ NULL }
};
static elfedit_cmd_optarg_t arg_si_flags[] = {
{ MSG_ORIG(MSG_STR_SYM),
ELFEDIT_I18NHDL(MSG_A1_SYM),
ELFEDIT_CMDOA_F_OPT },
{ MSG_ORIG(MSG_STR_VALUE),
ELFEDIT_I18NHDL(MSG_A2_DESC_SI_FLAGS),
ELFEDIT_CMDOA_F_OPT | ELFEDIT_CMDOA_F_MULT },
{ NULL }
};
static elfedit_cmd_t cmds[] = {
{ cmd_dump, NULL, name_dump,
ELFEDIT_I18NHDL(MSG_DESC_DUMP),
ELFEDIT_I18NHDL(MSG_HELP_DUMP),
opt_dump, arg_dump },
{ cmd_si_boundto, cpl_si_boundto, name_si_boundto,
ELFEDIT_I18NHDL(MSG_DESC_SI_BOUNDTO),
ELFEDIT_I18NHDL(MSG_HELP_SI_BOUNDTO),
opt_si_boundto, arg_si_boundto },
{ cmd_si_flags, cpl_si_flags, name_si_flags,
ELFEDIT_I18NHDL(MSG_DESC_SI_FLAGS),
ELFEDIT_I18NHDL(MSG_HELP_SI_FLAGS),
opt_si_flags, arg_si_flags },
{ NULL }
};
static elfedit_module_t module = {
ELFEDIT_VER_CURRENT, MSG_ORIG(MSG_MOD_NAME),
ELFEDIT_I18NHDL(MSG_MOD_DESC),
cmds, mod_i18nhdl_to_str };
return (&module);
}