1 | /* $NetBSD: kern_ksyms.c,v 1.84 2016/07/07 06:55:43 msaitoh Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2008 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software developed for The NetBSD Foundation |
8 | * by Andrew Doran. |
9 | * |
10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions |
12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. |
18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ |
31 | |
32 | /* |
33 | * Copyright (c) 2001, 2003 Anders Magnusson (ragge@ludd.luth.se). |
34 | * All rights reserved. |
35 | * |
36 | * Redistribution and use in source and binary forms, with or without |
37 | * modification, are permitted provided that the following conditions |
38 | * are met: |
39 | * 1. Redistributions of source code must retain the above copyright |
40 | * notice, this list of conditions and the following disclaimer. |
41 | * 2. Redistributions in binary form must reproduce the above copyright |
42 | * notice, this list of conditions and the following disclaimer in the |
43 | * documentation and/or other materials provided with the distribution. |
44 | * 3. The name of the author may not be used to endorse or promote products |
45 | * derived from this software without specific prior written permission |
46 | * |
47 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
48 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
49 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
50 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
51 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
52 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
53 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
54 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
55 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
56 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
57 | */ |
58 | |
59 | /* |
60 | * Code to deal with in-kernel symbol table management + /dev/ksyms. |
61 | * |
62 | * For each loaded module the symbol table info is kept track of by a |
63 | * struct, placed in a circular list. The first entry is the kernel |
64 | * symbol table. |
65 | */ |
66 | |
67 | /* |
68 | * TODO: |
69 | * |
70 | * Add support for mmap, poll. |
71 | * Constify tables. |
72 | * Constify db_symtab and move it to .rodata. |
73 | */ |
74 | |
75 | #include <sys/cdefs.h> |
76 | __KERNEL_RCSID(0, "$NetBSD: kern_ksyms.c,v 1.84 2016/07/07 06:55:43 msaitoh Exp $" ); |
77 | |
78 | #if defined(_KERNEL) && defined(_KERNEL_OPT) |
79 | #include "opt_copy_symtab.h" |
80 | #include "opt_ddb.h" |
81 | #include "opt_dtrace.h" |
82 | #endif |
83 | |
84 | #define _KSYMS_PRIVATE |
85 | |
86 | #include <sys/param.h> |
87 | #include <sys/queue.h> |
88 | #include <sys/exec.h> |
89 | #include <sys/systm.h> |
90 | #include <sys/conf.h> |
91 | #include <sys/kmem.h> |
92 | #include <sys/proc.h> |
93 | #include <sys/atomic.h> |
94 | #include <sys/ksyms.h> |
95 | |
96 | #ifdef DDB |
97 | #include <ddb/db_output.h> |
98 | #endif |
99 | |
100 | #include "ksyms.h" |
101 | #if NKSYMS > 0 |
102 | #include "ioconf.h" |
103 | #endif |
104 | |
105 | #define KSYMS_MAX_ID 98304 |
106 | #ifdef KDTRACE_HOOKS |
107 | static uint32_t ksyms_nmap[KSYMS_MAX_ID]; /* sorted symbol table map */ |
108 | #else |
109 | static uint32_t *ksyms_nmap = NULL; |
110 | #endif |
111 | |
112 | static int ksyms_maxlen; |
113 | static bool ksyms_isopen; |
114 | static bool ksyms_initted; |
115 | static bool ksyms_loaded; |
116 | static kmutex_t ksyms_lock __cacheline_aligned; |
117 | static struct ksyms_symtab kernel_symtab; |
118 | |
119 | static void ksyms_hdr_init(const void *); |
120 | static void ksyms_sizes_calc(void); |
121 | |
122 | #ifdef KSYMS_DEBUG |
123 | #define FOLLOW_CALLS 1 |
124 | #define FOLLOW_MORE_CALLS 2 |
125 | #define FOLLOW_DEVKSYMS 4 |
126 | static int ksyms_debug; |
127 | #endif |
128 | |
129 | #define SYMTAB_FILLER "|This is the symbol table!" |
130 | |
131 | #ifdef makeoptions_COPY_SYMTAB |
132 | extern char db_symtab[]; |
133 | extern int db_symtabsize; |
134 | #endif |
135 | |
136 | /* |
137 | * used by savecore(8) so non-static |
138 | */ |
139 | struct ksyms_hdr ksyms_hdr; |
140 | int ksyms_symsz; |
141 | int ksyms_strsz; |
142 | int ksyms_ctfsz; /* this is not currently used by savecore(8) */ |
143 | TAILQ_HEAD(, ksyms_symtab) ksyms_symtabs = |
144 | TAILQ_HEAD_INITIALIZER(ksyms_symtabs); |
145 | |
146 | static int |
147 | ksyms_verify(const void *symstart, const void *strstart) |
148 | { |
149 | #if defined(DIAGNOSTIC) || defined(DEBUG) |
150 | if (symstart == NULL) |
151 | printf("ksyms: Symbol table not found\n" ); |
152 | if (strstart == NULL) |
153 | printf("ksyms: String table not found\n" ); |
154 | if (symstart == NULL || strstart == NULL) |
155 | printf("ksyms: Perhaps the kernel is stripped?\n" ); |
156 | #endif |
157 | if (symstart == NULL || strstart == NULL) |
158 | return 0; |
159 | return 1; |
160 | } |
161 | |
162 | /* |
163 | * Finds a certain symbol name in a certain symbol table. |
164 | */ |
165 | static Elf_Sym * |
166 | findsym(const char *name, struct ksyms_symtab *table, int type) |
167 | { |
168 | Elf_Sym *sym, *maxsym; |
169 | int low, mid, high, nglob; |
170 | char *str, *cmp; |
171 | |
172 | sym = table->sd_symstart; |
173 | str = table->sd_strstart - table->sd_usroffset; |
174 | nglob = table->sd_nglob; |
175 | low = 0; |
176 | high = nglob; |
177 | |
178 | /* |
179 | * Start with a binary search of all global symbols in this table. |
180 | * Global symbols must have unique names. |
181 | */ |
182 | while (low < high) { |
183 | mid = (low + high) >> 1; |
184 | cmp = sym[mid].st_name + str; |
185 | if (cmp[0] < name[0] || strcmp(cmp, name) < 0) { |
186 | low = mid + 1; |
187 | } else { |
188 | high = mid; |
189 | } |
190 | } |
191 | KASSERT(low == high); |
192 | if (__predict_true(low < nglob && |
193 | strcmp(sym[low].st_name + str, name) == 0)) { |
194 | KASSERT(ELF_ST_BIND(sym[low].st_info) == STB_GLOBAL); |
195 | return &sym[low]; |
196 | } |
197 | |
198 | /* |
199 | * Perform a linear search of local symbols (rare). Many local |
200 | * symbols with the same name can exist so are not included in |
201 | * the binary search. |
202 | */ |
203 | if (type != KSYMS_EXTERN) { |
204 | maxsym = sym + table->sd_symsize / sizeof(Elf_Sym); |
205 | for (sym += nglob; sym < maxsym; sym++) { |
206 | if (strcmp(name, sym->st_name + str) == 0) { |
207 | return sym; |
208 | } |
209 | } |
210 | } |
211 | return NULL; |
212 | } |
213 | |
214 | /* |
215 | * The "attach" is in reality done in ksyms_init(). |
216 | */ |
217 | #if NKSYMS > 0 |
218 | /* |
219 | * ksyms can be loaded even if the kernel has a missing "pseudo-device ksyms" |
220 | * statement because ddb and modules require it. Fixing it properly requires |
221 | * fixing config to warn about required, but missing preudo-devices. For now, |
222 | * if we don't have the pseudo-device we don't need the attach function; this |
223 | * is fine, as it does nothing. |
224 | */ |
225 | void |
226 | ksymsattach(int arg) |
227 | { |
228 | } |
229 | #endif |
230 | |
231 | void |
232 | ksyms_init(void) |
233 | { |
234 | |
235 | #ifdef makeoptions_COPY_SYMTAB |
236 | if (!ksyms_loaded && |
237 | strncmp(db_symtab, SYMTAB_FILLER, sizeof(SYMTAB_FILLER))) { |
238 | ksyms_addsyms_elf(db_symtabsize, db_symtab, |
239 | db_symtab + db_symtabsize); |
240 | } |
241 | #endif |
242 | |
243 | if (!ksyms_initted) { |
244 | mutex_init(&ksyms_lock, MUTEX_DEFAULT, IPL_NONE); |
245 | ksyms_initted = true; |
246 | } |
247 | } |
248 | |
249 | /* |
250 | * Add a symbol table. |
251 | * This is intended for use when the symbol table and its corresponding |
252 | * string table are easily available. If they are embedded in an ELF |
253 | * image, use addsymtab_elf() instead. |
254 | * |
255 | * name - Symbol's table name. |
256 | * symstart, symsize - Address and size of the symbol table. |
257 | * strstart, strsize - Address and size of the string table. |
258 | * tab - Symbol table to be updated with this information. |
259 | * newstart - Address to which the symbol table has to be copied during |
260 | * shrinking. If NULL, it is not moved. |
261 | */ |
262 | static const char *addsymtab_strstart; |
263 | |
264 | static int |
265 | addsymtab_compar(const void *a, const void *b) |
266 | { |
267 | const Elf_Sym *sa, *sb; |
268 | |
269 | sa = a; |
270 | sb = b; |
271 | |
272 | /* |
273 | * Split the symbol table into two, with globals at the start |
274 | * and locals at the end. |
275 | */ |
276 | if (ELF_ST_BIND(sa->st_info) != ELF_ST_BIND(sb->st_info)) { |
277 | if (ELF_ST_BIND(sa->st_info) == STB_GLOBAL) { |
278 | return -1; |
279 | } |
280 | if (ELF_ST_BIND(sb->st_info) == STB_GLOBAL) { |
281 | return 1; |
282 | } |
283 | } |
284 | |
285 | /* Within each band, sort by name. */ |
286 | return strcmp(sa->st_name + addsymtab_strstart, |
287 | sb->st_name + addsymtab_strstart); |
288 | } |
289 | |
290 | static void |
291 | addsymtab(const char *name, void *symstart, size_t symsize, |
292 | void *strstart, size_t strsize, struct ksyms_symtab *tab, |
293 | void *newstart, void *ctfstart, size_t ctfsize, uint32_t *nmap) |
294 | { |
295 | Elf_Sym *sym, *nsym, ts; |
296 | int i, j, n, nglob; |
297 | char *str; |
298 | int nsyms = symsize / sizeof(Elf_Sym); |
299 | |
300 | /* Sanity check for pre-allocated map table used during startup. */ |
301 | if ((nmap == ksyms_nmap) && (nsyms >= KSYMS_MAX_ID)) { |
302 | printf("kern_ksyms: ERROR %d > %d, increase KSYMS_MAX_ID\n" , |
303 | nsyms, KSYMS_MAX_ID); |
304 | |
305 | /* truncate for now */ |
306 | nsyms = KSYMS_MAX_ID - 1; |
307 | } |
308 | |
309 | tab->sd_symstart = symstart; |
310 | tab->sd_symsize = symsize; |
311 | tab->sd_strstart = strstart; |
312 | tab->sd_strsize = strsize; |
313 | tab->sd_name = name; |
314 | tab->sd_minsym = UINTPTR_MAX; |
315 | tab->sd_maxsym = 0; |
316 | tab->sd_usroffset = 0; |
317 | tab->sd_gone = false; |
318 | tab->sd_ctfstart = ctfstart; |
319 | tab->sd_ctfsize = ctfsize; |
320 | tab->sd_nmap = nmap; |
321 | tab->sd_nmapsize = nsyms; |
322 | #ifdef KSYMS_DEBUG |
323 | printf("newstart %p sym %p ksyms_symsz %zu str %p strsz %zu send %p\n" , |
324 | newstart, symstart, symsize, strstart, strsize, |
325 | tab->sd_strstart + tab->sd_strsize); |
326 | #endif |
327 | |
328 | if (nmap) { |
329 | memset(nmap, 0, nsyms * sizeof(uint32_t)); |
330 | } |
331 | |
332 | /* Pack symbol table by removing all file name references. */ |
333 | sym = tab->sd_symstart; |
334 | nsym = (Elf_Sym *)newstart; |
335 | str = tab->sd_strstart; |
336 | nglob = 0; |
337 | for (i = n = 0; i < nsyms; i++) { |
338 | |
339 | /* This breaks CTF mapping, so don't do it when |
340 | * DTrace is enabled |
341 | */ |
342 | #ifndef KDTRACE_HOOKS |
343 | /* |
344 | * Remove useless symbols. |
345 | * Should actually remove all typeless symbols. |
346 | */ |
347 | if (sym[i].st_name == 0) |
348 | continue; /* Skip nameless entries */ |
349 | if (sym[i].st_shndx == SHN_UNDEF) |
350 | continue; /* Skip external references */ |
351 | if (ELF_ST_TYPE(sym[i].st_info) == STT_FILE) |
352 | continue; /* Skip filenames */ |
353 | if (ELF_ST_TYPE(sym[i].st_info) == STT_NOTYPE && |
354 | sym[i].st_value == 0 && |
355 | strcmp(str + sym[i].st_name, "*ABS*" ) == 0) |
356 | continue; /* XXX */ |
357 | if (ELF_ST_TYPE(sym[i].st_info) == STT_NOTYPE && |
358 | strcmp(str + sym[i].st_name, "gcc2_compiled." ) == 0) |
359 | continue; /* XXX */ |
360 | #endif |
361 | |
362 | /* Save symbol. Set it as an absolute offset */ |
363 | nsym[n] = sym[i]; |
364 | |
365 | #ifdef KDTRACE_HOOKS |
366 | if (nmap != NULL) { |
367 | /* |
368 | * Save the size, replace it with the symbol id so |
369 | * the mapping can be done after the cleanup and sort. |
370 | */ |
371 | nmap[i] = nsym[n].st_size; |
372 | nsym[n].st_size = i + 1; /* zero is reserved */ |
373 | } |
374 | #endif |
375 | |
376 | nsym[n].st_shndx = SHBSS; |
377 | j = strlen(nsym[n].st_name + str) + 1; |
378 | if (j > ksyms_maxlen) |
379 | ksyms_maxlen = j; |
380 | nglob += (ELF_ST_BIND(nsym[n].st_info) == STB_GLOBAL); |
381 | |
382 | /* Compute min and max symbols. */ |
383 | if (strcmp(str + sym[i].st_name, "*ABS*" ) != 0 |
384 | && ELF_ST_TYPE(nsym[n].st_info) != STT_NOTYPE) { |
385 | if (nsym[n].st_value < tab->sd_minsym) { |
386 | tab->sd_minsym = nsym[n].st_value; |
387 | } |
388 | if (nsym[n].st_value > tab->sd_maxsym) { |
389 | tab->sd_maxsym = nsym[n].st_value; |
390 | } |
391 | } |
392 | n++; |
393 | } |
394 | |
395 | /* Fill the rest of the record, and sort the symbols. */ |
396 | tab->sd_symstart = nsym; |
397 | tab->sd_symsize = n * sizeof(Elf_Sym); |
398 | tab->sd_nglob = nglob; |
399 | addsymtab_strstart = str; |
400 | if (kheapsort(nsym, n, sizeof(Elf_Sym), addsymtab_compar, &ts) != 0) |
401 | panic("addsymtab" ); |
402 | |
403 | #ifdef KDTRACE_HOOKS |
404 | /* |
405 | * Build the mapping from original symbol id to new symbol table. |
406 | * Deleted symbols will have a zero map, indices will be one based |
407 | * instead of zero based. |
408 | * Resulting map is sd_nmap[original_index] = new_index + 1 |
409 | */ |
410 | if (nmap != NULL) { |
411 | int new; |
412 | for (new = 0; new < n; new++) { |
413 | uint32_t orig = nsym[new].st_size - 1; |
414 | uint32_t size = nmap[orig]; |
415 | |
416 | nmap[orig] = new + 1; |
417 | |
418 | /* restore the size */ |
419 | nsym[new].st_size = size; |
420 | } |
421 | } |
422 | #endif |
423 | |
424 | /* ksymsread() is unlocked, so membar. */ |
425 | membar_producer(); |
426 | TAILQ_INSERT_TAIL(&ksyms_symtabs, tab, sd_queue); |
427 | ksyms_sizes_calc(); |
428 | ksyms_loaded = true; |
429 | } |
430 | |
431 | /* |
432 | * Setup the kernel symbol table stuff. |
433 | */ |
434 | void |
435 | ksyms_addsyms_elf(int symsize, void *start, void *end) |
436 | { |
437 | int i, j; |
438 | Elf_Shdr *shdr; |
439 | char *symstart = NULL, *strstart = NULL; |
440 | size_t strsize = 0; |
441 | Elf_Ehdr *ehdr; |
442 | char *ctfstart = NULL; |
443 | size_t ctfsize = 0; |
444 | |
445 | if (symsize <= 0) { |
446 | printf("[ Kernel symbol table missing! ]\n" ); |
447 | return; |
448 | } |
449 | |
450 | /* Sanity check */ |
451 | if (ALIGNED_POINTER(start, long) == 0) { |
452 | printf("[ Kernel symbol table has bad start address %p ]\n" , |
453 | start); |
454 | return; |
455 | } |
456 | |
457 | ehdr = (Elf_Ehdr *)start; |
458 | |
459 | /* check if this is a valid ELF header */ |
460 | /* No reason to verify arch type, the kernel is actually running! */ |
461 | if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) || |
462 | ehdr->e_ident[EI_CLASS] != ELFCLASS || |
463 | ehdr->e_version > 1) { |
464 | printf("[ Kernel symbol table invalid! ]\n" ); |
465 | return; /* nothing to do */ |
466 | } |
467 | |
468 | /* Loaded header will be scratched in addsymtab */ |
469 | ksyms_hdr_init(start); |
470 | |
471 | /* Find the symbol table and the corresponding string table. */ |
472 | shdr = (Elf_Shdr *)((uint8_t *)start + ehdr->e_shoff); |
473 | for (i = 1; i < ehdr->e_shnum; i++) { |
474 | if (shdr[i].sh_type != SHT_SYMTAB) |
475 | continue; |
476 | if (shdr[i].sh_offset == 0) |
477 | continue; |
478 | symstart = (uint8_t *)start + shdr[i].sh_offset; |
479 | symsize = shdr[i].sh_size; |
480 | j = shdr[i].sh_link; |
481 | if (shdr[j].sh_offset == 0) |
482 | continue; /* Can this happen? */ |
483 | strstart = (uint8_t *)start + shdr[j].sh_offset; |
484 | strsize = shdr[j].sh_size; |
485 | break; |
486 | } |
487 | |
488 | #ifdef KDTRACE_HOOKS |
489 | /* Find the CTF section */ |
490 | shdr = (Elf_Shdr *)((uint8_t *)start + ehdr->e_shoff); |
491 | if (ehdr->e_shstrndx != 0) { |
492 | char *shstr = (uint8_t *)start + |
493 | shdr[ehdr->e_shstrndx].sh_offset; |
494 | for (i = 1; i < ehdr->e_shnum; i++) { |
495 | #ifdef DEBUG |
496 | printf("ksyms: checking %s\n" , &shstr[shdr[i].sh_name]); |
497 | #endif |
498 | if (shdr[i].sh_type != SHT_PROGBITS) |
499 | continue; |
500 | if (strncmp(".SUNW_ctf" , &shstr[shdr[i].sh_name], 10) |
501 | != 0) |
502 | continue; |
503 | ctfstart = (uint8_t *)start + shdr[i].sh_offset; |
504 | ctfsize = shdr[i].sh_size; |
505 | ksyms_ctfsz = ctfsize; |
506 | #ifdef DEBUG |
507 | aprint_normal("Found CTF at %p, size 0x%zx\n" , |
508 | ctfstart, ctfsize); |
509 | #endif |
510 | break; |
511 | } |
512 | #ifdef DEBUG |
513 | } else { |
514 | printf("ksyms: e_shstrndx == 0\n" ); |
515 | #endif |
516 | } |
517 | #endif |
518 | |
519 | if (!ksyms_verify(symstart, strstart)) |
520 | return; |
521 | |
522 | addsymtab("netbsd" , symstart, symsize, strstart, strsize, |
523 | &kernel_symtab, symstart, ctfstart, ctfsize, ksyms_nmap); |
524 | |
525 | #ifdef DEBUG |
526 | aprint_normal("Loaded initial symtab at %p, strtab at %p, # entries %ld\n" , |
527 | kernel_symtab.sd_symstart, kernel_symtab.sd_strstart, |
528 | (long)kernel_symtab.sd_symsize/sizeof(Elf_Sym)); |
529 | #endif |
530 | } |
531 | |
532 | /* |
533 | * Setup the kernel symbol table stuff. |
534 | * Use this when the address of the symbol and string tables are known; |
535 | * otherwise use ksyms_init with an ELF image. |
536 | * We need to pass a minimal ELF header which will later be completed by |
537 | * ksyms_hdr_init and handed off to userland through /dev/ksyms. We use |
538 | * a void *rather than a pointer to avoid exposing the Elf_Ehdr type. |
539 | */ |
540 | void |
541 | ksyms_addsyms_explicit(void *ehdr, void *symstart, size_t symsize, |
542 | void *strstart, size_t strsize) |
543 | { |
544 | if (!ksyms_verify(symstart, strstart)) |
545 | return; |
546 | |
547 | ksyms_hdr_init(ehdr); |
548 | addsymtab("netbsd" , symstart, symsize, strstart, strsize, |
549 | &kernel_symtab, symstart, NULL, 0, ksyms_nmap); |
550 | } |
551 | |
552 | /* |
553 | * Get the value associated with a symbol. |
554 | * "mod" is the module name, or null if any module. |
555 | * "sym" is the symbol name. |
556 | * "val" is a pointer to the corresponding value, if call succeeded. |
557 | * Returns 0 if success or ENOENT if no such entry. |
558 | * |
559 | * Call with ksyms_lock, unless known that the symbol table can't change. |
560 | */ |
561 | int |
562 | ksyms_getval_unlocked(const char *mod, const char *sym, unsigned long *val, |
563 | int type) |
564 | { |
565 | struct ksyms_symtab *st; |
566 | Elf_Sym *es; |
567 | |
568 | #ifdef KSYMS_DEBUG |
569 | if (ksyms_debug & FOLLOW_CALLS) |
570 | printf("ksyms_getval_unlocked: mod %s sym %s valp %p\n" , |
571 | mod, sym, val); |
572 | #endif |
573 | |
574 | TAILQ_FOREACH(st, &ksyms_symtabs, sd_queue) { |
575 | if (__predict_false(st->sd_gone)) |
576 | continue; |
577 | if (mod != NULL && strcmp(st->sd_name, mod)) |
578 | continue; |
579 | if ((es = findsym(sym, st, type)) != NULL) { |
580 | *val = es->st_value; |
581 | return 0; |
582 | } |
583 | } |
584 | return ENOENT; |
585 | } |
586 | |
587 | int |
588 | ksyms_getval(const char *mod, const char *sym, unsigned long *val, int type) |
589 | { |
590 | int rc; |
591 | |
592 | if (!ksyms_loaded) |
593 | return ENOENT; |
594 | |
595 | mutex_enter(&ksyms_lock); |
596 | rc = ksyms_getval_unlocked(mod, sym, val, type); |
597 | mutex_exit(&ksyms_lock); |
598 | return rc; |
599 | } |
600 | |
601 | struct ksyms_symtab * |
602 | ksyms_get_mod(const char *mod) |
603 | { |
604 | struct ksyms_symtab *st; |
605 | |
606 | mutex_enter(&ksyms_lock); |
607 | TAILQ_FOREACH(st, &ksyms_symtabs, sd_queue) { |
608 | if (__predict_false(st->sd_gone)) |
609 | continue; |
610 | if (mod != NULL && strcmp(st->sd_name, mod)) |
611 | continue; |
612 | break; |
613 | } |
614 | mutex_exit(&ksyms_lock); |
615 | |
616 | return st; |
617 | } |
618 | |
619 | |
620 | /* |
621 | * ksyms_mod_foreach() |
622 | * |
623 | * Iterate over the symbol table of the specified module, calling the callback |
624 | * handler for each symbol. Stop iterating if the handler return is non-zero. |
625 | * |
626 | */ |
627 | |
628 | int |
629 | ksyms_mod_foreach(const char *mod, ksyms_callback_t callback, void *opaque) |
630 | { |
631 | struct ksyms_symtab *st; |
632 | Elf_Sym *sym, *maxsym; |
633 | char *str; |
634 | int symindx; |
635 | |
636 | if (!ksyms_loaded) |
637 | return ENOENT; |
638 | |
639 | mutex_enter(&ksyms_lock); |
640 | |
641 | /* find the module */ |
642 | TAILQ_FOREACH(st, &ksyms_symtabs, sd_queue) { |
643 | if (__predict_false(st->sd_gone)) |
644 | continue; |
645 | if (mod != NULL && strcmp(st->sd_name, mod)) |
646 | continue; |
647 | |
648 | sym = st->sd_symstart; |
649 | str = st->sd_strstart - st->sd_usroffset; |
650 | |
651 | /* now iterate through the symbols */ |
652 | maxsym = sym + st->sd_symsize / sizeof(Elf_Sym); |
653 | for (symindx = 0; sym < maxsym; sym++, symindx++) { |
654 | if (callback(str + sym->st_name, symindx, |
655 | (void *)sym->st_value, |
656 | sym->st_size, |
657 | sym->st_info, |
658 | opaque) != 0) { |
659 | break; |
660 | } |
661 | } |
662 | } |
663 | mutex_exit(&ksyms_lock); |
664 | |
665 | return 0; |
666 | } |
667 | |
668 | /* |
669 | * Get "mod" and "symbol" associated with an address. |
670 | * Returns 0 if success or ENOENT if no such entry. |
671 | * |
672 | * Call with ksyms_lock, unless known that the symbol table can't change. |
673 | */ |
674 | int |
675 | ksyms_getname(const char **mod, const char **sym, vaddr_t v, int f) |
676 | { |
677 | struct ksyms_symtab *st; |
678 | Elf_Sym *les, *es = NULL; |
679 | vaddr_t laddr = 0; |
680 | const char *lmod = NULL; |
681 | char *stable = NULL; |
682 | int type, i, sz; |
683 | |
684 | if (!ksyms_loaded) |
685 | return ENOENT; |
686 | |
687 | TAILQ_FOREACH(st, &ksyms_symtabs, sd_queue) { |
688 | if (st->sd_gone) |
689 | continue; |
690 | if (v < st->sd_minsym || v > st->sd_maxsym) |
691 | continue; |
692 | sz = st->sd_symsize/sizeof(Elf_Sym); |
693 | for (i = 0; i < sz; i++) { |
694 | les = st->sd_symstart + i; |
695 | type = ELF_ST_TYPE(les->st_info); |
696 | |
697 | if ((f & KSYMS_PROC) && (type != STT_FUNC)) |
698 | continue; |
699 | |
700 | if (type == STT_NOTYPE) |
701 | continue; |
702 | |
703 | if (((f & KSYMS_ANY) == 0) && |
704 | (type != STT_FUNC) && (type != STT_OBJECT)) |
705 | continue; |
706 | |
707 | if ((les->st_value <= v) && (les->st_value > laddr)) { |
708 | laddr = les->st_value; |
709 | es = les; |
710 | lmod = st->sd_name; |
711 | stable = st->sd_strstart - st->sd_usroffset; |
712 | } |
713 | } |
714 | } |
715 | if (es == NULL) |
716 | return ENOENT; |
717 | if ((f & KSYMS_EXACT) && (v != es->st_value)) |
718 | return ENOENT; |
719 | if (mod) |
720 | *mod = lmod; |
721 | if (sym) |
722 | *sym = stable + es->st_name; |
723 | return 0; |
724 | } |
725 | |
726 | /* |
727 | * Add a symbol table from a loadable module. |
728 | */ |
729 | void |
730 | ksyms_modload(const char *name, void *symstart, vsize_t symsize, |
731 | char *strstart, vsize_t strsize) |
732 | { |
733 | struct ksyms_symtab *st; |
734 | |
735 | st = kmem_zalloc(sizeof(*st), KM_SLEEP); |
736 | mutex_enter(&ksyms_lock); |
737 | addsymtab(name, symstart, symsize, strstart, strsize, st, symstart, |
738 | NULL, 0, NULL); |
739 | mutex_exit(&ksyms_lock); |
740 | } |
741 | |
742 | /* |
743 | * Remove a symbol table from a loadable module. |
744 | */ |
745 | void |
746 | ksyms_modunload(const char *name) |
747 | { |
748 | struct ksyms_symtab *st; |
749 | |
750 | mutex_enter(&ksyms_lock); |
751 | TAILQ_FOREACH(st, &ksyms_symtabs, sd_queue) { |
752 | if (st->sd_gone) |
753 | continue; |
754 | if (strcmp(name, st->sd_name) != 0) |
755 | continue; |
756 | st->sd_gone = true; |
757 | if (!ksyms_isopen) { |
758 | TAILQ_REMOVE(&ksyms_symtabs, st, sd_queue); |
759 | ksyms_sizes_calc(); |
760 | kmem_free(st, sizeof(*st)); |
761 | } |
762 | break; |
763 | } |
764 | mutex_exit(&ksyms_lock); |
765 | KASSERT(st != NULL); |
766 | } |
767 | |
768 | #ifdef DDB |
769 | /* |
770 | * Keep sifting stuff here, to avoid export of ksyms internals. |
771 | * |
772 | * Systems is expected to be quiescent, so no locking done. |
773 | */ |
774 | int |
775 | ksyms_sift(char *mod, char *sym, int mode) |
776 | { |
777 | struct ksyms_symtab *st; |
778 | char *sb; |
779 | int i, sz; |
780 | |
781 | if (!ksyms_loaded) |
782 | return ENOENT; |
783 | |
784 | TAILQ_FOREACH(st, &ksyms_symtabs, sd_queue) { |
785 | if (st->sd_gone) |
786 | continue; |
787 | if (mod && strcmp(mod, st->sd_name)) |
788 | continue; |
789 | sb = st->sd_strstart - st->sd_usroffset; |
790 | |
791 | sz = st->sd_symsize/sizeof(Elf_Sym); |
792 | for (i = 0; i < sz; i++) { |
793 | Elf_Sym *les = st->sd_symstart + i; |
794 | char c; |
795 | |
796 | if (strstr(sb + les->st_name, sym) == NULL) |
797 | continue; |
798 | |
799 | if (mode == 'F') { |
800 | switch (ELF_ST_TYPE(les->st_info)) { |
801 | case STT_OBJECT: |
802 | c = '+'; |
803 | break; |
804 | case STT_FUNC: |
805 | c = '*'; |
806 | break; |
807 | case STT_SECTION: |
808 | c = '&'; |
809 | break; |
810 | case STT_FILE: |
811 | c = '/'; |
812 | break; |
813 | default: |
814 | c = ' '; |
815 | break; |
816 | } |
817 | db_printf("%s%c " , sb + les->st_name, c); |
818 | } else |
819 | db_printf("%s " , sb + les->st_name); |
820 | } |
821 | } |
822 | return ENOENT; |
823 | } |
824 | #endif /* DDB */ |
825 | |
826 | /* |
827 | * In case we exposing the symbol table to the userland using the pseudo- |
828 | * device /dev/ksyms, it is easier to provide all the tables as one. |
829 | * However, it means we have to change all the st_name fields for the |
830 | * symbols so they match the ELF image that the userland will read |
831 | * through the device. |
832 | * |
833 | * The actual (correct) value of st_name is preserved through a global |
834 | * offset stored in the symbol table structure. |
835 | * |
836 | * Call with ksyms_lock held. |
837 | */ |
838 | static void |
839 | ksyms_sizes_calc(void) |
840 | { |
841 | struct ksyms_symtab *st; |
842 | int i, delta; |
843 | |
844 | ksyms_symsz = ksyms_strsz = 0; |
845 | TAILQ_FOREACH(st, &ksyms_symtabs, sd_queue) { |
846 | delta = ksyms_strsz - st->sd_usroffset; |
847 | if (delta != 0) { |
848 | for (i = 0; i < st->sd_symsize/sizeof(Elf_Sym); i++) |
849 | st->sd_symstart[i].st_name += delta; |
850 | st->sd_usroffset = ksyms_strsz; |
851 | } |
852 | ksyms_symsz += st->sd_symsize; |
853 | ksyms_strsz += st->sd_strsize; |
854 | } |
855 | } |
856 | |
857 | static void |
858 | ksyms_fill_note(void) |
859 | { |
860 | int32_t *note = ksyms_hdr.kh_note; |
861 | note[0] = ELF_NOTE_NETBSD_NAMESZ; |
862 | note[1] = ELF_NOTE_NETBSD_DESCSZ; |
863 | note[2] = ELF_NOTE_TYPE_NETBSD_TAG; |
864 | memcpy(¬e[3], "NetBSD\0" , 8); |
865 | note[5] = __NetBSD_Version__; |
866 | } |
867 | |
868 | static void |
869 | ksyms_hdr_init(const void *hdraddr) |
870 | { |
871 | /* Copy the loaded elf exec header */ |
872 | memcpy(&ksyms_hdr.kh_ehdr, hdraddr, sizeof(Elf_Ehdr)); |
873 | |
874 | /* Set correct program/section header sizes, offsets and numbers */ |
875 | ksyms_hdr.kh_ehdr.e_phoff = offsetof(struct ksyms_hdr, kh_phdr[0]); |
876 | ksyms_hdr.kh_ehdr.e_phentsize = sizeof(Elf_Phdr); |
877 | ksyms_hdr.kh_ehdr.e_phnum = NPRGHDR; |
878 | ksyms_hdr.kh_ehdr.e_shoff = offsetof(struct ksyms_hdr, kh_shdr[0]); |
879 | ksyms_hdr.kh_ehdr.e_shentsize = sizeof(Elf_Shdr); |
880 | ksyms_hdr.kh_ehdr.e_shnum = NSECHDR; |
881 | ksyms_hdr.kh_ehdr.e_shstrndx = SHSTRTAB; |
882 | |
883 | /* Text/data - fake */ |
884 | ksyms_hdr.kh_phdr[0].p_type = PT_LOAD; |
885 | ksyms_hdr.kh_phdr[0].p_memsz = (unsigned long)-1L; |
886 | ksyms_hdr.kh_phdr[0].p_flags = PF_R | PF_X | PF_W; |
887 | |
888 | #define SHTCOPY(name) strlcpy(&ksyms_hdr.kh_strtab[offs], (name), \ |
889 | sizeof(ksyms_hdr.kh_strtab) - offs), offs += sizeof(name) |
890 | |
891 | uint32_t offs = 1; |
892 | /* First section header ".note.netbsd.ident" */ |
893 | ksyms_hdr.kh_shdr[SHNOTE].sh_name = offs; |
894 | ksyms_hdr.kh_shdr[SHNOTE].sh_type = SHT_NOTE; |
895 | ksyms_hdr.kh_shdr[SHNOTE].sh_offset = |
896 | offsetof(struct ksyms_hdr, kh_note[0]); |
897 | ksyms_hdr.kh_shdr[SHNOTE].sh_size = sizeof(ksyms_hdr.kh_note); |
898 | ksyms_hdr.kh_shdr[SHNOTE].sh_addralign = sizeof(int); |
899 | SHTCOPY(".note.netbsd.ident" ); |
900 | ksyms_fill_note(); |
901 | |
902 | /* Second section header; ".symtab" */ |
903 | ksyms_hdr.kh_shdr[SYMTAB].sh_name = offs; |
904 | ksyms_hdr.kh_shdr[SYMTAB].sh_type = SHT_SYMTAB; |
905 | ksyms_hdr.kh_shdr[SYMTAB].sh_offset = sizeof(struct ksyms_hdr); |
906 | /* ksyms_hdr.kh_shdr[SYMTAB].sh_size = filled in at open */ |
907 | ksyms_hdr.kh_shdr[SYMTAB].sh_link = STRTAB; /* Corresponding strtab */ |
908 | ksyms_hdr.kh_shdr[SYMTAB].sh_addralign = sizeof(long); |
909 | ksyms_hdr.kh_shdr[SYMTAB].sh_entsize = sizeof(Elf_Sym); |
910 | SHTCOPY(".symtab" ); |
911 | |
912 | /* Third section header; ".strtab" */ |
913 | ksyms_hdr.kh_shdr[STRTAB].sh_name = offs; |
914 | ksyms_hdr.kh_shdr[STRTAB].sh_type = SHT_STRTAB; |
915 | /* ksyms_hdr.kh_shdr[STRTAB].sh_offset = filled in at open */ |
916 | /* ksyms_hdr.kh_shdr[STRTAB].sh_size = filled in at open */ |
917 | ksyms_hdr.kh_shdr[STRTAB].sh_addralign = sizeof(char); |
918 | SHTCOPY(".strtab" ); |
919 | |
920 | /* Fourth section, ".shstrtab" */ |
921 | ksyms_hdr.kh_shdr[SHSTRTAB].sh_name = offs; |
922 | ksyms_hdr.kh_shdr[SHSTRTAB].sh_type = SHT_STRTAB; |
923 | ksyms_hdr.kh_shdr[SHSTRTAB].sh_offset = |
924 | offsetof(struct ksyms_hdr, kh_strtab); |
925 | ksyms_hdr.kh_shdr[SHSTRTAB].sh_size = SHSTRSIZ; |
926 | ksyms_hdr.kh_shdr[SHSTRTAB].sh_addralign = sizeof(char); |
927 | SHTCOPY(".shstrtab" ); |
928 | |
929 | /* Fifth section, ".bss". All symbols reside here. */ |
930 | ksyms_hdr.kh_shdr[SHBSS].sh_name = offs; |
931 | ksyms_hdr.kh_shdr[SHBSS].sh_type = SHT_NOBITS; |
932 | ksyms_hdr.kh_shdr[SHBSS].sh_offset = 0; |
933 | ksyms_hdr.kh_shdr[SHBSS].sh_size = (unsigned long)-1L; |
934 | ksyms_hdr.kh_shdr[SHBSS].sh_addralign = PAGE_SIZE; |
935 | ksyms_hdr.kh_shdr[SHBSS].sh_flags = SHF_ALLOC | SHF_EXECINSTR; |
936 | SHTCOPY(".bss" ); |
937 | |
938 | /* Sixth section header; ".SUNW_ctf" */ |
939 | ksyms_hdr.kh_shdr[SHCTF].sh_name = offs; |
940 | ksyms_hdr.kh_shdr[SHCTF].sh_type = SHT_PROGBITS; |
941 | /* ksyms_hdr.kh_shdr[SHCTF].sh_offset = filled in at open */ |
942 | /* ksyms_hdr.kh_shdr[SHCTF].sh_size = filled in at open */ |
943 | ksyms_hdr.kh_shdr[SHCTF].sh_link = SYMTAB; /* Corresponding symtab */ |
944 | ksyms_hdr.kh_shdr[SHCTF].sh_addralign = sizeof(char); |
945 | SHTCOPY(".SUNW_ctf" ); |
946 | } |
947 | |
948 | static int |
949 | ksymsopen(dev_t dev, int oflags, int devtype, struct lwp *l) |
950 | { |
951 | if (minor(dev) != 0 || !ksyms_loaded) |
952 | return ENXIO; |
953 | |
954 | /* |
955 | * Create a "snapshot" of the kernel symbol table. Setting |
956 | * ksyms_isopen will prevent symbol tables from being freed. |
957 | */ |
958 | mutex_enter(&ksyms_lock); |
959 | ksyms_hdr.kh_shdr[SYMTAB].sh_size = ksyms_symsz; |
960 | ksyms_hdr.kh_shdr[SYMTAB].sh_info = ksyms_symsz / sizeof(Elf_Sym); |
961 | ksyms_hdr.kh_shdr[STRTAB].sh_offset = ksyms_symsz + |
962 | ksyms_hdr.kh_shdr[SYMTAB].sh_offset; |
963 | ksyms_hdr.kh_shdr[STRTAB].sh_size = ksyms_strsz; |
964 | ksyms_hdr.kh_shdr[SHCTF].sh_offset = ksyms_strsz + |
965 | ksyms_hdr.kh_shdr[STRTAB].sh_offset; |
966 | ksyms_hdr.kh_shdr[SHCTF].sh_size = ksyms_ctfsz; |
967 | ksyms_isopen = true; |
968 | mutex_exit(&ksyms_lock); |
969 | |
970 | return 0; |
971 | } |
972 | |
973 | static int |
974 | ksymsclose(dev_t dev, int oflags, int devtype, struct lwp *l) |
975 | { |
976 | struct ksyms_symtab *st, *next; |
977 | bool resize; |
978 | |
979 | /* Discard references to symbol tables. */ |
980 | mutex_enter(&ksyms_lock); |
981 | ksyms_isopen = false; |
982 | resize = false; |
983 | for (st = TAILQ_FIRST(&ksyms_symtabs); st != NULL; st = next) { |
984 | next = TAILQ_NEXT(st, sd_queue); |
985 | if (st->sd_gone) { |
986 | TAILQ_REMOVE(&ksyms_symtabs, st, sd_queue); |
987 | kmem_free(st, sizeof(*st)); |
988 | resize = true; |
989 | } |
990 | } |
991 | if (resize) |
992 | ksyms_sizes_calc(); |
993 | mutex_exit(&ksyms_lock); |
994 | |
995 | return 0; |
996 | } |
997 | |
998 | static int |
999 | ksymsread(dev_t dev, struct uio *uio, int ioflag) |
1000 | { |
1001 | struct ksyms_symtab *st; |
1002 | size_t filepos, inpos, off; |
1003 | int error; |
1004 | |
1005 | /* |
1006 | * First: Copy out the ELF header. XXX Lose if ksymsopen() |
1007 | * occurs during read of the header. |
1008 | */ |
1009 | off = uio->uio_offset; |
1010 | if (off < sizeof(struct ksyms_hdr)) { |
1011 | error = uiomove((char *)&ksyms_hdr + off, |
1012 | sizeof(struct ksyms_hdr) - off, uio); |
1013 | if (error != 0) |
1014 | return error; |
1015 | } |
1016 | |
1017 | /* |
1018 | * Copy out the symbol table. |
1019 | */ |
1020 | filepos = sizeof(struct ksyms_hdr); |
1021 | TAILQ_FOREACH(st, &ksyms_symtabs, sd_queue) { |
1022 | if (uio->uio_resid == 0) |
1023 | return 0; |
1024 | if (uio->uio_offset <= st->sd_symsize + filepos) { |
1025 | inpos = uio->uio_offset - filepos; |
1026 | error = uiomove((char *)st->sd_symstart + inpos, |
1027 | st->sd_symsize - inpos, uio); |
1028 | if (error != 0) |
1029 | return error; |
1030 | } |
1031 | filepos += st->sd_symsize; |
1032 | } |
1033 | |
1034 | /* |
1035 | * Copy out the string table |
1036 | */ |
1037 | KASSERT(filepos == sizeof(struct ksyms_hdr) + |
1038 | ksyms_hdr.kh_shdr[SYMTAB].sh_size); |
1039 | TAILQ_FOREACH(st, &ksyms_symtabs, sd_queue) { |
1040 | if (uio->uio_resid == 0) |
1041 | return 0; |
1042 | if (uio->uio_offset <= st->sd_strsize + filepos) { |
1043 | inpos = uio->uio_offset - filepos; |
1044 | error = uiomove((char *)st->sd_strstart + inpos, |
1045 | st->sd_strsize - inpos, uio); |
1046 | if (error != 0) |
1047 | return error; |
1048 | } |
1049 | filepos += st->sd_strsize; |
1050 | } |
1051 | |
1052 | /* |
1053 | * Copy out the CTF table. |
1054 | */ |
1055 | st = TAILQ_FIRST(&ksyms_symtabs); |
1056 | if (st->sd_ctfstart != NULL) { |
1057 | if (uio->uio_resid == 0) |
1058 | return 0; |
1059 | if (uio->uio_offset <= st->sd_ctfsize + filepos) { |
1060 | inpos = uio->uio_offset - filepos; |
1061 | error = uiomove((char *)st->sd_ctfstart + inpos, |
1062 | st->sd_ctfsize - inpos, uio); |
1063 | if (error != 0) |
1064 | return error; |
1065 | } |
1066 | filepos += st->sd_ctfsize; |
1067 | } |
1068 | |
1069 | return 0; |
1070 | } |
1071 | |
1072 | static int |
1073 | ksymswrite(dev_t dev, struct uio *uio, int ioflag) |
1074 | { |
1075 | return EROFS; |
1076 | } |
1077 | |
1078 | __CTASSERT(offsetof(struct ksyms_ogsymbol, kg_name) == offsetof(struct ksyms_gsymbol, kg_name)); |
1079 | __CTASSERT(offsetof(struct ksyms_gvalue, kv_name) == offsetof(struct ksyms_gsymbol, kg_name)); |
1080 | |
1081 | static int |
1082 | ksymsioctl(dev_t dev, u_long cmd, void *data, int fflag, struct lwp *l) |
1083 | { |
1084 | struct ksyms_ogsymbol *okg = (struct ksyms_ogsymbol *)data; |
1085 | struct ksyms_gsymbol *kg = (struct ksyms_gsymbol *)data; |
1086 | struct ksyms_gvalue *kv = (struct ksyms_gvalue *)data; |
1087 | struct ksyms_symtab *st; |
1088 | Elf_Sym *sym = NULL, copy; |
1089 | unsigned long val; |
1090 | int error = 0; |
1091 | char *str = NULL; |
1092 | int len; |
1093 | |
1094 | /* Read ksyms_maxlen only once while not holding the lock. */ |
1095 | len = ksyms_maxlen; |
1096 | |
1097 | if (cmd == OKIOCGVALUE || cmd == OKIOCGSYMBOL || |
1098 | cmd == KIOCGVALUE || cmd == KIOCGSYMBOL) { |
1099 | str = kmem_alloc(len, KM_SLEEP); |
1100 | if ((error = copyinstr(kg->kg_name, str, len, NULL)) != 0) { |
1101 | kmem_free(str, len); |
1102 | return error; |
1103 | } |
1104 | } |
1105 | |
1106 | switch (cmd) { |
1107 | case OKIOCGVALUE: |
1108 | /* |
1109 | * Use the in-kernel symbol lookup code for fast |
1110 | * retreival of a value. |
1111 | */ |
1112 | error = ksyms_getval(NULL, str, &val, KSYMS_EXTERN); |
1113 | if (error == 0) |
1114 | error = copyout(&val, okg->kg_value, sizeof(long)); |
1115 | kmem_free(str, len); |
1116 | break; |
1117 | |
1118 | case OKIOCGSYMBOL: |
1119 | /* |
1120 | * Use the in-kernel symbol lookup code for fast |
1121 | * retreival of a symbol. |
1122 | */ |
1123 | mutex_enter(&ksyms_lock); |
1124 | TAILQ_FOREACH(st, &ksyms_symtabs, sd_queue) { |
1125 | if (st->sd_gone) |
1126 | continue; |
1127 | if ((sym = findsym(str, st, KSYMS_ANY)) == NULL) |
1128 | continue; |
1129 | #ifdef notdef |
1130 | /* Skip if bad binding */ |
1131 | if (ELF_ST_BIND(sym->st_info) != STB_GLOBAL) { |
1132 | sym = NULL; |
1133 | continue; |
1134 | } |
1135 | #endif |
1136 | break; |
1137 | } |
1138 | if (sym != NULL) { |
1139 | memcpy(©, sym, sizeof(copy)); |
1140 | mutex_exit(&ksyms_lock); |
1141 | error = copyout(©, okg->kg_sym, sizeof(Elf_Sym)); |
1142 | } else { |
1143 | mutex_exit(&ksyms_lock); |
1144 | error = ENOENT; |
1145 | } |
1146 | kmem_free(str, len); |
1147 | break; |
1148 | |
1149 | case KIOCGVALUE: |
1150 | /* |
1151 | * Use the in-kernel symbol lookup code for fast |
1152 | * retreival of a value. |
1153 | */ |
1154 | error = ksyms_getval(NULL, str, &val, KSYMS_EXTERN); |
1155 | if (error == 0) |
1156 | kv->kv_value = val; |
1157 | kmem_free(str, len); |
1158 | break; |
1159 | |
1160 | case KIOCGSYMBOL: |
1161 | /* |
1162 | * Use the in-kernel symbol lookup code for fast |
1163 | * retreival of a symbol. |
1164 | */ |
1165 | mutex_enter(&ksyms_lock); |
1166 | TAILQ_FOREACH(st, &ksyms_symtabs, sd_queue) { |
1167 | if (st->sd_gone) |
1168 | continue; |
1169 | if ((sym = findsym(str, st, KSYMS_ANY)) == NULL) |
1170 | continue; |
1171 | #ifdef notdef |
1172 | /* Skip if bad binding */ |
1173 | if (ELF_ST_BIND(sym->st_info) != STB_GLOBAL) { |
1174 | sym = NULL; |
1175 | continue; |
1176 | } |
1177 | #endif |
1178 | break; |
1179 | } |
1180 | if (sym != NULL) { |
1181 | kg->kg_sym = *sym; |
1182 | } else { |
1183 | error = ENOENT; |
1184 | } |
1185 | mutex_exit(&ksyms_lock); |
1186 | kmem_free(str, len); |
1187 | break; |
1188 | |
1189 | case KIOCGSIZE: |
1190 | /* |
1191 | * Get total size of symbol table. |
1192 | */ |
1193 | mutex_enter(&ksyms_lock); |
1194 | *(int *)data = ksyms_strsz + ksyms_symsz + |
1195 | sizeof(struct ksyms_hdr); |
1196 | mutex_exit(&ksyms_lock); |
1197 | break; |
1198 | |
1199 | default: |
1200 | error = ENOTTY; |
1201 | break; |
1202 | } |
1203 | |
1204 | return error; |
1205 | } |
1206 | |
1207 | const struct cdevsw ksyms_cdevsw = { |
1208 | .d_open = ksymsopen, |
1209 | .d_close = ksymsclose, |
1210 | .d_read = ksymsread, |
1211 | .d_write = ksymswrite, |
1212 | .d_ioctl = ksymsioctl, |
1213 | .d_stop = nullstop, |
1214 | .d_tty = notty, |
1215 | .d_poll = nopoll, |
1216 | .d_mmap = nommap, |
1217 | .d_kqfilter = nullkqfilter, |
1218 | .d_discard = nodiscard, |
1219 | .d_flag = D_OTHER | D_MPSAFE |
1220 | }; |
1221 | |