elf.c revision 386e9c9ebfe4116f62e7a0950acd30564fc60125
/*
* 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
* 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) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* Copyright (c) 2013, Joyent, Inc. All rights reserved.
*/
#include <sys/sysmacros.h>
#include <sys/pathname.h>
#include <sys/systeminfo.h>
#include <sys/shm_impl.h>
#include <sys/archsystm.h>
#include <sys/fasttrap.h>
#include "elf_impl.h"
extern int at_flags;
#define ORIGIN_STR "ORIGIN"
#define ORIGIN_STR_SIZE 6
ssize_t *);
typedef enum {
} shstrtype_t;
static const char *shstrtab_data[] = {
".SUNW_ctf",
".symtab",
".dynsym",
".strtab",
".dynstr",
".shstrtab"
};
typedef struct shstrtab {
int sst_cur;
} shstrtab_t;
static void
{
s->sst_cur = 1;
}
static int
{
int ret;
return (ret);
return (ret);
}
static size_t
shstrtab_size(const shstrtab_t *s)
{
return (s->sst_cur);
}
static void
{
int i, ndx;
*buf = '\0';
for (i = 0; i < STR_NUM; i++) {
}
}
static int
{
/*
* See the comment in fasttrap.h for information on how to safely
* update this program header.
*/
return (-1);
return (0);
}
/*
* Map in the executable pointed to by vp. Returns 0 on success.
*/
int
{
int error = 0;
long execsz;
return (error);
}
&nphdrs)) != 0 ||
&phdrsize)) != 0) {
return (error);
}
return (ENOEXEC);
}
return (error);
}
/*
* Inform our caller if the executable needs an interpreter.
*/
/*
* If this is a statically linked executable, voffset should indicate
* the address of the executable itself (it normally holds the address
* of the interpreter).
*/
} else {
}
return (error);
}
/*ARGSUSED*/
int
int brand_action)
{
int error;
int fd = -1;
int postfixsize = 0;
int i, hsize;
int hasu = 0;
int hasauxv = 0;
int hasdy = 0;
int branded = 0;
struct bigwad {
char dl_name[MAXPATHLEN];
char pathbuf[MAXPATHLEN];
} *bigwad; /* kmem_alloc this behemoth so we don't blow stack */
char *dlnp;
char *pathbufp;
/*
* Obtain ELF and program header information.
*/
&nphdrs)) != 0 ||
&phdrsize)) != 0)
goto out;
/*
* Prevent executing an ELF file that has no entry point.
*/
goto bad;
}
/*
* Put data model that we're exec-ing to into the args passed to
* exec_args(), so it will know what it is copying to on new stack.
* Now that we know whether we are exec-ing a 32-bit or 64-bit
* executable, we can set execsz with the appropriate NCARGS.
*/
#ifdef _LP64
} else {
#endif
}
#else /* _LP64 */
#endif /* _LP64 */
/*
* We delay invoking the brand callback until we've figured out
* what kind of elf binary we're trying to run, 32-bit or 64-bit.
* We do this because now the brand library can just check
* args->to_model to see if the target is 32-bit or 64-bit without
* having do duplicate all the code above.
*/
if ((level < 2) &&
goto out;
}
/*
* Determine aux size now so that stack can be built
* in one shot (except actual copyout of aux image),
* determine any non-default stack protections,
* and still have this code be machine independent.
*/
for (i = nphdrs; i > 0; i--) {
case PT_INTERP:
break;
case PT_PHDR:
hasu = 1;
break;
case PT_SUNWSTACK:
break;
case PT_LOAD:
break;
case PT_SUNWCAP:
break;
}
}
hasauxv = 1;
}
/* Copy BSS permissions to args->dat_prot */
}
/*
* If a auxvector will be required - reserve the space for
* it now. This may be increased by exec_args if there are
* ISA-specific types (included in __KERN_NAUXV_IMPL).
*/
if (hasauxv) {
/*
* If a AUX vector is being built - the base AUX
* entries are:
*
* AT_BASE
* AT_FLAGS
* AT_PAGESZ
* AT_SUN_AUXFLAGS
* AT_SUN_HWCAP
* AT_SUN_HWCAP2
* AT_SUN_PLATFORM (added in stk_copyout)
* AT_SUN_EXECNAME (added in stk_copyout)
* AT_NULL
*
* total == 9
*/
/*
* Has PT_INTERP & PT_PHDR - the auxvectors that
* will be built are:
*
* AT_PHDR
* AT_PHENT
* AT_PHNUM
* AT_ENTRY
* AT_LDDATA
*
* total = 5
*/
} else if (hasdy) {
/*
* Has PT_INTERP but no PT_PHDR
*
* AT_EXECFD
* AT_LDDATA
*
* total = 2
*/
} else {
}
} else {
}
/*
* If this binary is using an emulator, we need to add an
* AT_SUN_EMULATOR aux entry.
*/
branded = 1;
/*
* We will be adding 4 entries to the aux vectors. One for
* the the brandname and 3 for the brand specific aux vectors.
*/
}
uprintf("%s: Cannot read capabilities section\n",
goto out;
}
break;
}
}
}
/*
* Move args to the user's stack.
* This can fill in the AT_SUN_PLATFORM and AT_SUN_EXECNAME aux entries.
*/
if (error == -1) {
goto bad;
}
goto out;
}
/* we're single threaded after this point */
/*
* If this is an ET_DYN executable (shared object),
* determine its memory size so that mapelfexec() can load it.
*/
else
len = 0;
goto bad;
goto bad;
goto bad;
}
char *p;
goto bad;
/*
* Read in "interpreter" pathname.
*/
uprintf("%s: Cannot obtain interpreter pathname\n",
goto bad;
}
goto bad;
/*
* Search for '$ORIGIN' token in interpreter path.
* If found, expand it.
*/
char *_ptr;
continue;
curlen = 0;
if (len) {
}
break;
} else {
/*
* executable is a basename found in the
* current directory. So - just substitue
* '.' for ORIGIN.
*/
curlen++;
}
p += ORIGIN_STR_SIZE;
break;
}
/*
* Just in case /usr is not mounted, change it now.
*/
dlnp += 4;
/* new kernel, old user-level */
}
if (error) {
goto bad;
}
/*
* Setup the "aux" vector.
*/
if (uphdr) {
/* don't use the first page */
} else {
}
} else {
goto bad;
}
}
goto bad;
}
/*
* Now obtain the ELF header along with the entire program
* header contained in "nvp".
*/
&phdrsize)) != 0) {
goto bad;
}
/*
* Determine memory size of the "interpreter's" loadable
* sections. This size is then used to obtain the virtual
* address of a hole, in the user's address space, large
* enough to map the "interpreter".
*/
goto bad;
}
goto bad;
}
/*
* We use the DTrace program header to initialize the
* architecture-specific user per-LWP location. The dtrace
* fasttrap provider requires ready access to per-LWP scratch
* space. We assume that there is only one such program header
* in the interpreter.
*/
goto bad;
}
}
if (hasauxv) {
int auxf = AF_SUN_HWCAPVERIFY;
/*
* Note: AT_SUN_PLATFORM and AT_SUN_EXECNAME were filled in via
* exec_args()
*/
/*
* Linker flags. (security)
* p_flag not yet set at this time.
* We rely on gexec() to provide us with the information.
* If the application is set-uid but this is not reflected
* don't treat this as a set-uid exec. So we care about
* the EXECSETID_UGIDS flag but not the ...SETID flag.
*/
if ((setid &= ~EXECSETID_SETID) != 0)
auxf |= AF_SUN_SETUGID;
/*
* If we're running a native process from within a branded
* zone under pfexec then we clear the AF_SUN_SETUGID flag so
* that the native ld.so.1 is able to link with the native
* libraries instead of using the brand libraries that are
* installed in the zone. We only do this for processes
* which we trust because we see they are already running
* under pfexec (where uid != euid). This prevents a
* malicious user within the zone from crafting a wrapper to
* run native suid commands with unsecure libraries interposed.
*/
(setid &= ~EXECSETID_SETID) != 0))
auxf &= ~AF_SUN_SETUGID;
/*
* Record the user addr of the auxflags aux vector entry
* since brands may optionally want to manipulate this field.
*/
/*
* Hardware capability flag word (performance hints)
* Used for choosing faster library routines.
* (Potentially different between 32-bit and 64-bit ABIs)
*/
#if defined(_LP64)
} else {
}
#else
#endif
if (branded) {
/*
* Reserve space for the brand-private aux vectors,
* and record the user addr of that space.
*/
args->auxp_brand =
}
/*
* We make assumptions above when we determine how many aux
* vector entries we will be adding. However, if we have an
* invalid elf file, it is possible that mapelfexec might
* behave differently (but not return an error), in which case
* the number of aux entries we actually add will be different.
* We detect that now and error out.
*/
goto bad;
}
}
/*
* For the 64-bit kernel, the limit is big enough that rounding it up
* to a page can overflow the 64-bit limit, so we check for btopr()
* overflowing here by comparing it with the unrounded limit in pages.
* If it hasn't overflowed, compare the exec size with the rounded up
* limit in pages. Otherwise, just compare with the unrounded limit.
*/
mutex_enter(&p->p_lock);
RCA_SAFE);
mutex_exit(&p->p_lock);
goto bad;
}
if (postfixsize) {
int num_auxv;
/*
* Copy the aux vector to the user stack.
*/
if (error)
goto bad;
/*
* Copy auxv to the process's user structure for use by /proc.
* If this is a branded process, the brand's exec routine will
* copy it's private entries to the user structure later. It
* relies on the fact that the blank entries are at the end.
*/
for (i = 0; i < num_auxv; i++) {
}
}
/*
* Pass back the starting address so we can set the program counter.
*/
if (!uphdr) {
/*
* If we are executing a shared library which doesn't
* have a interpreter (probably ld.so.1) then
* we don't set the brkbase now. Instead we
* delay it's setting until the first call
* initialize brkbase to the tail of the executable it
* loads (which is where it needs to be).
*/
} else {
}
}
goto out;
bad:
if (error == 0)
out:
return (error);
}
/*
* Compute the memory size requirement for the ELF file.
*/
static size_t
{
int first = 1;
int i;
for (i = nphdrs; i > 0; i--) {
if (first) {
first = 0;
} else {
}
/*
* save the address of the first data segment
* of a object - used for the AT_SUNW_LDDATA
* aux entry.
*/
dfirst = 0;
}
}
}
return (len);
}
/*
* Read in the ELF header and program header table.
* SUSV3 requires:
* ENOEXEC File format is not recognized
* EINVAL Format recognized but execution not supported
*/
static int
int *nphdrs)
{
int error;
/*
* We got here by the first two bytes in ident,
* now read the entire ELF header.
*/
return (error);
/*
* Since a separate version is compiled for handling 32-bit and
* 64-bit ELF executables on a 64-bit kernel, the 64-bit version
* doesn't need to be able to deal with 32-bit ELF files.
*/
if (resid != 0 ||
return (ENOEXEC);
#if defined(_ILP32) || defined(_ELF32_COMPAT)
#else
#endif
return (EINVAL);
/*
* If e_shnum, e_shstrndx, or e_phnum is its sentinel value, we need
* to read in the section header at index zero to acces the true
* values for those fields.
*/
return (EINVAL);
return (error);
if (*nshdrs == 0)
if (*shstrndx == SHN_XINDEX)
}
return (0);
}
#ifdef _ELF32_COMPAT
extern size_t elf_nphdr_max;
#else
#endif
static int
{
int err;
/*
* Since we're going to be using e_phentsize to iterate down the
* array of program headers, it must be 8-byte aligned or else
* a we might cause a misaligned access. We use all members through
* p_flags on 32-bit ELF files and p_memsz on 64-bit ELF files so
* e_phentsize must be at least large enough to include those
* members.
*/
#if !defined(_LP64) || defined(_ELF32_COMPAT)
#else
#endif
return (EINVAL);
return (ENOMEM);
} else {
}
return (err);
}
return (0);
}
#ifdef _ELF32_COMPAT
extern size_t elf_nshdr_max;
extern size_t elf_shstrtab_max;
#else
#endif
static int
{
int err;
/*
* Since we're going to be using e_shentsize to iterate down the
* array of section headers, it must be 8-byte aligned or else
* a we might cause a misaligned access. We use all members through
* sh_entsize (on both 32- and 64-bit ELF files) so e_shentsize
* must be at least large enough to include that member. The index
* of the string table section must also be valid.
*/
return (EINVAL);
return (ENOMEM);
} else {
}
return (err);
}
/*
* Pull the section string table out of the vnode; fail if the size
* is zero.
*/
return (EINVAL);
}
if (*shstrsizep > elf_shstrtab_max) {
KM_NOSLEEP)) == NULL) {
return (ENOMEM);
}
} else {
}
return (err);
}
/*
* Make sure the strtab is null-terminated to make sure we
* don't run off the end of the table.
*/
return (0);
}
static int
int nphdrs,
long *execsz,
{
int ptload = 0;
int page;
extern int use_brk_lpg;
/*
* Obtain the virtual address of a hole in the
* address space to map the "interpreter".
*/
return (ENOMEM);
/*
* Calculate the minimum vaddr so it can be subtracted out.
* According to the ELF specification, since PT_LOAD sections
* must be sorted by increasing p_vaddr values, this is
* guaranteed to be the first PT_LOAD section.
*/
for (i = nphdrs; i > 0; i--) {
break;
}
}
} else {
*voffset = 0;
}
for (i = nphdrs; i > 0; i--) {
case PT_LOAD:
return (0);
ptload = 1;
prot |= PROT_WRITE;
/*
* Keep track of the segment with the lowest starting
* address.
*/
page = 1;
} else {
page = 0;
}
/*
* Set the heap pagesize for OOB when the bss size
* is known and use_brk_lpg is not 0.
*/
(prot & PROT_WRITE)) {
}
}
(prot & PROT_WRITE)) {
goto bad;
*brksize = extra_zfodsz;
} else {
goto bad;
}
}
}
break;
case PT_INTERP:
if (ptload)
goto bad;
break;
case PT_SHLIB:
break;
case PT_PHDR:
if (ptload)
goto bad;
break;
case PT_NULL:
case PT_DYNAMIC:
case PT_NOTE:
break;
case PT_SUNWDTRACE:
break;
default:
break;
}
}
}
return (0);
bad:
if (error == 0)
return (error);
}
int
{
int error;
/*
* The System V ABI states that n_namesz must be the length of the
* string that follows the Nhdr structure including the terminating
* null. The ABI also specifies that sufficient padding should be
* included so that the description that follows the name string
* begins on a 4- or 8-byte boundary for 32- and 64-bit binaries
* respectively. However, since this change was not made correctly
* at the time of the 64-bit port, both 32- and 64-bit binaries
* descriptions are only guaranteed to begin on a 4-byte boundary.
*/
return (error);
return (error);
return (0);
}
/*
* Copy the section data from one vnode to the section of another vnode.
*/
static void
{
while (n != 0) {
return;
}
}
}
#ifdef _ELF32_COMPAT
extern size_t elf_datasz_max;
#else
#endif
/*
* This function processes mappings that correspond to load objects to
* examine their respective sections for elfcore(). It's called once with
* v set to NULL to count the number of sections that we're going to need
* and then again with v set to some allocated buffer that we fill in with
* all the section data.
*/
static int
{
int i, j;
int error = 0;
if (v != NULL)
i = 1;
char *shstrbase;
const char *name;
int ctf_ndx = 0;
int symtab_ndx = 0;
/*
* Since we're just looking for text segments of load
* objects, we only care about the protection bits; we don't
* care about the actual size of the segment so we use the
* reserved size. If the segment's size is zero, there's
* something fishy going on so we ignore this segment.
*/
continue;
/*
* Skip this segment unless the protection bits look like
* what we'd expect for a text segment.
*/
continue;
&nphdrs) != 0 ||
continue;
continue;
if ((content & CC_CONTENT_CTF) == 0 ||
ctf_ndx != 0)
continue;
}
KM_SLEEP);
}
STR_CTF);
v[i].sh_type = SHT_PROGBITS;
v[i].sh_addralign = 4;
v[i].sh_addralign);
v[i].sh_link = 0;
SHT_SYMTAB &&
symtab_ndx != 0) {
v[i].sh_link =
} else {
v[i].sh_link = i + 1;
}
rlimit);
}
ctf_ndx = i++;
/*
* We've already dumped the symtab.
*/
symtab_ndx != 0)
continue;
shstrtab_data[STR_SYMTAB]) == 0) {
if ((content & CC_CONTENT_SYMTAB) == 0 ||
symtab != 0)
continue;
}
continue;
continue;
sz <= elf_datasz_max) {
KM_SLEEP);
}
v[i].sh_name = shstrtab_ndx(
&shstrtab, STR_DYNSYM);
&shstrtab, STR_DYNSTR);
} else {
v[i].sh_name = shstrtab_ndx(
&shstrtab, STR_SYMTAB);
&shstrtab, STR_STRTAB);
}
v[i].sh_addr == 0)
v[i].sh_addr +=
v[i].sh_addralign =
v[i].sh_addralign);
v[i].sh_link = i + 1;
rlimit);
v[i + 1].sh_addr == 0)
v[i + 1].sh_addr +=
v[i + 1].sh_addralign =
v[i + 1].sh_addralign);
rlimit);
}
symtab_ndx = i;
i += 2;
}
}
}
if (v == NULL) {
if (i == 1)
*nshdrsp = 0;
else
*nshdrsp = i + 1;
goto done;
}
if (i != nv - 1) {
"process %d; address space is changing", p->p_pid);
goto done;
}
v[i].sh_addralign = 1;
v[i].sh_flags = SHF_STRINGS;
v[i].sh_type = SHT_STRTAB;
KM_SLEEP);
}
goto done;
done:
return (error);
}
int
{
int overflow = 0;
union {
} *bigwad;
Phdr *v;
int ntries = 0;
top:
/*
* Make sure we have everything we need (registers, etc.).
* All other lwps have already stopped and are in an orderly state.
*/
prstop(0, 0);
/*
* Count the number of section headers we're going to need.
*/
nshdrs = 0;
}
/*
* The core file contents may required zero section headers, but if
* we overflow the 16 bits allotted to the program header count in
* the ELF header, we'll need that program header at index zero.
*/
nshdrs = 1;
#if !defined(_LP64) || defined(_ELF32_COMPAT)
#if defined(__sparc)
#else
#error "no recognized machine type is defined"
#endif
#else /* !defined(_LP64) || defined(_ELF32_COMPAT) */
#if defined(__sparc)
#else
#error "no recognized 64-bit machine type is defined"
#endif
#endif /* !defined(_LP64) || defined(_ELF32_COMPAT) */
/*
* If the count of program headers or section headers or the index
* of the section string table can't fit in the mere 16 bits
* shortsightedly allotted to them in the ELF header, we use the
* extended formats and put the real values in the section header
* as index 0.
*/
else
if (nshdrs > 0) {
if (nshdrs >= SHN_LORESERVE)
else
else
}
goto done;
setup_old_note_header(&v[0], p);
setup_note_header(&v[1], p);
mutex_enter(&p->p_lock);
mutex_exit(&p->p_lock);
i = 2;
extern struct seg_ops segspt_shmops;
int type;
continue;
if (i == nphdrs) {
overflow++;
continue;
}
if (prot & PROT_WRITE)
/*
* Figure out which mappings to include in the core.
*/
if (!(content & CC_CONTENT_STACK))
goto exclude;
if (!(content & CC_CONTENT_HEAP))
goto exclude;
if (type & MAP_NORESERVE) {
if (!(content & CC_CONTENT_DISM))
goto exclude;
} else {
if (!(content & CC_CONTENT_ISM))
goto exclude;
}
goto exclude;
} else if (type & MAP_SHARED) {
if (!(content & CC_CONTENT_SHM))
goto exclude;
if (!(content & CC_CONTENT_SHANON))
goto exclude;
} else {
if (!(content & CC_CONTENT_SHFILE))
goto exclude;
}
if (!(content & CC_CONTENT_ANON))
goto exclude;
if (!(content & CC_CONTENT_TEXT))
goto exclude;
if (!(content & CC_CONTENT_RODATA))
goto exclude;
} else {
if (!(content & CC_CONTENT_DATA))
goto exclude;
}
i++;
}
}
if (ntries++ == 0) {
overflow = 0;
goto top;
}
"process %d; address space is changing", p->p_pid);
goto done;
}
goto done;
credp)) != 0)
goto done;
goto done;
for (i = 2; i < nphdrs; i++) {
sigqueue_t *sq;
int sig, j;
if (v[i].p_filesz == 0)
continue;
/*
* If dumping out this segment fails, rather than failing
* the core dump entirely, we reset the size of the mapping
* to zero to indicate that the data is absent from the core
* file and or in the PF_SUNW_FAILURE flag to differentiate
* this from mappings that were excluded due to the core file
* content settings.
*/
continue;
}
/*
* We failed due to something other than a signal.
* Since the space reserved for the segment is now
* unused, we stash the errno in the first four
* bytes. This undocumented interface will let us
* understand the nature of the failure.
*/
v[i].p_filesz = 0;
v[i].p_flags |= PF_SUNW_FAILURE;
poffset + sizeof (v[i]) * i, &v[i], sizeof (v[i]),
goto done;
continue;
}
/*
* We took a signal. We want to abort the dump entirely, but
* we also want to indicate what failed and why. We therefore
* use the space reserved for the first failing segment to
* write our error (which, for purposes of compatability with
* older core dump readers, we set to EINTR) followed by any
* siginfo associated with the signal.
*/
} else {
}
#if (defined(_SYSCALL32_IMPL) || defined(_LP64))
/*
* If this is a 32-bit process, we need to translate from the
* native siginfo to the 32-bit variant. (Core readers must
* always have the same data model as their target or must
* be aware of -- and compensate for -- data model differences.)
*/
}
#endif
/*
* For the segment on which we took the signal, indicate that
* its data now refers to a siginfo.
*/
v[i].p_filesz = 0;
/*
* And for every other segment, indicate that its absence
* is due to a signal.
*/
for (j = i + 1; j < nphdrs; j++) {
v[j].p_filesz = 0;
}
/*
* Finally, write out our modified program headers.
*/
poffset + sizeof (v[i]) * i, &v[i],
goto done;
break;
}
if (nshdrs > 0) {
if (nshdrs >= SHN_LORESERVE)
if (nshdrs > 1) {
NULL)) != 0) {
goto done;
}
}
goto done;
}
done:
return (error);
}
#ifndef _ELF32_COMPAT
#ifdef _LP64
#else /* _LP64 */
#endif /* _LP64 */
0,
5,
};
};
#ifdef _LP64
int brand_action);
0,
5,
};
static struct modlexec modlexec32 = {
};
#endif /* _LP64 */
static struct modlinkage modlinkage = {
(void *)&modlexec,
#ifdef _LP64
(void *)&modlexec32,
#endif /* _LP64 */
};
int
_init(void)
{
return (mod_install(&modlinkage));
}
int
_fini(void)
{
return (mod_remove(&modlinkage));
}
int
{
}
#endif /* !_ELF32_COMPAT */