aboutsummaryrefslogtreecommitdiff
path: root/elf/dl-load.c
diff options
context:
space:
mode:
Diffstat (limited to 'elf/dl-load.c')
-rw-r--r--elf/dl-load.c193
1 files changed, 123 insertions, 70 deletions
diff --git a/elf/dl-load.c b/elf/dl-load.c
index bf3e419..2bc9af4 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -555,6 +555,33 @@ _dl_init_paths (const char *llp)
}
+#define LOSE(code, s) lose (code, fd, name, realname, l, s)
+static void volatile
+__attribute__ ((noreturn))
+lose (int code, int fd, const char *name, char *realname, struct link_map *l,
+ const char *msg)
+{
+ /* The use of `alloca' here looks ridiculous but it helps. The goal
+ is to avoid the function from being inlined. There is no official
+ way to do this so we use this trick. gcc never inlines functions
+ which use `alloca'. */
+ int *a = alloca (sizeof (int));
+ a[0] = fd;
+ (void) __close (a[0]);
+ if (l != NULL)
+ {
+ /* Remove the stillborn object from the list and free it. */
+ if (l->l_prev)
+ l->l_prev->l_next = l->l_next;
+ if (l->l_next)
+ l->l_next->l_prev = l->l_prev;
+ free (l);
+ }
+ free (realname);
+ _dl_signal_error (code, name, msg);
+}
+
+
/* Map in the shared object NAME, actually located in REALNAME, and already
opened on FD. */
@@ -565,25 +592,23 @@ struct link_map *
_dl_map_object_from_fd (const char *name, int fd, char *realname,
struct link_map *loader, int l_type)
{
+ /* This is the expected ELF header. */
+#define ELF32_CLASS ELFCLASS32
+#define ELF64_CLASS ELFCLASS64
+ static const unsigned char expected[EI_PAD] =
+ {
+ [EI_MAG0] = ELFMAG0,
+ [EI_MAG1] = ELFMAG1,
+ [EI_MAG2] = ELFMAG2,
+ [EI_MAG3] = ELFMAG3,
+ [EI_CLASS] = ELFW(CLASS),
+ [EI_DATA] = byteorder,
+ [EI_VERSION] = EV_CURRENT,
+ [EI_OSABI] = ELFOSABI_SYSV,
+ [EI_ABIVERSION] = 0
+ };
struct link_map *l = NULL;
-#define LOSE(s) lose (0, (s))
- void lose (int code, const char *msg)
- {
- (void) __close (fd);
- if (l)
- {
- /* Remove the stillborn object from the list and free it. */
- if (l->l_prev)
- l->l_prev->l_next = l->l_next;
- if (l->l_next)
- l->l_next->l_prev = l->l_prev;
- free (l);
- }
- free (realname);
- _dl_signal_error (code, name, msg);
- }
-
inline caddr_t map_segment (ElfW(Addr) mapstart, size_t len,
int prot, int fixed, off_t offset)
{
@@ -591,7 +616,7 @@ _dl_map_object_from_fd (const char *name, int fd, char *realname,
fixed|MAP_COPY|MAP_FILE,
fd, offset);
if (mapat == MAP_FAILED)
- lose (errno, "failed to map segment from shared object");
+ LOSE (errno, "failed to map segment from shared object");
return mapat;
}
@@ -605,8 +630,8 @@ _dl_map_object_from_fd (const char *name, int fd, char *realname,
struct stat st;
/* Get file information. */
- if (__fstat (fd, &st) < 0)
- lose (errno, "cannot stat shared object");
+ if (__fxstat (_STAT_VER, fd, &st) < 0)
+ LOSE (errno, "cannot stat shared object");
/* Look again to see if the real name matched another already loaded. */
for (l = _dl_loaded; l; l = l->l_next)
@@ -631,48 +656,52 @@ _dl_map_object_from_fd (const char *name, int fd, char *realname,
/* Read the header directly. */
readbuf = alloca (_dl_pagesize);
readlength = __libc_read (fd, readbuf, _dl_pagesize);
- if (readlength < (ssize_t) sizeof(*header))
- lose (errno, "cannot read file data");
+ if (readlength < (ssize_t) sizeof (*header))
+ LOSE (errno, "cannot read file data");
header = (void *) readbuf;
/* Check the header for basic validity. */
- if (*(Elf32_Word *) &header->e_ident !=
+ if (memcmp (header->e_ident, expected, EI_PAD) != 0)
+ {
+ /* Something is wrong. */
+ if (*(Elf32_Word *) &header->e_ident !=
#if BYTE_ORDER == LITTLE_ENDIAN
- ((ELFMAG0 << (EI_MAG0 * 8)) |
- (ELFMAG1 << (EI_MAG1 * 8)) |
- (ELFMAG2 << (EI_MAG2 * 8)) |
- (ELFMAG3 << (EI_MAG3 * 8)))
+ ((ELFMAG0 << (EI_MAG0 * 8)) |
+ (ELFMAG1 << (EI_MAG1 * 8)) |
+ (ELFMAG2 << (EI_MAG2 * 8)) |
+ (ELFMAG3 << (EI_MAG3 * 8)))
#else
- ((ELFMAG0 << (EI_MAG3 * 8)) |
- (ELFMAG1 << (EI_MAG2 * 8)) |
- (ELFMAG2 << (EI_MAG1 * 8)) |
- (ELFMAG3 << (EI_MAG0 * 8)))
+ ((ELFMAG0 << (EI_MAG3 * 8)) |
+ (ELFMAG1 << (EI_MAG2 * 8)) |
+ (ELFMAG2 << (EI_MAG1 * 8)) |
+ (ELFMAG3 << (EI_MAG0 * 8)))
#endif
- )
- LOSE ("invalid ELF header");
-#define ELF32_CLASS ELFCLASS32
-#define ELF64_CLASS ELFCLASS64
- if (header->e_ident[EI_CLASS] != ELFW(CLASS))
- LOSE ("ELF file class not " STRING(__ELF_NATIVE_CLASS) "-bit");
- if (header->e_ident[EI_DATA] != byteorder)
- LOSE ("ELF file data encoding not " byteorder_name);
- if (header->e_ident[EI_VERSION] != EV_CURRENT)
- LOSE ("ELF file version ident not " STRING(EV_CURRENT));
- /* XXX We should be able so set system specific versions which are
- allowed here. */
- if (header->e_ident[EI_OSABI] != ELFOSABI_SYSV)
- LOSE ("ELF file OS ABI not " STRING(ELFOSABI_SYSV));
- if (header->e_ident[EI_ABIVERSION] != 0)
- LOSE ("ELF file ABI version not 0");
+ )
+ LOSE (0, "invalid ELF header");
+ if (header->e_ident[EI_CLASS] != ELFW(CLASS))
+ LOSE (0, "ELF file class not " STRING(__ELF_NATIVE_CLASS) "-bit");
+ if (header->e_ident[EI_DATA] != byteorder)
+ LOSE (0, "ELF file data encoding not " byteorder_name);
+ if (header->e_ident[EI_VERSION] != EV_CURRENT)
+ LOSE (0, "ELF file version ident not " STRING(EV_CURRENT));
+ /* XXX We should be able so set system specific versions which are
+ allowed here. */
+ if (header->e_ident[EI_OSABI] != ELFOSABI_SYSV)
+ LOSE (0, "ELF file OS ABI not " STRING(ELFOSABI_SYSV));
+ if (header->e_ident[EI_ABIVERSION] != 0)
+ LOSE (0, "ELF file ABI version not 0");
+ LOSE (0, "internal error");
+ }
+
if (header->e_version != EV_CURRENT)
- LOSE ("ELF file version not " STRING(EV_CURRENT));
+ LOSE (0, "ELF file version not " STRING(EV_CURRENT));
if (! elf_machine_matches_host (header->e_machine))
- LOSE ("ELF file machine architecture not " ELF_MACHINE_NAME);
+ LOSE (0, "ELF file machine architecture not " ELF_MACHINE_NAME);
if (header->e_phentsize != sizeof (ElfW(Phdr)))
- LOSE ("ELF file's phentsize not the expected size");
+ LOSE (0, "ELF file's phentsize not the expected size");
#ifndef MAP_ANON
-#define MAP_ANON 0
+# define MAP_ANON 0
if (_dl_zerofd == -1)
{
_dl_zerofd = _dl_sysdep_open_zero_fill ();
@@ -687,7 +716,7 @@ _dl_map_object_from_fd (const char *name, int fd, char *realname,
/* Enter the new object in the list of loaded objects. */
l = _dl_new_object (realname, name, l_type, loader);
if (! l)
- lose (ENOMEM, "cannot create shared object descriptor");
+ LOSE (ENOMEM, "cannot create shared object descriptor");
l->l_opencount = 1;
/* Extract the remaining details we need from the ELF header
@@ -704,7 +733,7 @@ _dl_map_object_from_fd (const char *name, int fd, char *realname,
phdr = alloca (maplength);
__lseek (fd, SEEK_SET, header->e_phoff);
if (__libc_read (fd, (void *) phdr, maplength) != maplength)
- lose (errno, "cannot read file data");
+ LOSE (errno, "cannot read file data");
}
{
@@ -717,9 +746,10 @@ _dl_map_object_from_fd (const char *name, int fd, char *realname,
} loadcmds[l->l_phnum], *c;
size_t nloadcmds = 0;
+ /* The struct is initialized to zero so this is not necessary:
l->l_ld = 0;
l->l_phdr = 0;
- l->l_addr = 0;
+ l->l_addr = 0; */
for (ph = phdr; ph < &phdr[l->l_phnum]; ++ph)
switch (ph->p_type)
{
@@ -737,9 +767,9 @@ _dl_map_object_from_fd (const char *name, int fd, char *realname,
/* A load command tells us to map in part of the file.
We record the load commands and process them all later. */
if (ph->p_align % _dl_pagesize != 0)
- LOSE ("ELF load command alignment not page-aligned");
+ LOSE (0, "ELF load command alignment not page-aligned");
if ((ph->p_vaddr - ph->p_offset) % ph->p_align)
- LOSE ("ELF load command address/offset not properly aligned");
+ LOSE (0, "ELF load command address/offset not properly aligned");
{
struct loadcmd *c = &loadcmds[nloadcmds++];
c->mapstart = ph->p_vaddr & ~(ph->p_align - 1);
@@ -748,13 +778,34 @@ _dl_map_object_from_fd (const char *name, int fd, char *realname,
c->dataend = ph->p_vaddr + ph->p_filesz;
c->allocend = ph->p_vaddr + ph->p_memsz;
c->mapoff = ph->p_offset & ~(ph->p_align - 1);
- c->prot = 0;
- if (ph->p_flags & PF_R)
- c->prot |= PROT_READ;
- if (ph->p_flags & PF_W)
- c->prot |= PROT_WRITE;
- if (ph->p_flags & PF_X)
- c->prot |= PROT_EXEC;
+
+ /* Optimize a common case. */
+ if ((PF_R | PF_W | PF_X) == 7
+ && (PROT_READ | PROT_WRITE | PROT_EXEC) == 7)
+ {
+ static const unsigned char pf_to_prot[8] =
+ {
+ [0] = PROT_NONE,
+ [PF_R] = PROT_READ,
+ [PF_W] = PROT_WRITE,
+ [PF_R | PF_W] = PROT_READ | PROT_WRITE,
+ [PF_X] = PROT_EXEC,
+ [PF_R | PF_X] = PROT_READ | PROT_EXEC,
+ [PF_W | PF_X] = PROT_WRITE | PROT_EXEC,
+ [PF_R | PF_W | PF_X] = PROT_READ | PROT_WRITE | PROT_EXEC
+ };
+ c->prot = pf_to_prot[ph->p_flags & (PF_R | PF_W | PF_X)];
+ }
+ else
+ {
+ c->prot = 0;
+ if (ph->p_flags & PF_R)
+ c->prot |= PROT_READ;
+ if (ph->p_flags & PF_W)
+ c->prot |= PROT_WRITE;
+ if (ph->p_flags & PF_X)
+ c->prot |= PROT_EXEC;
+ }
break;
}
}
@@ -842,7 +893,7 @@ _dl_map_object_from_fd (const char *name, int fd, char *realname,
/* Dag nab it. */
if (__mprotect ((caddr_t) (zero & ~(_dl_pagesize - 1)),
_dl_pagesize, c->prot|PROT_WRITE) < 0)
- lose (errno, "cannot change memory protections");
+ LOSE (errno, "cannot change memory protections");
}
memset ((void *) zero, 0, zeropage - zero);
if ((c->prot & PROT_WRITE) == 0)
@@ -858,7 +909,7 @@ _dl_map_object_from_fd (const char *name, int fd, char *realname,
c->prot, MAP_ANON|MAP_PRIVATE|MAP_FIXED,
ANONFD, 0);
if (mapat == MAP_FAILED)
- lose (errno, "cannot map zero-fill pages");
+ LOSE (errno, "cannot map zero-fill pages");
}
}
@@ -880,7 +931,7 @@ _dl_map_object_from_fd (const char *name, int fd, char *realname,
break;
}
if (l->l_phdr == 0)
- LOSE ("program headers not contained in any loaded segment");
+ LOSE (0, "program headers not contained in any loaded segment");
}
else
/* Adjust the PT_PHDR value by the runtime load address. */
@@ -896,7 +947,7 @@ _dl_map_object_from_fd (const char *name, int fd, char *realname,
if (l->l_ld == 0)
{
if (type == ET_DYN)
- LOSE ("object file has no dynamic section");
+ LOSE (0, "object file has no dynamic section");
}
else
(ElfW(Addr)) l->l_ld += l->l_addr;
@@ -951,7 +1002,7 @@ _dl_map_object_from_fd (const char *name, int fd, char *realname,
(struct link_map **) malloc (sizeof (struct link_map *));
if (l->l_symbolic_searchlist.r_list == NULL)
- lose (ENOMEM, "cannot create searchlist");
+ LOSE (ENOMEM, "cannot create searchlist");
l->l_symbolic_searchlist.r_list[0] = l;
l->l_symbolic_searchlist.r_nlist = 1;
@@ -1280,9 +1331,11 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded,
|| (l = _dl_new_object (name_copy, name, type, loader)) == NULL)
_dl_signal_error (ENOMEM, name,
"cannot create shared object descriptor");
- /* We use an opencount of 0 as a sign for the faked entry. */
+ /* We use an opencount of 0 as a sign for the faked entry.
+ Since the descriptor is initialized with zero we do not
+ have do this here.
l->l_opencount = 0;
- l->l_reserved = 0;
+ l->l_reserved = 0; */
l->l_buckets = &dummy_bucket;
l->l_nbuckets = 1;
l->l_relocated = 1;