1015 lines
25 KiB
C
Executable File
1015 lines
25 KiB
C
Executable File
/*
|
|
update.c - implementation of the elf_update(3) function.
|
|
Copyright (C) 1995 - 2001 Michael Riepe <michael@stud.uni-hannover.de>
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public
|
|
License along with this library; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include <private.h>
|
|
|
|
#ifndef lint
|
|
static const char rcsid[] = "@(#) Id: update.c,v 1.18 2001/11/17 21:02:51 michael Exp ";
|
|
#endif /* lint */
|
|
|
|
#if HAVE_MMAP
|
|
#include <sys/mman.h>
|
|
#endif /* HAVE_MMAP */
|
|
|
|
/*@unchecked@*/
|
|
static const unsigned short __encoding = ELFDATA2LSB + (ELFDATA2MSB << 8);
|
|
#define native_encoding (*(unsigned char*)&__encoding)
|
|
|
|
#define rewrite(var,val,f) \
|
|
do{if((var)!=(val)){(var)=(val);(f)|=ELF_F_DIRTY;}}while(0)
|
|
|
|
#define align(var,val) \
|
|
do{if((val)>1){(var)+=(val)-1;(var)-=(var)%(val);}}while(0)
|
|
|
|
#define max(a,b) ((a)>(b)?(a):(b))
|
|
|
|
static off_t
|
|
scn_data_layout(Elf_Scn *scn, unsigned v, unsigned type, size_t *algn, unsigned *flag)
|
|
/*@globals _elf_errno @*/
|
|
/*@modifies *scn, *algn, *flag, _elf_errno @*/
|
|
{
|
|
Elf *elf = scn->s_elf;
|
|
int layout = (elf->e_elf_flags & ELF_F_LAYOUT) == 0;
|
|
size_t scn_align = 1;
|
|
size_t len = 0;
|
|
Scn_Data *sd;
|
|
size_t fsize;
|
|
|
|
for (sd = scn->s_data_1; sd; sd = sd->sd_link) {
|
|
elf_assert(sd->sd_magic == DATA_MAGIC);
|
|
elf_assert(sd->sd_scn == scn);
|
|
|
|
if (!valid_version(sd->sd_data.d_version)) {
|
|
return (off_t)-1;
|
|
}
|
|
|
|
fsize = sd->sd_data.d_size;
|
|
if (type != SHT_NOBITS && valid_type(sd->sd_data.d_type)) {
|
|
if (elf->e_class == ELFCLASS32) {
|
|
fsize = _elf32_xltsize(&sd->sd_data, v, ELFDATA2LSB, 1);
|
|
}
|
|
#if __LIBELF64
|
|
else if (elf->e_class == ELFCLASS64) {
|
|
fsize = _elf64_xltsize(&sd->sd_data, v, ELFDATA2LSB, 1);
|
|
}
|
|
#endif /* __LIBELF64 */
|
|
else {
|
|
elf_assert(valid_class(elf->e_class));
|
|
seterr(ERROR_UNIMPLEMENTED);
|
|
return (off_t)-1;
|
|
}
|
|
if (fsize == (size_t)-1) {
|
|
return (off_t)-1;
|
|
}
|
|
}
|
|
|
|
if (layout) {
|
|
align(len, sd->sd_data.d_align);
|
|
scn_align = max(scn_align, sd->sd_data.d_align);
|
|
rewrite(sd->sd_data.d_off, (off_t)len, sd->sd_data_flags);
|
|
len += fsize;
|
|
}
|
|
else {
|
|
len = max(len, sd->sd_data.d_off + fsize);
|
|
}
|
|
|
|
/*@-boundsread@*/
|
|
*flag |= sd->sd_data_flags;
|
|
/*@=boundsread@*/
|
|
}
|
|
/*@-boundswrite@*/
|
|
*algn = scn_align;
|
|
/*@=boundswrite@*/
|
|
return (off_t)len;
|
|
}
|
|
|
|
static size_t
|
|
scn_entsize(const Elf *elf, unsigned version, unsigned stype)
|
|
/*@*/
|
|
{
|
|
Elf_Type type;
|
|
|
|
switch ((type = _elf_scn_type(stype))) {
|
|
case ELF_T_BYTE:
|
|
return 0;
|
|
case ELF_T_VDEF:
|
|
case ELF_T_VNEED:
|
|
return 0; /* What else can I do? Thank you, Sun! */
|
|
default:
|
|
return _fsize(elf->e_class, version, type);
|
|
}
|
|
}
|
|
|
|
static off_t
|
|
_elf32_layout(Elf *elf, unsigned *flag)
|
|
/*@globals _elf_errno @*/
|
|
/*@modifies *elf, *flag, _elf_errno @*/
|
|
{
|
|
int layout = (elf->e_elf_flags & ELF_F_LAYOUT) == 0;
|
|
Elf32_Ehdr *ehdr = (Elf32_Ehdr*)elf->e_ehdr;
|
|
size_t off = 0;
|
|
unsigned version;
|
|
unsigned encoding;
|
|
size_t align_addr;
|
|
size_t entsize;
|
|
unsigned shnum;
|
|
Elf_Scn *scn;
|
|
|
|
/*@-boundswrite@*/
|
|
*flag = elf->e_elf_flags | elf->e_phdr_flags;
|
|
/*@=boundswrite@*/
|
|
|
|
if ((version = ehdr->e_version) == EV_NONE) {
|
|
version = EV_CURRENT;
|
|
}
|
|
if (!valid_version(version)) {
|
|
seterr(ERROR_UNKNOWN_VERSION);
|
|
return -1;
|
|
}
|
|
/*@-boundsread@*/
|
|
if ((encoding = ehdr->e_ident[EI_DATA]) == ELFDATANONE) {
|
|
encoding = native_encoding;
|
|
}
|
|
/*@=boundsread@*/
|
|
if (!valid_encoding(encoding)) {
|
|
seterr(ERROR_UNKNOWN_ENCODING);
|
|
return -1;
|
|
}
|
|
/*@-boundsread@*/
|
|
entsize = _fsize(ELFCLASS32, version, ELF_T_EHDR);
|
|
/*@=boundsread@*/
|
|
elf_assert(entsize);
|
|
rewrite(ehdr->e_ehsize, entsize, elf->e_ehdr_flags);
|
|
off = entsize;
|
|
|
|
align_addr = _fsize(ELFCLASS32, version, ELF_T_ADDR);
|
|
elf_assert(align_addr);
|
|
|
|
if (elf->e_phnum) {
|
|
entsize = _fsize(ELFCLASS32, version, ELF_T_PHDR);
|
|
elf_assert(entsize);
|
|
if (layout) {
|
|
align(off, align_addr);
|
|
rewrite(ehdr->e_phoff, off, elf->e_ehdr_flags);
|
|
off += elf->e_phnum * entsize;
|
|
}
|
|
else {
|
|
off = max(off, ehdr->e_phoff + elf->e_phnum * entsize);
|
|
}
|
|
}
|
|
else {
|
|
entsize = 0;
|
|
if (layout) {
|
|
rewrite(ehdr->e_phoff, 0, elf->e_ehdr_flags);
|
|
}
|
|
}
|
|
rewrite(ehdr->e_phnum, elf->e_phnum, elf->e_ehdr_flags);
|
|
rewrite(ehdr->e_phentsize, entsize, elf->e_ehdr_flags);
|
|
|
|
for (scn = elf->e_scn_1, shnum = 0; scn; scn = scn->s_link, ++shnum) {
|
|
Elf32_Shdr *shdr = &scn->s_shdr32;
|
|
size_t scn_align = 1;
|
|
off_t len;
|
|
|
|
elf_assert(scn->s_index == shnum);
|
|
|
|
/*@-boundsread@*/
|
|
*flag |= scn->s_scn_flags | scn->s_shdr_flags;
|
|
/*@=boundsread@*/
|
|
|
|
if (scn->s_index == SHN_UNDEF) {
|
|
rewrite(shdr->sh_entsize, 0, scn->s_shdr_flags);
|
|
if (layout) {
|
|
rewrite(shdr->sh_offset, 0, scn->s_shdr_flags);
|
|
rewrite(shdr->sh_size, 0, scn->s_shdr_flags);
|
|
rewrite(shdr->sh_addralign, 0, scn->s_shdr_flags);
|
|
}
|
|
continue;
|
|
}
|
|
if (shdr->sh_type == SHT_NULL) {
|
|
continue;
|
|
}
|
|
|
|
len = scn_data_layout(scn, version, shdr->sh_type, &scn_align, flag);
|
|
if (len == -1) {
|
|
return -1;
|
|
}
|
|
|
|
entsize = scn_entsize(elf, version, shdr->sh_type);
|
|
if (entsize > 1) {
|
|
rewrite(shdr->sh_entsize, entsize, scn->s_shdr_flags);
|
|
}
|
|
|
|
if (layout) {
|
|
align(off, scn_align);
|
|
rewrite(shdr->sh_offset, off, scn->s_shdr_flags);
|
|
rewrite(shdr->sh_size, (size_t)len, scn->s_shdr_flags);
|
|
rewrite(shdr->sh_addralign, scn_align, scn->s_shdr_flags);
|
|
|
|
if (shdr->sh_type != SHT_NOBITS) {
|
|
off += (size_t)len;
|
|
}
|
|
}
|
|
else if ((size_t)len > shdr->sh_size) {
|
|
seterr(ERROR_SCN2SMALL);
|
|
return -1;
|
|
}
|
|
else {
|
|
Elf_Scn *scn2;
|
|
size_t end1, end2;
|
|
|
|
end1 = shdr->sh_offset;
|
|
if (shdr->sh_type != SHT_NOBITS) {
|
|
end1 += shdr->sh_size;
|
|
}
|
|
if (shdr->sh_offset < off) {
|
|
/*
|
|
* check for overlapping sections
|
|
*/
|
|
for (scn2 = elf->e_scn_1; scn2; scn2 = scn2->s_link) {
|
|
if (scn2 == scn) {
|
|
/*@innerbreak@*/ break;
|
|
}
|
|
end2 = scn2->s_shdr32.sh_offset;
|
|
if (scn2->s_shdr32.sh_type != SHT_NOBITS) {
|
|
end2 += scn2->s_shdr32.sh_size;
|
|
}
|
|
if (end1 > scn2->s_shdr32.sh_offset
|
|
&& end2 > shdr->sh_offset) {
|
|
seterr(ERROR_SCN_OVERLAP);
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
if (off < end1) {
|
|
off = end1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (shnum) {
|
|
entsize = _fsize(ELFCLASS32, version, ELF_T_SHDR);
|
|
elf_assert(entsize);
|
|
if (layout) {
|
|
align(off, align_addr);
|
|
rewrite(ehdr->e_shoff, off, elf->e_ehdr_flags);
|
|
off += shnum * entsize;
|
|
}
|
|
else {
|
|
off = max(off, ehdr->e_shoff + shnum * entsize);
|
|
}
|
|
}
|
|
else {
|
|
entsize = 0;
|
|
if (layout) {
|
|
rewrite(ehdr->e_shoff, 0, elf->e_ehdr_flags);
|
|
}
|
|
}
|
|
rewrite(ehdr->e_shnum, shnum, elf->e_ehdr_flags);
|
|
rewrite(ehdr->e_shentsize, entsize, elf->e_ehdr_flags);
|
|
|
|
/*@-boundsread@*/
|
|
rewrite(ehdr->e_ident[EI_MAG0], ELFMAG0, elf->e_ehdr_flags);
|
|
rewrite(ehdr->e_ident[EI_MAG1], ELFMAG1, elf->e_ehdr_flags);
|
|
rewrite(ehdr->e_ident[EI_MAG2], ELFMAG2, elf->e_ehdr_flags);
|
|
rewrite(ehdr->e_ident[EI_MAG3], ELFMAG3, elf->e_ehdr_flags);
|
|
rewrite(ehdr->e_ident[EI_CLASS], ELFCLASS32, elf->e_ehdr_flags);
|
|
rewrite(ehdr->e_ident[EI_DATA], encoding, elf->e_ehdr_flags);
|
|
rewrite(ehdr->e_ident[EI_VERSION], version, elf->e_ehdr_flags);
|
|
rewrite(ehdr->e_version, version, elf->e_ehdr_flags);
|
|
|
|
*flag |= elf->e_ehdr_flags;
|
|
/*@=boundsread@*/
|
|
|
|
return off;
|
|
}
|
|
|
|
#if __LIBELF64
|
|
|
|
static off_t
|
|
_elf64_layout(Elf *elf, unsigned *flag)
|
|
/*@globals _elf_errno @*/
|
|
/*@modifies *elf, *flag, _elf_errno @*/
|
|
{
|
|
int layout = (elf->e_elf_flags & ELF_F_LAYOUT) == 0;
|
|
Elf64_Ehdr *ehdr = (Elf64_Ehdr*)elf->e_ehdr;
|
|
size_t off = 0;
|
|
unsigned version;
|
|
unsigned encoding;
|
|
size_t align_addr;
|
|
size_t entsize;
|
|
unsigned shnum;
|
|
Elf_Scn *scn;
|
|
|
|
/*@-boundswrite@*/
|
|
*flag = elf->e_elf_flags | elf->e_phdr_flags;
|
|
/*@=boundswrite@*/
|
|
|
|
if ((version = ehdr->e_version) == EV_NONE) {
|
|
version = EV_CURRENT;
|
|
}
|
|
if (!valid_version(version)) {
|
|
seterr(ERROR_UNKNOWN_VERSION);
|
|
return -1;
|
|
}
|
|
/*@-boundsread@*/
|
|
if ((encoding = ehdr->e_ident[EI_DATA]) == ELFDATANONE) {
|
|
encoding = native_encoding;
|
|
}
|
|
/*@=boundsread@*/
|
|
if (!valid_encoding(encoding)) {
|
|
seterr(ERROR_UNKNOWN_ENCODING);
|
|
return -1;
|
|
}
|
|
/*@-boundsread@*/
|
|
entsize = _fsize(ELFCLASS64, version, ELF_T_EHDR);
|
|
/*@=boundsread@*/
|
|
elf_assert(entsize);
|
|
rewrite(ehdr->e_ehsize, entsize, elf->e_ehdr_flags);
|
|
off = entsize;
|
|
|
|
align_addr = _fsize(ELFCLASS64, version, ELF_T_ADDR);
|
|
elf_assert(align_addr);
|
|
|
|
if (elf->e_phnum) {
|
|
entsize = _fsize(ELFCLASS64, version, ELF_T_PHDR);
|
|
elf_assert(entsize);
|
|
if (layout) {
|
|
align(off, align_addr);
|
|
rewrite(ehdr->e_phoff, off, elf->e_ehdr_flags);
|
|
off += elf->e_phnum * entsize;
|
|
}
|
|
else {
|
|
off = max(off, ehdr->e_phoff + elf->e_phnum * entsize);
|
|
}
|
|
}
|
|
else {
|
|
entsize = 0;
|
|
if (layout) {
|
|
rewrite(ehdr->e_phoff, 0, elf->e_ehdr_flags);
|
|
}
|
|
}
|
|
rewrite(ehdr->e_phnum, elf->e_phnum, elf->e_ehdr_flags);
|
|
rewrite(ehdr->e_phentsize, entsize, elf->e_ehdr_flags);
|
|
|
|
for (scn = elf->e_scn_1, shnum = 0; scn; scn = scn->s_link, ++shnum) {
|
|
Elf64_Shdr *shdr = &scn->s_shdr64;
|
|
size_t scn_align = 1;
|
|
off_t len;
|
|
|
|
elf_assert(scn->s_index == shnum);
|
|
|
|
/*@-boundsread@*/
|
|
*flag |= scn->s_scn_flags | scn->s_shdr_flags;
|
|
/*@=boundsread@*/
|
|
|
|
if (scn->s_index == SHN_UNDEF) {
|
|
rewrite(shdr->sh_entsize, 0, scn->s_shdr_flags);
|
|
if (layout) {
|
|
rewrite(shdr->sh_offset, 0, scn->s_shdr_flags);
|
|
rewrite(shdr->sh_size, 0, scn->s_shdr_flags);
|
|
rewrite(shdr->sh_addralign, 0, scn->s_shdr_flags);
|
|
}
|
|
continue;
|
|
}
|
|
if (shdr->sh_type == SHT_NULL) {
|
|
continue;
|
|
}
|
|
|
|
len = scn_data_layout(scn, version, shdr->sh_type, &scn_align, flag);
|
|
if (len == -1) {
|
|
return -1;
|
|
}
|
|
|
|
entsize = scn_entsize(elf, version, shdr->sh_type);
|
|
if (entsize > 1) {
|
|
/* Some architectures use 64-bit hash entries. */
|
|
/*@-boundsread@*/
|
|
if (shdr->sh_type != SHT_HASH
|
|
|| shdr->sh_entsize != _fsize(elf->e_class, version, ELF_T_ADDR))
|
|
rewrite(shdr->sh_entsize, entsize, scn->s_shdr_flags);
|
|
/*@=boundsread@*/
|
|
}
|
|
|
|
if (layout) {
|
|
align(off, scn_align);
|
|
rewrite(shdr->sh_offset, off, scn->s_shdr_flags);
|
|
rewrite(shdr->sh_size, (size_t)len, scn->s_shdr_flags);
|
|
rewrite(shdr->sh_addralign, scn_align, scn->s_shdr_flags);
|
|
|
|
if (shdr->sh_type != SHT_NOBITS) {
|
|
off += (size_t)len;
|
|
}
|
|
}
|
|
else if ((size_t)len > shdr->sh_size) {
|
|
seterr(ERROR_SCN2SMALL);
|
|
return -1;
|
|
}
|
|
else {
|
|
Elf_Scn *scn2;
|
|
size_t end1, end2;
|
|
|
|
end1 = shdr->sh_offset;
|
|
if (shdr->sh_type != SHT_NOBITS) {
|
|
end1 += shdr->sh_size;
|
|
}
|
|
if (shdr->sh_offset < off) {
|
|
/*
|
|
* check for overlapping sections
|
|
*/
|
|
for (scn2 = elf->e_scn_1; scn2; scn2 = scn2->s_link) {
|
|
if (scn2 == scn) {
|
|
/*@innerbreak@*/ break;
|
|
}
|
|
end2 = scn2->s_shdr64.sh_offset;
|
|
if (scn2->s_shdr64.sh_type != SHT_NOBITS) {
|
|
end2 += scn2->s_shdr64.sh_size;
|
|
}
|
|
if (end1 > scn2->s_shdr64.sh_offset
|
|
&& end2 > shdr->sh_offset) {
|
|
seterr(ERROR_SCN_OVERLAP);
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
if (off < end1) {
|
|
off = end1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (shnum) {
|
|
entsize = _fsize(ELFCLASS64, version, ELF_T_SHDR);
|
|
elf_assert(entsize);
|
|
if (layout) {
|
|
align(off, align_addr);
|
|
rewrite(ehdr->e_shoff, off, elf->e_ehdr_flags);
|
|
off += shnum * entsize;
|
|
}
|
|
else {
|
|
off = max(off, ehdr->e_shoff + shnum * entsize);
|
|
}
|
|
}
|
|
else {
|
|
entsize = 0;
|
|
if (layout) {
|
|
rewrite(ehdr->e_shoff, 0, elf->e_ehdr_flags);
|
|
}
|
|
}
|
|
rewrite(ehdr->e_shnum, shnum, elf->e_ehdr_flags);
|
|
rewrite(ehdr->e_shentsize, entsize, elf->e_ehdr_flags);
|
|
|
|
/*@-boundsread@*/
|
|
rewrite(ehdr->e_ident[EI_MAG0], ELFMAG0, elf->e_ehdr_flags);
|
|
rewrite(ehdr->e_ident[EI_MAG1], ELFMAG1, elf->e_ehdr_flags);
|
|
rewrite(ehdr->e_ident[EI_MAG2], ELFMAG2, elf->e_ehdr_flags);
|
|
rewrite(ehdr->e_ident[EI_MAG3], ELFMAG3, elf->e_ehdr_flags);
|
|
rewrite(ehdr->e_ident[EI_CLASS], ELFCLASS64, elf->e_ehdr_flags);
|
|
rewrite(ehdr->e_ident[EI_DATA], encoding, elf->e_ehdr_flags);
|
|
rewrite(ehdr->e_ident[EI_VERSION], version, elf->e_ehdr_flags);
|
|
rewrite(ehdr->e_version, version, elf->e_ehdr_flags);
|
|
|
|
*flag |= elf->e_ehdr_flags;
|
|
/*@=boundsread@*/
|
|
|
|
return off;
|
|
}
|
|
|
|
#endif /* __LIBELF64 */
|
|
|
|
#define ptrinside(p,a,l) ((p)>=(a)&&(p)<(a)+(l))
|
|
#define newptr(p,o,n) ((p)=((p)-(o))+(n))
|
|
|
|
static int
|
|
_elf_update_pointers(Elf *elf, char *outbuf, size_t len)
|
|
/*@globals _elf_errno @*/
|
|
/*@modifies *elf, _elf_errno @*/
|
|
/*@requires maxSet(outbuf) >= (len - 1) @*/
|
|
{
|
|
Elf_Scn *scn;
|
|
Scn_Data *sd;
|
|
char *data, *rawdata;
|
|
|
|
elf_assert(elf);
|
|
elf_assert(elf->e_data);
|
|
elf_assert(!elf->e_parent);
|
|
elf_assert(!elf->e_unmap_data);
|
|
elf_assert(elf->e_kind == ELF_K_ELF);
|
|
elf_assert(len >= EI_NIDENT);
|
|
|
|
/* resize memory images */
|
|
if (len <= elf->e_dsize) {
|
|
/* don't shorten the memory image */
|
|
data = elf->e_data;
|
|
}
|
|
else if ((data = (char*)realloc(elf->e_data, len))) {
|
|
elf->e_dsize = len;
|
|
}
|
|
else {
|
|
seterr(ERROR_IO_2BIG);
|
|
return -1;
|
|
}
|
|
if (elf->e_rawdata == elf->e_data) {
|
|
/* update frozen raw image */
|
|
/*@-boundswrite@*/ /* FIX: realloc ensures annotation? */
|
|
memcpy(data, outbuf, len);
|
|
/*@=boundswrite@*/
|
|
elf->e_data = elf->e_rawdata = data;
|
|
/* cooked data is stored outside the raw image */
|
|
return 0;
|
|
}
|
|
if (elf->e_rawdata) {
|
|
/* update raw image */
|
|
if (!(rawdata = (char*)realloc(elf->e_rawdata, len))) {
|
|
seterr(ERROR_IO_2BIG);
|
|
return -1;
|
|
}
|
|
/*@-boundsread@*/
|
|
memcpy(rawdata, outbuf, len);
|
|
/*@=boundsread@*/
|
|
elf->e_rawdata = rawdata;
|
|
}
|
|
if (data == elf->e_data) {
|
|
/* nothing more to do */
|
|
return 0;
|
|
}
|
|
/* adjust internal pointers */
|
|
if (elf->e_ehdr && !elf->e_free_ehdr) {
|
|
elf_assert(ptrinside(elf->e_ehdr, elf->e_data, elf->e_dsize));
|
|
newptr(elf->e_ehdr, elf->e_data, data);
|
|
}
|
|
if (elf->e_phdr && !elf->e_free_phdr) {
|
|
elf_assert(ptrinside(elf->e_phdr, elf->e_data, elf->e_dsize));
|
|
newptr(elf->e_phdr, elf->e_data, data);
|
|
}
|
|
for (scn = elf->e_scn_1; scn; scn = scn->s_link) {
|
|
elf_assert(scn->s_magic == SCN_MAGIC);
|
|
elf_assert(scn->s_elf == elf);
|
|
if ((sd = scn->s_data_1)) {
|
|
elf_assert(sd->sd_magic == DATA_MAGIC);
|
|
elf_assert(sd->sd_scn == scn);
|
|
if (sd->sd_memdata && !sd->sd_free_data) {
|
|
elf_assert(ptrinside(sd->sd_memdata, elf->e_data, elf->e_dsize));
|
|
if (sd->sd_data.d_buf == sd->sd_memdata) {
|
|
newptr(sd->sd_memdata, elf->e_data, data);
|
|
sd->sd_data.d_buf = sd->sd_memdata;
|
|
}
|
|
else {
|
|
newptr(sd->sd_memdata, elf->e_data, data);
|
|
}
|
|
}
|
|
}
|
|
if ((sd = scn->s_rawdata)) {
|
|
elf_assert(sd->sd_magic == DATA_MAGIC);
|
|
elf_assert(sd->sd_scn == scn);
|
|
if (sd->sd_memdata && sd->sd_free_data) {
|
|
size_t off, len;
|
|
|
|
if (elf->e_class == ELFCLASS32) {
|
|
off = scn->s_shdr32.sh_offset;
|
|
len = scn->s_shdr32.sh_size;
|
|
}
|
|
#if __LIBELF64
|
|
else if (elf->e_class == ELFCLASS64) {
|
|
off = scn->s_shdr64.sh_offset;
|
|
len = scn->s_shdr64.sh_size;
|
|
}
|
|
#endif /* __LIBELF64 */
|
|
else {
|
|
seterr(ERROR_UNIMPLEMENTED);
|
|
return -1;
|
|
}
|
|
if (!(rawdata = (char*)realloc(sd->sd_memdata, len))) {
|
|
seterr(ERROR_IO_2BIG);
|
|
return -1;
|
|
}
|
|
/*@-boundsread@*/
|
|
memcpy(rawdata, outbuf + off, len);
|
|
/*@=boundsread@*/
|
|
if (sd->sd_data.d_buf == sd->sd_memdata) {
|
|
sd->sd_data.d_buf = rawdata;
|
|
}
|
|
sd->sd_memdata = rawdata;
|
|
}
|
|
}
|
|
}
|
|
elf->e_data = data;
|
|
return 0;
|
|
}
|
|
|
|
#undef ptrinside
|
|
#undef newptr
|
|
|
|
static off_t
|
|
_elf32_write(Elf *elf, char *outbuf, size_t len)
|
|
/*@globals _elf_errno @*/
|
|
/*@modifies *elf, _elf_errno @*/
|
|
{
|
|
Elf32_Ehdr *ehdr;
|
|
Elf32_Shdr *shdr;
|
|
Elf_Scn *scn;
|
|
Scn_Data *sd;
|
|
Elf_Data src;
|
|
Elf_Data dst;
|
|
unsigned encode;
|
|
|
|
elf_assert(len);
|
|
elf_assert(elf->e_ehdr);
|
|
ehdr = (Elf32_Ehdr*)elf->e_ehdr;
|
|
/*@-boundsread@*/
|
|
encode = ehdr->e_ident[EI_DATA];
|
|
/*@=boundsread@*/
|
|
|
|
src.d_buf = ehdr;
|
|
src.d_type = ELF_T_EHDR;
|
|
/*@-boundsread@*/
|
|
src.d_size = _msize(ELFCLASS32, _elf_version, ELF_T_EHDR);
|
|
/*@=boundsread@*/
|
|
src.d_version = _elf_version;
|
|
dst.d_buf = outbuf;
|
|
dst.d_size = ehdr->e_ehsize;
|
|
dst.d_version = ehdr->e_version;
|
|
if (!elf32_xlatetof(&dst, &src, encode)) {
|
|
return -1;
|
|
}
|
|
|
|
if (ehdr->e_phnum) {
|
|
src.d_buf = elf->e_phdr;
|
|
src.d_type = ELF_T_PHDR;
|
|
src.d_size = ehdr->e_phnum * _msize(ELFCLASS32, _elf_version, ELF_T_PHDR);
|
|
src.d_version = _elf_version;
|
|
dst.d_buf = outbuf + ehdr->e_phoff;
|
|
dst.d_size = ehdr->e_phnum * ehdr->e_phentsize;
|
|
dst.d_version = ehdr->e_version;
|
|
if (!elf32_xlatetof(&dst, &src, encode)) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
for (scn = elf->e_scn_1; scn; scn = scn->s_link) {
|
|
elf_assert(scn->s_magic == SCN_MAGIC);
|
|
elf_assert(scn->s_elf == elf);
|
|
|
|
src.d_buf = &scn->s_uhdr;
|
|
src.d_type = ELF_T_SHDR;
|
|
/*@-boundsread@*/
|
|
src.d_size = _msize(ELFCLASS32, EV_CURRENT, ELF_T_SHDR);
|
|
/*@=boundsread@*/
|
|
src.d_version = EV_CURRENT;
|
|
dst.d_buf = outbuf + ehdr->e_shoff + scn->s_index * ehdr->e_shentsize;
|
|
dst.d_size = ehdr->e_shentsize;
|
|
dst.d_version = ehdr->e_version;
|
|
if (!elf32_xlatetof(&dst, &src, encode)) {
|
|
return -1;
|
|
}
|
|
|
|
if (scn->s_index == SHN_UNDEF) {
|
|
continue;
|
|
}
|
|
shdr = &scn->s_shdr32;
|
|
if (shdr->sh_type == SHT_NULL || shdr->sh_type == SHT_NOBITS) {
|
|
continue;
|
|
}
|
|
if (scn->s_data_1 && !elf_getdata(scn, NULL)) {
|
|
return -1;
|
|
}
|
|
for (sd = scn->s_data_1; sd; sd = sd->sd_link) {
|
|
elf_assert(sd->sd_magic == DATA_MAGIC);
|
|
elf_assert(sd->sd_scn == scn);
|
|
src = sd->sd_data;
|
|
if (!src.d_size) {
|
|
/*@innercontinue@*/ continue;
|
|
}
|
|
if (!src.d_buf) {
|
|
seterr(ERROR_NULLBUF);
|
|
return -1;
|
|
}
|
|
dst.d_buf = outbuf + shdr->sh_offset + src.d_off;
|
|
dst.d_size = src.d_size;
|
|
dst.d_version = ehdr->e_version;
|
|
if (valid_type(src.d_type)) {
|
|
size_t tmp;
|
|
|
|
tmp = _elf32_xltsize(&src, dst.d_version, ELFDATA2LSB, 1);
|
|
if (tmp == (size_t)-1) {
|
|
return -1;
|
|
}
|
|
dst.d_size = tmp;
|
|
}
|
|
else {
|
|
src.d_type = ELF_T_BYTE;
|
|
}
|
|
if (!elf32_xlatetof(&dst, &src, encode)) {
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* cleanup */
|
|
/*@-boundswrite@*/
|
|
if (elf->e_readable && _elf_update_pointers(elf, outbuf, len)) {
|
|
return -1;
|
|
}
|
|
/*@=boundswrite@*/
|
|
/* NOTE: ehdr is no longer valid! */
|
|
ehdr = (Elf32_Ehdr*)elf->e_ehdr; elf_assert(ehdr);
|
|
elf->e_encoding = ehdr->e_ident[EI_DATA];
|
|
/*@-boundsread@*/
|
|
elf->e_version = ehdr->e_ident[EI_VERSION];
|
|
/*@=boundsread@*/
|
|
elf->e_elf_flags &= ~ELF_F_DIRTY;
|
|
elf->e_ehdr_flags &= ~ELF_F_DIRTY;
|
|
elf->e_phdr_flags &= ~ELF_F_DIRTY;
|
|
for (scn = elf->e_scn_1; scn; scn = scn->s_link) {
|
|
scn->s_scn_flags &= ~ELF_F_DIRTY;
|
|
scn->s_shdr_flags &= ~ELF_F_DIRTY;
|
|
for (sd = scn->s_data_1; sd; sd = sd->sd_link) {
|
|
sd->sd_data_flags &= ~ELF_F_DIRTY;
|
|
}
|
|
if (elf->e_readable) {
|
|
shdr = &scn->s_shdr32;
|
|
scn->s_type = shdr->sh_type;
|
|
scn->s_size = shdr->sh_size;
|
|
scn->s_offset = shdr->sh_offset;
|
|
}
|
|
}
|
|
elf->e_size = len;
|
|
return len;
|
|
}
|
|
|
|
#if __LIBELF64
|
|
|
|
static off_t
|
|
_elf64_write(Elf *elf, char *outbuf, size_t len)
|
|
/*@globals _elf_errno @*/
|
|
/*@modifies *elf, _elf_errno @*/
|
|
{
|
|
Elf64_Ehdr *ehdr;
|
|
Elf64_Shdr *shdr;
|
|
Elf_Scn *scn;
|
|
Scn_Data *sd;
|
|
Elf_Data src;
|
|
Elf_Data dst;
|
|
unsigned encode;
|
|
|
|
elf_assert(len);
|
|
elf_assert(elf->e_ehdr);
|
|
ehdr = (Elf64_Ehdr*)elf->e_ehdr;
|
|
/*@-boundsread@*/
|
|
encode = ehdr->e_ident[EI_DATA];
|
|
/*@=boundsread@*/
|
|
|
|
src.d_buf = ehdr;
|
|
src.d_type = ELF_T_EHDR;
|
|
/*@-boundsread@*/
|
|
src.d_size = _msize(ELFCLASS64, _elf_version, ELF_T_EHDR);
|
|
/*@=boundsread@*/
|
|
src.d_version = _elf_version;
|
|
dst.d_buf = outbuf;
|
|
dst.d_size = ehdr->e_ehsize;
|
|
dst.d_version = ehdr->e_version;
|
|
if (!elf64_xlatetof(&dst, &src, encode)) {
|
|
return -1;
|
|
}
|
|
|
|
if (ehdr->e_phnum) {
|
|
src.d_buf = elf->e_phdr;
|
|
src.d_type = ELF_T_PHDR;
|
|
src.d_size = ehdr->e_phnum * _msize(ELFCLASS64, _elf_version, ELF_T_PHDR);
|
|
src.d_version = _elf_version;
|
|
dst.d_buf = outbuf + ehdr->e_phoff;
|
|
dst.d_size = ehdr->e_phnum * ehdr->e_phentsize;
|
|
dst.d_version = ehdr->e_version;
|
|
if (!elf64_xlatetof(&dst, &src, encode)) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
for (scn = elf->e_scn_1; scn; scn = scn->s_link) {
|
|
elf_assert(scn->s_magic == SCN_MAGIC);
|
|
elf_assert(scn->s_elf == elf);
|
|
|
|
src.d_buf = &scn->s_uhdr;
|
|
src.d_type = ELF_T_SHDR;
|
|
/*@-boundsread@*/
|
|
src.d_size = _msize(ELFCLASS64, EV_CURRENT, ELF_T_SHDR);
|
|
/*@=boundsread@*/
|
|
src.d_version = EV_CURRENT;
|
|
dst.d_buf = outbuf + ehdr->e_shoff + scn->s_index * ehdr->e_shentsize;
|
|
dst.d_size = ehdr->e_shentsize;
|
|
dst.d_version = ehdr->e_version;
|
|
if (!elf64_xlatetof(&dst, &src, encode)) {
|
|
return -1;
|
|
}
|
|
|
|
if (scn->s_index == SHN_UNDEF) {
|
|
continue;
|
|
}
|
|
shdr = &scn->s_shdr64;
|
|
if (shdr->sh_type == SHT_NULL || shdr->sh_type == SHT_NOBITS) {
|
|
continue;
|
|
}
|
|
if (scn->s_data_1 && !elf_getdata(scn, NULL)) {
|
|
return -1;
|
|
}
|
|
for (sd = scn->s_data_1; sd; sd = sd->sd_link) {
|
|
elf_assert(sd->sd_magic == DATA_MAGIC);
|
|
elf_assert(sd->sd_scn == scn);
|
|
src = sd->sd_data;
|
|
if (!src.d_size) {
|
|
/*@innercontinue@*/ continue;
|
|
}
|
|
if (!src.d_buf) {
|
|
seterr(ERROR_NULLBUF);
|
|
return -1;
|
|
}
|
|
dst.d_buf = outbuf + shdr->sh_offset + src.d_off;
|
|
dst.d_size = src.d_size;
|
|
dst.d_version = ehdr->e_version;
|
|
if (valid_type(src.d_type)) {
|
|
size_t tmp;
|
|
|
|
tmp = _elf64_xltsize(&src, dst.d_version, ELFDATA2LSB, 1);
|
|
if (tmp == (size_t)-1) {
|
|
return -1;
|
|
}
|
|
dst.d_size = tmp;
|
|
}
|
|
else {
|
|
src.d_type = ELF_T_BYTE;
|
|
}
|
|
if (!elf64_xlatetof(&dst, &src, encode)) {
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* cleanup */
|
|
/*@-boundswrite@*/
|
|
if (elf->e_readable && _elf_update_pointers(elf, outbuf, len)) {
|
|
return -1;
|
|
}
|
|
/*@=boundswrite@*/
|
|
/* NOTE: ehdr is no longer valid! */
|
|
ehdr = (Elf64_Ehdr*)elf->e_ehdr; elf_assert(ehdr);
|
|
elf->e_encoding = ehdr->e_ident[EI_DATA];
|
|
/*@-boundsread@*/
|
|
elf->e_version = ehdr->e_ident[EI_VERSION];
|
|
/*@=boundsread@*/
|
|
elf->e_elf_flags &= ~ELF_F_DIRTY;
|
|
elf->e_ehdr_flags &= ~ELF_F_DIRTY;
|
|
elf->e_phdr_flags &= ~ELF_F_DIRTY;
|
|
for (scn = elf->e_scn_1; scn; scn = scn->s_link) {
|
|
scn->s_scn_flags &= ~ELF_F_DIRTY;
|
|
scn->s_shdr_flags &= ~ELF_F_DIRTY;
|
|
for (sd = scn->s_data_1; sd; sd = sd->sd_link) {
|
|
sd->sd_data_flags &= ~ELF_F_DIRTY;
|
|
}
|
|
if (elf->e_readable) {
|
|
shdr = &scn->s_shdr64;
|
|
scn->s_type = shdr->sh_type;
|
|
scn->s_size = shdr->sh_size;
|
|
scn->s_offset = shdr->sh_offset;
|
|
}
|
|
}
|
|
elf->e_size = len;
|
|
return len;
|
|
}
|
|
|
|
#endif /* __LIBELF64 */
|
|
|
|
static off_t
|
|
_elf_output(Elf *elf, int fd, size_t len, off_t (*_elf_write)(Elf*, char*, size_t))
|
|
/*@globals _elf_errno, fileSystem @*/
|
|
/*@modifies _elf_errno, fileSystem @*/
|
|
{
|
|
char *buf;
|
|
off_t err;
|
|
|
|
elf_assert(len);
|
|
#if HAVE_FTRUNCATE
|
|
ftruncate(fd, 0);
|
|
#endif /* HAVE_FTRUNCATE */
|
|
#if HAVE_MMAP
|
|
/*
|
|
* Make sure the file is (at least) len bytes long
|
|
*/
|
|
#if HAVE_FTRUNCATE
|
|
if (ftruncate(fd, len)) {
|
|
#else /* HAVE_FTRUNCATE */
|
|
{
|
|
#endif /* HAVE_FTRUNCATE */
|
|
if (lseek(fd, (off_t)len - 1, SEEK_SET) != (off_t)len - 1) {
|
|
seterr(ERROR_IO_SEEK);
|
|
return -1;
|
|
}
|
|
if (write(fd, "", 1) != 1) {
|
|
seterr(ERROR_IO_WRITE);
|
|
return -1;
|
|
}
|
|
}
|
|
/*@-boundswrite@*/ /* FIX: mmap ensures annotation */
|
|
buf = (void*)mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
|
if (buf != (char*)-1) {
|
|
/*@-nullpass@*/
|
|
if ((char)_elf_fill && !(elf->e_elf_flags & ELF_F_LAYOUT)) {
|
|
memset(buf, _elf_fill, len);
|
|
}
|
|
err = _elf_write(elf, buf, len);
|
|
munmap(buf, len);
|
|
/*@=nullpass@*/
|
|
return err;
|
|
}
|
|
/*@=boundswrite@*/
|
|
#endif /* HAVE_MMAP */
|
|
if (!(buf = (char*)malloc(len))) {
|
|
seterr(ERROR_MEM_OUTBUF);
|
|
return -1;
|
|
}
|
|
memset(buf, _elf_fill, len);
|
|
err = _elf_write(elf, buf, len);
|
|
if (err != -1 && (size_t)err == len) {
|
|
if (lseek(fd, (off_t)0, SEEK_SET)) {
|
|
seterr(ERROR_IO_SEEK);
|
|
err = -1;
|
|
}
|
|
else if (write(fd, buf, len) != len) {
|
|
seterr(ERROR_IO_WRITE);
|
|
err = -1;
|
|
}
|
|
}
|
|
free(buf);
|
|
return err;
|
|
}
|
|
|
|
off_t
|
|
elf_update(Elf *elf, Elf_Cmd cmd) {
|
|
unsigned flag;
|
|
off_t len;
|
|
|
|
if (!elf) {
|
|
return -1;
|
|
}
|
|
elf_assert(elf->e_magic == ELF_MAGIC);
|
|
if (cmd == ELF_C_WRITE) {
|
|
if (!elf->e_writable) {
|
|
seterr(ERROR_RDONLY);
|
|
return -1;
|
|
}
|
|
if (elf->e_disabled) {
|
|
seterr(ERROR_FDDISABLED);
|
|
return -1;
|
|
}
|
|
}
|
|
else if (cmd != ELF_C_NULL) {
|
|
seterr(ERROR_INVALID_CMD);
|
|
return -1;
|
|
}
|
|
|
|
if (!elf->e_ehdr) {
|
|
seterr(ERROR_NOEHDR);
|
|
}
|
|
else if (elf->e_kind != ELF_K_ELF) {
|
|
seterr(ERROR_NOTELF);
|
|
}
|
|
else if (elf->e_class == ELFCLASS32) {
|
|
len = _elf32_layout(elf, &flag);
|
|
if (len != -1 && cmd == ELF_C_WRITE && (flag & ELF_F_DIRTY)) {
|
|
len = _elf_output(elf, elf->e_fd, (size_t)len, _elf32_write);
|
|
}
|
|
return len;
|
|
}
|
|
#if __LIBELF64
|
|
else if (elf->e_class == ELFCLASS64) {
|
|
len = _elf64_layout(elf, &flag);
|
|
if (len != -1 && cmd == ELF_C_WRITE && (flag & ELF_F_DIRTY)) {
|
|
len = _elf_output(elf, elf->e_fd, (size_t)len, _elf64_write);
|
|
}
|
|
return len;
|
|
}
|
|
#endif /* __LIBELF64 */
|
|
else if (valid_class(elf->e_class)) {
|
|
seterr(ERROR_UNIMPLEMENTED);
|
|
}
|
|
else {
|
|
seterr(ERROR_UNKNOWN_CLASS);
|
|
}
|
|
return -1;
|
|
}
|