Home | History | Annotate | Download | only in os
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License, Version 1.0 only
      6  * (the "License").  You may not use this file except in compliance
      7  * with the License.
      8  *
      9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10  * or http://www.opensolaris.org/os/licensing.
     11  * See the License for the specific language governing permissions
     12  * and limitations under the License.
     13  *
     14  * When distributing Covered Code, include this CDDL HEADER in each
     15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16  * If applicable, add the following below this CDDL HEADER, with the
     17  * fields enclosed by brackets "[]" replaced with your own identifying
     18  * information: Portions Copyright [yyyy] [name of copyright owner]
     19  *
     20  * CDDL HEADER END
     21  */
     22 /*
     23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #pragma ident	"@(#)ksyms_snapshot.c	1.5	05/06/08 SMI"
     28 
     29 #include <sys/ksyms.h>
     30 #include <sys/systm.h>
     31 #include <sys/sysmacros.h>
     32 #include <sys/debug.h>
     33 #include <sys/cmn_err.h>
     34 
     35 static const char ksyms_shstrtab[] = "\0.symtab\0.strtab\0.shstrtab\0";
     36 
     37 #define	KSHDR_NULL	0
     38 #define	KSHDR_SYMTAB	1
     39 #define	KSHDR_STRTAB	2
     40 #define	KSHDR_SHSTRTAB	3
     41 #define	KSHDR_NUM	4
     42 
     43 typedef struct ksyms_header {
     44 	Ehdr	elf_hdr;		/* Elf file header */
     45 	Phdr	text_phdr;		/* text program header */
     46 	Phdr	data_phdr;		/* data program header */
     47 	Shdr	shdr[KSHDR_NUM];	/* section headers */
     48 	char	shstrings[sizeof (ksyms_shstrtab)];	/* shstrtab strings */
     49 } ksyms_header_t;
     50 
     51 #define	KW_HEADER	0x1
     52 #define	KW_LOCALS	0x2
     53 #define	KW_GLOBALS	0x4
     54 #define	KW_STRINGS	0x8
     55 
     56 typedef struct ksyms_walkinfo {
     57 	void	(*kw_emit)(const void *, void *, size_t);
     58 	char	*kw_target;
     59 	ssize_t	kw_resid;
     60 	ssize_t kw_totalsize;
     61 	int	kw_actions;
     62 	size_t	kw_size[KW_STRINGS + 1];
     63 } ksyms_walkinfo_t;
     64 
     65 krwlock_t ksyms_lock;
     66 vmem_t *ksyms_arena;
     67 
     68 static void
     69 ksyms_emit(ksyms_walkinfo_t *kwp, void *src, size_t size, int action)
     70 {
     71 	if (kwp->kw_actions & action) {
     72 		if ((kwp->kw_resid -= size) >= 0)
     73 			kwp->kw_emit(src, kwp->kw_target, size);
     74 		kwp->kw_totalsize += size;
     75 	}
     76 	kwp->kw_size[action] += size;
     77 }
     78 
     79 /*ARGSUSED*/
     80 static void
     81 ksyms_walk_one(void *arg, void *base, size_t size)
     82 {
     83 	ksyms_walkinfo_t *kwp = arg;
     84 	Shdr *symhdr = base;
     85 	Shdr *strhdr = symhdr + symhdr->sh_link;
     86 	size_t symsize = symhdr->sh_entsize;
     87 	size_t nsyms = symhdr->sh_size / symsize;
     88 	char *strings = (char *)strhdr->sh_addr;
     89 	int i;
     90 
     91 	for (i = 1; i < nsyms; i++) {
     92 		Sym *sym = (Sym *)(symhdr->sh_addr + i * symsize);
     93 		Sym tmp = *sym;
     94 		char *name = strings + sym->st_name;
     95 		tmp.st_name = kwp->kw_size[KW_STRINGS];
     96 		tmp.st_shndx = SHN_ABS;
     97 		ksyms_emit(kwp, &tmp, sizeof (Sym),
     98 		    ELF_ST_BIND(sym->st_info) == STB_LOCAL ?
     99 		    KW_LOCALS : KW_GLOBALS);
    100 		ksyms_emit(kwp, name, strlen(name) + 1, KW_STRINGS);
    101 	}
    102 }
    103 
    104 static ssize_t
    105 ksyms_walk(ksyms_walkinfo_t *kwp, void *target, ssize_t resid,
    106 	void (*emit)(const void *, void *, size_t), void *src, int actions)
    107 {
    108 	Sym tmp;
    109 
    110 	bzero(kwp, sizeof (ksyms_walkinfo_t));
    111 	kwp->kw_emit = emit;
    112 	kwp->kw_target = target;
    113 	kwp->kw_resid = resid;
    114 	kwp->kw_actions = actions;
    115 
    116 	ksyms_emit(kwp, src, sizeof (ksyms_header_t), KW_HEADER);
    117 	/*
    118 	 * The first symbol table entry is all zeroes; it's unused
    119 	 * because index 0 marks the end of symbol hash chains.
    120 	 */
    121 	bzero(&tmp, sizeof (Sym));
    122 	ksyms_emit(kwp, &tmp, sizeof (Sym), KW_LOCALS);
    123 	ksyms_emit(kwp, &tmp, 1, KW_STRINGS);
    124 	vmem_walk(ksyms_arena, VMEM_ALLOC, ksyms_walk_one, kwp);
    125 	return (kwp->kw_totalsize);
    126 }
    127 
    128 size_t
    129 ksyms_snapshot(void (*emit)(const void *, void *, size_t),
    130 	void *buf, size_t len)
    131 {
    132 	ksyms_walkinfo_t kw;
    133 	ksyms_header_t hdr;
    134 	ssize_t size = 0, bufsize = len;
    135 	Shdr *shp;
    136 
    137 	rw_enter(&ksyms_lock, RW_READER);
    138 
    139 	/*
    140 	 * Compute the size of the header, locals, globals, and strings.
    141 	 */
    142 	(void) ksyms_walk(&kw, NULL, 0, NULL, NULL,
    143 	    KW_HEADER | KW_LOCALS | KW_GLOBALS | KW_STRINGS);
    144 
    145 	/*
    146 	 * Construct the ELF header.
    147 	 */
    148 	bzero(&hdr, sizeof (hdr));
    149 
    150 	hdr.elf_hdr = ((struct module *)modules.mod_mp)->hdr;
    151 	hdr.elf_hdr.e_phoff = offsetof(ksyms_header_t, text_phdr);
    152 	hdr.elf_hdr.e_shoff = offsetof(ksyms_header_t, shdr);
    153 	hdr.elf_hdr.e_phnum = 2;
    154 	hdr.elf_hdr.e_shnum = KSHDR_NUM;
    155 	hdr.elf_hdr.e_shstrndx = KSHDR_SHSTRTAB;
    156 
    157 	hdr.text_phdr.p_type = PT_LOAD;
    158 	hdr.text_phdr.p_vaddr = (Addr)s_text;
    159 	hdr.text_phdr.p_memsz = (Word)(e_text - s_text);
    160 	hdr.text_phdr.p_flags = PF_R | PF_X;
    161 
    162 	hdr.data_phdr.p_type = PT_LOAD;
    163 	hdr.data_phdr.p_vaddr = (Addr)s_data;
    164 	hdr.data_phdr.p_memsz = (Word)(e_data - s_data);
    165 	hdr.data_phdr.p_flags = PF_R | PF_W | PF_X;
    166 
    167 	shp = &hdr.shdr[KSHDR_SYMTAB];
    168 	shp->sh_name = 1;	/* ksyms_shstrtab[1] = ".symtab" */
    169 	shp->sh_type = SHT_SYMTAB;
    170 	shp->sh_offset = kw.kw_size[KW_HEADER];
    171 	shp->sh_size = kw.kw_size[KW_LOCALS] + kw.kw_size[KW_GLOBALS];
    172 	shp->sh_link = KSHDR_STRTAB;
    173 	shp->sh_info = kw.kw_size[KW_LOCALS] / sizeof (Sym);
    174 	shp->sh_addralign = sizeof (Addr);
    175 	shp->sh_entsize = sizeof (Sym);
    176 
    177 	shp = &hdr.shdr[KSHDR_STRTAB];
    178 	shp->sh_name = 9;	/* ksyms_shstrtab[9] = ".strtab" */
    179 	shp->sh_type = SHT_STRTAB;
    180 	shp->sh_offset = kw.kw_size[KW_HEADER] +
    181 	    kw.kw_size[KW_LOCALS] + kw.kw_size[KW_GLOBALS];
    182 	shp->sh_size = kw.kw_size[KW_STRINGS];
    183 	shp->sh_addralign = 1;
    184 
    185 	shp = &hdr.shdr[KSHDR_SHSTRTAB];
    186 	shp->sh_name = 17;	/* ksyms_shstrtab[17] = ".shstrtab" */
    187 	shp->sh_type = SHT_STRTAB;
    188 	shp->sh_offset = offsetof(ksyms_header_t, shstrings);
    189 	shp->sh_size = sizeof (ksyms_shstrtab);
    190 	shp->sh_addralign = 1;
    191 
    192 	bcopy(ksyms_shstrtab, hdr.shstrings, sizeof (ksyms_shstrtab));
    193 
    194 	/*
    195 	 * Emit the symbol table.
    196 	 */
    197 	size += ksyms_walk(&kw, buf, (bufsize - size), emit, &hdr,
    198 					    KW_HEADER);
    199 	size += ksyms_walk(&kw, buf, (bufsize - size), emit,
    200 					    NULL, KW_LOCALS);
    201 	size += ksyms_walk(&kw, buf, (bufsize - size), emit,
    202 					    NULL, KW_GLOBALS);
    203 	size += ksyms_walk(&kw, buf, (bufsize - size), emit, NULL,
    204 					    KW_STRINGS);
    205 
    206 	rw_exit(&ksyms_lock);
    207 
    208 	return ((size_t)size);
    209 }
    210