/* * 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 #include #include #include #include <_machelf.h> #include #include #include #include #include "msg.h" #include #include #include #include /* * 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); }