root/usr/src/cmd/sgs/elfedit/common/util_machelf.c
/*
 * 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        <stdlib.h>
#include        <stdio.h>
#include        <unistd.h>
#include        <libintl.h>
#include        <_machelf.h>
#include        <libelf.h>
#include        <link.h>
#include        <strings.h>
#include        <ctype.h>
#include        "msg.h"
#include        <elfedit.h>
#include        <conv.h>
#include        <sys/elf_SPARC.h>
#include        <sys/elf_amd64.h>



/*
 * ELFCLASS specific code that would otherwise be found in util.c
 */




/*
 * When you modify ELF constructs, you need to tell libelf that you've
 * done so. Otherwise, the changes may not be flushed back to the
 * output file.
 *
 * The elfedit_modified_*() functions exist to simplify the calls to
 * the underlying elf_flag*() functions.
 */
void
elfedit_modified_ehdr(elfedit_obj_state_t *obj_state)
{
        (void) elf_flagehdr(obj_state->os_elf, ELF_C_SET, ELF_F_DIRTY);
}

void
elfedit_modified_phdr(elfedit_obj_state_t *obj_state)
{
        (void) elf_flagphdr(obj_state->os_elf, ELF_C_SET, ELF_F_DIRTY);
}

void
elfedit_modified_shdr(elfedit_section_t *s)
{
        (void) elf_flagshdr(s->sec_scn, ELF_C_SET, ELF_F_DIRTY);
}

void
elfedit_modified_data(elfedit_section_t *s)
{
        (void) elf_flagdata(s->sec_data, ELF_C_SET, ELF_F_DIRTY);
}



/*
 * Prepare an elfedit_dyn_elt_t structure for use.
 */
void
elfedit_dyn_elt_init(elfedit_dyn_elt_t *elt)
{
        elt->dn_seen = 0;
}

/*
 * Given a dynamic section item, save it in the given elfedit_dyn_elt_t
 * structure and mark that structure to show that it is present.
 */
void
elfedit_dyn_elt_save(elfedit_dyn_elt_t *elt, Word ndx, Dyn *dyn)
{
        elt->dn_seen = 1;
        elt->dn_ndx = ndx;
        elt->dn_dyn = *dyn;
}


/*
 * Return the index of the first section that has the given name.
 *
 * entry:
 *      obj_state - Object state.
 *      shnam - Name of desired section
 *
 * exit:
 *      On success, returns the section index. On failure, an error
 *      is issued, and this routine does not return to the caller.
 */
Word
elfedit_name_to_shndx(elfedit_obj_state_t *obj_state, const char *shnam)
{
        elfedit_section_t *sec = obj_state->os_secarr;
        Word    ndx;
        Word    shnum = obj_state->os_shnum;

        for (ndx = 0; ndx < shnum; ndx++, sec++) {
                if (strcmp(shnam, sec->sec_name) == 0) {
                        elfedit_msg(ELFEDIT_MSG_DEBUG,
                            MSG_INTL(MSG_DEBUG_SHNAM2NDX),
                            EC_WORD(sec->sec_shndx), sec->sec_name, shnam);
                        return (ndx);
                }
        }

        /* If didn't return in loop above, the name doesn't match */
        elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOSECNAM), shnam);
        /*NOTREACHED*/
        return (SHN_UNDEF);
}



/*
 * Return the index of the first section that has the given type.
 *
 * entry:
 *      obj_state - Object state.
 *      shtype - Type of desired section
 *
 * exit:
 *      On success, returns the section index. On failure, an error
 *      is issued, and this routine does not return to the caller.
 */
Word
elfedit_type_to_shndx(elfedit_obj_state_t *obj_state, Word shtype)
{
        Conv_inv_buf_t inv_buf;
        elfedit_section_t *sec = obj_state->os_secarr;
        Word    ndx;
        Word    shnum = obj_state->os_shnum;

        for (ndx = 0; ndx < shnum; ndx++, sec++) {
                if (shtype == sec->sec_shdr->sh_type) {
                        elfedit_msg(ELFEDIT_MSG_DEBUG,
                            MSG_INTL(MSG_DEBUG_SHNAM2NDX),
                            EC_WORD(sec->sec_shndx), sec->sec_name,
                            conv_sec_type(
                            obj_state->os_ehdr->e_ident[EI_OSABI],
                            obj_state->os_ehdr->e_machine,
                            shtype, 0, &inv_buf));
                        return (ndx);
                }
        }

        /* If didn't return in loop above, the name doesn't match */
        elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOSECTYP),
            conv_sec_type(obj_state->os_ehdr->e_ident[EI_OSABI],
            obj_state->os_ehdr->e_machine, shtype, 0, &inv_buf));
        /*NOTREACHED*/
        return (SHN_UNDEF);
}



/*
 * Locate the index of the first symbol that has the given name
 *
 * entry:
 *      obj_state - Object state.
 *      symsec - Symbol section
 *      strsec = String section
 *      name - String giving name of symbol to lookup
 *      msg_type - ELFEDIT_MSG_ type code to use with message
 *              issued if name does not exist in symbol table.
 *      ret_symndx - Address of variable to receive index.
 *
 * exit:
 *      On success, issues debug message, sets *ret_symndx, and returns
 *      True (1).
 *
 *      On failure, issues a message using msg_type to determine
 *      the type of message sent. If the message does not take control away
 *      from the caller, False (0) is returned.
 *
 * note:
 *      Although the string table is referenced by the sh_link field of
 *      the symbol table, we require the user to supply it rather than
 *      look it up. The reason for this is that the caller will usually
 *      have looked it up, and we wish to avoid multiple debug messages
 *      from being issued to that effect.
 */
