#pragma ident "@(#)update.c 1.31 08/05/31 SMI"
#include <memory.h>
#include <limits.h>
#include "decl.h"
#include "msg.h"
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#if defined(_ELF64)
#define FSZ_LONG ELF64_FSZ_XWORD
#define ELFCLASS ELFCLASS64
#define _elf_snode_init _elf64_snode_init
#define _elfxx_cookscn _elf64_cookscn
#define _elf_upd_lib _elf64_upd_lib
#define elf_fsize elf64_fsize
#define _elf_entsz _elf64_entsz
#define _elf_msize _elf64_msize
#define _elf_upd_usr _elf64_upd_usr
#define wrt wrt64
#define elf_xlatetof elf64_xlatetof
#define _elfxx_update _elf64_update
#define _elfxx_swap_wrimage _elf64_swap_wrimage
#else
#define FSZ_LONG ELF32_FSZ_WORD
#define ELFCLASS ELFCLASS32
#define _elf_snode_init _elf32_snode_init
#define _elfxx_cookscn _elf32_cookscn
#define _elf_upd_lib _elf32_upd_lib
#define elf_fsize elf32_fsize
#define _elf_entsz _elf32_entsz
#define _elf_msize _elf32_msize
#define _elf_upd_usr _elf32_upd_usr
#define wrt wrt32
#define elf_xlatetof elf32_xlatetof
#define _elfxx_update _elf32_update
#define _elfxx_swap_wrimage _elf32_swap_wrimage
#endif
#if !(defined(_LP64) && defined(_ELF64))
#define TEST_SIZE
static int
test_size(Lword hi)
{
#ifndef _LP64
if (hi > INT_MAX) {
#ifndef _ELF64
if (hi > UINT_MAX) {
_elf_seterr(EFMT_FBIG_CLASS32, 0);
return (0);
}
#endif
_elf_seterr(EFMT_FBIG_LARGEFILE, 0);
return (0);
}
#endif
#if defined(_LP64) && !defined(_ELF64)
if (hi > UINT_MAX) {
_elf_seterr(EFMT_FBIG_CLASS32, 0);
return (0);
}
#endif
return (1);
}
#endif
uint_t _elf_sys_encoding(void);
static size_t
_elf_upd_lib(Elf * elf)
{
NOTE(ASSUMING_PROTECTED(*elf))
Lword hi;
Lword hibit;
Elf_Scn * s;
register Lword sz;
Ehdr * eh = elf->ed_ehdr;
unsigned ver = eh->e_version;
register char *p = (char *)eh->e_ident;
size_t scncnt;
p[EI_MAG0] = ELFMAG0;
p[EI_MAG1] = ELFMAG1;
p[EI_MAG2] = ELFMAG2;
p[EI_MAG3] = ELFMAG3;
p[EI_CLASS] = ELFCLASS;
p[EI_VERSION] = (Byte)ver;
hi = elf_fsize(ELF_T_EHDR, 1, ver);
eh->e_ehsize = (Half)hi;
if (eh->e_phnum != 0) {
eh->e_phentsize = (Half)elf_fsize(ELF_T_PHDR, 1, ver);
eh->e_phoff = (Off)hi;
hi += eh->e_phentsize * eh->e_phnum;
} else {
eh->e_phoff = 0;
eh->e_phentsize = 0;
}
if ((s = elf->ed_hdscn) == 0) {
eh->e_shnum = 0;
scncnt = 0;
} else {
s = s->s_next;
scncnt = 1;
}
hibit = 0;
for (; s != 0; s = s->s_next) {
register Dnode *d;
register Lword fsz, j;
Shdr *sh = s->s_shdr;
scncnt++;
if (sh->sh_type == SHT_NULL) {
*sh = _elf_snode_init.sb_shdr;
continue;
}
if ((s->s_myflags & SF_READY) == 0)
(void) _elfxx_cookscn(s);
sh->sh_addralign = 1;
if ((sz = (Lword)_elf_entsz(elf, sh->sh_type, ver)) != 0)
sh->sh_entsize = (Half)sz;
sz = 0;
for (d = s->s_hdnode; d != 0; d = d->db_next) {
if ((fsz = elf_fsize(d->db_data.d_type,
1, ver)) == 0)
return (0);
j = _elf_msize(d->db_data.d_type, ver);
fsz *= (d->db_data.d_size / j);
d->db_osz = (size_t)fsz;
if ((j = d->db_data.d_align) > 1) {
if (j > sh->sh_addralign)
sh->sh_addralign = (Xword)j;
if (sz % j != 0)
sz += j - sz % j;
}
d->db_data.d_off = (off_t)sz;
d->db_xoff = sz;
sz += fsz;
}
sh->sh_size = (Xword) sz;
if (sh->sh_type == SHT_NOBITS) {
if (hibit == 0)
hibit = hi;
} else {
if (hibit) {
hi = hibit;
hibit = 0;
}
}
j = sh->sh_addralign;
if ((fsz = hi % j) != 0)
hi += j - fsz;
sh->sh_offset = (Off)hi;
hi += sz;
}
if (hibit) {
hi = hibit;
hibit = 0;
}
if (scncnt != 0) {
if (hi % FSZ_LONG != 0)
hi += FSZ_LONG - hi % FSZ_LONG;
eh->e_shoff = (Off)hi;
if (scncnt < SHN_LORESERVE)
eh->e_shnum = scncnt;
else {
Shdr *sh;
sh = (Shdr *)elf->ed_hdscn->s_shdr;
sh->sh_size = scncnt;
eh->e_shnum = 0;
}
eh->e_shentsize = (Half)elf_fsize(ELF_T_SHDR, 1, ver);
hi += eh->e_shentsize * scncnt;
} else {
eh->e_shoff = 0;
eh->e_shentsize = 0;
}
#ifdef TEST_SIZE
if (test_size(hi) == 0)
return (0);
#endif
return ((size_t)hi);
}
static size_t
_elf_upd_usr(Elf * elf)
{
NOTE(ASSUMING_PROTECTED(*elf))
Lword hi;
Elf_Scn * s;
register Lword sz;
Ehdr * eh = elf->ed_ehdr;
unsigned ver = eh->e_version;
register char *p = (char *)eh->e_ident;
p[EI_MAG0] = ELFMAG0;
p[EI_MAG1] = ELFMAG1;
p[EI_MAG2] = ELFMAG2;
p[EI_MAG3] = ELFMAG3;
p[EI_CLASS] = ELFCLASS;
p[EI_VERSION] = (Byte)ver;
hi = elf_fsize(ELF_T_EHDR, 1, ver);
eh->e_ehsize = (Half)hi;
if (eh->e_phnum != 0)
eh->e_phentsize = (Half)elf_fsize(ELF_T_PHDR, 1, ver);
else
eh->e_phentsize = 0;
if ((sz = eh->e_phoff + eh->e_phentsize * eh->e_phnum) > hi)
hi = sz;
if ((s = elf->ed_hdscn) == 0)
eh->e_shnum = 0;
else {
eh->e_shnum = 1;
*(Shdr*)s->s_shdr = _elf_snode_init.sb_shdr;
s = s->s_next;
}
for (; s != 0; s = s->s_next) {
register Dnode *d;
register Lword fsz, j;
Shdr *sh = s->s_shdr;
if ((s->s_myflags & SF_READY) == 0)
(void) _elfxx_cookscn(s);
++eh->e_shnum;
sz = 0;
for (d = s->s_hdnode; d != 0; d = d->db_next) {
if ((fsz = elf_fsize(d->db_data.d_type, 1,
ver)) == 0)
return (0);
j = _elf_msize(d->db_data.d_type, ver);
fsz *= (d->db_data.d_size / j);
d->db_osz = (size_t)fsz;
if ((sh->sh_type != SHT_NOBITS) &&
((j = (d->db_data.d_off + d->db_osz)) > sz))
sz = j;
}
if (sh->sh_size < sz) {
_elf_seterr(EFMT_SCNSZ, 0);
return (0);
}
if ((sh->sh_type != SHT_NOBITS) &&
(hi < sh->sh_offset + sh->sh_size))
hi = sh->sh_offset + sh->sh_size;
}
if (eh->e_shnum != 0)
eh->e_shentsize = (Half)elf_fsize(ELF_T_SHDR, 1, ver);
else
eh->e_shentsize = 0;
if ((sz = eh->e_shoff + eh->e_shentsize * eh->e_shnum) > hi)
hi = sz;
#ifdef TEST_SIZE
if (test_size(hi) == 0)
return (0);
#endif
return ((size_t)hi);
}
static size_t
wrt(Elf * elf, Xword outsz, unsigned fill, int update_cmd)
{
NOTE(ASSUMING_PROTECTED(*elf))
Elf_Data dst, src;
unsigned flag;
Xword hi, sz;
char *image;
Elf_Scn *s;
Ehdr *eh = elf->ed_ehdr;
unsigned ver = eh->e_version;
unsigned encode;
int byte;
encode = (update_cmd == ELF_C_WRIMAGE) ? _elf_sys_encoding() :
eh->e_ident[EI_DATA];
if ((elf->ed_myflags & EDF_READ) &&
(_elf_vm(elf, (size_t)0, elf->ed_fsz) != OK_YES))
return (0);
flag = elf->ed_myflags & EDF_WRALLOC;
if ((image = _elf_outmap(elf->ed_fd, outsz, &flag)) == 0)
return (0);
if (flag == 0)
elf->ed_myflags |= EDF_IMALLOC;
src.d_buf = (Elf_Void *)eh;
src.d_type = ELF_T_EHDR;
src.d_size = sizeof (Ehdr);
src.d_version = EV_CURRENT;
dst.d_buf = (Elf_Void *)image;
dst.d_size = eh->e_ehsize;
dst.d_version = ver;
if (elf_xlatetof(&dst, &src, encode) == 0)
return (0);
elf->ed_ehflags &= ~ELF_F_DIRTY;
hi = eh->e_ehsize;
if (eh->e_phnum != 0) {
unsigned work;
src.d_buf = (Elf_Void *)elf->ed_phdr;
src.d_type = ELF_T_PHDR;
src.d_size = elf->ed_phdrsz;
ELFACCESSDATA(work, _elf_work)
src.d_version = work;
dst.d_buf = (Elf_Void *)(image + eh->e_phoff);
dst.d_size = eh->e_phnum * eh->e_phentsize;
hi = (Xword)(eh->e_phoff + dst.d_size);
if (elf_xlatetof(&dst, &src, encode) == 0) {
elf->ed_uflags |= ELF_F_DIRTY;
return (0);
}
elf->ed_phflags &= ~ELF_F_DIRTY;
src.d_version = EV_CURRENT;
}
ELFACCESSDATA(byte, _elf_byte);
for (s = elf->ed_hdscn; s != 0; s = s->s_next) {
register Dnode *d, *prevd;
Xword off = 0;
Shdr *sh = s->s_shdr;
char *start = image + sh->sh_offset;
char *here;
if ((sh->sh_type == SHT_NOBITS) ||
(sh->sh_type == SHT_NULL)) {
d = s->s_hdnode, prevd = 0;
for (; d != 0; prevd = d, d = d->db_next)
d->db_uflags &= ~ELF_F_DIRTY;
continue;
}
if (fill && (sh->sh_offset > hi)) {
sz = sh->sh_offset - hi;
(void) memset(start - sz, byte, sz);
}
for (d = s->s_hdnode, prevd = 0;
d != 0; prevd = d, d = d->db_next) {
d->db_uflags &= ~ELF_F_DIRTY;
here = start + d->db_data.d_off;
if (fill && (d->db_data.d_off > off)) {
sz = (Xword)(d->db_data.d_off - off);
(void) memset(here - sz, byte, sz);
}
if ((d->db_myflags & DBF_READY) == 0) {
SCNLOCK(s);
if (_elf_locked_getdata(s, &prevd->db_data) !=
&d->db_data) {
elf->ed_uflags |= ELF_F_DIRTY;
SCNUNLOCK(s);
return (0);
}
SCNUNLOCK(s);
}
dst.d_buf = (Elf_Void *)here;
dst.d_size = d->db_osz;
if (elf_xlatetof(&dst, &d->db_data, encode) == 0) {
elf->ed_uflags |= ELF_F_DIRTY;
return (0);
}
off = (Xword)(d->db_data.d_off + dst.d_size);
}
hi = sh->sh_offset + sh->sh_size;
}
if (fill && (eh->e_shoff > hi)) {
sz = eh->e_shoff - hi;
(void) memset(image + hi, byte, sz);
}
src.d_type = ELF_T_SHDR;
src.d_size = sizeof (Shdr);
dst.d_buf = (Elf_Void *)(image + eh->e_shoff);
dst.d_size = eh->e_shentsize;
for (s = elf->ed_hdscn; s != 0; s = s->s_next) {
assert((uintptr_t)dst.d_buf < ((uintptr_t)image + outsz));
s->s_shflags &= ~ELF_F_DIRTY;
s->s_uflags &= ~ELF_F_DIRTY;
src.d_buf = s->s_shdr;
if (elf_xlatetof(&dst, &src, encode) == 0) {
elf->ed_uflags |= ELF_F_DIRTY;
return (0);
}
dst.d_buf = (char *)dst.d_buf + eh->e_shentsize;
}
if (update_cmd == ELF_C_WRIMAGE) {
elf->ed_uflags &= ~ELF_F_DIRTY;
elf->ed_wrimage = image;
elf->ed_wrimagesz = outsz;
return (outsz);
}
if (_elf_outsync(elf->ed_fd, image, outsz,
((elf->ed_myflags & EDF_IMALLOC) ? 0 : 1)) != 0) {
elf->ed_uflags &= ~ELF_F_DIRTY;
elf->ed_myflags &= ~EDF_IMALLOC;
return (outsz);
}
elf->ed_uflags |= ELF_F_DIRTY;
return (0);
}
off_t
_elfxx_update(Elf * elf, Elf_Cmd cmd)
{
size_t sz;
unsigned u;
Ehdr *eh = elf->ed_ehdr;
if (elf == 0)
return (-1);
ELFWLOCK(elf)
switch (cmd) {
default:
_elf_seterr(EREQ_UPDATE, 0);
ELFUNLOCK(elf)
return (-1);
case ELF_C_WRIMAGE:
if ((elf->ed_myflags & EDF_WRITE) == 0) {
_elf_seterr(EREQ_UPDWRT, 0);
ELFUNLOCK(elf)
return (-1);
}
break;
case ELF_C_WRITE:
if ((elf->ed_myflags & EDF_WRITE) == 0) {
_elf_seterr(EREQ_UPDWRT, 0);
ELFUNLOCK(elf)
return (-1);
}
if (elf->ed_wrimage) {
if (elf->ed_myflags & EDF_WRALLOC) {
free(elf->ed_wrimage);
sz = elf->ed_wrimagesz;
elf->ed_wrimage = 0;
elf->ed_wrimagesz = 0;
ELFUNLOCK(elf);
return ((off_t)sz);
}
sz = _elf_outsync(elf->ed_fd, elf->ed_wrimage,
elf->ed_wrimagesz,
(elf->ed_myflags & EDF_IMALLOC ? 0 : 1));
elf->ed_myflags &= ~EDF_IMALLOC;
elf->ed_wrimage = 0;
elf->ed_wrimagesz = 0;
ELFUNLOCK(elf);
return ((off_t)sz);
}
case ELF_C_NULL:
break;
}
if (eh == 0) {
_elf_seterr(ESEQ_EHDR, 0);
ELFUNLOCK(elf)
return (-1);
}
if ((u = eh->e_version) > EV_CURRENT) {
_elf_seterr(EREQ_VER, 0);
ELFUNLOCK(elf)
return (-1);
}
if (u == EV_NONE)
eh->e_version = EV_CURRENT;
if ((u = eh->e_ident[EI_DATA]) == ELFDATANONE) {
unsigned encode;
ELFACCESSDATA(encode, _elf_encode)
if (encode == ELFDATANONE) {
_elf_seterr(EREQ_ENCODE, 0);
ELFUNLOCK(elf)
return (-1);
}
eh->e_ident[EI_DATA] = (Byte)encode;
}
u = 1;
if (elf->ed_uflags & ELF_F_LAYOUT) {
sz = _elf_upd_usr(elf);
u = 0;
} else
sz = _elf_upd_lib(elf);
if ((sz != 0) && ((cmd == ELF_C_WRITE) || (cmd == ELF_C_WRIMAGE)))
sz = wrt(elf, (Xword)sz, u, cmd);
if (sz == 0) {
ELFUNLOCK(elf)
return (-1);
}
ELFUNLOCK(elf)
return ((off_t)sz);
}
int
_elfxx_swap_wrimage(Elf *elf)
{
Elf_Data dst, src;
Elf_Scn *s;
Ehdr *eh;
Half e_phnum;
unsigned ver;
unsigned encode;
ELFWLOCK(elf);
eh = elf->ed_ehdr;
e_phnum = eh->e_phnum;
ver = eh->e_version;
encode = eh->e_ident[EI_DATA];
src.d_buf = dst.d_buf = (Elf_Void *)eh;
src.d_type = dst.d_type = ELF_T_EHDR;
src.d_size = dst.d_size = sizeof (Ehdr);
src.d_version = dst.d_version = ver;
if (elf_xlatetof(&dst, &src, encode) == 0) {
ELFUNLOCK(elf);
return (1);
}
if (e_phnum != 0) {
unsigned work;
src.d_buf = dst.d_buf = (Elf_Void *)elf->ed_phdr;
src.d_type = dst.d_type = ELF_T_PHDR;
src.d_size = dst.d_size = elf->ed_phdrsz;
ELFACCESSDATA(work, _elf_work)
src.d_version = dst.d_version = work;
if (elf_xlatetof(&dst, &src, encode) == 0) {
ELFUNLOCK(elf);
return (1);
}
}
for (s = elf->ed_hdscn; s != 0; s = s->s_next) {
register Dnode *d, *prevd;
Shdr *sh = s->s_shdr;
if ((sh->sh_type == SHT_NOBITS) || (sh->sh_type == SHT_NULL))
continue;
for (d = s->s_hdnode, prevd = 0;
d != 0; prevd = d, d = d->db_next) {
if ((d->db_myflags & DBF_READY) == 0) {
SCNLOCK(s);
if (_elf_locked_getdata(s, &prevd->db_data) !=
&d->db_data) {
SCNUNLOCK(s);
ELFUNLOCK(elf);
return (1);
}
SCNUNLOCK(s);
}
dst = d->db_data;
if (elf_xlatetof(&dst, &d->db_data, encode) == 0) {
ELFUNLOCK(elf);
return (1);
}
}
}
src.d_type = dst.d_type = ELF_T_SHDR;
src.d_version = dst.d_version = ver;
for (s = elf->ed_hdscn; s != 0; s = s->s_next) {
src.d_buf = dst.d_buf = s->s_shdr;
src.d_size = dst.d_size = sizeof (Shdr);
if (elf_xlatetof(&dst, &src, encode) == 0) {
ELFUNLOCK(elf);
return (1);
}
}
ELFUNLOCK(elf);
return (0);
}
#ifndef _ELF64
off_t
elf_update(Elf *elf, Elf_Cmd cmd)
{
if (elf == 0)
return (-1);
if (elf->ed_class == ELFCLASS32)
return (_elf32_update(elf, cmd));
else if (elf->ed_class == ELFCLASS64) {
return (_elf64_update(elf, cmd));
}
_elf_seterr(EREQ_CLASS, 0);
return (-1);
}
int
_elf_swap_wrimage(Elf *elf)
{
if (elf == 0)
return (0);
if (elf->ed_class == ELFCLASS32)
return (_elf32_swap_wrimage(elf));
if (elf->ed_class == ELFCLASS64)
return (_elf64_swap_wrimage(elf));
_elf_seterr(EREQ_CLASS, 0);
return (0);
}
Elf64_Off
_elf_getxoff(Elf_Data * d)
{
return (((Dnode *)d)->db_xoff);
}
#endif