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 #include <sys/ksyms.h>
28 #include <sys/systm.h>
29 #include <sys/sysmacros.h>
30 #include <sys/debug.h>
31 #include <sys/cmn_err.h>
32
33 static const char ksyms_shstrtab[] = "\0.symtab\0.strtab\0.shstrtab\0";
34
35 #define KSHDR_NULL 0
36 #define KSHDR_SYMTAB 1
37 #define KSHDR_STRTAB 2
38 #define KSHDR_SHSTRTAB 3
39 #define KSHDR_NUM 4
40
41 typedef struct ksyms_header {
42 Ehdr elf_hdr; /* Elf file header */
43 Phdr text_phdr; /* text program header */
44 Phdr data_phdr; /* data program header */
45 Shdr shdr[KSHDR_NUM]; /* section headers */
46 char shstrings[sizeof (ksyms_shstrtab)]; /* shstrtab strings */
47 } ksyms_header_t;
48
49 #define KW_HEADER 0x1
50 #define KW_LOCALS 0x2
51 #define KW_GLOBALS 0x4
52 #define KW_STRINGS 0x8
53
54 typedef struct ksyms_walkinfo {
55 void (*kw_emit)(const void *, void *, size_t);
56 char *kw_target;
57 ssize_t kw_resid;
58 ssize_t kw_totalsize;
59 int kw_actions;
60 size_t kw_size[KW_STRINGS + 1];
61 } ksyms_walkinfo_t;
62
63 krwlock_t ksyms_lock;
64 vmem_t *ksyms_arena;
65
66 static void
ksyms_emit(ksyms_walkinfo_t * kwp,void * src,size_t size,int action)67 ksyms_emit(ksyms_walkinfo_t *kwp, void *src, size_t size, int action)
68 {
69 if (kwp->kw_actions & action) {
70 if ((kwp->kw_resid -= size) >= 0)
71 kwp->kw_emit(src, kwp->kw_target, size);
72 kwp->kw_totalsize += size;
73 }
74 kwp->kw_size[action] += size;
75 }
76
77 /*ARGSUSED*/
78 static void
ksyms_walk_one(void * arg,void * base,size_t size)79 ksyms_walk_one(void *arg, void *base, size_t size)
80 {
81 ksyms_walkinfo_t *kwp = arg;
82 Shdr *symhdr = base;
83 Shdr *strhdr = symhdr + symhdr->sh_link;
84 size_t symsize = symhdr->sh_entsize;
85 size_t nsyms = symhdr->sh_size / symsize;
86 char *strings = (char *)strhdr->sh_addr;
87 int i;
88
89 for (i = 1; i < nsyms; i++) {
90 Sym *sym = (Sym *)(symhdr->sh_addr + i * symsize);
91 Sym tmp = *sym;
92 char *name = strings + sym->st_name;
93 tmp.st_name = kwp->kw_size[KW_STRINGS];
94 tmp.st_shndx = SHN_ABS;
95 ksyms_emit(kwp, &tmp, sizeof (Sym),
96 ELF_ST_BIND(sym->st_info) == STB_LOCAL ?
97 KW_LOCALS : KW_GLOBALS);
98 ksyms_emit(kwp, name, strlen(name) + 1, KW_STRINGS);
99 }
100 }
101
102 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 ksyms_walk(ksyms_walkinfo_t *kwp, void *target, ssize_t resid,
104 void (*emit)(const void *, void *, size_t), void *src, int actions)
105 {
106 Sym tmp;
107
108 bzero(kwp, sizeof (ksyms_walkinfo_t));
109 kwp->kw_emit = emit;
110 kwp->kw_target = target;
111 kwp->kw_resid = resid;
112 kwp->kw_actions = actions;
113
114 ksyms_emit(kwp, src, sizeof (ksyms_header_t), KW_HEADER);
115 /*
116 * The first symbol table entry is all zeroes; it's unused
117 * because index 0 marks the end of symbol hash chains.
118 */
119 bzero(&tmp, sizeof (Sym));
120 ksyms_emit(kwp, &tmp, sizeof (Sym), KW_LOCALS);
121 ksyms_emit(kwp, &tmp, 1, KW_STRINGS);
122 vmem_walk(ksyms_arena, VMEM_ALLOC, ksyms_walk_one, kwp);
123 return (kwp->kw_totalsize);
124 }
125
126 size_t
ksyms_snapshot(void (* emit)(const void *,void *,size_t),void * buf,size_t len)127 ksyms_snapshot(void (*emit)(const void *, void *, size_t),
128 void *buf, size_t len)
129 {
130 ksyms_walkinfo_t kw;
131 ksyms_header_t hdr;
132 ssize_t size = 0, bufsize = len;
133 Shdr *shp;
134
135 rw_enter(&ksyms_lock, RW_READER);
136
137 /*
138 * Compute the size of the header, locals, globals, and strings.
139 */
140 (void) ksyms_walk(&kw, NULL, 0, NULL, NULL,
141 KW_HEADER | KW_LOCALS | KW_GLOBALS | KW_STRINGS);
142
143 /*
144 * Construct the ELF header.
145 */
146 bzero(&hdr, sizeof (hdr));
147
148 hdr.elf_hdr = ((struct module *)modules.mod_mp)->hdr;
149 hdr.elf_hdr.e_phoff = offsetof(ksyms_header_t, text_phdr);
150 hdr.elf_hdr.e_shoff = offsetof(ksyms_header_t, shdr);
151 hdr.elf_hdr.e_phnum = 2;
152 hdr.elf_hdr.e_shnum = KSHDR_NUM;
153 hdr.elf_hdr.e_shstrndx = KSHDR_SHSTRTAB;
154
155 hdr.text_phdr.p_type = PT_LOAD;
156 hdr.text_phdr.p_vaddr = (Addr)s_text;
157 hdr.text_phdr.p_memsz = (Word)(e_text - s_text);
158 hdr.text_phdr.p_flags = PF_R | PF_X;
159
160 hdr.data_phdr.p_type = PT_LOAD;
161 hdr.data_phdr.p_vaddr = (Addr)s_data;
162 hdr.data_phdr.p_memsz = (Word)(e_data - s_data);
163 hdr.data_phdr.p_flags = PF_R | PF_W | PF_X;
164
165 shp = &hdr.shdr[KSHDR_SYMTAB];
166 shp->sh_name = 1; /* ksyms_shstrtab[1] = ".symtab" */
167 shp->sh_type = SHT_SYMTAB;
168 shp->sh_offset = kw.kw_size[KW_HEADER];
169 shp->sh_size = kw.kw_size[KW_LOCALS] + kw.kw_size[KW_GLOBALS];
170 shp->sh_link = KSHDR_STRTAB;
171 shp->sh_info = kw.kw_size[KW_LOCALS] / sizeof (Sym);
172 shp->sh_addralign = sizeof (Addr);
173 shp->sh_entsize = sizeof (Sym);
174
175 shp = &hdr.shdr[KSHDR_STRTAB];
176 shp->sh_name = 9; /* ksyms_shstrtab[9] = ".strtab" */
177 shp->sh_type = SHT_STRTAB;
178 shp->sh_offset = kw.kw_size[KW_HEADER] +
179 kw.kw_size[KW_LOCALS] + kw.kw_size[KW_GLOBALS];
180 shp->sh_size = kw.kw_size[KW_STRINGS];
181 shp->sh_addralign = 1;
182
183 shp = &hdr.shdr[KSHDR_SHSTRTAB];
184 shp->sh_name = 17; /* ksyms_shstrtab[17] = ".shstrtab" */
185 shp->sh_type = SHT_STRTAB;
186 shp->sh_offset = offsetof(ksyms_header_t, shstrings);
187 shp->sh_size = sizeof (ksyms_shstrtab);
188 shp->sh_addralign = 1;
189
190 bcopy(ksyms_shstrtab, hdr.shstrings, sizeof (ksyms_shstrtab));
191
192 /*
193 * Emit the symbol table.
194 */
195 size += ksyms_walk(&kw, buf, (bufsize - size), emit, &hdr,
196 KW_HEADER);
197 size += ksyms_walk(&kw, buf, (bufsize - size), emit,
198 NULL, KW_LOCALS);
199 size += ksyms_walk(&kw, buf, (bufsize - size), emit,
200 NULL, KW_GLOBALS);
201 size += ksyms_walk(&kw, buf, (bufsize - size), emit, NULL,
202 KW_STRINGS);
203
204 rw_exit(&ksyms_lock);
205
206 return ((size_t)size);
207 }
208