int
elfedit_name_to_symndx(elfedit_section_t *symsec, elfedit_section_t *strsec,
    const char *name, elfedit_msg_t msg_type, Word *ret_symndx)

{
        Sym     *sym = (Sym *) symsec->sec_data->d_buf;
        Word    cnt = symsec->sec_shdr->sh_size / symsec->sec_shdr->sh_entsize;
        Word    ndx, offset;
        const char      *curname;

        for (ndx = 0; ndx < cnt; ndx++) {
                offset = sym[ndx].st_name;

                curname = elfedit_offset_to_str(strsec, offset,
                    ELFEDIT_MSG_ERR, 0);
                if (strcmp(curname, name) == 0) {
                        elfedit_msg(ELFEDIT_MSG_DEBUG,
                            MSG_INTL(MSG_DEBUG_SYMNAM2NDX),
                            EC_WORD(symsec->sec_shndx),
                            symsec->sec_name, EC_WORD(ndx), name);
                        *ret_symndx = ndx;
                        return (1);
                }
        }

        /* If didn't return in loop above, the name doesn't match */
        elfedit_msg(msg_type, MSG_INTL(MSG_ERR_NOSYM),
            EC_WORD(symsec->sec_shndx), symsec->sec_name, name);
        /*NOTREACHED*/
        return (0);             /* lint */
}


/*
 * Given a section index, turn it into a descriptive string.
 *      - If it is one of the special reserved indexes, the
 *              symbolic name is returned.
 *      - If it is a regular section, in range for the file,
 *              the name associated with the section is returned.
 *      - Otherwise, the number is formatted as numeric ASCII.
 *
 * exit:
 *      A pointer to the static buffer containing the name is
 *      returned. This pointer is valid until the next call
 *      to elfedit_shndx_to_name(), and which point it may
 *      be overwritten.
 */
const char *
elfedit_shndx_to_name(elfedit_obj_state_t *obj_state, Word shndx)
{
        /*
         * This routine can be called twice within a single C statement,
         * so we use alternating buffers on each call to allow this
         * without requiring the caller to supply a buffer (the size of
         * which they don't know).
         */
        static Conv_inv_buf_t   buf1, buf2;
        static Conv_inv_buf_t   *buf;

        /*
         * If it is outside of the reserved area, and inside the
         * range of section indexes in the ELF file, then show
         * the section name.
         */
        if ((shndx < obj_state->os_shnum) &&
            ((shndx < SHN_LORESERVE) || (shndx > SHN_HIRESERVE)) &&
            (shndx != SHN_UNDEF))
                return (obj_state->os_secarr[shndx].sec_name);

        /*
         * Anything else is handled by libconv. It will return standard
         * names for known items, or format as a number otherwise.
         */
        buf = (buf == &buf1) ? &buf2 : &buf1;   /* Switch buffers */
        return (conv_sym_shndx(obj_state->os_ehdr->e_ident[EI_OSABI],
            obj_state->os_ehdr->e_machine, shndx,
            CONV_FMT_ALT_CF | CONV_FMT_DECIMAL, buf));
}


/*
 * Locate the arbitrary section specified by shndx for this object.
 *
 * exit:
 *      Returns section descriptor on success. On failure, does not return.
 */
elfedit_section_t *
elfedit_sec_get(elfedit_obj_state_t *obj_state, Word shndx)
{
        elfedit_section_t *sec;

        if ((shndx == 0) || (shndx >= obj_state->os_shnum))
                elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_BADSECNDX),
                    EC_WORD(shndx), EC_WORD(obj_state->os_shnum - 1));

        sec = &obj_state->os_secarr[shndx];

        elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_FNDSEC),
            EC_WORD(shndx), sec->sec_name);
        return (sec);
}



/*
 * Compare the a specified osabi with that of the current object.
 *
 * entry:
 *      obj_state - Object state for open object to query.
 *      issue_err - True if this routine should issue an error and
 *              not return to the caller if osabi is not native.
 *
 * exit:
 *      If current osabi is the one specified, True (1) is returned.
 *
 *      Otherwise, if issue_err is True, an error is issued and this
 *      routine does not return to the caller. If issue_err is False,
 *      False (0) is returned.
 *
 * note:
 *      ELFOSABI_NONE is considered to be equivalent to ELFOSABI_SOLARIS.
 */
int
elfedit_test_osabi(elfedit_obj_state_t *obj_state, uchar_t osabi,
    int issue_err)
{
        uchar_t         obj_osabi = obj_state->os_ehdr->e_ident[EI_OSABI];
        Conv_inv_buf_t  inv_buf;

        if (obj_osabi == ELFOSABI_NONE)
                obj_osabi = ELFOSABI_SOLARIS;

        if (osabi == obj_osabi)
                return (1);

        if (issue_err)
                elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_BADOSABI),
                    conv_ehdr_osabi(osabi, 0, &inv_buf));
        return (0);
}

