/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <elfedit.h>
#include <strings.h>
#include <debug.h>
#include <conv.h>
#include <str_msg.h>
#define MAXNDXSIZE 10
/*
* This module uses shared code for several of the commands.
* It is sometimes necessary to know which specific command
* is active.
*/
typedef enum {
STR_CMD_T_DUMP = 0, /* str:dump */
STR_CMD_T_SET = 1, /* str:set */
STR_CMD_T_ADD = 2, /* str:add */
STR_CMD_T_ZERO = 3, /* str:zero */
} STR_CMD_T;
#ifndef _ELF64
/*
* We supply this function for the msg module. Only one copy is needed.
*/
const char *
_str_msg(Msg mid)
{
return (gettext(MSG_ORIG(mid)));
}
#endif
/*
* This function is supplied to elfedit through our elfedit_module_t
* definition. It translates the opaque elfedit_i18nhdl_t handles
* in our module interface into the actual strings for elfedit to
* use.
*
* note:
* This module uses Msg codes for its i18n handle type.
* So the translation is simply to use MSG_INTL() to turn
* it into a string and return it.
*/
static const char *
mod_i18nhdl_to_str(elfedit_i18nhdl_t hdl)
{
Msg msg = (Msg)hdl;
return (MSG_INTL(msg));
}
/*
* The sym_opt_t enum specifies a bit value for every optional
* argument allowed by a command in this module.
*/
typedef enum {
STR_OPT_F_ANY = 1, /* -any: treat any sec. as strtab */
STR_OPT_F_END = 2, /* -end: zero to end of strtab */
STR_OPT_F_NOTERM = 4, /* -noterm: str:set won't term string */
STR_OPT_F_SHNAME = 8, /* -shnam name: section spec. by name */
STR_OPT_F_SHNDX = 16, /* -shndx ndx: strtab spec. by index */
STR_OPT_F_SHTYP = 32, /* -shtyp type: section spec. by type */
STR_OPT_F_STRNDX = 64, /* -strndx: String specified by index */
} str_opt_t;
/*
* A variable of type ARGSTATE is used by each command to maintain
* information about the string table section being used, and for any
* auxiliary sections that are related to it.
*/
typedef struct {
elfedit_obj_state_t *obj_state;
str_opt_t optmask; /* Mask of options used */
int argc; /* # of plain arguments */
const char **argv; /* Plain arguments */
struct { /* String table */
elfedit_section_t *sec;
Word ndx; /* Table offset if (argc > 0) */
} str;
struct { /* Dynamic section */
elfedit_section_t *sec;
Dyn *data;
Word n;
elfedit_dyn_elt_t strpad;
} dyn;
} ARGSTATE;
/*
* Given an ELF SHT_ section type constant, shndx_to_strtab() returns
* one of the following
*/
typedef enum {
SHTOSTR_NONE = 0, /* Type can't lead to a string table */
SHTOSTR_STRTAB = 1, /* type is SHT_STRTAB */
SHTOSTR_LINK_STRTAB = 2, /* sh_link for type yields strtab */
SHTOSTR_LINK_SYMTAB = 3, /* sh_link for type yields symtab */
SHTOSTR_SHF_STRINGS = 4, /* Not strtab, but SHF_STRINGS set */
} SHTOSTR_T;
static SHTOSTR_T
shtype_to_strtab(Word sh_type, Word sh_flags)
{
/*
* A string table section always leads to itself. A
* non-string table that has it's SHF_STRINGS section flag
* set trumps anything else.
*/
if (sh_type == SHT_STRTAB)
return (SHTOSTR_STRTAB);
if (sh_flags & SHF_STRINGS)
return (SHTOSTR_SHF_STRINGS);
/*
* Look at non-stringtable section types that can lead to
* string tables via sh_link.
*/
switch (sh_type) {
/* These sections reference a string table via sh_link */
case SHT_DYNAMIC:
case SHT_SYMTAB:
case SHT_DYNSYM:
case SHT_SUNW_LDYNSYM:
case SHT_SUNW_verdef:
case SHT_SUNW_verneed:
return (SHTOSTR_LINK_STRTAB);
/*
* These sections reference a symbol table via sh_link.
* Symbol tables, in turn, reference a string table
* via their sh_link.
*/
case SHT_HASH:
case SHT_REL:
case SHT_RELA:
case SHT_GROUP:
case SHT_SYMTAB_SHNDX:
case SHT_SUNW_move:
case SHT_SUNW_syminfo:
case SHT_SUNW_versym:
case SHT_SUNW_symsort:
case SHT_SUNW_tlssort:
return (SHTOSTR_LINK_SYMTAB);
}
/* Types that lead to string tables were caught above */
return (SHTOSTR_NONE);
}
/*
* Given a section index, attempt to convert it into an index
* to a string table section.
*/
static Word
shndx_to_strtab(elfedit_obj_state_t *obj_state, Word ndx)
{
/*
* Locate and validate the string table. In the case where
* a non-string table section is given that references a string
* table, we will use the referenced table.
*/
if (ndx < obj_state->os_shnum) {
Shdr *shdr = obj_state->os_secarr[ndx].sec_shdr;
switch (shtype_to_strtab(shdr->sh_type, shdr->sh_flags)) {
/* Sections that reference a string table via sh_link */
case SHTOSTR_LINK_STRTAB:
ndx = shdr->sh_link;
break;
/*
* Sections that reference a symbol tabel via sh_link,
* which in turn reference a string table via their sh_link.
*/
case SHTOSTR_LINK_SYMTAB:
ndx = shdr->sh_link;
if (ndx < obj_state->os_shnum)
ndx =
obj_state->os_secarr[ndx].sec_shdr->sh_link;
break;
}
}
return (ndx);
}
/*
* Standard argument processing for string table module
*
* entry
* obj_state, argc, argv - Standard command arguments
* optmask - Mask of allowed optional arguments.
* argstate - Address of ARGSTATE block to be initialized
*
* exit:
* On success, *argstate is initialized. On error,
* an error is issued and this routine does not return.
*/
static void
process_args(elfedit_obj_state_t *obj_state, int argc, const char *argv[],
STR_CMD_T cmd, ARGSTATE *argstate, int *print_only)
{
elfedit_getopt_state_t getopt_state;
elfedit_getopt_ret_t *getopt_ret;
Word ndx;
int argc_ok;
bzero(argstate, sizeof (*argstate));
argstate->obj_state = obj_state;
/*
* By default, we use the section name string table pointed at
* by the ELF header.
*/
ndx = obj_state->os_ehdr->e_shstrndx;
elfedit_getopt_init(&getopt_state, &argc, &argv);
/* Add each new option to the options mask */
while ((getopt_ret = elfedit_getopt(&getopt_state)) != NULL) {
argstate->optmask |= getopt_ret->gor_idmask;
switch (getopt_ret->gor_idmask) {
case STR_OPT_F_SHNAME: /* -shnam name */
ndx = elfedit_name_to_shndx(obj_state,
getopt_ret->gor_value);
break;
case STR_OPT_F_SHNDX: /* -shndx index */
ndx = elfedit_atoui(getopt_ret->gor_value, NULL);
break;
case STR_OPT_F_SHTYP: /* -shtyp type */
ndx = elfedit_type_to_shndx(obj_state,
elfedit_atoconst(getopt_ret->gor_value,
ELFEDIT_CONST_SHT));
break;
}
}
/*
* Usage error if there are the wrong number of plain arguments.
*/
switch (cmd) {
case STR_CMD_T_DUMP:
argc_ok = (argc == 0) || (argc == 1);
*print_only = 1;
break;
case STR_CMD_T_SET:
argc_ok = (argc == 1) || (argc == 2);
*print_only = (argc == 1);
break;
case STR_CMD_T_ADD:
argc_ok = (argc == 1);
*print_only = 0;
break;
case STR_CMD_T_ZERO:
/*
* The second argument (count) and the -end option are
* mutally exclusive.
*/
argc_ok = ((argc == 1) || (argc == 2)) &&
!((argc == 2) && (argstate->optmask & STR_OPT_F_END));
*print_only = 0;
break;
default:
argc_ok = 0; /* Unknown command? */
break;
}
if (!argc_ok)
elfedit_command_usage();
/* If there may be an arbitrary amount of output, use a pager */
if (argc == 0)
elfedit_pager_init();
/* Return the updated values of argc/argv */
argstate->argc = argc;
argstate->argv = argv;
if (argstate->optmask & STR_OPT_F_ANY) {
/* Take the arbitrary section */
argstate->str.sec = elfedit_sec_get(obj_state, ndx);
} else {
/*
* Locate and validate the string table. In the case where
* a non-string table section is given that references a string
* table, we will use the referenced table.
*/
ndx = shndx_to_strtab(obj_state, ndx);
/*
* If ndx is a string table, the following will issue the
* proper debug messages. If it is out of range, or of any
* other type, an error is issued and it doesn't return.
*/
argstate->str.sec = elfedit_sec_getstr(obj_state, ndx, 1);
}
/*
* If there is a dynamic section, check its sh_link to the
* string table index. If these match, then we have the
* dynamic string table. In that case, fetch the dynamic
* section and locate the DT_SUNW_STRPAD entry, causing
* debug messages to be issued.
*/
argstate->dyn.sec = NULL;
elfedit_dyn_elt_init(&argstate->dyn.strpad);
if (obj_state->os_dynndx != SHN_UNDEF) {
elfedit_section_t *dynsec =
&obj_state->os_secarr[obj_state->os_dynndx];
if ((dynsec->sec_shdr->sh_type == SHT_DYNAMIC) &&
(argstate->str.sec->sec_shndx ==
dynsec->sec_shdr->sh_link)) {
argstate->dyn.sec = elfedit_sec_getdyn(obj_state,
&argstate->dyn.data, &argstate->dyn.n);
(void) elfedit_dynstr_getpad(obj_state, dynsec,
&argstate->dyn.strpad);
/*
* Does the pad value make sense?
* Issue debug message and ignore it if not.
*/
if ((argstate->dyn.strpad.dn_seen != 0) &&
(argstate->dyn.strpad.dn_dyn.d_un.d_val >
argstate->str.sec->sec_data->d_size)) {
argstate->dyn.strpad.dn_seen = 0;
elfedit_msg(ELFEDIT_MSG_DEBUG,
MSG_INTL(MSG_DEBUG_BADSTRPAD),
EC_WORD(argstate->str.sec->sec_shndx),
argstate->str.sec->sec_name,
EC_XWORD(argstate->dyn.strpad.dn_dyn.
d_un.d_val),
EC_XWORD(argstate->str.sec->
sec_data->d_size));
}
}
}
/* Locate the string table offset if argument is present */
if ((argc > 0) && (cmd != STR_CMD_T_ADD)) {
/*
* If the -strndx option was specified, arg is an index
* into the string table. Otherwise it is a string
* to be looked up.
*/
if (argstate->optmask & STR_OPT_F_STRNDX) {
argstate->str.ndx = (elfedit_atoui_range(argv[0],
MSG_ORIG(MSG_STR_STRING), 0,
argstate->str.sec->sec_data->d_size - 1, NULL));
} else {
if (elfedit_sec_findstr(argstate->str.sec, 0, argv[0],
&argstate->str.ndx) == 0)
elfedit_msg(ELFEDIT_MSG_ERR,
MSG_INTL(MSG_ERR_STRNOTFND),
EC_WORD(argstate->str.sec->sec_shndx),
argstate->str.sec->sec_name, argv[0]);
}
} else {
argstate->str.ndx = 0;
}
}
/*
* Print string table values, taking output style into account.
*
* entry:
* autoprint - If True, output is only produced if the elfedit
* autoprint flag is set. If False, output is always produced.
* argstate - State block for current symbol table.
*/
static void
print_strtab(int autoprint, ARGSTATE *argstate)
{
char index[(MAXNDXSIZE * 2) + 4];
elfedit_outstyle_t outstyle;
const char *str, *limit, *tbl_limit;
Word ndx;
if (autoprint && ((elfedit_flags() & ELFEDIT_F_AUTOPRINT) == 0))
return;
outstyle = elfedit_outstyle();
if (outstyle == ELFEDIT_OUTSTYLE_DEFAULT) {
elfedit_printf(MSG_INTL(MSG_FMT_STRTAB),
argstate->str.sec->sec_name);
if (argstate->dyn.strpad.dn_seen)
elfedit_printf(MSG_INTL(MSG_FMT_DYNSTRPAD),
EC_WORD(argstate->str.sec->sec_data->d_size -
argstate->dyn.strpad.dn_dyn.d_un.d_val),
EC_WORD(argstate->str.sec->sec_data->d_size - 1),
EC_WORD(argstate->dyn.strpad.dn_dyn.d_un.d_val));
elfedit_printf(MSG_INTL(MSG_FMT_DUMPTITLE));
}
str = argstate->str.sec->sec_data->d_buf;
tbl_limit = str + argstate->str.sec->sec_data->d_size;
ndx = argstate->str.ndx;
if (argstate->argc > 0) {
str += ndx;
/*
* If first byte is NULL and this is the default output style,
* then we want to display the range of NULL bytes, and we
* push limit out to the last one in the sequence. Otherwise,
* just display the string.
*/
if ((*str == '\0') && (outstyle == ELFEDIT_OUTSTYLE_DEFAULT)) {
limit = str;
while (((limit + 1) < tbl_limit) &&
(*(limit + 1) == '\0'))
limit++;
} else {
limit = str + strlen(str) + 1;
}
} else {
/* Display the entire string table */
limit = tbl_limit;
}
while (str < limit) {
Word skip = strlen(str) + 1;
Word start_ndx;
if (outstyle != ELFEDIT_OUTSTYLE_DEFAULT) {
elfedit_printf(MSG_ORIG(MSG_FMT_STRNL), str);
str += skip;
ndx += skip;
continue;
}
start_ndx = ndx;
if (*str == '\0')
while (((str + 1) < limit) && (*(str + 1) == '\0')) {
ndx++;
str++;
}
if (start_ndx != ndx) {
(void) snprintf(index, sizeof (index),
MSG_ORIG(MSG_FMT_INDEXRANGE),
EC_XWORD(start_ndx), EC_XWORD(ndx));
} else {
(void) snprintf(index, sizeof (index),
MSG_ORIG(MSG_FMT_INDEX), EC_XWORD(ndx));
}
elfedit_printf(MSG_ORIG(MSG_FMT_DUMPENTRY), index);
elfedit_write(MSG_ORIG(MSG_STR_DQUOTE), MSG_STR_DQUOTE_SIZE);
if (start_ndx == ndx)
elfedit_str_to_c_literal(str, elfedit_write);
elfedit_write(MSG_ORIG(MSG_STR_DQUOTENL),
MSG_STR_DQUOTENL_SIZE);
str += skip;
ndx += skip;
}
}
/*
* Command body for str:set, handling the case where the 3rd
* argument (new-str) is present.
*/
static elfedit_cmdret_t
cmd_body_set(ARGSTATE *argstate)
{
elfedit_section_t *strsec = argstate->str.sec;
const char *newstr = argstate->argv[1];
Word ndx = argstate->str.ndx;
char *oldstr;
int i, len, ncp;
len = strlen(newstr);
ncp = len;
if (!(argstate->optmask & STR_OPT_F_NOTERM))
ncp++;
/* NULL string with no termination? Nothing to do */
if (ncp == 0)
return (ELFEDIT_CMDRET_NONE);
/* Does it fit? */
if ((ndx + ncp) > strsec->sec_data->d_size)
elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOFIT),
EC_WORD(strsec->sec_shndx), strsec->sec_name,
EC_WORD(ndx), newstr);
/* Does it clobber the final NULL termination? */
if (((ndx + ncp) == strsec->sec_data->d_size) &&
(argstate->optmask & STR_OPT_F_NOTERM))
elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_FINALNULL),
EC_WORD(strsec->sec_shndx), strsec->sec_name,
EC_WORD(ndx), newstr);
/*
* strtab[0] is always supposed to contain a NULL byte. You're not
* supposed to mess with it. We will carry out this operation,
* but with a debug message indicating that it is unorthodox.
*/
if ((ndx == 0) && (*newstr != '\0'))
elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_CHGSTR0),
EC_WORD(strsec->sec_shndx), strsec->sec_name,
EC_WORD(ndx), newstr);
/* Does it alter the existing value? */
oldstr = ndx + (char *)strsec->sec_data->d_buf;
for (i = 0; i < ncp; i++)
if (newstr[i] != oldstr[i])
break;
if (i == ncp) { /* No change */
elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_S_OK),
strsec->sec_shndx, strsec->sec_name, ndx, newstr);
return (ELFEDIT_CMDRET_NONE);
}
/*
* If the new string is longer than the old one, then it will
* clobber the start of the following string. The resulting
* string table is perfectly legal, but issue a debug message
* letting the user know.
*/
i = strlen(oldstr);
if (len > i)
elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_LONGSTR),
EC_WORD(strsec->sec_shndx), strsec->sec_name,
EC_WORD(ndx), len, i);
/*
* If we have strayed into the reserved part of the dynstr, then
* update DT_SUNW_STRPAD.
*/
if (argstate->dyn.strpad.dn_seen) {
elfedit_dyn_elt_t *strpad = &argstate->dyn.strpad;
Word new_pad_ndx = ndx + len + 1;
Word pad_ndx = argstate->str.sec->sec_data->d_size -
strpad->dn_dyn.d_un.d_val;
if (new_pad_ndx > pad_ndx) {
elfedit_msg(ELFEDIT_MSG_DEBUG,
MSG_INTL(MSG_DEBUG_ADDDYNSTR),
EC_WORD(strsec->sec_shndx), strsec->sec_name,
EC_WORD(ndx), EC_WORD(new_pad_ndx - pad_ndx),
EC_WORD(strpad->dn_dyn.d_un.d_val),
newstr);
strpad->dn_dyn.d_un.d_val =
argstate->dyn.data[strpad->dn_ndx].d_un.d_val =
(argstate->str.sec->sec_data->d_size - new_pad_ndx);
elfedit_modified_data(argstate->dyn.sec);
}
}
elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_S_CHG),
strsec->sec_shndx, strsec->sec_name, ndx, len, oldstr, newstr);
bcopy(newstr, oldstr, ncp);
return (ELFEDIT_CMDRET_MOD);
}
/*
* Command body for str:zero
*/
static elfedit_cmdret_t
cmd_body_zero(ARGSTATE *argstate)
{
elfedit_section_t *strsec = argstate->str.sec;
Word count;
Word ndx = argstate->str.ndx;
char *oldstr = ndx + (char *)strsec->sec_data->d_buf;
Word i;
/* How many bytes to zero? */
if (argstate->optmask & STR_OPT_F_END)
count = strsec->sec_data->d_size - argstate->str.ndx;
else if (argstate->argc == 2)
count = elfedit_atoui_range(argstate->argv[1],
MSG_ORIG(MSG_STR_COUNT), 0,
argstate->str.sec->sec_data->d_size - argstate->str.ndx,
NULL);
else
count = strlen(oldstr);
/* Does it alter the existing value? */
for (i = 0; i < count; i++)
if (oldstr[i] != '\0')
break;
if (i == count) { /* No change */
elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_Z_OK),
strsec->sec_shndx, strsec->sec_name, ndx);
return (ELFEDIT_CMDRET_NONE);
}
elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_Z_CHG),
strsec->sec_shndx, strsec->sec_name, ndx, count);
bzero(oldstr, count);
return (ELFEDIT_CMDRET_MOD);
}
/*
* Common body for the str: module commands.
*
* entry:
* cmd - One of the STR_CMD_T_* constants listed above, specifying
* which command to implement.
* obj_state, argc, argv - Standard command arguments
*/
static elfedit_cmdret_t
cmd_body(STR_CMD_T cmd, elfedit_obj_state_t *obj_state,
int argc, const char *argv[])
{
ARGSTATE argstate;
elfedit_cmdret_t ret = ELFEDIT_CMDRET_NONE;
int print_only;
process_args(obj_state, argc, argv, cmd, &argstate, &print_only);
/*
* If this call call does not change data, display the current
* value(s) and return.
*/
if (print_only) {
print_strtab(0, &argstate);
return (ELFEDIT_CMDRET_NONE);
}
switch (cmd) {
/* NOTE: STR_CMD_T_DUMP can't get here --- it's always print_only */
case STR_CMD_T_SET:
ret = cmd_body_set(&argstate);
break;
case STR_CMD_T_ADD:
argstate.str.ndx = elfedit_strtab_insert(obj_state,
argstate.str.sec, argstate.dyn.sec, argstate.argv[0]);
break;
case STR_CMD_T_ZERO:
ret = cmd_body_zero(&argstate);
break;
}
/*
* If we modified the strtab section, tell libelf.
*/
if (ret == ELFEDIT_CMDRET_MOD)
elfedit_modified_data(argstate.str.sec);
/* Do autoprint */
print_strtab(1, &argstate);
return (ret);
}
/*
* Command completion functions for the various commands
*/
static void
add_shtyp_match(Word sh_type, void *cpldata)
{
char buf[128];
const char *s;
char *s2;
s = elfedit_atoconst_value_to_str(ELFEDIT_CONST_SHT, sh_type, 0);
elfedit_cpl_match(cpldata, s, 1);
/*
* To get the informal tag names that are lowercase
* and lack the leading SHT_, we copy the string we
* have into a buffer and process it.
*/
if (strlen(s) < 4)
return;
(void) strlcpy(buf, s + 4, sizeof (buf));
for (s2 = buf; *s2 != '\0'; s2++)
if (isupper(*s2))
*s2 = tolower(*s2);
elfedit_cpl_match(cpldata, buf, 1);
}
/*
* Handle filling in the values for -shnam, -shndx, and -shtyp options.
*/
/*ARGSUSED*/
static void
cpl_sh_opt(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
const char *argv[], int num_opt)
{
enum { NAME, INDEX, TYPE } op;
elfedit_section_t *sec;
Word ndx;
if ((argc != num_opt) || (argc < 2))
return;
if (strcmp(argv[argc - 2], MSG_ORIG(MSG_STR_MINUS_SHNAM)) == 0) {
op = NAME;
} else if (strcmp(argv[argc - 2], MSG_ORIG(MSG_STR_MINUS_SHNDX)) == 0) {
op = INDEX;
} else if (strcmp(argv[argc - 2], MSG_ORIG(MSG_STR_MINUS_SHTYP)) == 0) {
op = TYPE;
if (obj_state == NULL) { /* No object available */
elfedit_atoui_sym_t *atoui_sym;
atoui_sym = elfedit_const_to_atoui(ELFEDIT_CONST_SHT);
for (; atoui_sym->sym_name != NULL; atoui_sym++)
if (shtype_to_strtab(atoui_sym->sym_value, 0) !=
SHTOSTR_NONE)
elfedit_cpl_match(cpldata,
atoui_sym->sym_name, 1);
}
} else {
return;
}
if (obj_state == NULL) /* No object available */
return;
/*
* Loop over the section headers and supply command completion
* for the items in the file that can yield a string table.
*/
sec = obj_state->os_secarr;
for (ndx = 0; ndx < obj_state->os_shnum; ndx++, sec++) {
Shdr *shdr = sec->sec_shdr;
SHTOSTR_T shtostr_type;
shtostr_type = shtype_to_strtab(shdr->sh_type, shdr->sh_flags);
if (shtostr_type == SHTOSTR_NONE)
continue;
switch (op) {
case NAME:
elfedit_cpl_match(cpldata, sec->sec_name, 0);
break;
case INDEX:
elfedit_cpl_ndx(cpldata, sec->sec_shndx);
break;
case TYPE:
if (shtostr_type != SHTOSTR_SHF_STRINGS)
add_shtyp_match(shdr->sh_type, cpldata);
break;
}
}
}
/*
* Most of the commands accept an -shXXX option for the string table
* and a string first argument. This routine examines which argument
* is being processed, and supplies completion for these items.
*/
static void
cpl_sec_str(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
const char *argv[], int num_opt)
{
const char *str, *limit;
elfedit_section_t *sec;
Word strtab_ndx;
Word ndx;
/* Handle -shXXX options */
cpl_sh_opt(obj_state, cpldata, argc, argv, num_opt);
/* Without object state, there's no data to work from */
if (obj_state == NULL)
return;
/* If not first plain arg, return */
if (argc != (num_opt + 1))
return;
/*
* Look at the options, looking for two things:
* 1) A -shXXX option specifying a section. If so, turn that
* into a section index if possible.
* 2) Was -strndx used? If so, we are looking at an integer
* value and have nothing to complete.
*/
strtab_ndx = obj_state->os_ehdr->e_shstrndx;
for (ndx = 0; ndx < num_opt; ndx++) {
if (strcmp(argv[ndx], MSG_ORIG(MSG_STR_MINUS_STRNDX)) == 0)
return;
if ((ndx+1) < num_opt) {
if (strcmp(argv[ndx],
MSG_ORIG(MSG_STR_MINUS_SHNAM)) == 0) {
Word i;
for (i = 1; i < obj_state->os_shnum; i++)
if (strcmp(obj_state->os_secarr[i].
sec_name, argv[ndx+1]) == 0) {
strtab_ndx = i;
break;
}
} else if (strcmp(argv[ndx],
MSG_ORIG(MSG_STR_MINUS_SHNDX)) == 0) {
elfedit_atoui_t val;
if (elfedit_atoui2(argv[ndx+1], NULL,
&val) != 0)
strtab_ndx = val;
} else if (strcmp(argv[ndx],
MSG_ORIG(MSG_STR_MINUS_SHTYP)) == 0) {
elfedit_atoui_t sh_type;
Word i;
if (elfedit_atoconst2(argv[ndx+1],
ELFEDIT_CONST_SHT, &sh_type) == 0)
continue;
for (i = 1; i < obj_state->os_shnum; i++)
if (obj_state->os_secarr[i].sec_shdr->
sh_type == sh_type) {
strtab_ndx = i;
break;
}
}
}
}
/*
* Locate and validate the string table. In the case where
* a non-string table section is given that references a string
* table, we will use the referenced table.
*/
strtab_ndx = shndx_to_strtab(obj_state, strtab_ndx);
if ((strtab_ndx >= obj_state->os_shnum) ||
(obj_state->os_secarr[strtab_ndx].sec_shdr->sh_type != SHT_STRTAB))
return;
sec = &obj_state->os_secarr[strtab_ndx];
str = sec->sec_data->d_buf;
limit = str + sec->sec_data->d_size;
while (str < limit) {
if (*str != '\0')
elfedit_cpl_match(cpldata, str, 0);
str += strlen(str) + 1;
}
}
/*
* Implementation functions for the commands
*/
static elfedit_cmdret_t
cmd_dump(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
{
return (cmd_body(STR_CMD_T_DUMP, obj_state, argc, argv));
}
static elfedit_cmdret_t
cmd_set(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
{
return (cmd_body(STR_CMD_T_SET, obj_state, argc, argv));
}
static elfedit_cmdret_t
cmd_add(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
{
return (cmd_body(STR_CMD_T_ADD, obj_state, argc, argv));
}
static elfedit_cmdret_t
cmd_zero(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
{
return (cmd_body(STR_CMD_T_ZERO, obj_state, argc, argv));
}
/*ARGSUSED*/
elfedit_module_t *
elfedit_init(elfedit_module_version_t version)
{
/* str:dump */
static const char *name_dump[] = {
MSG_ORIG(MSG_CMD_DUMP),
MSG_ORIG(MSG_STR_EMPTY), /* "" makes this the default command */
NULL
};
static elfedit_cmd_optarg_t opt_dump[] = {
{ MSG_ORIG(MSG_STR_MINUS_ANY),
/* MSG_INTL(MSG_OPTDESC_ANY) */
ELFEDIT_I18NHDL(MSG_OPTDESC_ANY), 0,
STR_OPT_F_ANY, 0 },
{ ELFEDIT_STDOA_OPT_O, NULL,
ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
{ MSG_ORIG(MSG_STR_MINUS_SHNAM),
/* MSG_INTL(MSG_OPTDESC_SHNAM) */
ELFEDIT_I18NHDL(MSG_OPTDESC_SHNAM), ELFEDIT_CMDOA_F_VALUE,
STR_OPT_F_SHNAME, STR_OPT_F_SHNDX | STR_OPT_F_SHTYP },
{ MSG_ORIG(MSG_STR_NAME), NULL, 0 },
{ MSG_ORIG(MSG_STR_MINUS_SHNDX),
/* MSG_INTL(MSG_OPTDESC_SHNDX) */
ELFEDIT_I18NHDL(MSG_OPTDESC_SHNDX), ELFEDIT_CMDOA_F_VALUE,
STR_OPT_F_SHNDX, STR_OPT_F_SHNAME | STR_OPT_F_SHTYP },
{ MSG_ORIG(MSG_STR_INDEX), NULL, 0 },
{ MSG_ORIG(MSG_STR_MINUS_SHTYP),
/* MSG_INTL(MSG_OPTDESC_SHTYP) */
ELFEDIT_I18NHDL(MSG_OPTDESC_SHTYP), ELFEDIT_CMDOA_F_VALUE,
STR_OPT_F_SHTYP, STR_OPT_F_SHNAME | STR_OPT_F_SHNDX },
{ MSG_ORIG(MSG_STR_TYPE), NULL, 0 },
{ MSG_ORIG(MSG_STR_MINUS_STRNDX),
/* MSG_INTL(MSG_OPTDESC_STRNDX) */
ELFEDIT_I18NHDL(MSG_OPTDESC_STRNDX), 0,
STR_OPT_F_STRNDX, 0 },
{ NULL }
};
static elfedit_cmd_optarg_t arg_dump[] = {
{ MSG_ORIG(MSG_STR_STRING),
/* MSG_INTL(MSG_A1_STRING) */
ELFEDIT_I18NHDL(MSG_A1_STRING),
ELFEDIT_CMDOA_F_OPT },
{ NULL }
};
/* str:set */
static const char *name_set[] = {
MSG_ORIG(MSG_CMD_SET), NULL };
static elfedit_cmd_optarg_t opt_set[] = {
{ MSG_ORIG(MSG_STR_MINUS_ANY),
/* MSG_INTL(MSG_OPTDESC_ANY) */
ELFEDIT_I18NHDL(MSG_OPTDESC_ANY), 0,
STR_OPT_F_ANY, 0 },
{ ELFEDIT_STDOA_OPT_O, NULL,
ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
{ MSG_ORIG(MSG_STR_MINUS_NOTERM),
/* MSG_INTL(MSG_OPTDESC_NOTERM) */
ELFEDIT_I18NHDL(MSG_OPTDESC_NOTERM), 0,
STR_OPT_F_NOTERM, 0 },
{ MSG_ORIG(MSG_STR_MINUS_SHNAM),
/* MSG_INTL(MSG_OPTDESC_SHNAM) */
ELFEDIT_I18NHDL(MSG_OPTDESC_SHNAM), ELFEDIT_CMDOA_F_VALUE,
STR_OPT_F_SHNAME, STR_OPT_F_SHNDX | STR_OPT_F_SHTYP },
{ MSG_ORIG(MSG_STR_NAME), NULL, 0 },
{ MSG_ORIG(MSG_STR_MINUS_SHNDX),
/* MSG_INTL(MSG_OPTDESC_SHNDX) */
ELFEDIT_I18NHDL(MSG_OPTDESC_SHNDX), ELFEDIT_CMDOA_F_VALUE,
STR_OPT_F_SHNDX, STR_OPT_F_SHNAME | STR_OPT_F_SHTYP },
{ MSG_ORIG(MSG_STR_INDEX), NULL, 0 },
{ MSG_ORIG(MSG_STR_MINUS_SHTYP),
/* MSG_INTL(MSG_OPTDESC_SHTYP) */
ELFEDIT_I18NHDL(MSG_OPTDESC_SHTYP), ELFEDIT_CMDOA_F_VALUE,
STR_OPT_F_SHTYP, STR_OPT_F_SHNAME | STR_OPT_F_SHNDX },
{ MSG_ORIG(MSG_STR_TYPE), NULL, 0 },
{ MSG_ORIG(MSG_STR_MINUS_STRNDX),
/* MSG_INTL(MSG_OPTDESC_STRNDX) */
ELFEDIT_I18NHDL(MSG_OPTDESC_STRNDX), 0,
STR_OPT_F_STRNDX, 0 },
{ NULL }
};
static elfedit_cmd_optarg_t arg_set[] = {
{ MSG_ORIG(MSG_STR_STRING),
/* MSG_INTL(MSG_A1_STRING) */
ELFEDIT_I18NHDL(MSG_A1_STRING),
0 },
{ MSG_ORIG(MSG_STR_NEWSTRING),
/* MSG_INTL(MSG_A2_NEWSTRING) */
ELFEDIT_I18NHDL(MSG_A2_NEWSTRING),
ELFEDIT_CMDOA_F_OPT },
{ NULL }
};
/* str:add */
static const char *name_add[] = {
MSG_ORIG(MSG_CMD_ADD), NULL };
static elfedit_cmd_optarg_t opt_add[] = {
{ ELFEDIT_STDOA_OPT_O, NULL,
ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
{ MSG_ORIG(MSG_STR_MINUS_SHNAM),
/* MSG_INTL(MSG_OPTDESC_SHNAM) */
ELFEDIT_I18NHDL(MSG_OPTDESC_SHNAM), ELFEDIT_CMDOA_F_VALUE,
STR_OPT_F_SHNAME, STR_OPT_F_SHNDX | STR_OPT_F_SHTYP },
{ MSG_ORIG(MSG_STR_NAME), NULL, 0 },
{ MSG_ORIG(MSG_STR_MINUS_SHNDX),
/* MSG_INTL(MSG_OPTDESC_SHNDX) */
ELFEDIT_I18NHDL(MSG_OPTDESC_SHNDX), ELFEDIT_CMDOA_F_VALUE,
STR_OPT_F_SHNDX, STR_OPT_F_SHNAME | STR_OPT_F_SHTYP },
{ MSG_ORIG(MSG_STR_INDEX), NULL, 0 },
{ MSG_ORIG(MSG_STR_MINUS_SHTYP),
/* MSG_INTL(MSG_OPTDESC_SHTYP) */
ELFEDIT_I18NHDL(MSG_OPTDESC_SHTYP), ELFEDIT_CMDOA_F_VALUE,
STR_OPT_F_SHTYP, STR_OPT_F_SHNAME | STR_OPT_F_SHNDX },
{ MSG_ORIG(MSG_STR_TYPE), NULL, 0 },
{ NULL }
};
static elfedit_cmd_optarg_t arg_add[] = {
{ MSG_ORIG(MSG_STR_NEWSTRING),
/* MSG_INTL(MSG_A1_NEWSTRING) */
ELFEDIT_I18NHDL(MSG_A1_NEWSTRING),
0 },
{ NULL }
};
/* str:zero */
static const char *name_zero[] = {
MSG_ORIG(MSG_CMD_ZERO), NULL };
static elfedit_cmd_optarg_t opt_zero[] = {
{ MSG_ORIG(MSG_STR_MINUS_ANY),
/* MSG_INTL(MSG_OPTDESC_ANY) */
ELFEDIT_I18NHDL(MSG_OPTDESC_ANY), 0,
STR_OPT_F_ANY, 0 },
{ ELFEDIT_STDOA_OPT_O, NULL,
ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
{ MSG_ORIG(MSG_STR_MINUS_SHNAM),
/* MSG_INTL(MSG_OPTDESC_SHNAM) */
ELFEDIT_I18NHDL(MSG_OPTDESC_SHNAM), ELFEDIT_CMDOA_F_VALUE,
STR_OPT_F_SHNAME, STR_OPT_F_SHNDX | STR_OPT_F_SHTYP },
{ MSG_ORIG(MSG_STR_NAME), NULL, 0 },
{ MSG_ORIG(MSG_STR_MINUS_SHNDX),
/* MSG_INTL(MSG_OPTDESC_SHNDX) */
ELFEDIT_I18NHDL(MSG_OPTDESC_SHNDX), ELFEDIT_CMDOA_F_VALUE,
STR_OPT_F_SHNDX, STR_OPT_F_SHNAME | STR_OPT_F_SHTYP },
{ MSG_ORIG(MSG_STR_INDEX), NULL, 0 },
{ MSG_ORIG(MSG_STR_MINUS_SHTYP),
/* MSG_INTL(MSG_OPTDESC_SHTYP) */
ELFEDIT_I18NHDL(MSG_OPTDESC_SHTYP), ELFEDIT_CMDOA_F_VALUE,
STR_OPT_F_SHTYP, STR_OPT_F_SHNAME | STR_OPT_F_SHNDX },
{ MSG_ORIG(MSG_STR_TYPE), NULL, 0 },
{ MSG_ORIG(MSG_STR_MINUS_STRNDX),
/* MSG_INTL(MSG_OPTDESC_STRNDX) */
ELFEDIT_I18NHDL(MSG_OPTDESC_STRNDX), 0,
STR_OPT_F_STRNDX, 0 },
{ MSG_ORIG(MSG_STR_MINUS_END),
/* MSG_INTL(MSG_OPTDESC_END) */
ELFEDIT_I18NHDL(MSG_OPTDESC_END), 0,
STR_OPT_F_END, 0 },
{ NULL }
};
static elfedit_cmd_optarg_t arg_zero[] = {
{ MSG_ORIG(MSG_STR_STRING),
/* MSG_INTL(MSG_A1_STRING) */
ELFEDIT_I18NHDL(MSG_A1_STRING),
0 },
{ MSG_ORIG(MSG_STR_COUNT),
/* MSG_INTL(MSG_A2_COUNT) */
ELFEDIT_I18NHDL(MSG_A2_COUNT),
ELFEDIT_CMDOA_F_OPT },
{ NULL }
};
static elfedit_cmd_t cmds[] = {
/* str:dump */
{ cmd_dump, cpl_sec_str, name_dump,
/* MSG_INTL(MSG_DESC_DUMP) */
ELFEDIT_I18NHDL(MSG_DESC_DUMP),
/* MSG_INTL(MSG_HELP_DUMP) */
ELFEDIT_I18NHDL(MSG_HELP_DUMP),
opt_dump, arg_dump },
/* str:set */
{ cmd_set, cpl_sec_str, name_set,
/* MSG_INTL(MSG_DESC_SET) */
ELFEDIT_I18NHDL(MSG_DESC_SET),
/* MSG_INTL(MSG_HELP_SET) */
ELFEDIT_I18NHDL(MSG_HELP_SET),
opt_set, arg_set },
/* str:add */
{ cmd_add, cpl_sh_opt, name_add,
/* MSG_INTL(MSG_DESC_ADD) */
ELFEDIT_I18NHDL(MSG_DESC_ADD),
/* MSG_INTL(MSG_HELP_ADD) */
ELFEDIT_I18NHDL(MSG_HELP_ADD),
opt_add, arg_add },
/* str:zero */
{ cmd_zero, cpl_sec_str, name_zero,
/* MSG_INTL(MSG_DESC_ZERO) */
ELFEDIT_I18NHDL(MSG_DESC_ZERO),
/* MSG_INTL(MSG_HELP_ZERO) */
ELFEDIT_I18NHDL(MSG_HELP_ZERO),
opt_zero, arg_zero },
{ NULL }
};
static elfedit_module_t module = {
ELFEDIT_VER_CURRENT, MSG_ORIG(MSG_MOD_NAME),
/* MSG_INTL(MSG_MOD_DESC) */
ELFEDIT_I18NHDL(MSG_MOD_DESC),
cmds, mod_i18nhdl_to_str };
return (&module);
}