update.c revision 0bc07c75e71baa4cc26f90611864f7e60dcea093
/*
* 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 (c) 1988 AT&T
* All Rights Reserved
*
*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#if !defined(_ELF64)
#pragma weak elf_update = _elf_update
#endif
#include "syn.h"
#include <memory.h>
#include <malloc.h>
#include <limits.h>
#include <sgs.h>
#include "decl.h"
#include "msg.h"
/*
* This module is compiled twice, the second time having
* -D_ELF64 defined. The following set of macros, along
* with machelf.h, represent the differences between the
* two compilations. Be careful *not* to add any class-
* dependent code (anything that has elf32 or elf64 in the
* name) to this code without hiding it behind a switch-
* able macro like these.
*/
#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
#else /* ELF32 */
#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
#endif /* ELF64 */
/*
* Output file update
* These functions walk an Elf structure, update its information,
* and optionally write the output file. Because the application
* may control of the output file layout, two upd_... routines
* exist. They're similar but too different to merge cleanly.
*
* The library defines a "dirty" bit to force parts of the file
* to be written on update. These routines ignore the dirty bit
* and do everything. A minimal update routine might be useful
* someday.
*/
static size_t
_elf_upd_lib(Elf * elf)
{
NOTE(ASSUMING_PROTECTED(*elf))
Lword hi;
Lword hibit;
Elf_Scn * s;
register Xword sz;
Ehdr * eh = elf->ed_ehdr;
unsigned ver = eh->e_version;
register char *p = (char *)eh->e_ident;
size_t scncnt;
/*
* Ehdr and Phdr table go first
*/
p[EI_MAG0] = ELFMAG0;
p[EI_MAG1] = ELFMAG1;
p[EI_MAG2] = ELFMAG2;
p[EI_MAG3] = ELFMAG3;
p[EI_CLASS] = ELFCLASS;
/* LINTED */
p[EI_VERSION] = (Byte)ver;
hi = elf_fsize(ELF_T_EHDR, 1, ver);
/* LINTED */
eh->e_ehsize = (Half)hi;
if (eh->e_phnum != 0) {
/* LINTED */
eh->e_phentsize = (Half)elf_fsize(ELF_T_PHDR, 1, ver);
/* LINTED */
eh->e_phoff = (Off)hi;
hi += eh->e_phentsize * eh->e_phnum;
} else {
eh->e_phoff = 0;
eh->e_phentsize = 0;
}
/*
* Obtain the first section header. Typically, this section has NULL
* contents, however in the case of Extended ELF Sections this section
* is used to hold an alternative e_shnum, e_shstrndx and e_phnum.
* On initial allocation (see _elf_snode) the elements of this section
* would have been zeroed. The e_shnum is initialized later, after the
* section header count has been determined. The e_shstrndx and
* e_phnum may have already been initialized by the caller (for example,
* gelf_update_shdr() in mcs(1)).
*/
if ((s = elf->ed_hdscn) == 0) {
eh->e_shnum = 0;
scncnt = 0;
} else {
s = s->s_next;
scncnt = 1;
}
/*
* Loop through sections. Compute section size before changing hi.
* Allow null buffers for NOBITS.
*/
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 = (Xword)_elf_entsz(sh->sh_type, ver)) != 0)
/* LINTED */
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 += (Xword)fsz;
}
sh->sh_size = sz;
/*
* We want to take into account the offsets for NOBITS
* sections and let the "sh_offsets" point to where
* the section would 'conceptually' fit within
* the file (as required by the ABI).
*
* But - we must also make sure that the NOBITS does
* not take up any actual space in the file. We preserve
* the actual offset into the file in the 'hibit' variable.
* When we come to the first non-NOBITS section after a
* encountering a NOBITS section the hi counter is restored
* to its proper place in the file.
*/
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;
/* LINTED */
sh->sh_offset = (Off)hi;
hi += sz;
}
/*
* if last section was a 'NOBITS' section then we need to
* restore the 'hi' counter to point to the end of the last
* non 'NOBITS' section.
*/
if (hibit) {
hi = hibit;
hibit = 0;
}
/*
* Shdr table last
*/
if (scncnt != 0) {
if (hi % FSZ_LONG != 0)
hi += FSZ_LONG - hi % FSZ_LONG;
/* LINTED */
eh->e_shoff = (Off)hi;
/*
* If we are using 'extended sections' then the
* e_shnum is stored in the sh_size field of the
* first section header.
*
* NOTE: we set e_shnum to '0' because it's specified
* this way in the gABI, and in the hopes that
* this will cause less problems to unaware
* tools then if we'd set it to SHN_XINDEX (0xffff).
*/
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;
}
/* LINTED */
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;
}
#if !(defined(_LP64) && defined(_ELF64))
if (hi > INT_MAX) {
_elf_seterr(EFMT_FBIG, 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 Xword sz;
Ehdr * eh = elf->ed_ehdr;
unsigned ver = eh->e_version;
register char *p = (char *)eh->e_ident;
/*
* Ehdr and Phdr table go first
*/
p[EI_MAG0] = ELFMAG0;
p[EI_MAG1] = ELFMAG1;
p[EI_MAG2] = ELFMAG2;
p[EI_MAG3] = ELFMAG3;
p[EI_CLASS] = ELFCLASS;
/* LINTED */
p[EI_VERSION] = (Byte)ver;
hi = elf_fsize(ELF_T_EHDR, 1, ver);
/* LINTED */
eh->e_ehsize = (Half)hi;
/*
* If phnum is zero, phoff "should" be zero too,
* but the application is responsible for it.
* Allow a non-zero value here and update the
* hi water mark accordingly.
*/
if (eh->e_phnum != 0)
/* LINTED */
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;
/*
* Loop through sections, skipping index zero.
* Compute section size before changing hi.
* Allow null buffers for NOBITS.
*/
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 Xword 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 = (Xword)elf_fsize(d->db_data.d_type, 1,
ver)) == 0)
return (0);
j = (Xword)_elf_msize(d->db_data.d_type, ver);
fsz *= (Xword)(d->db_data.d_size / j);
d->db_osz = (size_t)fsz;
if ((sh->sh_type != SHT_NOBITS) &&
((j = (Xword)(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;
}
/*
* Shdr table last. Comment above for phnum/phoff applies here.
*/
if (eh->e_shnum != 0)
/* LINTED */
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;
#if !(defined(_LP64) && defined(_ELF64))
if (hi > INT_MAX) {
_elf_seterr(EFMT_FBIG, 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 = eh->e_ident[EI_DATA];
int byte;
/*
* Two issues can cause trouble for the output file.
* First, begin() with ELF_C_RDWR opens a file for both
* read and write. On the write update(), the library
* has to read everything it needs before truncating
* the file. Second, using mmap for both read and write
* is too tricky. Consequently, the library disables mmap
* on the read side. Using mmap for the output saves swap
* space, because that mapping is SHARED, not PRIVATE.
*
* If the file is write-only, there can be nothing of
* interest to bother with.
*
* The following reads the entire file, which might be
* more than necessary. Better safe than sorry.
*/
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;
/*
* If an error occurs below, a "dirty" bit may be cleared
* improperly. To save a second pass through the file,
* this code sets the dirty bit on the elf descriptor
* when an error happens, assuming that will "cover" any
* accidents.
*/
/*
* Hi is needed only when 'fill' is non-zero.
* Fill is non-zero only when the library
* calculates file/section/data buffer offsets.
* The lib guarantees they increase monotonically.
* That guarantees proper filling below.
*/
/*
* Ehdr first
*/
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;
/*
* Phdr table if one exists
*/
if (eh->e_phnum != 0) {
unsigned work;
/*
* Unlike other library data, phdr table is
* in the user version. Change src buffer
* version here, fix it after translation.
*/
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;
}
/*
* Loop through sections
*/
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;
/*
* Just "clean" DIRTY flag for "empty" sections. Even if
* NOBITS needs padding, the next thing in the
* file will provide it. (And if this NOBITS is
* the last thing in the file, no padding needed.)
*/
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;
}
/*
* Clear out the memory between the end of the last
* section and the begining of this section.
*/
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;
/*
* Clear out the memory between the end of the
* last update and the start of this data buffer.
*/
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;
/*
* Copy the translated bits out to the destination
* image.
*/
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;
}
/*
* Shdr table last
*/
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;
}
/*
* ELF_C_WRIMAGE signifyes that we build the memory image, but
* that we do not actually write it to disk. This is used
* by ld(1) to build up a full image of an elf file and then
* to process the file before it's actually written out to
* disk. This saves ld(1) the overhead of having to write
* the image out to disk twice.
*/
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);
}
/*
* The following is a private interface between the linkers (ld & ld.so.1)
* and libelf:
*
* elf_update(elf, ELF_C_WRIMAGE)
* This will cause full image representing the elf file
* described by the elf pointer to be built in memory. If the
* elf pointer has a valid file descriptor associated with it
* we will attempt to build the memory image from mmap()'ed
* storage. If the elf descriptor does not have a valid
* file descriptor (opened with elf_begin(0, ELF_C_IMAGE, 0))
* then the image will be allocated from dynamic memory (malloc()).
*
* elf_update() will return the size of the memory image built
* when sucessful.
*
* When a subsequent call to elf_update() with ELF_C_WRITE as
* the command is performed it will sync the image created
* by ELF_C_WRIMAGE to disk (if fd available) and
* free the memory allocated.
*/
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);
/*
* The size is still returned even
* though nothing is actually written
* out. This is just to be consistant
* with the rest of the interface.
*/
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);
}
/* FALLTHROUGH */
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);
}
/* LINTED */
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);
}
#ifndef _ELF64
/* class-independent, only needs to be compiled once */
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);
}
/*
* 4106312, 4106398, This is an ad-hoc means for the 32-bit
* Elf64 version of libld.so.3 to get around the limitation
* of a 32-bit d_off field. This is only intended to be
* used by libld to relocate symbols in large NOBITS sections.
*/
Elf64_Off
_elf_getxoff(Elf_Data * d)
{
return (((Dnode *)d)->db_xoff);
}
#endif /* !_ELF64 */