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