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	"%Z%%M%	%I%	%E% 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
35static 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
43typedef 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
56typedef 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
65krwlock_t ksyms_lock;
66vmem_t *ksyms_arena;
67
68static void
69ksyms_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*/
80static void
81ksyms_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
104static ssize_t
105ksyms_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
128size_t
129ksyms_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