/*
 * Locate the capabilities section for this object
 *
 * entry:
 *      obj_state - Object state for open object to query.
 *      cap - Address of variable to recieve pointer to capabilities
 *              section data buffer.
 *      num - Address of variable to receive number of items
 *              referenced by cap.
 *
 * exit:
 *      On success, returns section descriptor, and sets the
 *      variables referenced by cap and num.  On failure,
 *      does not return.
 */
elfedit_section_t *
elfedit_sec_getcap(elfedit_obj_state_t *obj_state, Cap **cap, Word *num)
{
        Word cnt;
        elfedit_section_t *cache;

        (void) elfedit_test_osabi(obj_state, ELFOSABI_SOLARIS, 1);

        for (cnt = 1; cnt < obj_state->os_shnum; cnt++) {
                cache = &obj_state->os_secarr[cnt];
                if (cache->sec_shdr->sh_type == SHT_SUNW_cap) {
                        elfedit_msg(ELFEDIT_MSG_DEBUG,
                            MSG_INTL(MSG_DEBUG_FNDCAP),
                            EC_WORD(cnt), cache->sec_name);
                        *cap = (Cap *) cache->sec_data->d_buf;
                        *num = cache->sec_shdr->sh_size /
                            cache->sec_shdr->sh_entsize;
                        return (cache);
                }
        }

        /* If here, this object has no capabilities section */
        elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOCAP));

        /*NOTREACHED*/
        return (NULL);
}


/*
 * Locate the dynamic section for this object
 *
 * entry:
 *      obj_state - Object state for open object to query.
 *      dyn - Address of variable to recieve pointer to dynamic
 *              section data buffer.
 *      numdyn - Address of variable to receive number of items
 *              referenced by dyn.
 *
 * exit:
 *      On success, returns section descriptor, and sets the
 *      variables referenced by dyn and numdyn.  On failure,
 *      does not return.
 */
elfedit_section_t *
elfedit_sec_getdyn(elfedit_obj_state_t *obj_state, Dyn **dyn, Word *num)
{
        elfedit_section_t *cache;

        if (obj_state->os_dynndx != SHN_UNDEF) {
                cache = &obj_state->os_secarr[obj_state->os_dynndx];
                elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_FNDDYN),
                    EC_WORD(cache->sec_shndx), cache->sec_name);
                *dyn = (Dyn *) cache->sec_data->d_buf;
                *num = cache->sec_shdr->sh_size / cache->sec_shdr->sh_entsize;
                return (cache);
        }

        /* If here, this object has no dynamic section */
        elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NODYN));

        /*NOTREACHED*/
        return (NULL);
}


/*
 * Locate the syminfo section for this object
 *
 * entry:
 *      obj_state - Object state for open object to query.
 *      syminfo - Address of variable to recieve pointer to syminfo
 *              section data buffer.
 *      num - Address of variable to receive number of items
 *              referenced by syminfo.
 *
 * exit:
 *      On success, returns section descriptor, and sets the
 *      variables referenced by syminfo and num.  On failure,
 *      does not return.
 */
elfedit_section_t *
elfedit_sec_getsyminfo(elfedit_obj_state_t *obj_state, Syminfo **syminfo,
    Word *num)
{
        Word cnt;
        elfedit_section_t *cache;

        for (cnt = 1; cnt < obj_state->os_shnum; cnt++) {
                cache = &obj_state->os_secarr[cnt];
                if (cache->sec_shdr->sh_type == SHT_SUNW_syminfo) {
                        elfedit_msg(ELFEDIT_MSG_DEBUG,
                            MSG_INTL(MSG_DEBUG_FNDSYMINFO),
                            EC_WORD(cnt), cache->sec_name);
                        *syminfo = (Syminfo *) cache->sec_data->d_buf;
                        *num = cache->sec_shdr->sh_size /
                            cache->sec_shdr->sh_entsize;
                        return (cache);
                }
        }

        /* If here, this object has no syminfo section */
        elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOSYMINFO));

        /*NOTREACHED*/
        return (NULL);
}


/*
 * Check the given section to see if it is a known symbol table type.
 *
 * entry:
 *      obj_state - Object state for open object to query.
 *      sec - Section to check
 *      issue_err - True if this routine should issue an error and
 *              not return to the caller if sec is not a symbol table.
 *      atoui_list - NULL, or address of variable to receive a pointer to
 *              an array of elfedit_atoui_sym_t items describing the
 *              type of symbol table found. This array is useful for
 *              doing command completion.
 *
 * exit:
 *      If sec is a symbol table:
 *              - If atoui_list is non-NULL, *atoui_list is set to the
 *                appropriate ELFEDIT_CONST_xx list of items.
 *              - True (1) is returned
 *      If sec is not a symbol table and issue_err is True:
 *              - An error is issued, and this routine does not
 *                      return to the caller.
 *      Otherwise:
 *              - If atoui_list is non-NULL, *atoui_list is set to NULL.
 *              - False (0) is returned
 */
