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 2003 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 | #if HAVE_NBTOOL_CONFIG_H |
30 | # include "nbtool_config.h" |
31 | #endif |
32 | |
33 | #include <sys/types.h> |
34 | #include <sys/stat.h> |
35 | #include <sys/mman.h> |
36 | #include <sys/zmod.h> |
37 | #include <ctf_impl.h> |
38 | #include <unistd.h> |
39 | #include <fcntl.h> |
40 | #include <errno.h> |
41 | #ifdef illumos |
42 | #include <dlfcn.h> |
43 | #else |
44 | #include <zlib.h> |
45 | #endif |
46 | #include <gelf.h> |
47 | |
48 | #ifdef illumos |
49 | #ifdef _LP64 |
50 | static const char *_libctf_zlib = "/usr/lib/64/libz.so" ; |
51 | #else |
52 | static const char *_libctf_zlib = "/usr/lib/libz.so" ; |
53 | #endif |
54 | #endif |
55 | |
56 | static struct { |
57 | int (*z_uncompress)(uchar_t *, ulong_t *, const uchar_t *, ulong_t); |
58 | const char *(*z_error)(int); |
59 | void *z_dlp; |
60 | } zlib; |
61 | |
62 | static size_t _PAGESIZE; |
63 | static size_t _PAGEMASK; |
64 | |
65 | #ifdef illumos |
66 | #pragma init(_libctf_init) |
67 | #else |
68 | void _libctf_init(void) __attribute__ ((constructor)); |
69 | #endif |
70 | void |
71 | _libctf_init(void) |
72 | { |
73 | #ifdef illumos |
74 | const char *p = getenv("LIBCTF_DECOMPRESSOR" ); |
75 | |
76 | if (p != NULL) |
77 | _libctf_zlib = p; /* use alternate decompression library */ |
78 | #endif |
79 | |
80 | _libctf_debug = getenv("LIBCTF_DEBUG" ) != NULL; |
81 | |
82 | _PAGESIZE = getpagesize(); |
83 | _PAGEMASK = ~(_PAGESIZE - 1); |
84 | } |
85 | |
86 | /* |
87 | * Attempt to dlopen the decompression library and locate the symbols of |
88 | * interest that we will need to call. This information in cached so |
89 | * that multiple calls to ctf_bufopen() do not need to reopen the library. |
90 | */ |
91 | void * |
92 | ctf_zopen(int *errp) |
93 | { |
94 | #ifdef illumos |
95 | ctf_dprintf("decompressing CTF data using %s\n" , _libctf_zlib); |
96 | |
97 | if (zlib.z_dlp != NULL) |
98 | return (zlib.z_dlp); /* library is already loaded */ |
99 | |
100 | if (access(_libctf_zlib, R_OK) == -1) |
101 | return (ctf_set_open_errno(errp, ECTF_ZMISSING)); |
102 | |
103 | if ((zlib.z_dlp = dlopen(_libctf_zlib, RTLD_LAZY | RTLD_LOCAL)) == NULL) |
104 | return (ctf_set_open_errno(errp, ECTF_ZINIT)); |
105 | |
106 | zlib.z_uncompress = (int (*)(uchar_t *, ulong_t *, const uchar_t *, ulong_t)) dlsym(zlib.z_dlp, "uncompress" ); |
107 | zlib.z_error = (const char *(*)(int)) dlsym(zlib.z_dlp, "zError" ); |
108 | |
109 | if (zlib.z_uncompress == NULL || zlib.z_error == NULL) { |
110 | (void) dlclose(zlib.z_dlp); |
111 | bzero(&zlib, sizeof (zlib)); |
112 | return (ctf_set_open_errno(errp, ECTF_ZINIT)); |
113 | } |
114 | #else |
115 | zlib.z_uncompress = uncompress; |
116 | zlib.z_error = zError; |
117 | |
118 | /* Dummy return variable as 'no error' */ |
119 | zlib.z_dlp = (void *) (uintptr_t) 1; |
120 | #endif |
121 | |
122 | return (zlib.z_dlp); |
123 | } |
124 | |
125 | /* |
126 | * The ctf_bufopen() routine calls these subroutines, defined by <sys/zmod.h>, |
127 | * which we then patch through to the functions in the decompression library. |
128 | */ |
129 | int |
130 | z_uncompress(void *dst, size_t *dstlen, const void *src, size_t srclen) |
131 | { |
132 | return (zlib.z_uncompress(dst, (ulong_t *)dstlen, src, srclen)); |
133 | } |
134 | |
135 | const char * |
136 | z_strerror(int err) |
137 | { |
138 | return (zlib.z_error(err)); |
139 | } |
140 | |
141 | /* |
142 | * Convert a 32-bit ELF file header into GElf. |
143 | */ |
144 | static void |
145 | ehdr_to_gelf(const Elf32_Ehdr *src, GElf_Ehdr *dst) |
146 | { |
147 | bcopy(src->e_ident, dst->e_ident, EI_NIDENT); |
148 | dst->e_type = src->e_type; |
149 | dst->e_machine = src->e_machine; |
150 | dst->e_version = src->e_version; |
151 | dst->e_entry = (Elf64_Addr)src->e_entry; |
152 | dst->e_phoff = (Elf64_Off)src->e_phoff; |
153 | dst->e_shoff = (Elf64_Off)src->e_shoff; |
154 | dst->e_flags = src->e_flags; |
155 | dst->e_ehsize = src->e_ehsize; |
156 | dst->e_phentsize = src->e_phentsize; |
157 | dst->e_phnum = src->e_phnum; |
158 | dst->e_shentsize = src->e_shentsize; |
159 | dst->e_shnum = src->e_shnum; |
160 | dst->e_shstrndx = src->e_shstrndx; |
161 | } |
162 | |
163 | /* |
164 | * Convert a 32-bit ELF section header into GElf. |
165 | */ |
166 | static void |
167 | shdr_to_gelf(const Elf32_Shdr *src, GElf_Shdr *dst) |
168 | { |
169 | dst->sh_name = src->sh_name; |
170 | dst->sh_type = src->sh_type; |
171 | dst->sh_flags = src->sh_flags; |
172 | dst->sh_addr = src->sh_addr; |
173 | dst->sh_offset = src->sh_offset; |
174 | dst->sh_size = src->sh_size; |
175 | dst->sh_link = src->sh_link; |
176 | dst->sh_info = src->sh_info; |
177 | dst->sh_addralign = src->sh_addralign; |
178 | dst->sh_entsize = src->sh_entsize; |
179 | } |
180 | |
181 | /* |
182 | * In order to mmap a section from the ELF file, we must round down sh_offset |
183 | * to the previous page boundary, and mmap the surrounding page. We store |
184 | * the pointer to the start of the actual section data back into sp->cts_data. |
185 | */ |
186 | const void * |
187 | ctf_sect_mmap(ctf_sect_t *sp, int fd) |
188 | { |
189 | size_t pageoff = sp->cts_offset & ~_PAGEMASK; |
190 | |
191 | caddr_t base = mmap64(NULL, sp->cts_size + pageoff, PROT_READ, |
192 | MAP_PRIVATE, fd, sp->cts_offset & _PAGEMASK); |
193 | |
194 | if (base != MAP_FAILED) |
195 | sp->cts_data = base + pageoff; |
196 | |
197 | return (base); |
198 | } |
199 | |
200 | /* |
201 | * Since sp->cts_data has the adjusted offset, we have to again round down |
202 | * to get the actual mmap address and round up to get the size. |
203 | */ |
204 | void |
205 | ctf_sect_munmap(const ctf_sect_t *sp) |
206 | { |
207 | uintptr_t addr = (uintptr_t)sp->cts_data; |
208 | uintptr_t pageoff = addr & ~_PAGEMASK; |
209 | |
210 | (void) munmap((void *)(addr - pageoff), sp->cts_size + pageoff); |
211 | } |
212 | |
213 | /* |
214 | * Open the specified file descriptor and return a pointer to a CTF container. |
215 | * The file can be either an ELF file or raw CTF file. The caller is |
216 | * responsible for closing the file descriptor when it is no longer needed. |
217 | */ |
218 | ctf_file_t * |
219 | ctf_fdopen(int fd, int *errp) |
220 | { |
221 | ctf_sect_t ctfsect, symsect, strsect; |
222 | ctf_file_t *fp = NULL; |
223 | size_t shstrndx, shnum; |
224 | |
225 | struct stat64 st; |
226 | ssize_t nbytes; |
227 | |
228 | union { |
229 | ctf_preamble_t ctf; |
230 | Elf32_Ehdr e32; |
231 | GElf_Ehdr e64; |
232 | } hdr; |
233 | |
234 | bzero(&ctfsect, sizeof (ctf_sect_t)); |
235 | bzero(&symsect, sizeof (ctf_sect_t)); |
236 | bzero(&strsect, sizeof (ctf_sect_t)); |
237 | bzero(&hdr.ctf, sizeof (hdr)); |
238 | |
239 | if (fstat64(fd, &st) == -1) |
240 | return (ctf_set_open_errno(errp, errno)); |
241 | |
242 | if ((nbytes = pread64(fd, &hdr.ctf, sizeof (hdr), 0)) <= 0) |
243 | return (ctf_set_open_errno(errp, nbytes < 0? errno : ECTF_FMT)); |
244 | |
245 | /* |
246 | * If we have read enough bytes to form a CTF header and the magic |
247 | * string matches, attempt to interpret the file as raw CTF. |
248 | */ |
249 | if (nbytes >= (ssize_t) sizeof (ctf_preamble_t) && |
250 | hdr.ctf.ctp_magic == CTF_MAGIC) { |
251 | if (hdr.ctf.ctp_version > CTF_VERSION) |
252 | return (ctf_set_open_errno(errp, ECTF_CTFVERS)); |
253 | |
254 | ctfsect.cts_data = mmap64(NULL, st.st_size, PROT_READ, |
255 | MAP_PRIVATE, fd, 0); |
256 | |
257 | if (ctfsect.cts_data == MAP_FAILED) |
258 | return (ctf_set_open_errno(errp, errno)); |
259 | |
260 | ctfsect.cts_name = _CTF_SECTION; |
261 | ctfsect.cts_type = SHT_PROGBITS; |
262 | ctfsect.cts_flags = SHF_ALLOC; |
263 | ctfsect.cts_size = (size_t)st.st_size; |
264 | ctfsect.cts_entsize = 1; |
265 | ctfsect.cts_offset = 0; |
266 | |
267 | if ((fp = ctf_bufopen(&ctfsect, NULL, NULL, errp)) == NULL) |
268 | ctf_sect_munmap(&ctfsect); |
269 | |
270 | return (fp); |
271 | } |
272 | |
273 | /* |
274 | * If we have read enough bytes to form an ELF header and the magic |
275 | * string matches, attempt to interpret the file as an ELF file. We |
276 | * do our own largefile ELF processing, and convert everything to |
277 | * GElf structures so that clients can operate on any data model. |
278 | */ |
279 | if (nbytes >= (ssize_t) sizeof (Elf32_Ehdr) && |
280 | bcmp(&hdr.e32.e_ident[EI_MAG0], ELFMAG, SELFMAG) == 0) { |
281 | #if BYTE_ORDER == _BIG_ENDIAN |
282 | uchar_t order = ELFDATA2MSB; |
283 | #else |
284 | uchar_t order = ELFDATA2LSB; |
285 | #endif |
286 | GElf_Shdr *sp; |
287 | |
288 | void *strs_map; |
289 | size_t strs_mapsz, i; |
290 | char *strs; |
291 | |
292 | if (hdr.e32.e_ident[EI_DATA] != order) |
293 | return (ctf_set_open_errno(errp, ECTF_ENDIAN)); |
294 | if (hdr.e32.e_version != EV_CURRENT) |
295 | return (ctf_set_open_errno(errp, ECTF_ELFVERS)); |
296 | |
297 | if (hdr.e32.e_ident[EI_CLASS] == ELFCLASS64) { |
298 | if (nbytes < (ssize_t) sizeof (GElf_Ehdr)) |
299 | return (ctf_set_open_errno(errp, ECTF_FMT)); |
300 | } else { |
301 | Elf32_Ehdr e32 = hdr.e32; |
302 | ehdr_to_gelf(&e32, &hdr.e64); |
303 | } |
304 | |
305 | shnum = hdr.e64.e_shnum; |
306 | shstrndx = hdr.e64.e_shstrndx; |
307 | |
308 | /* Extended ELF sections */ |
309 | if ((shstrndx == SHN_XINDEX) || (shnum == 0)) { |
310 | if (hdr.e32.e_ident[EI_CLASS] == ELFCLASS32) { |
311 | Elf32_Shdr x32; |
312 | |
313 | if (pread64(fd, &x32, sizeof (x32), |
314 | hdr.e64.e_shoff) != sizeof (x32)) |
315 | return (ctf_set_open_errno(errp, |
316 | errno)); |
317 | |
318 | shnum = x32.sh_size; |
319 | shstrndx = x32.sh_link; |
320 | } else { |
321 | Elf64_Shdr x64; |
322 | |
323 | if (pread64(fd, &x64, sizeof (x64), |
324 | hdr.e64.e_shoff) != sizeof (x64)) |
325 | return (ctf_set_open_errno(errp, |
326 | errno)); |
327 | |
328 | shnum = x64.sh_size; |
329 | shstrndx = x64.sh_link; |
330 | } |
331 | } |
332 | |
333 | if (shstrndx >= shnum) |
334 | return (ctf_set_open_errno(errp, ECTF_CORRUPT)); |
335 | |
336 | nbytes = sizeof (GElf_Shdr) * shnum; |
337 | |
338 | if ((sp = malloc(nbytes)) == NULL) |
339 | return (ctf_set_open_errno(errp, errno)); |
340 | |
341 | /* |
342 | * Read in and convert to GElf the array of Shdr structures |
343 | * from e_shoff so we can locate sections of interest. |
344 | */ |
345 | if (hdr.e32.e_ident[EI_CLASS] == ELFCLASS32) { |
346 | Elf32_Shdr *sp32; |
347 | |
348 | nbytes = sizeof (Elf32_Shdr) * shnum; |
349 | |
350 | if ((sp32 = malloc(nbytes)) == NULL || pread64(fd, |
351 | sp32, nbytes, hdr.e64.e_shoff) != nbytes) { |
352 | free(sp); |
353 | free(sp32); |
354 | return (ctf_set_open_errno(errp, errno)); |
355 | } |
356 | |
357 | for (i = 0; i < shnum; i++) |
358 | shdr_to_gelf(&sp32[i], &sp[i]); |
359 | |
360 | free(sp32); |
361 | |
362 | } else if (pread64(fd, sp, nbytes, hdr.e64.e_shoff) != nbytes) { |
363 | free(sp); |
364 | return (ctf_set_open_errno(errp, errno)); |
365 | } |
366 | |
367 | /* |
368 | * Now mmap the section header strings section so that we can |
369 | * perform string comparison on the section names. |
370 | */ |
371 | strs_mapsz = sp[shstrndx].sh_size + |
372 | (sp[shstrndx].sh_offset & ~_PAGEMASK); |
373 | |
374 | strs_map = mmap64(NULL, strs_mapsz, PROT_READ, MAP_PRIVATE, |
375 | fd, sp[shstrndx].sh_offset & _PAGEMASK); |
376 | |
377 | strs = (char *)strs_map + |
378 | (sp[shstrndx].sh_offset & ~_PAGEMASK); |
379 | |
380 | if (strs_map == MAP_FAILED) { |
381 | free(sp); |
382 | return (ctf_set_open_errno(errp, ECTF_MMAP)); |
383 | } |
384 | |
385 | /* |
386 | * Iterate over the section header array looking for the CTF |
387 | * section and symbol table. The strtab is linked to symtab. |
388 | */ |
389 | for (i = 0; i < shnum; i++) { |
390 | const GElf_Shdr *shp = &sp[i]; |
391 | const GElf_Shdr *lhp = &sp[shp->sh_link]; |
392 | |
393 | if (shp->sh_link >= shnum) |
394 | continue; /* corrupt sh_link field */ |
395 | |
396 | if (shp->sh_name >= sp[shstrndx].sh_size || |
397 | lhp->sh_name >= sp[shstrndx].sh_size) |
398 | continue; /* corrupt sh_name field */ |
399 | |
400 | if (shp->sh_type == SHT_PROGBITS && |
401 | strcmp(strs + shp->sh_name, _CTF_SECTION) == 0) { |
402 | ctfsect.cts_name = strs + shp->sh_name; |
403 | ctfsect.cts_type = shp->sh_type; |
404 | ctfsect.cts_flags = shp->sh_flags; |
405 | ctfsect.cts_size = shp->sh_size; |
406 | ctfsect.cts_entsize = shp->sh_entsize; |
407 | ctfsect.cts_offset = (off64_t)shp->sh_offset; |
408 | |
409 | } else if (shp->sh_type == SHT_SYMTAB) { |
410 | symsect.cts_name = strs + shp->sh_name; |
411 | symsect.cts_type = shp->sh_type; |
412 | symsect.cts_flags = shp->sh_flags; |
413 | symsect.cts_size = shp->sh_size; |
414 | symsect.cts_entsize = shp->sh_entsize; |
415 | symsect.cts_offset = (off64_t)shp->sh_offset; |
416 | |
417 | strsect.cts_name = strs + lhp->sh_name; |
418 | strsect.cts_type = lhp->sh_type; |
419 | strsect.cts_flags = lhp->sh_flags; |
420 | strsect.cts_size = lhp->sh_size; |
421 | strsect.cts_entsize = lhp->sh_entsize; |
422 | strsect.cts_offset = (off64_t)lhp->sh_offset; |
423 | } |
424 | } |
425 | |
426 | free(sp); /* free section header array */ |
427 | |
428 | if (ctfsect.cts_type == SHT_NULL) { |
429 | (void) munmap(strs_map, strs_mapsz); |
430 | return (ctf_set_open_errno(errp, ECTF_NOCTFDATA)); |
431 | } |
432 | |
433 | /* |
434 | * Now mmap the CTF data, symtab, and strtab sections and |
435 | * call ctf_bufopen() to do the rest of the work. |
436 | */ |
437 | if (ctf_sect_mmap(&ctfsect, fd) == MAP_FAILED) { |
438 | (void) munmap(strs_map, strs_mapsz); |
439 | return (ctf_set_open_errno(errp, ECTF_MMAP)); |
440 | } |
441 | |
442 | if (symsect.cts_type != SHT_NULL && |
443 | strsect.cts_type != SHT_NULL) { |
444 | if (ctf_sect_mmap(&symsect, fd) == MAP_FAILED || |
445 | ctf_sect_mmap(&strsect, fd) == MAP_FAILED) { |
446 | (void) ctf_set_open_errno(errp, ECTF_MMAP); |
447 | goto bad; /* unmap all and abort */ |
448 | } |
449 | fp = ctf_bufopen(&ctfsect, &symsect, &strsect, errp); |
450 | } else |
451 | fp = ctf_bufopen(&ctfsect, NULL, NULL, errp); |
452 | bad: |
453 | if (fp == NULL) { |
454 | ctf_sect_munmap(&ctfsect); |
455 | ctf_sect_munmap(&symsect); |
456 | ctf_sect_munmap(&strsect); |
457 | } else |
458 | fp->ctf_flags |= LCTF_MMAP; |
459 | |
460 | (void) munmap(strs_map, strs_mapsz); |
461 | return (fp); |
462 | } |
463 | |
464 | return (ctf_set_open_errno(errp, ECTF_FMT)); |
465 | } |
466 | |
467 | /* |
468 | * Open the specified file and return a pointer to a CTF container. The file |
469 | * can be either an ELF file or raw CTF file. This is just a convenient |
470 | * wrapper around ctf_fdopen() for callers. |
471 | */ |
472 | ctf_file_t * |
473 | ctf_open(const char *filename, int *errp) |
474 | { |
475 | ctf_file_t *fp; |
476 | int fd; |
477 | |
478 | if ((fd = open64(filename, O_RDONLY)) == -1) { |
479 | if (errp != NULL) |
480 | *errp = errno; |
481 | return (NULL); |
482 | } |
483 | |
484 | fp = ctf_fdopen(fd, errp); |
485 | (void) close(fd); |
486 | return (fp); |
487 | } |
488 | |
489 | /* |
490 | * Write the uncompressed CTF data stream to the specified file descriptor. |
491 | * This is useful for saving the results of dynamic CTF containers. |
492 | */ |
493 | int |
494 | ctf_write(ctf_file_t *fp, int fd) |
495 | { |
496 | const uchar_t *buf = fp->ctf_base; |
497 | ssize_t resid = fp->ctf_size; |
498 | ssize_t len; |
499 | |
500 | while (resid != 0) { |
501 | if ((len = write(fd, buf, resid)) <= 0) |
502 | return (ctf_set_errno(fp, errno)); |
503 | resid -= len; |
504 | buf += len; |
505 | } |
506 | |
507 | return (0); |
508 | } |
509 | |
510 | /* |
511 | * Set the CTF library client version to the specified version. If version is |
512 | * zero, we just return the default library version number. |
513 | */ |
514 | int |
515 | ctf_version(int version) |
516 | { |
517 | if (version < 0) { |
518 | errno = EINVAL; |
519 | return (-1); |
520 | } |
521 | |
522 | if (version > 0) { |
523 | if (version > CTF_VERSION) { |
524 | errno = ENOTSUP; |
525 | return (-1); |
526 | } |
527 | ctf_dprintf("ctf_version: client using version %d\n" , version); |
528 | _libctf_version = version; |
529 | } |
530 | |
531 | return (_libctf_version); |
532 | } |
533 | |