/* Map in a shared object's segments from the file.
Copyright (C) 1995-2005, 2006 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
#include <elf.h>
#include <errno.h>
#include <fcntl.h>
#include <libintl.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ldsodefs.h>
#include <bits/wordsize.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "dynamic-link.h"
#include <abi-tag.h>
#include <stackinfo.h>
#include <caller.h>
#include <sysdep.h>
#include <dl-dst.h>
/* On some systems, no flag bits are given to specify file mapping. */
#ifndef MAP_FILE
# define MAP_FILE 0
#endif
/* The right way to map in the shared library files is MAP_COPY, which
makes a virtual copy of the data at the time of the mmap call; this
guarantees the mapped pages will be consistent even if the file is
overwritten. Some losing VM systems like Linux's lack MAP_COPY. All we
get is MAP_PRIVATE, which copies each page when it is modified; this
means if the file is overwritten, we may at some point get some pages
from the new version after starting with pages from the old version.
To make up for the lack and avoid the overwriting problem,
what Linux does have is MAP_DENYWRITE. This prevents anyone
from modifying the file while we have it mapped. */
#ifndef MAP_COPY
# ifdef MAP_DENYWRITE
# define MAP_COPY (MAP_PRIVATE | MAP_DENYWRITE)
# else
# define MAP_COPY MAP_PRIVATE
# endif
#endif
/* Some systems link their relocatable objects for another base address
than 0. We want to know the base address for these such that we can
subtract this address from the segment addresses during mapping.
This results in a more efficient address space usage. Defaults to
zero for almost all systems. */
#ifndef MAP_BASE_ADDR
# define MAP_BASE_ADDR(l) 0
#endif
#include <endian.h>
#if BYTE_ORDER == BIG_ENDIAN
# define byteorder ELFDATA2MSB
#elif BYTE_ORDER == LITTLE_ENDIAN
# define byteorder ELFDATA2LSB
#else
# error "Unknown BYTE_ORDER " BYTE_ORDER
# define byteorder ELFDATANONE
#endif
#define STRING(x) __STRING (x)
#ifdef MAP_ANON
/* The fd is not examined when using MAP_ANON. */
# define ANONFD -1
#else
int _dl_zerofd = -1;
# define ANONFD _dl_zerofd
#endif
/* Handle situations where we have a preferred location in memory for
the shared objects. */
#ifdef ELF_PREFERRED_ADDRESS_DATA
ELF_PREFERRED_ADDRESS_DATA;
#endif
#ifndef ELF_PREFERRED_ADDRESS
# define ELF_PREFERRED_ADDRESS(loader, maplength, mapstartpref) (mapstartpref)
#endif
#ifndef ELF_FIXED_ADDRESS
# define ELF_FIXED_ADDRESS(loader, mapstart) ((void) 0)
#endif
int __stack_prot attribute_hidden attribute_relro
#if _STACK_GROWS_DOWN && defined PROT_GROWSDOWN
= PROT_GROWSDOWN;
#elif _STACK_GROWS_UP && defined PROT_GROWSUP
= PROT_GROWSUP;
#else
= 0;
#endif
/* Type for the buffer we put the ELF header and hopefully the program
header. This buffer does not really have to be too large. In most
cases the program header follows the ELF header directly. If this
is not the case all bets are off and we can make the header
arbitrarily large and still won't get it read. This means the only
question is how large are the ELF and program header combined. The
ELF header 32-bit files is 52 bytes long and in 64-bit files is 64
bytes long. Each program header entry is again 32 and 56 bytes
long respectively. I.e., even with a file which has 10 program
header entries we only have to read 372B/624B respectively. Add to
this a bit of margin for program notes and reading 512B and 832B
for 32-bit and 64-bit files respecitvely is enough. If this
heuristic should really fail for some file the code in
`_dl_map_object_from_fd' knows how to recover. */
struct filebuf
{
ssize_t len;
#if __WORDSIZE == 32
# define FILEBUF_SIZE 512
#else
# define FILEBUF_SIZE 832
#endif
char buf[FILEBUF_SIZE] __attribute__ ((aligned (__alignof (ElfW(Ehdr)))));
};
/* This is the decomposed LD_LIBRARY_PATH search path. */
static struct r_search_path_struct env_path_list attribute_relro;
/* List of the hardware capabilities we might end up using. */
static const struct r_strlenpair *capstr attribute_relro;
static size_t ncapstr attribute_relro;
static size_t max_capstrlen attribute_relro;
/* Get the generated information about the trusted directories. */
#include "trusted-dirs.h"
static const char system_dirs[] = SYSTEM_DIRS;
static const size_t system_dirs_len[] =
{
SYSTEM_DIRS_LEN
};
#define nsystem_dirs_len \
(sizeof (system_dirs_len) / sizeof (system_dirs_len[0]))
/* Local version of `strdup' function. */
static inline char *
local_strdup (const char *s)
{
size_t len = strlen (s) + 1;
void *new = malloc (len);
if (new == NULL)
return NULL;
return (char *) memcpy (new, s, len);
}
static size_t
is_dst (const char *start, const char *name, const char *str,
int is_path, int secure)
{
size_t len;
bool is_curly = false;
if (name[0] == '{')
{
is_curly = true;
++name;
}
len = 0;
while (name[len] == str[len] && name[len] != '\0')
++len;
if (is_curly)
{
if (name[len] != '}')
return 0;
/* Point again at the beginning of the name. */
--name;
/* Skip over closing curly brace and adjust for the --name. */
len += 2;
}
else if (name[len] != '\0' && name[len] != '/'
&& (!is_path || name[len] != ':'))
return 0;
if (__builtin_expect (secure, 0)
&& ((name[len] != '\0' && (!is_path || name[len] != ':'))
|| (name != start + 1 && (!is_path || name[-2] != ':'))))
return 0;
return len;
}
size_t
_dl_dst_count (const char *name, int is_path)
{
const char *const start = name;
size_t cnt = 0;
do
{
size_t len;
/* $ORIGIN is not expanded for SUID/GUID programs (except if it
is $ORIGIN alone) and it must always appear first in path. */
++name;
if ((len = is_dst (start, name, "ORIGIN", is_path,
INTUSE(__libc_enable_secure))) != 0
|| (len = is_dst (start, name, "PLATFORM", is_path, 0)) != 0
|| (len = is_dst (start, name, "LIB", is_path, 0)) != 0)
++cnt;
name = strchr (name + len, '$');
}
while (name != NULL);
return cnt;
}
char *
_dl_dst_substitute (struct link_map *l, const char *name, char *result,
int is_path)
{
const char *const start = name;
char *last_elem, *wp;
/* Now fill the result path. While copying over the string we keep
track of the start of the last path element. When we come accross
a DST we copy over the value or (if the value is not available)
leave the entire path element out. */
last_elem = wp = result;
do
{
if (__builtin_expect (*name == '$', 0))
{
const char *repl = NULL;
size_t len;
++name;
if ((len = is_dst (start, name, "ORIGIN", is_path,
INTUSE(__libc_enable_secure))) != 0)
{
#ifndef SHARED
if (l == NULL)
repl = _dl_get_origin ();
else
#endif
repl = l->l_origin;
}
else if ((len = is_dst (start, name, "PLATFORM", is_path, 0)) != 0)
repl = GLRO(dl_platform);
else if ((len = is_dst (start, name, "LIB", is_path, 0)) != 0)
repl = DL_DST_LIB;
if (repl != NULL && repl != (const char *) -1)
{
wp = __stpcpy (wp, repl);
name += len;
}
else if (len > 1)
{
/* We cannot use this path element, the value of the
replacement is unknown. */
wp = last_elem;
name += len;
while (*name != '\0' && (!is_path || *name != ':'))
++name;
}
else
/* No DST we recognize. */
*wp++ = '$';
}
else
{
*wp++ = *name++;
if (is_path && *name == ':')
last_elem = wp;
}
}
while (*name != '\0');
*wp = '\0';
return result;
}
/* Return copy of argument with all recognized dynamic string tokens
($ORIGIN and $PLATFORM for now) replaced. On some platforms it
might not be possible to determine the path from which the object
belonging to the map is loaded. In this case the path element
containing $ORIGIN is left out. */
static char *
expand_dynamic_string_token (struct link_map *l, const char *s)
{
/* We make two runs over the string. First we determine how large the
resulting string is and then we copy it over. Since this is now
frequently executed operation we are looking here not for performance
but rather for code size. */
size_t cnt;
size_t total;
char *result;
/* Determine the number of DST elements. */
|