int
elfedit_sec_issymtab(elfedit_obj_state_t *obj_state, elfedit_section_t *sec,
    int issue_err, elfedit_atoui_sym_t **atoui_list)
{
        elfedit_const_t         const_type;
        int                     ret = 1;

        /* Is the section a symbol table? */
        switch (sec->sec_shdr->sh_type) {
        case SHT_SYMTAB:
                const_type = ELFEDIT_CONST_SHT_SYMTAB;
                break;
        case SHT_DYNSYM:
                const_type = ELFEDIT_CONST_SHT_DYNSYM;
                break;
        case SHT_SUNW_LDYNSYM:
                /*
                 * These sections are only known to be symbol tables
                 * if the osabi is Solaris.
                 */
                if (elfedit_test_osabi(obj_state, ELFOSABI_SOLARIS, 0)) {
                        const_type = ELFEDIT_CONST_SHT_LDYNSYM;
                        break;
                }
                /*FALLTHROUGH*/
        default:
                if (issue_err)
                        elfedit_msg(ELFEDIT_MSG_ERR,
                            MSG_INTL(MSG_ERR_NOTSYMTAB),
                            EC_WORD(sec->sec_shndx), sec->sec_name);
                ret = 0;
                break;
        }

        if (atoui_list != NULL)
                *atoui_list = (ret == 0) ? NULL :
                    elfedit_const_to_atoui(const_type);

        return (ret);
}



/*
 * Locate a symbol table section for this object
 *
 * entry:
 *      obj_state - Object state for open object to query.
 *      by_index - If True, we want to locate the section with the
 *              section index given by index. If False, we return
 *              the section with the name given by name.
 *      index, name - Key to search for. See by_index.
 *      sym - Address of variable to recieve pointer to symbol
 *              section data buffer.
 *      numsym - Address of variable to receive number of symbols
 *              referenced by sym.
 *      aux_info - Address of variable to receive pointer to the
 *              elfedit_symtab_t struct that ties the symbol table and
 *              its related auxiliary sections together. NULL if this
 *              information is not required.
 *
 * exit:
 *      On success, returns section descriptor, and sets the
 *      variables referenced by sym, and numsym. On failure,
 *      does not return.
 */
elfedit_section_t *
elfedit_sec_getsymtab(elfedit_obj_state_t *obj_state, int by_index,
    Word index, const char *name, Sym **sym, Word *num,
    elfedit_symtab_t **aux_info)
{
        Word                    ndx;
        elfedit_section_t       *symsec = NULL;
        elfedit_symtab_t        *symtab;
        const char              *type_name;

        /* If looking it up by index, make sure the index is in range */
        if (by_index && (index >= obj_state->os_shnum))
                elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_BADSECNDX),
                    EC_WORD(index), EC_WORD(obj_state->os_shnum - 1));

        /*
         * Look at each known symbol table in turn until the desired
         * one is hit, or there are no more.
         */
        symtab = obj_state->os_symtab;
        for (ndx = 0; ndx < obj_state->os_symtabnum; ndx++, symtab++) {
                elfedit_section_t *s =
                    &obj_state->os_secarr[symtab->symt_shndx];

                if ((by_index && (symtab->symt_shndx == index)) ||
                    (!by_index && (strcmp(s->sec_name, name) == 0))) {
                                symsec = s;
                                break;
                }
        }

        /* Did we get a section? */
        if (symsec == NULL)
                elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOSYMTAB));

        /* Got it. Report to the user and return the necessary data */
        (void) elfedit_sec_issymtab(obj_state, symsec, 1, NULL);
        type_name = elfedit_atoconst_value_to_str(ELFEDIT_CONST_SHT_ALLSYMTAB,
            symsec->sec_shdr->sh_type, 1);
        elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_FNDSYMTAB),
            EC_WORD(symsec->sec_shndx), symsec->sec_name, type_name);
        *sym = (Sym *) symsec->sec_data->d_buf;
        *num = symsec->sec_shdr->sh_size / symsec->sec_shdr->sh_entsize;
        if (aux_info != NULL)
                *aux_info = symtab;
        return (symsec);
}



/*
 * Locate the extended symbol index section associated with a symbol
 * table section.
 *
 * entry:
 *      obj_state - Object state for open object to query.
 *      symsec - Symbol table section for which extended index
 *              index section is required.
 *      xshndx - Address of variable to recieve pointer to section index
 *              array data buffer.
 *      numxshndx - Address of variable to receive number of indices
 *              referenced by ndx.
 *
 * exit:
 *      On success, returns extended index section descriptor, and sets the
 *      variables referenced by xshndx, and numxshndx. On failure,
 *      does not return.
 *
 * note:
 *      Since the extended section index is found in the sec_xshndx field
 *      of the elfedit_section_t, the caller may be tempted to bypass this
 *      routine and access it directly. That temptation should be resisted,
 *      as this routine performs useful error checking, and also handles
 *      the issuing of the standard MSG_DEBUG messages.
 */
