summaryrefslogtreecommitdiff
path: root/usr/src/cmd/file/elf_read.c
diff options
context:
space:
mode:
authorny155746 <none@none>2007-05-09 22:53:24 -0700
committerny155746 <none@none>2007-05-09 22:53:24 -0700
commitc2c65e21c1fd025c3fc6103cc713dd5343fc9ad6 (patch)
treef9536acdb5fb66efec3085b7a9607b5eac2d130f /usr/src/cmd/file/elf_read.c
parente92e3a8694f157faf8a9e44096a70ada86c556bf (diff)
downloadillumos-gate-c2c65e21c1fd025c3fc6103cc713dd5343fc9ad6.tar.gz
4984023 file and libelf is not able to handle core file greater than 2 Gb
Diffstat (limited to 'usr/src/cmd/file/elf_read.c')
-rw-r--r--usr/src/cmd/file/elf_read.c476
1 files changed, 476 insertions, 0 deletions
diff --git a/usr/src/cmd/file/elf_read.c b/usr/src/cmd/file/elf_read.c
new file mode 100644
index 0000000000..905c37b586
--- /dev/null
+++ b/usr/src/cmd/file/elf_read.c
@@ -0,0 +1,476 @@
+/*
+ * 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) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+/* Copyright (c) 1987, 1988 Microsoft Corporation */
+/* All Rights Reserved */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#define _LARGEFILE64_SOURCE
+
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <libelf.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <locale.h>
+#include <string.h>
+#include <errno.h>
+#include <procfs.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/elf.h>
+#include <elfcap.h>
+#include "file.h"
+#include "elf_read.h"
+
+extern const char *File;
+
+static int get_class(void);
+static int get_version(void);
+static int get_format(void);
+static int process_shdr(Elf_Info *);
+static int process_phdr(Elf_Info *);
+static int file_xlatetom(Elf_Type, char *);
+static int xlatetom_nhdr(Elf_Nhdr *);
+static int get_phdr(Elf_Info *, int);
+static int get_shdr(Elf_Info *, int);
+
+static Elf_Ehdr EI_Ehdr; /* Elf_Ehdr to be stored */
+static Elf_Shdr EI_Shdr; /* recent Elf_Shdr to be stored */
+static Elf_Phdr EI_Phdr; /* recent Elf_Phdr to be stored */
+
+
+static int
+get_class(void)
+{
+ return (EI_Ehdr.e_ident[EI_CLASS]);
+}
+
+static int
+get_version(void)
+{
+ /* do as what libelf:_elf_config() does */
+ return (EI_Ehdr.e_ident[EI_VERSION] ?
+ EI_Ehdr.e_ident[EI_VERSION] : 1);
+}
+
+static int
+get_format(void)
+{
+ return (EI_Ehdr.e_ident[EI_DATA]);
+}
+
+/*
+ * file_xlatetom: translate different headers from file
+ * representation to memory representaion.
+ */
+#define HDRSZ 512
+static int
+file_xlatetom(Elf_Type type, char *hdr)
+{
+ Elf_Data src, dst;
+ char *hbuf[HDRSZ];
+ int version, format;
+
+ version = get_version();
+ format = get_format();
+
+ /* will convert only these types */
+ if (type != ELF_T_EHDR && type != ELF_T_PHDR &&
+ type != ELF_T_SHDR && type != ELF_T_WORD &&
+ type != ELF_T_CAP)
+ return (ELF_READ_FAIL);
+
+ src.d_buf = (Elf_Void *)hdr;
+ src.d_type = type;
+ src.d_version = version;
+
+ dst.d_buf = (Elf_Void *)&hbuf;
+ dst.d_version = EV_CURRENT;
+
+ src.d_size = elf_fsize(type, 1, version);
+ dst.d_size = elf_fsize(type, 1, EV_CURRENT);
+ if (elf_xlatetom(&dst, &src, format) == NULL)
+ return (ELF_READ_FAIL);
+
+ (void) memcpy(hdr, &hbuf, dst.d_size);
+ return (ELF_READ_OKAY);
+}
+
+/*
+ * xlatetom_nhdr: There is no routine to convert Note header
+ * so we convert each field of this header.
+ */
+static int
+xlatetom_nhdr(Elf_Nhdr *nhdr)
+{
+ int r = ELF_READ_FAIL;
+
+ r |= file_xlatetom(ELF_T_WORD, (char *)&nhdr->n_namesz);
+ r |= file_xlatetom(ELF_T_WORD, (char *)&nhdr->n_descsz);
+ r |= file_xlatetom(ELF_T_WORD, (char *)&nhdr->n_type);
+ return (r);
+}
+
+/*
+ * elf_read: reads elf header, program, section headers to
+ * collect all information needed for file(1)
+ * output and stores them in Elf_Info.
+ */
+int
+elf_read(int fd, Elf_Info *EI)
+{
+ size_t size;
+ int ret = 1;
+
+ Elf_Ehdr *ehdr = &EI_Ehdr;
+
+ EI->elffd = fd;
+ size = sizeof (Elf_Ehdr);
+
+ if (pread64(EI->elffd, (void*)ehdr, size, 0) != size)
+ ret = 0;
+
+ if (file_xlatetom(ELF_T_EHDR, (char *)ehdr) == ELF_READ_FAIL)
+ ret = 0;
+
+ if (EI->file == NULL)
+ return (ELF_READ_FAIL);
+
+ EI->type = ehdr->e_type;
+ EI->machine = ehdr->e_machine;
+ EI->flags = ehdr->e_flags;
+
+ if (ret == 0) {
+ (void) fprintf(stderr, gettext("%s: %s: can't "
+ "read ELF header\n"), File, EI->file);
+ return (ELF_READ_FAIL);
+ }
+ if (process_phdr(EI) == ELF_READ_FAIL)
+ return (ELF_READ_FAIL);
+
+ /* We don't need section info for core files */
+ if (ehdr->e_type != ET_CORE)
+ if (process_shdr(EI) == ELF_READ_FAIL)
+ return (ELF_READ_FAIL);
+
+ return (ELF_READ_OKAY);
+}
+
+/*
+ * get_phdr: reads program header of specified index.
+ */
+static int
+get_phdr(Elf_Info *EI, int inx)
+{
+ off_t off = 0;
+ size_t size;
+ Elf_Ehdr *ehdr = &EI_Ehdr;
+
+ if (inx >= ehdr->e_phnum)
+ return (ELF_READ_FAIL);
+
+ size = sizeof (Elf_Phdr);
+ off = (off_t)ehdr->e_phoff + (inx * size);
+ if (pread64(EI->elffd, (void *)&EI_Phdr, size, off) != size)
+ return (ELF_READ_FAIL);
+
+ if (file_xlatetom(ELF_T_PHDR, (char *)&EI_Phdr) == ELF_READ_FAIL)
+ return (ELF_READ_FAIL);
+
+ return (ELF_READ_OKAY);
+}
+
+/*
+ * get_shdr: reads section header of specified index.
+ */
+static int
+get_shdr(Elf_Info *EI, int inx)
+{
+ off_t off = 0;
+ size_t size;
+ Elf_Ehdr *ehdr = &EI_Ehdr;
+
+ if (inx >= ehdr->e_shnum)
+ return (ELF_READ_FAIL);
+
+ size = sizeof (Elf_Shdr);
+ off = (off_t)ehdr->e_shoff + (inx * size);
+
+ if (pread64(EI->elffd, (void *)&EI_Shdr, size, off) != size)
+ return (ELF_READ_FAIL);
+
+ if (file_xlatetom(ELF_T_SHDR, (char *)&EI_Shdr) == ELF_READ_FAIL)
+ return (ELF_READ_FAIL);
+
+ return (ELF_READ_OKAY);
+}
+
+/*
+ * process_phdr: Read Program Headers and see if it is a core
+ * file of either new or (pre-restructured /proc)
+ * type, read the name of the file that dumped this
+ * core, else see if this is a dynamically linked.
+ */
+static int
+process_phdr(Elf_Info *EI)
+{
+ register int inx;
+
+ Elf_Nhdr Nhdr, *nhdr; /* note header just read */
+ Elf_Phdr *phdr = &EI_Phdr;
+
+ int class;
+ int ntype;
+ size_t nsz, nmsz, dsz;
+ off_t offset;
+ char *psinfo, *fname;
+
+ nsz = sizeof (Elf_Nhdr);
+ nhdr = &Nhdr;
+ class = get_class();
+ for (inx = 0; inx < EI_Ehdr.e_phnum; inx++) {
+ if (get_phdr(EI, inx) == ELF_READ_FAIL)
+ return (ELF_READ_FAIL);
+
+ /* read the note if it is a core */
+ if (phdr->p_type == PT_NOTE &&
+ EI_Ehdr.e_type == ET_CORE) {
+ /*
+ * If the next segment is also a note, use it instead.
+ */
+ if (get_phdr(EI, inx+1) == ELF_READ_FAIL)
+ return (ELF_READ_FAIL);
+ if (phdr->p_type != PT_NOTE) {
+ /* read the first phdr back */
+ if (get_phdr(EI, inx) == ELF_READ_FAIL)
+ return (ELF_READ_FAIL);
+ }
+ offset = phdr->p_offset;
+ if (pread64(EI->elffd, (void *)nhdr, nsz, offset)
+ != nsz)
+ return (ELF_READ_FAIL);
+
+ /* Translate the ELF note header */
+ if (xlatetom_nhdr(nhdr) == ELF_READ_FAIL)
+ return (ELF_READ_FAIL);
+
+ ntype = nhdr->n_type;
+ nmsz = nhdr->n_namesz;
+ dsz = nhdr->n_descsz;
+
+ offset += nsz + ((nmsz + 0x03) & ~0x3);
+ if ((psinfo = malloc(dsz)) == NULL) {
+ int err = errno;
+ (void) fprintf(stderr, gettext("%s: malloc "
+ "failed: %s\n"), File, strerror(err));
+ exit(1);
+ }
+ if (pread64(EI->elffd, psinfo, dsz, offset) != dsz)
+ return (ELF_READ_FAIL);
+ /*
+ * We want to print the string contained
+ * in psinfo->pr_fname[], where 'psinfo'
+ * is either an old NT_PRPSINFO structure
+ * or a new NT_PSINFO structure.
+ *
+ * Old core files have only type NT_PRPSINFO.
+ * New core files have type NT_PSINFO.
+ *
+ * These structures are also different by
+ * virtue of being contained in a core file
+ * of either 32-bit or 64-bit type.
+ *
+ * To further complicate matters, we ourself
+ * might be compiled either 32-bit or 64-bit.
+ *
+ * For these reason, we just *know* the offsets of
+ * pr_fname[] into the four different structures
+ * here, regardless of how we are compiled.
+ */
+ if (class == ELFCLASS32) {
+ /* 32-bit core file, 32-bit structures */
+ if (ntype == NT_PSINFO)
+ fname = psinfo + 88;
+ else /* old: NT_PRPSINFO */
+ fname = psinfo + 84;
+ } else if (class == ELFCLASS64) {
+ /* 64-bit core file, 64-bit structures */
+ if (ntype == NT_PSINFO)
+ fname = psinfo + 136;
+ else /* old: NT_PRPSINFO */
+ fname = psinfo + 120;
+ }
+ EI->core_type = (ntype == NT_PRPSINFO)?
+ EC_OLDCORE : EC_NEWCORE;
+ (void) memcpy(EI->fname, fname, strlen(fname));
+ free(psinfo);
+ }
+ if (phdr->p_type == PT_DYNAMIC) {
+ EI->dynamic = B_TRUE;
+ }
+ }
+ return (ELF_READ_OKAY);
+}
+
+/*
+ * process_shdr: Read Section Headers to attempt to get HW/SW
+ * capabilities by looking at the SUNW_cap
+ * section and set string in Elf_Info.
+ * Also look for symbol tables and debug
+ * information sections. Set the "stripped" field
+ * in Elf_Info with corresponding flags.
+ */
+static int
+process_shdr(Elf_Info *EI)
+{
+ int capn, mac;
+ int i, j, idx;
+ off_t cap_off;
+ size_t csize;
+ char *section_name;
+ Elf_Cap Chdr;
+ Elf_Shdr *shdr = &EI_Shdr;
+
+
+ csize = sizeof (Elf_Cap);
+ mac = EI_Ehdr.e_machine;
+
+ /* if there are no sections, return success anyway */
+ if (EI_Ehdr.e_shoff == 0 && EI_Ehdr.e_shnum == 0)
+ return (ELF_READ_OKAY);
+
+ /* read section names from String Section */
+ if (get_shdr(EI, EI_Ehdr.e_shstrndx) == ELF_READ_FAIL)
+ return (ELF_READ_FAIL);
+
+ if ((section_name = malloc(shdr->sh_size)) == NULL)
+ return (ELF_READ_FAIL);
+
+ if (pread64(EI->elffd, section_name, shdr->sh_size, shdr->sh_offset)
+ != shdr->sh_size)
+ return (ELF_READ_FAIL);
+
+ /* read all the sections and process them */
+ for (idx = 1, i = 0; i < EI_Ehdr.e_shnum; idx++, i++) {
+ char *str;
+
+ if (get_shdr(EI, i) == ELF_READ_FAIL)
+ return (ELF_READ_FAIL);
+
+ if (shdr->sh_type == SHT_NULL) {
+ idx--;
+ continue;
+ }
+
+ cap_off = shdr->sh_offset;
+ if (shdr->sh_type == SHT_SUNW_cap) {
+ if (shdr->sh_size == 0 || shdr->sh_entsize == 0) {
+ (void) fprintf(stderr, ELF_ERR_ELFCAP1,
+ File, EI->file);
+ return (ELF_READ_FAIL);
+ }
+ capn = (shdr->sh_size / shdr->sh_entsize);
+ for (j = 0; j < capn; j++) {
+ /*
+ * read cap and xlate the values
+ */
+ if (pread64(EI->elffd, &Chdr, csize, cap_off)
+ != csize ||
+ file_xlatetom(ELF_T_CAP, (char *)&Chdr)
+ == 0) {
+ (void) fprintf(stderr, ELF_ERR_ELFCAP2,
+ File, EI->file);
+ return (ELF_READ_FAIL);
+ }
+
+ if (Chdr.c_tag != CA_SUNW_NULL) {
+ (void) cap_val2str(Chdr.c_tag,
+ Chdr.c_un.c_val,
+ EI->cap_str,
+ sizeof (EI->cap_str),
+ 0, mac);
+ }
+ cap_off += csize;
+ }
+ }
+
+ /*
+ * Definition time:
+ * - "not stripped" means that an executable file
+ * contains a Symbol Table (.symtab)
+ * - "stripped" means that an executable file
+ * does not contain a Symbol Table.
+ * When strip -l or strip -x is run, it strips the
+ * debugging information (.line section name (strip -l),
+ * .line, .debug*, .stabs*, .dwarf* section names
+ * and SHT_SUNW_DEBUGSTR and SHT_SUNW_DEBUG
+ * section types (strip -x), however the Symbol
+ * Table will still be present.
+ * Therefore, if
+ * - No Symbol Table present, then report
+ * "stripped"
+ * - Symbol Table present with debugging
+ * information (line number or debug section names,
+ * or SHT_SUNW_DEBUGSTR or SHT_SUNW_DEBUG section
+ * types) then report:
+ * "not stripped"
+ * - Symbol Table present with no debugging
+ * information (line number or debug section names,
+ * or SHT_SUNW_DEBUGSTR or SHT_SUNW_DEBUG section
+ * types) then report:
+ * "not stripped, no debugging information
+ * available"
+ */
+ if ((EI->stripped & E_NOSTRIP) == E_NOSTRIP)
+ continue;
+
+ if (!(EI->stripped & E_SYMTAB) &&
+ (shdr->sh_type == SHT_SYMTAB)) {
+ EI->stripped |= E_SYMTAB;
+ continue;
+ }
+
+ str = &section_name[shdr->sh_name];
+
+ if (!(EI->stripped & E_DBGINF) &&
+ ((shdr->sh_type == SHT_SUNW_DEBUG) ||
+ (shdr->sh_type == SHT_SUNW_DEBUGSTR) ||
+ (is_in_list(str)))) {
+ EI->stripped |= E_DBGINF;
+ }
+ }
+ free(section_name);
+
+ return (ELF_READ_OKAY);
+}