#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <_machelf.h>
#include <libelf.h>
#include <strings.h>
#include <sgs.h>
#include "msg.h"
#include "_elfedit.h"
static elfedit_symtab_t *
get_symtab(elfedit_obj_state_t *obj_state, elfedit_section_t *auxsec)
{
elfedit_symtab_t *symtab = obj_state->os_symtab;
Word sh_link = auxsec->sec_shdr->sh_link;
Word i;
for (i = 0; i < obj_state->os_symtabnum; i++, symtab++)
if (symtab->symt_shndx == sh_link)
return (symtab);
elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_AUX_LINK),
EC_WORD(auxsec->sec_shndx), auxsec->sec_name,
EC_WORD(sh_link));
return (NULL);
}
#ifdef _ELF64
void
elfedit64_init_obj_state(const char *file, int fd, Elf *elf)
#else
void
elfedit32_init_obj_state(const char *file, int fd, Elf *elf)
#endif
{
#define INITIAL_SYMTABNDX_ALLOC 5
#define LIBELF_FAIL(_name) { libelf_fail_name = _name; goto libelf_failure; }
#define LIBELF(_libelf_expr, _name) \
if ((_libelf_expr) == NULL) \
LIBELF_FAIL(_name)
const char *libelf_fail_name;
Elf_Scn *scn;
Elf_Data *data;
uint_t ndx;
size_t len, os_size, secarr_size;
char *names = 0;
size_t names_len;
elfedit_section_t *_cache;
elfedit_obj_state_t tstate;
elfedit_obj_state_t *obj_state = NULL;
Word *symtabndx = NULL;
Word symtabndx_size = 0;
elfedit_symtab_t *symtab;
tstate.os_file = file;
tstate.os_fd = fd;
tstate.os_elf = elf;
tstate.os_dynndx = SHN_UNDEF;
tstate.os_symtabnum = 0;
LIBELF(tstate.os_ehdr = elf_getehdr(tstate.os_elf),
MSG_ORIG(MSG_ELF_GETEHDR))
if (elf_getphdrnum(tstate.os_elf, &tstate.os_phnum) == -1)
LIBELF_FAIL(MSG_ORIG(MSG_ELF_GETPHDRNUM))
if (tstate.os_phnum > 0) {
LIBELF((tstate.os_phdr = elf_getphdr(tstate.os_elf)),
MSG_ORIG(MSG_ELF_GETPHDR))
} else {
tstate.os_phdr = NULL;
}
if (elf_getshdrnum(tstate.os_elf, &tstate.os_shnum) == -1)
LIBELF_FAIL(MSG_ORIG(MSG_ELF_GETSHDRNUM))
if (elf_getshdrstrndx(tstate.os_elf, &tstate.os_shstrndx) == -1)
LIBELF_FAIL(MSG_ORIG(MSG_ELF_GETSHDRSTRNDX))
LIBELF((scn = elf_getscn(tstate.os_elf, tstate.os_shstrndx)),
MSG_ORIG(MSG_ELF_GETSCN))
LIBELF((data = elf_getdata(scn, NULL)), MSG_ORIG(MSG_ELF_GETDATA))
names = data->d_buf;
names_len = (names == NULL) ? 0 : data->d_size;
for (ndx = 1, scn = NULL;
(scn = elf_nextscn(tstate.os_elf, scn)) != NULL; ndx++) {
Shdr *shdr;
LIBELF(shdr = elf_getshdr(scn), MSG_ORIG(MSG_ELF_GETSHDR));
switch (shdr->sh_type) {
case SHT_DYNAMIC:
tstate.os_dynndx = ndx;
break;
case SHT_SYMTAB:
case SHT_DYNSYM:
case SHT_SUNW_LDYNSYM:
if (symtabndx_size <= tstate.os_symtabnum) {
symtabndx_size = (symtabndx_size == 0) ?
INITIAL_SYMTABNDX_ALLOC :
(symtabndx_size * 2);
symtabndx = elfedit_realloc(
MSG_INTL(MSG_ALLOC_SYMTABOS), symtabndx,
symtabndx_size * sizeof (symtabndx[0]));
}
symtabndx[tstate.os_symtabnum++] = ndx;
break;
}
}
os_size = S_DROUND(sizeof (tstate));
secarr_size = (tstate.os_shnum * sizeof (elfedit_section_t));
secarr_size = S_DROUND(secarr_size);
len = strlen(tstate.os_file) + 1;
obj_state = elfedit_malloc(MSG_INTL(MSG_ALLOC_OBJSTATE),
os_size + secarr_size +
(tstate.os_symtabnum * sizeof (elfedit_symtab_t)) + len);
*obj_state = tstate;
obj_state->os_secarr = (elfedit_section_t *)
((char *)obj_state + os_size);
if (obj_state->os_symtabnum == 0) {
obj_state->os_symtab = NULL;
} else {
obj_state->os_symtab = (elfedit_symtab_t *)
((char *)obj_state->os_secarr + secarr_size);
obj_state->os_file =
(char *)(obj_state->os_symtab + tstate.os_symtabnum);
(void) strncpy((char *)obj_state->os_file, tstate.os_file, len);
}
bzero(obj_state->os_secarr, sizeof (obj_state->os_secarr[0]));
_cache = obj_state->os_secarr;
LIBELF(scn = elf_getscn(tstate.os_elf, 0),
MSG_ORIG(MSG_ELF_GETSCN));
_cache->sec_scn = scn;
LIBELF(_cache->sec_shdr = elf_getshdr(scn), MSG_ORIG(MSG_ELF_GETSHDR));
_cache->sec_name = (_cache->sec_shdr->sh_name < names_len) ?
(names + _cache->sec_shdr->sh_name) : MSG_INTL(MSG_UNKNOWNSECNAM);
_cache++;
if (obj_state->os_symtab != NULL) {
bzero(obj_state->os_symtab,
sizeof (obj_state->os_symtab[0]) * obj_state->os_symtabnum);
for (ndx = 0; ndx < obj_state->os_symtabnum; ndx++)
obj_state->os_symtab[ndx].symt_shndx = symtabndx[ndx];
free(symtabndx);
}
for (ndx = 1, scn = NULL;
(scn = elf_nextscn(tstate.os_elf, scn)) != NULL; ndx++, _cache++) {
_cache->sec_shndx = ndx;
_cache->sec_scn = scn;
LIBELF(_cache->sec_shdr = elf_getshdr(scn),
MSG_ORIG(MSG_ELF_GETSHDR))
_cache->sec_data = elf_getdata(scn, NULL);
_cache->sec_name = (_cache->sec_shdr->sh_name < names_len) ?
(names + _cache->sec_shdr->sh_name) :
MSG_INTL(MSG_UNKNOWNSECNAM);
switch (_cache->sec_shdr->sh_type) {
case SHT_SYMTAB_SHNDX:
symtab = get_symtab(obj_state, _cache);
symtab->symt_xshndx = ndx;
break;
case SHT_SUNW_syminfo:
symtab = get_symtab(obj_state, _cache);
symtab->symt_syminfo = ndx;
break;
case SHT_SUNW_versym:
symtab = get_symtab(obj_state, _cache);
symtab->symt_versym = ndx;
break;
}
}
symtab = obj_state->os_symtab;
for (ndx = 0; ndx < obj_state->os_symtabnum; ndx++, symtab++) {
elfedit_section_t *symsec;
Word symsec_cnt, aux_cnt;
symsec = &obj_state->os_secarr[symtab->symt_shndx];
symsec_cnt = symsec->sec_shdr->sh_size / sizeof (Sym);
if (symtab->symt_xshndx != SHN_UNDEF) {
_cache = &obj_state->os_secarr[symtab->symt_xshndx];
aux_cnt = _cache->sec_shdr->sh_size / sizeof (Word);
if (symsec_cnt > aux_cnt)
elfedit_msg(ELFEDIT_MSG_DEBUG,
MSG_INTL(MSG_DEBUG_AUX_SIZE),
EC_WORD(ndx), _cache->sec_name,
EC_WORD(aux_cnt),
EC_WORD(symsec->sec_shndx),
symsec->sec_name, EC_WORD(aux_cnt));
}
if (symtab->symt_syminfo != SHN_UNDEF) {
_cache = &obj_state->os_secarr[symtab->symt_syminfo];
aux_cnt = _cache->sec_shdr->sh_size / sizeof (Syminfo);
if (symsec_cnt > aux_cnt)
elfedit_msg(ELFEDIT_MSG_DEBUG,
MSG_INTL(MSG_DEBUG_AUX_SIZE),
EC_WORD(ndx), _cache->sec_name,
EC_WORD(aux_cnt),
EC_WORD(symsec->sec_shndx),
symsec->sec_name, EC_WORD(aux_cnt));
}
if (symtab->symt_versym != SHN_UNDEF) {
_cache = &obj_state->os_secarr[symtab->symt_versym];
aux_cnt = _cache->sec_shdr->sh_size / sizeof (Versym);
if (symsec_cnt > aux_cnt)
elfedit_msg(ELFEDIT_MSG_DEBUG,
MSG_INTL(MSG_DEBUG_AUX_SIZE),
EC_WORD(ndx), _cache->sec_name,
EC_WORD(aux_cnt),
EC_WORD(symsec->sec_shndx),
symsec->sec_name, EC_WORD(aux_cnt));
}
}
if (obj_state->os_dynndx != SHN_UNDEF) {
Word i;
Word numdyn;
elfedit_section_t *dynsec;
elfedit_dyn_elt_t flags_1_elt;
elfedit_dyn_elt_t null_elt;
Dyn *dyn;
dynsec = &obj_state->os_secarr[obj_state->os_dynndx];
dyn = (Dyn *) dynsec->sec_data->d_buf;
numdyn = dynsec->sec_shdr->sh_size /
dynsec->sec_shdr->sh_entsize;
elfedit_dyn_elt_init(&flags_1_elt);
elfedit_dyn_elt_init(&null_elt);
for (i = 0; i < numdyn; i++) {
switch (dyn[i].d_tag) {
case DT_NULL:
if (!null_elt.dn_seen)
elfedit_dyn_elt_save(&null_elt, i,
&dyn[i]);
break;
case DT_FLAGS_1:
elfedit_dyn_elt_save(&flags_1_elt, i, &dyn[i]);
break;
}
}
if (!flags_1_elt.dn_seen && null_elt.dn_seen &&
(null_elt.dn_ndx < (numdyn - 1))) {
elfedit_msg(ELFEDIT_MSG_DEBUG,
MSG_INTL(MSG_DEBUG_NULL2DYNFL1),
EC_WORD(obj_state->os_dynndx),
dynsec->sec_name, EC_WORD(null_elt.dn_ndx));
flags_1_elt.dn_seen = 1;
flags_1_elt.dn_ndx = null_elt.dn_ndx;
flags_1_elt.dn_dyn.d_tag = DT_FLAGS_1;
flags_1_elt.dn_dyn.d_un.d_val = 0;
}
if (flags_1_elt.dn_seen) {
if (flags_1_elt.dn_dyn.d_un.d_val & DF_1_EDITED) {
elfedit_msg(ELFEDIT_MSG_DEBUG,
MSG_INTL(MSG_DEBUG_SEEDYNFLG),
EC_WORD(obj_state->os_dynndx),
dynsec->sec_name,
EC_WORD(flags_1_elt.dn_ndx));
} else {
elfedit_msg(ELFEDIT_MSG_DEBUG,
MSG_INTL(MSG_DEBUG_ADDDYNFLG),
EC_WORD(obj_state->os_dynndx),
dynsec->sec_name,
EC_WORD(flags_1_elt.dn_ndx));
flags_1_elt.dn_dyn.d_un.d_val |= DF_1_EDITED;
dyn[flags_1_elt.dn_ndx] = flags_1_elt.dn_dyn;
elfedit_modified_data(dynsec);
}
}
}
#ifdef _ELF64
state.elf.obj_state.s64 = obj_state;
#else
state.elf.obj_state.s32 = obj_state;
#endif
return;
libelf_failure:
if (obj_state != NULL)
free(obj_state);
(void) close(tstate.os_fd);
elfedit_elferr(tstate.os_file, libelf_fail_name);
#undef INITIAL_SYMTABNDX_ALLOC
#undef LIBELF_FAIL
#undef LIBELF
}