elfedit_section_t *
elfedit_sec_getxshndx(elfedit_obj_state_t *obj_state,
    elfedit_section_t *symsec, Word **xshndx, Word *num)
{
        elfedit_section_t       *xshndxsec;
        elfedit_symtab_t        *symtab;
        Word                    ndx;

        /* Sanity check: symsec must be a symbol table */
        (void) elfedit_sec_issymtab(obj_state, symsec, 1, NULL);

        symtab = obj_state->os_symtab;
        for (ndx = 0; ndx < obj_state->os_symtabnum; ndx++, symtab++)
                if (symsec->sec_shndx == symtab->symt_shndx)
                        break;

        /*
         * Issue error if the symbol table lacks an extended index section.
         * The caller won't ask unless they encounter an SHN_XINDEX value,
         * in which case the lack of the index section denotes a corrupt
         * ELF file.
         */
        if ((ndx == obj_state->os_symtabnum) ||
            (symtab->symt_xshndx == SHN_UNDEF))
                elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOXSHSEC),
                    EC_WORD(symsec->sec_shndx), symsec->sec_name);

        /* Got it. Report to the user and return the necessary data */
        xshndxsec = &obj_state->os_secarr[symtab->symt_xshndx];
        elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_FNDXSHNDX),
            EC_WORD(symsec->sec_shndx), symsec->sec_name,
            EC_WORD(xshndxsec->sec_shndx), xshndxsec->sec_name);
        *xshndx = (Word *) xshndxsec->sec_data->d_buf;
        *num = xshndxsec->sec_shdr->sh_size / xshndxsec->sec_shdr->sh_entsize;
        return (xshndxsec);
}



/*
 * Locate the versym section associated with a symbol table section.
 *
 * entry:
 *      obj_state - Object state for open object to query.
 *      symsec - Symbol table section for which extended index
 *              index section is required.
 *      versym - Address of variable to recieve pointer to section index
 *              array data buffer.
 *      numversym - Address of variable to receive number of indices
 *              referenced by ndx.
 *
 * exit:
 *      On success, returns versym section descriptor, and sets the
 *      variables referenced by versym, and numversym. On failure,
 *      does not return.
 *
 * note:
 *      Since the versym section index is found in the sec_versym field
 *      of the elfedit_section_t, the caller may be tempted to bypass this
 *      routine and access it directly. That temptation should be resisted,
 *      as this routine performs useful error checking, and also handles
 *      the issuing of the standard MSG_DEBUG messages.
 */
elfedit_section_t *
elfedit_sec_getversym(elfedit_obj_state_t *obj_state,
    elfedit_section_t *symsec, Versym **versym, Word *num)
{
        elfedit_section_t       *versymsec;
        elfedit_symtab_t        *symtab;
        Word                    ndx;

        /* Sanity check: symsec must be a symbol table */
        (void) elfedit_sec_issymtab(obj_state, symsec, 1, NULL);

        symtab = obj_state->os_symtab;
        for (ndx = 0; ndx < obj_state->os_symtabnum; ndx++, symtab++)
                if (symsec->sec_shndx == symtab->symt_shndx)
                        break;
        /*
         * Issue error if the symbol table lacks a versym section.
         * The caller won't ask unless they see a non-null
         * aux.symtab.sec_versym, so this should not be a problem.
         */
        if ((ndx == obj_state->os_symtabnum) ||
            (symtab->symt_versym == SHN_UNDEF))
                elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOVERSYMSEC),
                    EC_WORD(symsec->sec_shndx), symsec->sec_name);

        /* Got it. Report to the user and return the necessary data */
        versymsec = &obj_state->os_secarr[symtab->symt_versym];
        elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_FNDVERSYM),
            EC_WORD(symsec->sec_shndx), symsec->sec_name,
            EC_WORD(versymsec->sec_shndx), versymsec->sec_name);
        *versym = (Versym *) versymsec->sec_data->d_buf;
        *num = versymsec->sec_shdr->sh_size / versymsec->sec_shdr->sh_entsize;
        return (versymsec);
}



/*
 * Locate the string table specified by shndx for this object.
 *
 * entry:
 *      obj_state - Object state.
 *      shndx - Section index for string table section
 *      allow_shflags - If False (0), only sections of type SHT_STRTAB
 *              are accepted as being string tables, and any other type
 *              will fail. If True (1), non-stringtable sections with
 *              their SHF_STRINGS flag set are also accepted.
 *
 * exit:
 *      Returns section descriptor on success. On failure, does not return.
 *
 * note:
 *      At this time, we can only support SHF_STRINGS sections that
 *      use single byte characters and which do not require alignment >1.
 *      SHF_STRINGS sections that have multi-byte characters or alignment
 *      are not currently supported and will draw an error even if
 *      allow_shflags is True.
 */
elfedit_section_t *
elfedit_sec_getstr(elfedit_obj_state_t *obj_state, Word shndx,
    int allow_shflags)
{
        elfedit_section_t *strsec;

        if ((shndx == 0) || (shndx >= obj_state->os_shnum))
                elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_STRSHNDX),
                    EC_WORD(shndx), EC_WORD(obj_state->os_shnum - 1));

        strsec = &obj_state->os_secarr[shndx];
        if (strsec->sec_shdr->sh_type == SHT_STRTAB) {
                elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_FNDSTRTAB),
                    EC_WORD(shndx), strsec->sec_name);
        } else if (allow_shflags &&
            ((strsec->sec_shdr->sh_flags & SHF_STRINGS) != 0) &&
            (strsec->sec_shdr->sh_entsize <= 1) &&
            (strsec->sec_shdr->sh_addralign <= 1)) {
                elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_FNDSTRTABFL),
                    EC_WORD(shndx), strsec->sec_name);
        } else {
                elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOTSTRSH),
                    EC_WORD(shndx), strsec->sec_name);
        }

        return (strsec);
}


