util_machelf.c revision cce0e03bb2d07f0fe27cabb93acae9c23655859f
/*
* 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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <libintl.h>
#include <machdep.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_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_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 char buf1[64], buf2[64];
static char *buf;
if ((obj_state->os_ehdr->e_machine == EM_AMD64) &&
(shndx == SHN_AMD64_LCOMMON))
return (MSG_ORIG(MSG_SHN_AMD64_LCOMMON));
switch (shndx) {
case SHN_UNDEF:
return (MSG_ORIG(MSG_SHN_UNDEF));
case SHN_SUNW_IGNORE:
return (MSG_ORIG(MSG_SHN_SUNW_IGNORE));
case SHN_BEFORE:
return (MSG_ORIG(MSG_SHN_BEFORE));
case SHN_AFTER:
return (MSG_ORIG(MSG_SHN_AFTER));
case SHN_AMD64_LCOMMON:
if (obj_state->os_ehdr->e_machine == EM_AMD64)
return (MSG_ORIG(MSG_SHN_AMD64_LCOMMON));
break;
case SHN_ABS:
return (MSG_ORIG(MSG_SHN_ABS));
case SHN_COMMON:
return (MSG_ORIG(MSG_SHN_COMMON));
case SHN_XINDEX:
return (MSG_ORIG(MSG_SHN_XINDEX));
}
/*
* 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)))
return (obj_state->os_secarr[shndx].sec_name);
/* Switch buffers */
buf = (buf == buf1) ? buf2 : buf1;
/*
* If we haven't identified it by now, format the
* number in a static buffer and return that.
*/
(void) snprintf(buf, sizeof (buf1),
MSG_ORIG(MSG_FMT_WORDVAL), shndx);
return (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);
}
/*
* 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;
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:
* 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_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:
const_type = ELFEDIT_CONST_SHT_LDYNSYM;
break;
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(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(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(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.
*
* exit:
* Returns section descriptor on success. On failure, does not return.
*/
elfedit_section_t *
elfedit_sec_getstr(elfedit_obj_state_t *obj_state, Word shndx)
{
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_ERR, MSG_INTL(MSG_ERR_NOTSTRSH),
EC_WORD(shndx), strsec->sec_name);
elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_FNDSTRTAB),
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:
* 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_section_t *dynsec, elfedit_dyn_elt_t *dyn_strpad)
{
Dyn *dyn = (Dyn *) dynsec->sec_data->d_buf;
Word numdyn = dynsec->sec_shdr->sh_size / dynsec->sec_shdr->sh_entsize;
Word i;
/* 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(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(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);
}