diff options
Diffstat (limited to 'libelf/elf_begin.c')
-rw-r--r-- | libelf/elf_begin.c | 1070 |
1 files changed, 1070 insertions, 0 deletions
diff --git a/libelf/elf_begin.c b/libelf/elf_begin.c new file mode 100644 index 00000000..63fe013e --- /dev/null +++ b/libelf/elf_begin.c @@ -0,0 +1,1070 @@ +/* Create descriptor for processing file. + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 Red Hat, Inc. + Written by Ulrich Drepper <drepper@redhat.com>, 1998. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, version 2. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <stdbool.h> +#include <stddef.h> +#include <string.h> +#include <unistd.h> +#include <sys/mman.h> +#include <sys/param.h> +#include <sys/stat.h> + +#include "libelfP.h" +#include "common.h" + + +/* Create descriptor for archive in memory. */ +static inline Elf * +file_read_ar (int fildes, void *map_address, off_t offset, size_t maxsize, + Elf_Cmd cmd, Elf *parent) +{ + Elf *elf; + + /* Create a descriptor. */ + elf = allocate_elf (fildes, map_address, offset, maxsize, cmd, parent, + ELF_K_AR, 0); + if (elf != NULL) + { + /* We don't read all the symbol tables in advance. All this will + happen on demand. */ + elf->state.ar.offset = offset + SARMAG; + + elf->state.ar.elf_ar_hdr.ar_rawname = elf->state.ar.raw_name; + } + + return elf; +} + + +static size_t +get_shnum (void *map_address, unsigned char *e_ident, int fildes, off_t offset, + size_t maxsize) +{ + size_t result; + union + { + Elf32_Ehdr *e32; + Elf64_Ehdr *e64; + void *p; + } ehdr; + bool is32 = e_ident[EI_CLASS] == ELFCLASS32; + + /* Make the ELF header available. */ + if (likely (map_address != NULL) && e_ident[EI_DATA] == MY_ELFDATA + && (ALLOW_UNALIGNED + || (((size_t) ((char *) map_address + offset)) + & ((is32 ? __alignof__ (Elf32_Ehdr) : __alignof__ (Elf64_Ehdr)) + - 1)) == 0)) + ehdr.p = (char *) map_address + offset; + else + { + /* We have to read the data from the file. */ + size_t len = is32 ? sizeof (Elf32_Ehdr) : sizeof (Elf64_Ehdr); + + ehdr.p = alloca (len); + /* Fill it. */ + if ((size_t) pread (fildes, ehdr.p, len, offset) != len) + /* Failed reading. */ + return (size_t) -1l; + + if (e_ident[EI_DATA] != MY_ELFDATA) + { + if (is32) + { + CONVERT (ehdr.e32->e_shnum); + CONVERT (ehdr.e32->e_shoff); + } + else + { + CONVERT (ehdr.e64->e_shnum); + CONVERT (ehdr.e64->e_shoff); + } + } + } + + if (is32) + { + /* Get the number of sections from the ELF header. */ + result = ehdr.e32->e_shnum; + + if (unlikely (result == 0) && ehdr.e32->e_shoff != 0) + { + if (offset + ehdr.e32->e_shoff + sizeof (Elf32_Shdr) > maxsize) + /* Cannot read the first section header. */ + return (size_t) -1l; + + if (likely (map_address != NULL) && e_ident[EI_DATA] == MY_ELFDATA + && (ALLOW_UNALIGNED + || (((size_t) ((char *) map_address + offset)) + & (__alignof__ (Elf32_Ehdr) - 1)) == 0)) + /* We can directly access the memory. */ + result = ((Elf32_Shdr *) ((char *) map_address + + ehdr.e32->e_shoff + + offset))->sh_size; + else + { + Elf32_Word size; + + if (pread (fildes, &size, sizeof (Elf32_Word), + offset + ehdr.e32->e_shoff + + offsetof (Elf32_Shdr, sh_size)) + != sizeof (Elf32_Word)) + return (size_t) -1l; + + if (e_ident[EI_DATA] != MY_ELFDATA) + CONVERT (size); + + result = size; + } + } + } + else + { + /* Get the number of sections from the ELF header. */ + result = ehdr.e64->e_shnum; + + if (unlikely (result == 0) && ehdr.e64->e_shoff != 0) + { + if (offset + ehdr.e64->e_shoff + sizeof (Elf64_Shdr) > maxsize) + /* Cannot read the first section header. */ + return (size_t) -1l; + + if (likely (map_address != NULL) && e_ident[EI_DATA] == MY_ELFDATA + && (ALLOW_UNALIGNED + || (((size_t) ((char *) map_address + offset)) + & (__alignof__ (Elf64_Ehdr) - 1)) == 0)) + /* We can directly access the memory. */ + result = ((Elf64_Shdr *) ((char *) map_address + + ehdr.e64->e_shoff + + offset))->sh_size; + else + { + Elf64_Word size; + + if (pread (fildes, &size, sizeof (Elf64_Word), + offset + ehdr.e64->e_shoff + + offsetof (Elf64_Shdr, sh_size)) + != sizeof (Elf64_Word)) + return (size_t) -1l; + + if (e_ident[EI_DATA] != MY_ELFDATA) + CONVERT (size); + + result = size; + } + } + } + + return result; +} + + +/* Create descriptor for ELF file in memory. */ +static Elf * +file_read_elf (int fildes, void *map_address, off_t offset, size_t maxsize, + Elf_Cmd cmd, Elf *parent) +{ + /* We only read the ELF header now. */ + unsigned char *e_ident; + size_t scncnt; + Elf *elf; + + if (map_address != NULL) + /* It's right at the beginning of the file. No word access + required, just bytes. */ + e_ident = (unsigned char *) map_address + offset; + else + { + e_ident = (unsigned char *) alloca (EI_NIDENT); + + if (pread (fildes, e_ident, EI_NIDENT, offset) != EI_NIDENT) + { + __libelf_seterrno (ELF_E_READ_ERROR); + return NULL; + } + } + + /* Verify the binary is of the class we can handle. */ + if ((e_ident[EI_CLASS] != ELFCLASS32 + && e_ident[EI_CLASS] != ELFCLASS64) + /* We also can only handle two encodings. */ + || (e_ident[EI_DATA] != ELFDATA2LSB + && e_ident[EI_DATA] != ELFDATA2MSB)) + { + /* Cannot handle this. */ + __libelf_seterrno (ELF_E_INVALID_FILE); + return NULL; + } + + /* Determine the number of sections. */ + scncnt = get_shnum (map_address, e_ident, fildes, offset, maxsize); + if (scncnt == (size_t) -1l) + /* Could not determine the number of sections. */ + return NULL; + + /* We can now allocate the memory. */ + elf = allocate_elf (fildes, map_address, offset, maxsize, cmd, parent, + ELF_K_ELF, scncnt * sizeof (Elf_Scn)); + if (elf == NULL) + /* Not enough memory. */ + return NULL; + + /* Some more or less arbitrary value. */ + elf->state.elf.scnincr = 10; + + if (e_ident[EI_CLASS] == ELFCLASS32) + { + /* This pointer might not be directly usable if the alignment is + not sufficient for the architecture. */ + Elf32_Ehdr *ehdr = (Elf32_Ehdr *) ((char *) map_address + offset); + size_t cnt; + + assert ((unsigned int) scncnt == scncnt); + elf->state.elf32.scns.cnt = elf->state.elf32.scns.max = scncnt; + + /* This is a 32-bit binary. */ + if (map_address != NULL && e_ident[EI_DATA] == MY_ELFDATA + && (ALLOW_UNALIGNED + || ((((uintptr_t) ehdr) & (__alignof__ (Elf32_Ehdr) - 1)) == 0 + && ((uintptr_t) ((char *) ehdr + ehdr->e_shoff) + & (__alignof__ (Elf32_Shdr) - 1)) == 0 + && ((uintptr_t) ((char *) ehdr + ehdr->e_phoff) + & (__alignof__ (Elf32_Phdr) - 1)) == 0))) + { + /* We can use the mmapped memory. */ + elf->state.elf32.ehdr = + (Elf32_Ehdr *) ((char *) map_address + offset); + elf->state.elf32.shdr = + (Elf32_Shdr *) ((char *) map_address + offset + + elf->state.elf32.ehdr->e_shoff); + if (elf->state.elf32.ehdr->e_phnum) + /* Assign a value only if there really is a program + header. Otherwise the value remains NULL. */ + elf->state.elf32.phdr + = (Elf32_Phdr *) ((char *) map_address + offset + + elf->state.elf32.ehdr->e_phoff); + + for (cnt = 0; cnt < scncnt; ++cnt) + { + elf->state.elf32.scns.data[cnt].index = cnt; + elf->state.elf32.scns.data[cnt].elf = elf; + elf->state.elf32.scns.data[cnt].shdr.e32 = + &elf->state.elf32.shdr[cnt]; + elf->state.elf32.scns.data[cnt].rawdata_base = + elf->state.elf32.scns.data[cnt].data_base = + ((char *) map_address + offset + + elf->state.elf32.shdr[cnt].sh_offset); + elf->state.elf32.scns.data[cnt].list = &elf->state.elf32.scns; + } + } + else + { + /* Read the data. */ + if (pread (elf->fildes, &elf->state.elf32.ehdr_mem, + sizeof (Elf32_Ehdr), offset) != sizeof (Elf32_Ehdr)) + { + /* We must be able to read the ELF header. */ + __libelf_seterrno (ELF_E_INVALID_FILE); + return NULL; + } + + if (e_ident[EI_DATA] != MY_ELFDATA) + { + CONVERT (elf->state.elf32.ehdr_mem.e_type); + CONVERT (elf->state.elf32.ehdr_mem.e_machine); + CONVERT (elf->state.elf32.ehdr_mem.e_version); + CONVERT (elf->state.elf32.ehdr_mem.e_entry); + CONVERT (elf->state.elf32.ehdr_mem.e_phoff); + CONVERT (elf->state.elf32.ehdr_mem.e_shoff); + CONVERT (elf->state.elf32.ehdr_mem.e_flags); + CONVERT (elf->state.elf32.ehdr_mem.e_ehsize); + CONVERT (elf->state.elf32.ehdr_mem.e_phentsize); + CONVERT (elf->state.elf32.ehdr_mem.e_phnum); + CONVERT (elf->state.elf32.ehdr_mem.e_shentsize); + CONVERT (elf->state.elf32.ehdr_mem.e_shnum); + CONVERT (elf->state.elf32.ehdr_mem.e_shstrndx); + } + + elf->state.elf32.ehdr = &elf->state.elf32.ehdr_mem; + + for (cnt = 0; cnt < scncnt; ++cnt) + { + elf->state.elf32.scns.data[cnt].index = cnt; + elf->state.elf32.scns.data[cnt].elf = elf; + elf->state.elf32.scns.data[cnt].list = &elf->state.elf32.scns; + } + } + + /* So far only one block with sections. */ + elf->state.elf32.scns_last = &elf->state.elf32.scns; + } + else + { + /* This pointer might not be directly usable if the alignment is + not sufficient for the architecture. */ + Elf64_Ehdr *ehdr = (Elf64_Ehdr *) ((char *) map_address + offset); + size_t cnt; + + assert ((unsigned int) scncnt == scncnt); + elf->state.elf64.scns.cnt = elf->state.elf64.scns.max = scncnt; + + /* This is a 64-bit binary. */ + if (map_address != NULL && e_ident[EI_DATA] == MY_ELFDATA + && (ALLOW_UNALIGNED + || ((((uintptr_t) ehdr) & (__alignof__ (Elf64_Ehdr) - 1)) == 0 + && ((uintptr_t) ((char *) ehdr + ehdr->e_shoff) + & (__alignof__ (Elf64_Shdr) - 1)) == 0 + && ((uintptr_t) ((char *) ehdr + ehdr->e_phoff) + & (__alignof__ (Elf64_Phdr) - 1)) == 0))) + { + /* We can use the mmapped memory. */ + elf->state.elf64.ehdr = + (Elf64_Ehdr *) ((char *) map_address + offset); + elf->state.elf64.shdr = + (Elf64_Shdr *) ((char *) map_address + offset + + elf->state.elf64.ehdr->e_shoff); + if (elf->state.elf64.ehdr->e_phnum) + /* Assign a value only if there really is a program + header. Otherwise the value remains NULL. */ + elf->state.elf64.phdr + = (Elf64_Phdr *) ((char *) map_address + offset + + elf->state.elf64.ehdr->e_phoff); + + for (cnt = 0; cnt < scncnt; ++cnt) + { + elf->state.elf64.scns.data[cnt].index = cnt; + elf->state.elf64.scns.data[cnt].elf = elf; + elf->state.elf64.scns.data[cnt].shdr.e64 = + &elf->state.elf64.shdr[cnt]; + elf->state.elf64.scns.data[cnt].rawdata_base = + elf->state.elf64.scns.data[cnt].data_base = + ((char *) map_address + offset + + elf->state.elf64.shdr[cnt].sh_offset); + elf->state.elf64.scns.data[cnt].list = &elf->state.elf64.scns; + } + } + else + { + /* Read the data. */ + if (pread (elf->fildes, &elf->state.elf64.ehdr_mem, + sizeof (Elf64_Ehdr), offset) != sizeof (Elf64_Ehdr)) + { + /* We must be able to read the ELF header. */ + __libelf_seterrno (ELF_E_INVALID_FILE); + return NULL; + } + + if (e_ident[EI_DATA] != MY_ELFDATA) + { + CONVERT (elf->state.elf64.ehdr_mem.e_type); + CONVERT (elf->state.elf64.ehdr_mem.e_machine); + CONVERT (elf->state.elf64.ehdr_mem.e_version); + CONVERT (elf->state.elf64.ehdr_mem.e_entry); + CONVERT (elf->state.elf64.ehdr_mem.e_phoff); + CONVERT (elf->state.elf64.ehdr_mem.e_shoff); + CONVERT (elf->state.elf64.ehdr_mem.e_flags); + CONVERT (elf->state.elf64.ehdr_mem.e_ehsize); + CONVERT (elf->state.elf64.ehdr_mem.e_phentsize); + CONVERT (elf->state.elf64.ehdr_mem.e_phnum); + CONVERT (elf->state.elf64.ehdr_mem.e_shentsize); + CONVERT (elf->state.elf64.ehdr_mem.e_shnum); + CONVERT (elf->state.elf64.ehdr_mem.e_shstrndx); + } + + elf->state.elf64.ehdr = &elf->state.elf64.ehdr_mem; + + for (cnt = 0; cnt < scncnt; ++cnt) + { + elf->state.elf64.scns.data[cnt].index = cnt; + elf->state.elf64.scns.data[cnt].elf = elf; + elf->state.elf64.scns.data[cnt].list = &elf->state.elf64.scns; + } + } + + /* So far only one block with sections. */ + elf->state.elf64.scns_last = &elf->state.elf64.scns; + } + + /* Make the class easily available. */ + elf->class = e_ident[EI_CLASS]; + + return elf; +} + + +Elf * +internal_function_def +__libelf_read_mmaped_file (int fildes, void *map_address, off_t offset, + size_t maxsize, Elf_Cmd cmd, Elf *parent) +{ + /* We have to find out what kind of file this is. We handle ELF + files and archives. To find out what we have we must look at the + header. The header for an ELF file is EI_NIDENT bytes in size, + the header for an archive file SARMAG bytes long. */ + Elf_Kind kind; + + /* See what kind of object we have here. */ + kind = determine_kind (map_address + offset, maxsize); + + switch (kind) + { + case ELF_K_ELF: + return file_read_elf (fildes, map_address, offset, maxsize, cmd, parent); + + case ELF_K_AR: + return file_read_ar (fildes, map_address, offset, maxsize, cmd, parent); + + default: + break; + } + + /* This case is easy. Since we cannot do anything with this file + create a dummy descriptor. */ + return allocate_elf (fildes, map_address, offset, maxsize, cmd, parent, + ELF_K_NONE, 0); +} + + +static Elf * +read_unmmaped_file (int fildes, off_t offset, size_t maxsize, Elf_Cmd cmd, + Elf *parent) +{ + /* We have to find out what kind of file this is. We handle ELF + files and archives. To find out what we have we must read the + header. The header for an ELF file is EI_NIDENT bytes in size, + the header for an archive file SARMAG bytes long. Read the + maximum of these numbers. + + XXX We have to change this for the extended `ar' format some day. */ + unsigned char header[MAX (EI_NIDENT, SARMAG)]; + ssize_t nread; + Elf_Kind kind; + + /* Read the head of the file. */ + nread = pread (fildes, header, MIN (MAX (EI_NIDENT, SARMAG), maxsize), + offset); + if (nread == -1) + /* We cannot even read the head of the file. Maybe FILDES is associated + with an unseekable device. This is nothing we can handle. */ + return NULL; + + /* See what kind of object we have here. */ + kind = determine_kind (header, nread); + + switch (kind) + { + case ELF_K_AR: + return file_read_ar (fildes, NULL, offset, maxsize, cmd, parent); + + case ELF_K_ELF: + /* Make sure at least the ELF header is contained in the file. */ + if (maxsize >= (header[EI_CLASS] == ELFCLASS32 + ? sizeof (Elf32_Ehdr) : sizeof (Elf64_Ehdr))) + return file_read_elf (fildes, NULL, offset, maxsize, cmd, parent); + /* FALLTHROUGH */ + + default: + break; + } + + /* This case is easy. Since we cannot do anything with this file + create a dummy descriptor. */ + return allocate_elf (fildes, NULL, offset, maxsize, cmd, parent, + ELF_K_NONE, 0); +} + + +/* Open a file for reading. If possible we will try to mmap() the file. */ +static struct Elf * +read_file (int fildes, off_t offset, size_t maxsize, + Elf_Cmd cmd, Elf *parent) +{ + void *map_address = NULL; + int use_mmap = (cmd == ELF_C_READ_MMAP || cmd == ELF_C_RDWR_MMAP + || cmd == ELF_C_WRITE_MMAP + || cmd == ELF_C_READ_MMAP_PRIVATE); + + if (use_mmap) + { + if (parent == NULL) + { + if (maxsize == ~((size_t) 0)) + { + /* We don't know in the moment how large the file is. + Determine it now. */ + struct stat st; + + if (fstat (fildes, &st) == 0 + && (sizeof (size_t) >= sizeof (st.st_size) + || st.st_size <= ~((size_t) 0))) + maxsize = (size_t) st.st_size; + } + + /* We try to map the file ourself. */ + map_address = mmap (NULL, maxsize, (cmd == ELF_C_READ_MMAP + ? PROT_READ + : PROT_READ|PROT_WRITE), + cmd == ELF_C_READ_MMAP_PRIVATE + ? MAP_PRIVATE : MAP_SHARED, + fildes, offset); + + if (map_address == MAP_FAILED) + map_address = NULL; + } + else + { + /* The parent is already loaded. Use it. */ + assert (maxsize != ~((size_t) 0)); + + map_address = parent->map_address; + } + } + + /* If we have the file in memory optimize the access. */ + if (map_address != NULL) + { + struct Elf *result; + + result = __libelf_read_mmaped_file (fildes, map_address, offset, maxsize, + cmd, parent); + + /* If something went wrong during the initialization unmap the + memory if we mmaped here. */ + if (result == NULL + && (parent == NULL + || parent->map_address != map_address)) + munmap (map_address, maxsize); + else if (parent == NULL) + /* Remember that we mmap()ed the memory. */ + result->flags |= ELF_F_MMAPPED; + + return result; + } + + /* Otherwise we have to do it the hard way. We read as much as necessary + from the file whenever we need information which is not available. */ + return read_unmmaped_file (fildes, offset, maxsize, cmd, parent); +} + + +/* Find the entry with the long names for the content of this archive. */ +static const char * +read_long_names (Elf *elf) +{ + off_t offset = SARMAG; /* This is the first entry. */ + struct ar_hdr hdrm; + struct ar_hdr *hdr; + char *newp; + size_t len; + + while (1) + { + if (elf->map_address != NULL) + { + if (offset + sizeof (struct ar_hdr) > elf->maximum_size) + return NULL; + + /* The data is mapped. */ + hdr = (struct ar_hdr *) (elf->map_address + offset); + } + else + { + /* Read the header from the file. */ + if (pread (elf->fildes, &hdrm, sizeof (hdrm), + elf->start_offset + offset) != sizeof (hdrm)) + return NULL; + + hdr = &hdrm; + } + + len = atol (hdr->ar_size); + + if (memcmp (hdr->ar_name, "// ", 16) == 0) + break; + + offset += sizeof (struct ar_hdr) + ((len + 1) & ~1l); + } + + /* Due to the stupid format of the long name table entry (which are not + NUL terminted) we have to provide an appropriate representation anyhow. + Therefore we always make a copy which has the appropriate form. */ + newp = (char *) malloc (len); + if (newp != NULL) + { + char *runp; + + if (elf->map_address != NULL) + /* Simply copy it over. */ + elf->state.ar.long_names = (char *) memcpy (newp, + elf->map_address + offset + + sizeof (struct ar_hdr), + len); + else + { + if ((size_t) pread (elf->fildes, newp, len, + elf->start_offset + offset + + sizeof (struct ar_hdr)) + != len) + { + /* We were not able to read all data. */ + free (newp); + elf->state.ar.long_names = NULL; + return NULL; + } + elf->state.ar.long_names = newp; + } + + elf->state.ar.long_names_len = len; + + /* Now NUL-terminate the strings. */ + runp = newp; + while (1) + { + runp = (char *) memchr (runp, '/', newp + len - runp); + if (runp == NULL) + /* This was the last entry. */ + break; + + /* NUL-terminate the string. */ + *runp = '\0'; + + /* Skip the NUL bzte and the \012. */ + runp += 2; + + /* A sanity check. Somebody might have generated invalid + archive. */ + if (runp >= newp + len) + break; + } + } + + return newp; +} + + +/* Read the next archive header. */ +int +internal_function_def +__libelf_next_arhdr (elf) + Elf *elf; +{ + struct ar_hdr *ar_hdr; + Elf_Arhdr *elf_ar_hdr; + + if (elf->map_address != NULL) + { + /* See whether this entry is in the file. */ + if (elf->state.ar.offset + sizeof (struct ar_hdr) + > elf->start_offset + elf->maximum_size) + { + /* This record is not anymore in the file. */ + __libelf_seterrno (ELF_E_RANGE); + return -1; + } + ar_hdr = (struct ar_hdr *) (elf->map_address + elf->state.ar.offset); + } + else + { + ar_hdr = &elf->state.ar.ar_hdr; + + if (pread (elf->fildes, ar_hdr, sizeof (struct ar_hdr), + elf->state.ar.offset) + != sizeof (struct ar_hdr)) + { + /* Something went wrong while reading the file. */ + __libelf_seterrno (ELF_E_RANGE); + return -1; + } + } + + /* One little consistency check. */ + if (memcmp (ar_hdr->ar_fmag, ARFMAG, 2) != 0) + { + /* This is no valid archive. */ + __libelf_seterrno (ELF_E_ARCHIVE_FMAG); + return -1; + } + + /* Copy the raw name over to a NUL terminated buffer. */ + *((char *) __mempcpy (elf->state.ar.raw_name, ar_hdr->ar_name, 16)) = '\0'; + + elf_ar_hdr = &elf->state.ar.elf_ar_hdr; + + /* Now convert the `struct ar_hdr' into `Elf_Arhdr'. + Determine whether this is a special entry. */ + if (ar_hdr->ar_name[0] == '/') + { + if (ar_hdr->ar_name[1] == ' ' + && memcmp (ar_hdr->ar_name, "/ ", 16) == 0) + /* This is the index. */ + elf_ar_hdr->ar_name = memcpy (elf->state.ar.ar_name, "/", 2); + else if (ar_hdr->ar_name[1] == '/' + && memcmp (ar_hdr->ar_name, "// ", 16) == 0) + /* This is the array with the long names. */ + elf_ar_hdr->ar_name = memcpy (elf->state.ar.ar_name, "//", 3); + else if (isdigit (ar_hdr->ar_name[1])) + { + size_t offset; + + /* This is a long name. First we have to read the long name + table, if this hasn't happened already. */ + if (elf->state.ar.long_names == NULL + && read_long_names (elf) == NULL) + { + /* No long name table although it is reference. The archive is + broken. */ + __libelf_seterrno (ELF_E_INVALID_ARCHIVE); + return -1; + } + + offset = atol (ar_hdr->ar_name + 1); + if (offset >= elf->state.ar.long_names_len) + { + /* The index in the long name table is larger than the table. */ + __libelf_seterrno (ELF_E_INVALID_ARCHIVE); + return -1; + } + elf_ar_hdr->ar_name = elf->state.ar.long_names + offset; + } + else + { + /* This is none of the known special entries. */ + __libelf_seterrno (ELF_E_INVALID_ARCHIVE); + return -1; + } + } + else + { + char *endp; + + /* It is a normal entry. Copy over the name. */ + endp = (char *) memccpy (elf->state.ar.ar_name, ar_hdr->ar_name, + '/', 16); + if (endp != NULL) + endp[-1] = '\0'; + else + elf->state.ar.raw_name[16] = '\0'; + + elf_ar_hdr->ar_name = elf->state.ar.ar_name; + } + + /* Since there are no specialized functions to convert ASCII to + time_t, uid_t, gid_t, mode_t, and off_t we use either atol or + atoll depending on the size of the types. We are also prepared + for the case where the whole field in the `struct ar_hdr' is + filled in which case we cannot simply use atol/l but instead have + to create a temporary copy. */ + if (ar_hdr->ar_date[sizeof (ar_hdr->ar_date) - 1] == ' ') + { + if (ar_hdr->ar_date[0] == ' ') + elf_ar_hdr->ar_date = 0; + else + elf_ar_hdr->ar_date = (sizeof (time_t) <= sizeof (long int) + ? (time_t) atol (ar_hdr->ar_date) + : (time_t) atoll (ar_hdr->ar_date)); + } + else + { + char buf[sizeof (ar_hdr->ar_date) + 1]; + *((char *) __mempcpy (buf, ar_hdr->ar_date, sizeof (ar_hdr->ar_date))) + = '\0'; + elf_ar_hdr->ar_date = (sizeof (time_t) <= sizeof (long int) + ? (time_t) atol (ar_hdr->ar_date) + : (time_t) atoll (ar_hdr->ar_date)); + } + + if (ar_hdr->ar_uid[sizeof (ar_hdr->ar_uid) - 1] == ' ') + { + if (ar_hdr->ar_uid[0] == ' ') + elf_ar_hdr->ar_uid = 0; + else + elf_ar_hdr->ar_uid = (sizeof (uid_t) <= sizeof (long int) + ? (uid_t) atol (ar_hdr->ar_uid) + : (uid_t) atoll (ar_hdr->ar_uid)); + } + else + { + char buf[sizeof (ar_hdr->ar_uid) + 1]; + *((char *) __mempcpy (buf, ar_hdr->ar_uid, sizeof (ar_hdr->ar_uid))) + = '\0'; + elf_ar_hdr->ar_uid = (sizeof (uid_t) <= sizeof (long int) + ? (uid_t) atol (ar_hdr->ar_uid) + : (uid_t) atoll (ar_hdr->ar_uid)); + } + + if (ar_hdr->ar_gid[sizeof (ar_hdr->ar_gid) - 1] == ' ') + { + if (ar_hdr->ar_gid[0] == ' ') + elf_ar_hdr->ar_gid = 0; + else + elf_ar_hdr->ar_gid = (sizeof (gid_t) <= sizeof (long int) + ? (gid_t) atol (ar_hdr->ar_gid) + : (gid_t) atoll (ar_hdr->ar_gid)); + } + else + { + char buf[sizeof (ar_hdr->ar_gid) + 1]; + *((char *) __mempcpy (buf, ar_hdr->ar_gid, sizeof (ar_hdr->ar_gid))) + = '\0'; + elf_ar_hdr->ar_gid = (sizeof (gid_t) <= sizeof (long int) + ? (gid_t) atol (ar_hdr->ar_gid) + : (gid_t) atoll (ar_hdr->ar_gid)); + } + + if (ar_hdr->ar_mode[sizeof (ar_hdr->ar_mode) - 1] == ' ') + { + if (ar_hdr->ar_mode[0] == ' ') + elf_ar_hdr->ar_mode = 0; + else + elf_ar_hdr->ar_mode = (sizeof (mode_t) <= sizeof (long int) + ? (mode_t) strtol (ar_hdr->ar_mode, NULL, 8) + : (mode_t) strtoll (ar_hdr->ar_mode, NULL, 8)); + } + else + { + char buf[sizeof (ar_hdr->ar_mode) + 1]; + *((char *) __mempcpy (buf, ar_hdr->ar_mode, sizeof (ar_hdr->ar_mode))) + = '\0'; + elf_ar_hdr->ar_mode = (sizeof (mode_t) <= sizeof (long int) + ? (mode_t) strtol (ar_hdr->ar_mode, NULL, 8) + : (mode_t) strtoll (ar_hdr->ar_mode, NULL, 8)); + } + + if (ar_hdr->ar_size[sizeof (ar_hdr->ar_size) - 1] == ' ') + { + if (ar_hdr->ar_size[0] == ' ') + /* Something is really wrong. We cannot live without a size for + the member since it will not be possible to find the next + archive member. */ + { + __libelf_seterrno (ELF_E_INVALID_ARCHIVE); + return -1; + } + else + elf_ar_hdr->ar_size = (sizeof (time_t) == sizeof (long int) + ? (off_t) atol (ar_hdr->ar_size) + : (off_t) atoll (ar_hdr->ar_size)); + } + else + { + char buf[sizeof (ar_hdr->ar_size) + 1]; + *((char *) __mempcpy (buf, ar_hdr->ar_size, sizeof (ar_hdr->ar_size))) + = '\0'; + elf_ar_hdr->ar_size = (sizeof (time_t) == sizeof (long int) + ? (off_t) atol (ar_hdr->ar_size) + : (off_t) atoll (ar_hdr->ar_size)); + } + + return 0; +} + + +/* We were asked to return a clone of an existing descriptor. This + function must be called with the lock on the parent descriptor + being held. */ +static Elf * +dup_elf (int fildes, Elf_Cmd cmd, Elf *ref) +{ + struct Elf *result; + + if (fildes == -1) + /* Allow the user to pass -1 as the file descriptor for the new file. */ + fildes = ref->fildes; + /* The file descriptor better should be the same. If it was disconnected + already (using `elf_cntl') we do not test it. */ + else if (ref->fildes != -1 && fildes != ref->fildes) + { + __libelf_seterrno (ELF_E_FD_MISMATCH); + return NULL; + } + + /* The mode must allow reading. I.e., a descriptor creating with a + command different then ELF_C_READ, ELF_C_WRITE and ELF_C_RDWR is + not allowed. */ + if (ref->cmd != ELF_C_READ && ref->cmd != ELF_C_READ_MMAP + && ref->cmd != ELF_C_WRITE && ref->cmd != ELF_C_WRITE_MMAP + && ref->cmd != ELF_C_RDWR && ref->cmd != ELF_C_RDWR_MMAP + && ref->cmd != ELF_C_READ_MMAP_PRIVATE) + { + __libelf_seterrno (ELF_E_INVALID_OP); + return NULL; + } + + /* Now it is time to distinguish between reading normal files and + archives. Normal files can easily be handled be incrementing the + reference counter and return the same descriptor. */ + if (ref->kind != ELF_K_AR) + { + ++ref->ref_count; + return ref; + } + + /* This is an archive. We must create a descriptor for the archive + member the internal pointer of the archive file desriptor is + pointing to. First read the header of the next member if this + has not happened already. */ + if (ref->state.ar.elf_ar_hdr.ar_name == NULL + && __libelf_next_arhdr (ref) != 0) + /* Something went wrong. Maybe there is no member left. */ + return NULL; + + /* We have all the information we need about the next archive member. + Now create a descriptor for it. */ + result = read_file (fildes, ref->state.ar.offset + sizeof (struct ar_hdr), + ref->state.ar.elf_ar_hdr.ar_size, cmd, ref); + + /* Enlist this new descriptor in the list of children. */ + if (result != NULL) + { + result->next = ref->state.ar.children; + ref->state.ar.children = result; + } + + return result; +} + + +/* Return desriptor for empty file ready for writing. */ +static struct Elf * +write_file (int fd, Elf_Cmd cmd) +{ + /* We simply create an empty `Elf' structure. */ +#define NSCNSALLOC 10 + Elf *result = allocate_elf (fd, NULL, 0, 0, cmd, NULL, ELF_K_ELF, + NSCNSALLOC * sizeof (Elf_Scn)); + + if (result != NULL) + { + /* We have to write to the file in any case. */ + result->flags = ELF_F_DIRTY; + + /* Some more or less arbitrary value. */ + result->state.elf.scnincr = NSCNSALLOC; + + /* We have allocated room for some sections. */ + assert (offsetof (struct Elf, state.elf32.scns) + == offsetof (struct Elf, state.elf64.scns)); + result->state.elf.scns_last = &result->state.elf32.scns; + result->state.elf32.scns.max = NSCNSALLOC; + } + + return result; +} + + +/* Return a descriptor for the file belonging to FILDES. */ +Elf * +elf_begin (fildes, cmd, ref) + int fildes; + Elf_Cmd cmd; + Elf *ref; +{ + Elf *retval; + + if (! __libelf_version_initialized) + { + /* Version wasn't set so far. */ + __libelf_seterrno (ELF_E_NO_VERSION); + return NULL; + } + + if (ref != NULL) + /* Make sure the descriptor is not suddenly going away. */ + rwlock_rdlock (ref->lock); + else if (fcntl (fildes, F_GETFL) == -1 && errno == EBADF) + { + /* We cannot do anything productive without a file descriptor. */ + __libelf_seterrno (ELF_E_INVALID_FILE); + return NULL; + } + + switch (cmd) + { + case ELF_C_NULL: + /* We simply return a NULL pointer. */ + retval = NULL; + break; + + case ELF_C_READ_MMAP_PRIVATE: + /* If we have a reference it must also be opened this way. */ + if (ref != NULL && ref->cmd != ELF_C_READ_MMAP_PRIVATE) + { + __libelf_seterrno (ELF_E_INVALID_CMD); + retval = NULL; + break; + } + /* FALLTHROUGH */ + + case ELF_C_READ: + case ELF_C_READ_MMAP: + if (ref != NULL) + /* Duplicate the descriptor. */ + retval = dup_elf (fildes, cmd, ref); + else + /* Create descriptor for existing file. */ + retval = read_file (fildes, 0, ~((size_t) 0), cmd, NULL); + break; + + case ELF_C_RDWR: + case ELF_C_RDWR_MMAP: + /* If we have a REF object it must also be opened using this + command. */ + if (ref != NULL) + { + if (ref->cmd != ELF_C_RDWR && ref->cmd != ELF_C_RDWR_MMAP + && ref->cmd != ELF_C_WRITE && ref->cmd != ELF_C_WRITE_MMAP) + { + /* This is not ok. REF must also be opened for writing. */ + __libelf_seterrno (ELF_E_INVALID_CMD); + retval = NULL; + } + else + /* Duplicate this descriptor. */ + retval = dup_elf (fildes, cmd, ref); + } + else + /* Create descriptor for existing file. */ + retval = read_file (fildes, 0, ~((size_t) 0), cmd, NULL); + break; + + case ELF_C_WRITE: + case ELF_C_WRITE_MMAP: + /* We ignore REF and prepare a descriptor to write a new file. */ + retval = write_file (fildes, cmd); + break; + + default: + __libelf_seterrno (ELF_E_INVALID_CMD); + retval = NULL; + break; + } + + /* Release the lock. */ + if (ref != NULL) + rwlock_unlock (ref->lock); + + return retval; +} +INTDEF(elf_begin) |