/*
 * Returns the offset of the specified string from within
 * the given section.
 *
 * entry:
 *      sec - Descriptor for section
 *      tail_ign - If non-zero, the # of characters at the end of the
 *              section that should be ignored and not searched.
 *      str - String we are looking for.
 *      ret_offset - Address of variable to receive result
 *
 * exit:
 *      Returns 1 for success, and 0 for failure. If successful, *ret_offset
 *      is set to the offset of the found string within the section.
 */
int
elfedit_sec_findstr(elfedit_section_t *sec, Word tail_ign,
    const char *str, Word *ret_offset)
{
        int             str_fch = *str; /* First character in str */
        Word            len;            /* # characters in table */
        char            *s;             /* ptr to strings within table */
        const char      *tail;          /* 1 past final character of table */


        /* Size of the section, minus the reserved part (if any) at the end */
        len = sec->sec_shdr->sh_size - tail_ign;

        /*
         * Move through the section character by character looking for
         * a match. Moving character by character instead of skipping
         * from NULL terminated string to string allows us to use
         * the tails longer strings (i.e. we want "bar", and "foobar" exists).
         * We look at the first character manually before calling strcmp()
         * to lower the cost of this approach.
         */
        s = (char *)sec->sec_data->d_buf;
        tail = s + len;
        for (; s <= tail; s++) {
                if ((*s == str_fch) && (strcmp(s, str) == 0)) {
                        *ret_offset = s - (char *)sec->sec_data->d_buf;
                        elfedit_msg(ELFEDIT_MSG_DEBUG,
                            MSG_INTL(MSG_DEBUG_EXISTSTR),
                            EC_WORD(sec->sec_shndx), sec->sec_name,
                            EC_WORD(*ret_offset), s);
                        return (1);
                }
        }

        /* Didn't find it. Report failure */
        return (0);
}


/*
 * Locate the DT_SUNW_STRPAD element of the given dynamic section if
 * it exists.
 *
 * entry:
 *      obj_state - Object state for open object to query.
 *      dynsec - Dynamic section descriptor
 *      dyn_strpad - Address of variable to receive the results.
 *              The caller is responsible for calling elfedit_dyn_elt_init()
 *              on this variable beforehand.
 *
 * exit:
 *      The dynamic section is searched, and if a DT_SUNW_STRPAD element
 *      is found, dyn_strpad is updated via elfedit_dyn_elt_save() to
 *      reference it.
 *
 *      Returns the final value of dyn_strpad->dn_seen.
 */
int
elfedit_dynstr_getpad(elfedit_obj_state_t *obj_state, elfedit_section_t *dynsec,
    elfedit_dyn_elt_t *dyn_strpad)
{
        Word numdyn = dynsec->sec_shdr->sh_size / dynsec->sec_shdr->sh_entsize;
        Dyn     *dyn = (Dyn *) dynsec->sec_data->d_buf;
        Word    i;

        /*
         * DT_SUNW_STRPAD is specific to the Solaris OSABI.
         * If the object is tagged otherwise, don't even look.
         */
        if (!elfedit_test_osabi(obj_state, ELFOSABI_SOLARIS, 0))
                return (dyn_strpad->dn_seen);

        /* Go through dynamic section tags and find the STRPAD entry */
        for (i = 0; i < numdyn; i++) {
                if (dyn[i].d_tag == DT_SUNW_STRPAD) {
                        elfedit_dyn_elt_save(dyn_strpad, i, &dyn[i]);
                        break;
                }
        }

        return (dyn_strpad->dn_seen);
}



/*
 * Given references to the dynamic section, its string table,
 * and the DT_SUNW_STRPAD entry of the dynamic section, returns
 * the offset of the specified string from within the given string table,
 * adding it if possible.
 *
 * entry:
 *      dynsec - Dynamic section descriptor
 *      strsec - Descriptor for string table assocated with dynamic section
 *      dyn_strpad - DT_SUNW_STRPAD element from dynamic section
 *      str - String we are looking for.
 *
 * exit:
 *      On success, the offset of the given string within the string
 *      table is returned. If the string does not exist within the table,
 *      but there is a valid DT_SUNW_STRPAD reserved section, then we
 *      add the string, and update the dynamic section STRPAD element
 *      to reflect the space we use.
 *
 *      This routine does not return on failure.
 */
Word
elfedit_dynstr_insert(elfedit_section_t *dynsec, elfedit_section_t *strsec,
    elfedit_dyn_elt_t *dyn_strpad, const char *str)
{
        Word    ins_off;        /* Table offset to 1st reserved byte */
        char    *s;             /* ptr to strings within table */
        Word    len;            /* Length of str inc. NULL byte */
        Word    tail_ign;       /* # reserved bytes at end of strtab */


        tail_ign = dyn_strpad->dn_seen ? dyn_strpad->dn_dyn.d_un.d_val : 0;

        /* Does the string already existin the string table? */
        if (elfedit_sec_findstr(strsec, tail_ign, str, &len))
                return (len);

        /*
         * The desired string does not already exist. Do we have
         * room to add it?
         */
        len = strlen(str) + 1;
        if (!dyn_strpad->dn_seen || (len > dyn_strpad->dn_dyn.d_un.d_val))
                elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOSTRPAD),
                    EC_WORD(strsec->sec_shdr->sh_link),
                    strsec->sec_name);


        /*
         * We will add the string at the first byte of the reserved NULL
         * area at the end. The DT_SUNW_STRPAD dynamic element gives us
         * the size of that reserved space.
         */
        ins_off = strsec->sec_shdr->sh_size - tail_ign;
        s = ((char *)strsec->sec_data->d_buf) + ins_off;

        /* Announce the operation */
        elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_ADDSTR),
            EC_WORD(strsec->sec_shndx), strsec->sec_name,
            EC_WORD(ins_off), EC_WORD(len),
            EC_WORD(dyn_strpad->dn_dyn.d_un.d_val), str);

        /*
         * Copy the string into the pad area at the end, and
         * mark the data area as dirty so libelf will flush our
         * changes to the string data.
         */
        (void) strncpy(s, str, dyn_strpad->dn_dyn.d_un.d_val);
        elfedit_modified_data(strsec);

        /* Update the DT_STRPAD dynamic entry */
        dyn_strpad->dn_dyn.d_un.d_val -= len;
        ((Dyn *) dynsec->sec_data->d_buf)[dyn_strpad->dn_ndx] =
            dyn_strpad->dn_dyn;
        elfedit_modified_data(dynsec);

        return (ins_off);
}


/*
 * Test to see if a call to elfedit_strtab_insert() will succeed.
 *
 * entry:
 *      obj_state - Object state for open object to query.
 *      strsec - Descriptor for string table
 *      dynsec - NULL, or descriptor for dynamic section. Providing
 *              a non-NULL value here will prevent elfedit_strtab_insert()
 *              from looking it up, and the duplicate debug message that
 *              would result.
 *      str - String we are looking for.
 *
 * exit:
 *      If the string exists within the string table, or if an attempt
 *      to insert it will be successful, quietly return. Otherwise, throw
 *      the error elfedit_strtab_insert() would throw under the
 *      same circumstances.
 *
 */
void
elfedit_strtab_insert_test(elfedit_obj_state_t *obj_state,
    elfedit_section_t *strsec, elfedit_section_t *dynsec, const char *str)
{
        Word    len;            /* Length of str inc. NULL byte */
        int                     is_dynstr = 0;
        Word                    tail_ign = 0;


        /*
         * The dynstr is a special case, because we can add strings
         * to it under certain circumstances. So, we look for the
         * dynamic section, and if it exists, compare its sh_link to
         * the string section index. If they match, it is the dynstr,
         * and we use elfedit_dynstr_insert() to do the work.
         */
        if (dynsec == NULL) {
                if (obj_state->os_dynndx != SHN_UNDEF) {
                        dynsec = &obj_state->os_secarr[obj_state->os_dynndx];
                        if ((dynsec->sec_shdr->sh_type == SHT_DYNAMIC) &&
                            (strsec->sec_shndx == dynsec->sec_shdr->sh_link)) {
                                is_dynstr = 1;
                                elfedit_msg(ELFEDIT_MSG_DEBUG,
                                    MSG_INTL(MSG_DEBUG_FNDDYN),
                                    EC_WORD(dynsec->sec_shndx),
                                    dynsec->sec_name);
                        }
                }
        } else {
                if (strsec->sec_shndx == dynsec->sec_shdr->sh_link)
                        is_dynstr = 1;
        }


        if (is_dynstr) {
                elfedit_dyn_elt_t dyn_strpad;

                /* Determine the size of the STRPAD area, if any */
                elfedit_dyn_elt_init(&dyn_strpad);
                if (elfedit_dynstr_getpad(obj_state, dynsec, &dyn_strpad) != 0)
                        tail_ign = dyn_strpad.dn_dyn.d_un.d_val;
        }

        /*
         * If the string is already in the string table, we
         * can't fail.
         */
        if (elfedit_sec_findstr(strsec, tail_ign, str, &len) != 0)
                return;

        /*
         * It's not in the table, but if this is the dynstr, and
         * there is enough room, we will be able to add it.
         */
        if (is_dynstr && (tail_ign > strlen(str)))
                return;

        /* Can't do it. Issue error */
        elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOSTRPAD),
            EC_WORD(strsec->sec_shdr->sh_link), strsec->sec_name);
}


/*
 * Returns the offset of the specified string from within
 * the given string table, adding it if possible.
 *
 * entry:
 *      obj_state - Object state for open object to query.
 *      strsec - Descriptor for string table
 *      dynsec - NULL, or descriptor for dynamic section. Providing
 *              a non-NULL value here will prevent elfedit_strtab_insert()
 *              from looking it up, and the duplicate debug message that
 *              would result.
 *      str - String we are looking for.
 *
 * exit:
 *      On success, the offset of the given string within the string
 *      table is returned. If the string does not exist within the table,
 *      and it is possible to add it, elfedit_strtab_insert() will
 *      add the string, and then return the offset.
 *
 *      If the string does not exist in the string table, and cannot
 *      be added, this routine issues an error message and does not
 *      return to the caller.
 */
Word
elfedit_strtab_insert(elfedit_obj_state_t *obj_state, elfedit_section_t *strsec,
    elfedit_section_t *dynsec, const char *str)
{
        Word    len;            /* Length of str inc. NULL byte */
        int                     is_dynstr = 0;
        elfedit_dyn_elt_t       dyn_strpad;


        /*
         * The dynstr is a special case, because we can add strings
         * to it under certain circumstances. So, we look for the
         * dynamic section, and if it exists, compare its sh_link to
         * the string section index. If they match, it is the dynstr,
         * and we use elfedit_dynstr_insert() to do the work.
         */
        if (dynsec == NULL) {
                if (obj_state->os_dynndx != SHN_UNDEF) {
                        dynsec = &obj_state->os_secarr[obj_state->os_dynndx];
                        if ((dynsec->sec_shdr->sh_type == SHT_DYNAMIC) &&
                            (strsec->sec_shndx == dynsec->sec_shdr->sh_link)) {
                                is_dynstr = 1;
                                elfedit_msg(ELFEDIT_MSG_DEBUG,
                                    MSG_INTL(MSG_DEBUG_FNDDYN),
                                    EC_WORD(dynsec->sec_shndx),
                                    dynsec->sec_name);
                        }
                }
        } else {
                if (strsec->sec_shndx == dynsec->sec_shdr->sh_link)
                        is_dynstr = 1;
        }

        if (is_dynstr) {
                elfedit_dyn_elt_init(&dyn_strpad);
                (void) elfedit_dynstr_getpad(obj_state, dynsec, &dyn_strpad);
                return (elfedit_dynstr_insert(dynsec, strsec,
                    &dyn_strpad, str));
        }

        /*
         * This is not the dynstr, so we are limited to strings that
         * already exist within it. Try to find one.
         */
        if (elfedit_sec_findstr(strsec, 0, str, &len))
                return (len);

        /* Can't do it. Issue error */
        elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOSTRPAD),
            EC_WORD(strsec->sec_shdr->sh_link), strsec->sec_name);
        /*NOTREACHED*/

        return (0);
}


/*
 * Return the string found at the given offset within the specified
 * string table.
 *
 * entry:
 *      strsec - Section descriptor for string table section
 *      offset - Offset of desired string in string table
 *      msg_type - ELFEDIT_MSG_ type code to use with message
 *              issued if offset is out of range for the symbol table.
 *      debug_msg - True if should issue debug message for string found.
 *
 * exit:
 *      If the offset is within the section, the string pointer
 *      is returned. Otherwise an error is issued using msg_type
 *      to determine the type of message. If this routine retains
 *      control after the message is issued, a safe string is returned.
 */
const char *
elfedit_offset_to_str(elfedit_section_t *strsec, Word offset,
    elfedit_msg_t msg_type, int debug_msg)
{
        const char *str;

        /* Make sure it is a string table section */
        if (strsec->sec_shdr->sh_type != SHT_STRTAB)
                elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOTSTRSH),
                    EC_WORD(strsec->sec_shndx), strsec->sec_name);

        /* Ensure the offset is in range */
        if (offset >= strsec->sec_data->d_size) {
                elfedit_msg(msg_type, MSG_INTL(MSG_ERR_BADSTROFF),
                    EC_WORD(strsec->sec_shndx), strsec->sec_name,
                    EC_WORD(offset), EC_WORD(strsec->sec_data->d_size - 1));
                /*
                 * If the msg_type is a type that returns, give the
                 * user a safe string to use.
                 */
                str = MSG_INTL(MSG_BADSYMOFFSETNAM);
        } else {
                /* Return the string */
                str = ((const char *)strsec->sec_data->d_buf) + offset;
        }

        if (debug_msg)
                elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_FNDSTR),
                    EC_WORD(strsec->sec_shndx), strsec->sec_name,
                    EC_WORD(offset), str);
        return (str);
}


/*
 * Given a string table section, and a dynamic section entry
 * that supplies a string offset, return the string found at
 * the given offset. This routine is a convenience wrapper on
 * elfedit_offset_to_str().
 *
 * exit:
 *      As per elfedit_offset_to_str().
 */
const char *
elfedit_dyn_offset_to_str(elfedit_section_t *strsec, elfedit_dyn_elt_t *dynelt)
{
        return (elfedit_offset_to_str(strsec, dynelt->dn_dyn.d_un.d_val,
            ELFEDIT_MSG_ERR, 0));
}


/*
 * Given a section, fabricate a string for the form:
 *
 *      "[#: name]"
 *
 * as used at the beginning of debug messages. A pointer to static
 * memory is returned, and is good until the next such call.
 */
const char *
elfedit_sec_msgprefix(elfedit_section_t *sec)
{
        static char     *buf;
        static size_t   bufsize;

        size_t          need;

        need = 64 + strlen(sec->sec_name);
        if (need > bufsize) {
                buf = elfedit_realloc(MSG_INTL(MSG_ALLOC_SECMSGPRE), buf, need);
                bufsize = need;
        }

        (void) snprintf(buf, bufsize, MSG_ORIG(MSG_FMT_SECMSGPRE),
            EC_WORD(sec->sec_shndx), sec->sec_name);

        return (buf);
}