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
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.
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]
23 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 * Copyright (c) 2015, Joyent, Inc.
30 #include <sys/types.h>
40 #include <sys/debug.h>
43 static const char *_libctf_zlib
= "/usr/lib/64/libz.so.1";
45 static const char *_libctf_zlib
= "/usr/lib/libz.so.1";
49 int (*z_uncompress
)(uchar_t
*, ulong_t
*, const uchar_t
*, ulong_t
);
50 int (*z_initcomp
)(z_stream
*, int, const char *, int);
51 int (*z_compress
)(z_stream
*, int);
52 int (*z_finicomp
)(z_stream
*);
53 const char *(*z_error
)(int);
57 static size_t _PAGESIZE
;
58 static size_t _PAGEMASK
;
60 static uint64_t ctf_phase
= 0;
62 #define CTF_COMPRESS_CHUNK (64*1024)
64 typedef struct ctf_zdata
{
72 #pragma init(_libctf_init)
76 const char *p
= getenv("LIBCTF_DECOMPRESSOR");
79 _libctf_zlib
= p
; /* use alternate decompression library */
81 _libctf_debug
= getenv("LIBCTF_DEBUG") != NULL
;
83 _PAGESIZE
= getpagesize();
84 _PAGEMASK
= ~(_PAGESIZE
- 1);
88 * Attempt to dlopen the decompression library and locate the symbols of
89 * interest that we will need to call. This information in cached so
90 * that multiple calls to ctf_bufopen() do not need to reopen the library.
95 ctf_dprintf("decompressing CTF data using %s\n", _libctf_zlib
);
97 if (zlib
.z_dlp
!= NULL
)
98 return (zlib
.z_dlp
); /* library is already loaded */
100 if (access(_libctf_zlib
, R_OK
) == -1)
101 return (ctf_set_open_errno(errp
, ECTF_ZMISSING
));
103 if ((zlib
.z_dlp
= dlopen(_libctf_zlib
, RTLD_LAZY
| RTLD_LOCAL
)) == NULL
)
104 return (ctf_set_open_errno(errp
, ECTF_ZINIT
));
106 zlib
.z_uncompress
= (int (*)()) dlsym(zlib
.z_dlp
, "uncompress");
107 zlib
.z_initcomp
= (int (*)()) dlsym(zlib
.z_dlp
, "deflateInit_");
108 zlib
.z_compress
= (int (*)()) dlsym(zlib
.z_dlp
, "deflate");
109 zlib
.z_finicomp
= (int (*)()) dlsym(zlib
.z_dlp
, "deflateEnd");
110 zlib
.z_error
= (const char *(*)()) dlsym(zlib
.z_dlp
, "zError");
112 if (zlib
.z_uncompress
== NULL
|| zlib
.z_error
== NULL
||
113 zlib
.z_initcomp
== NULL
|| zlib
.z_compress
== NULL
||
114 zlib
.z_finicomp
== NULL
) {
115 (void) dlclose(zlib
.z_dlp
);
116 bzero(&zlib
, sizeof (zlib
));
117 return (ctf_set_open_errno(errp
, ECTF_ZINIT
));
124 * The ctf_bufopen() routine calls these subroutines, defined by <sys/zmod.h>,
125 * which we then patch through to the functions in the decompression library.
128 z_uncompress(void *dst
, size_t *dstlen
, const void *src
, size_t srclen
)
130 return (zlib
.z_uncompress(dst
, (ulong_t
*)dstlen
, src
, srclen
));
136 return (zlib
.z_error(err
));
140 ctf_zdata_init(ctf_zdata_t
*czd
, ctf_file_t
*fp
)
144 bzero(czd
, sizeof (ctf_zdata_t
));
146 czd
->czd_allocsz
= fp
->ctf_size
;
147 czd
->czd_buf
= ctf_data_alloc(czd
->czd_allocsz
);
148 if (czd
->czd_buf
== MAP_FAILED
)
149 return (ctf_set_errno(fp
, ENOMEM
));
151 bcopy(fp
->ctf_base
, czd
->czd_buf
, sizeof (ctf_header_t
));
154 cthp
->cth_flags
|= CTF_F_COMPRESS
;
155 czd
->czd_next
= (void *)((uintptr_t)czd
->czd_buf
+
156 sizeof (ctf_header_t
));
158 if (zlib
.z_initcomp(&czd
->czd_zstr
, Z_BEST_COMPRESSION
,
159 ZLIB_VERSION
, sizeof (z_stream
)) != Z_OK
)
160 return (ctf_set_errno(fp
, ECTF_ZLIB
));
166 ctf_zdata_grow(ctf_zdata_t
*czd
)
172 off
= (uintptr_t)czd
->czd_next
- (uintptr_t)czd
->czd_buf
;
173 newsz
= czd
->czd_allocsz
+ CTF_COMPRESS_CHUNK
;
174 ndata
= ctf_data_alloc(newsz
);
175 if (ndata
== MAP_FAILED
) {
176 return (ctf_set_errno(czd
->czd_ctfp
, ENOMEM
));
179 bcopy(czd
->czd_buf
, ndata
, off
);
180 ctf_data_free(czd
->czd_buf
, czd
->czd_allocsz
);
181 czd
->czd_allocsz
= newsz
;
182 czd
->czd_buf
= ndata
;
183 czd
->czd_next
= (void *)((uintptr_t)ndata
+ off
);
185 czd
->czd_zstr
.next_out
= (Bytef
*)czd
->czd_next
;
186 czd
->czd_zstr
.avail_out
= CTF_COMPRESS_CHUNK
;
191 ctf_zdata_compress_buffer(ctf_zdata_t
*czd
, const void *buf
, size_t bufsize
)
195 czd
->czd_zstr
.next_out
= czd
->czd_next
;
196 czd
->czd_zstr
.avail_out
= czd
->czd_allocsz
-
197 ((uintptr_t)czd
->czd_next
- (uintptr_t)czd
->czd_buf
);
198 czd
->czd_zstr
.next_in
= (Bytef
*)buf
;
199 czd
->czd_zstr
.avail_in
= bufsize
;
201 while (czd
->czd_zstr
.avail_in
!= 0) {
202 if (czd
->czd_zstr
.avail_out
== 0) {
203 czd
->czd_next
= czd
->czd_zstr
.next_out
;
204 if ((err
= ctf_zdata_grow(czd
)) != 0) {
209 if ((err
= zlib
.z_compress(&czd
->czd_zstr
, Z_NO_FLUSH
)) != Z_OK
)
210 return (ctf_set_errno(czd
->czd_ctfp
, ECTF_ZLIB
));
212 czd
->czd_next
= czd
->czd_zstr
.next_out
;
218 ctf_zdata_flush(ctf_zdata_t
*czd
, boolean_t finish
)
221 int flag
= finish
== B_TRUE
? Z_FINISH
: Z_FULL_FLUSH
;
222 int bret
= finish
== B_TRUE
? Z_STREAM_END
: Z_BUF_ERROR
;
225 if (czd
->czd_zstr
.avail_out
== 0) {
226 czd
->czd_next
= czd
->czd_zstr
.next_out
;
227 if ((err
= ctf_zdata_grow(czd
)) != 0) {
232 err
= zlib
.z_compress(&czd
->czd_zstr
, flag
);
237 return (ctf_set_errno(czd
->czd_ctfp
, ECTF_ZLIB
));
241 czd
->czd_next
= czd
->czd_zstr
.next_out
;
247 ctf_zdata_end(ctf_zdata_t
*czd
)
251 if ((ret
= ctf_zdata_flush(czd
, B_TRUE
)) != 0)
254 if ((ret
= zlib
.z_finicomp(&czd
->czd_zstr
)) != 0)
255 return (ctf_set_errno(czd
->czd_ctfp
, ECTF_ZLIB
));
261 ctf_zdata_cleanup(ctf_zdata_t
*czd
)
263 ctf_data_free(czd
->czd_buf
, czd
->czd_allocsz
);
264 (void) zlib
.z_finicomp(&czd
->czd_zstr
);
268 * Compress our CTF data and return both the size of the compressed data and the
269 * size of the allocation. These may be different due to the nature of
272 * In addition, we flush the compression inbetween our two phases such that we
273 * maintain a different dictionary bbetween the CTF data and the string section.
276 ctf_compress(ctf_file_t
*fp
, void **buf
, size_t *allocsz
, size_t *elfsize
)
282 /* LINTED: E_BAD_PTR_CAST_ALIGN */
283 cthp
= (ctf_header_t
*)fp
->ctf_base
;
285 if ((err
= ctf_zdata_init(&czd
, fp
)) != 0)
288 if ((err
= ctf_zdata_compress_buffer(&czd
, fp
->ctf_buf
,
289 cthp
->cth_stroff
)) != 0) {
290 ctf_zdata_cleanup(&czd
);
294 if ((err
= ctf_zdata_flush(&czd
, B_FALSE
)) != 0) {
295 ctf_zdata_cleanup(&czd
);
299 if ((err
= ctf_zdata_compress_buffer(&czd
,
300 fp
->ctf_buf
+ cthp
->cth_stroff
, cthp
->cth_strlen
)) != 0) {
301 ctf_zdata_cleanup(&czd
);
305 if ((err
= ctf_zdata_end(&czd
)) != 0) {
306 ctf_zdata_cleanup(&czd
);
311 *allocsz
= czd
.czd_allocsz
;
312 *elfsize
= (uintptr_t)czd
.czd_next
- (uintptr_t)czd
.czd_buf
;
318 z_compress(void *dst
, size_t *dstlen
, const void *src
, size_t srclen
)
323 bzero(&zs
, sizeof (z_stream
));
324 zs
.next_in
= (uchar_t
*)src
;
325 zs
.avail_in
= srclen
;
327 zs
.avail_out
= *dstlen
;
329 if ((err
= zlib
.z_initcomp(&zs
, Z_BEST_COMPRESSION
, ZLIB_VERSION
,
330 sizeof (z_stream
))) != Z_OK
)
333 if ((err
= zlib
.z_compress(&zs
, Z_FINISH
)) != Z_STREAM_END
) {
334 (void) zlib
.z_finicomp(&zs
);
335 return (err
== Z_OK
? Z_BUF_ERROR
: err
);
338 *dstlen
= zs
.total_out
;
339 return (zlib
.z_finicomp(&zs
));
343 * Convert a 32-bit ELF file header into GElf.
346 ehdr_to_gelf(const Elf32_Ehdr
*src
, GElf_Ehdr
*dst
)
348 bcopy(src
->e_ident
, dst
->e_ident
, EI_NIDENT
);
349 dst
->e_type
= src
->e_type
;
350 dst
->e_machine
= src
->e_machine
;
351 dst
->e_version
= src
->e_version
;
352 dst
->e_entry
= (Elf64_Addr
)src
->e_entry
;
353 dst
->e_phoff
= (Elf64_Off
)src
->e_phoff
;
354 dst
->e_shoff
= (Elf64_Off
)src
->e_shoff
;
355 dst
->e_flags
= src
->e_flags
;
356 dst
->e_ehsize
= src
->e_ehsize
;
357 dst
->e_phentsize
= src
->e_phentsize
;
358 dst
->e_phnum
= src
->e_phnum
;
359 dst
->e_shentsize
= src
->e_shentsize
;
360 dst
->e_shnum
= src
->e_shnum
;
361 dst
->e_shstrndx
= src
->e_shstrndx
;
365 * Convert a 32-bit ELF section header into GElf.
368 shdr_to_gelf(const Elf32_Shdr
*src
, GElf_Shdr
*dst
)
370 dst
->sh_name
= src
->sh_name
;
371 dst
->sh_type
= src
->sh_type
;
372 dst
->sh_flags
= src
->sh_flags
;
373 dst
->sh_addr
= src
->sh_addr
;
374 dst
->sh_offset
= src
->sh_offset
;
375 dst
->sh_size
= src
->sh_size
;
376 dst
->sh_link
= src
->sh_link
;
377 dst
->sh_info
= src
->sh_info
;
378 dst
->sh_addralign
= src
->sh_addralign
;
379 dst
->sh_entsize
= src
->sh_entsize
;
383 * In order to mmap a section from the ELF file, we must round down sh_offset
384 * to the previous page boundary, and mmap the surrounding page. We store
385 * the pointer to the start of the actual section data back into sp->cts_data.
388 ctf_sect_mmap(ctf_sect_t
*sp
, int fd
)
390 size_t pageoff
= sp
->cts_offset
& ~_PAGEMASK
;
392 caddr_t base
= mmap64(NULL
, sp
->cts_size
+ pageoff
, PROT_READ
,
393 MAP_PRIVATE
, fd
, sp
->cts_offset
& _PAGEMASK
);
395 if (base
!= MAP_FAILED
)
396 sp
->cts_data
= base
+ pageoff
;
402 * Since sp->cts_data has the adjusted offset, we have to again round down
403 * to get the actual mmap address and round up to get the size.
406 ctf_sect_munmap(const ctf_sect_t
*sp
)
408 uintptr_t addr
= (uintptr_t)sp
->cts_data
;
409 uintptr_t pageoff
= addr
& ~_PAGEMASK
;
411 (void) munmap((void *)(addr
- pageoff
), sp
->cts_size
+ pageoff
);
415 * Open the specified file descriptor and return a pointer to a CTF container.
416 * The file can be either an ELF file or raw CTF file. The caller is
417 * responsible for closing the file descriptor when it is no longer needed.
420 ctf_fdcreate_int(int fd
, int *errp
, ctf_sect_t
*ctfp
)
422 ctf_sect_t ctfsect
, symsect
, strsect
;
423 ctf_file_t
*fp
= NULL
;
424 size_t shstrndx
, shnum
;
435 bzero(&ctfsect
, sizeof (ctf_sect_t
));
436 bzero(&symsect
, sizeof (ctf_sect_t
));
437 bzero(&strsect
, sizeof (ctf_sect_t
));
438 bzero(&hdr
.ctf
, sizeof (hdr
));
440 if (fstat64(fd
, &st
) == -1)
441 return (ctf_set_open_errno(errp
, errno
));
443 if ((nbytes
= pread64(fd
, &hdr
.ctf
, sizeof (hdr
), 0)) <= 0)
444 return (ctf_set_open_errno(errp
, nbytes
< 0? errno
: ECTF_FMT
));
447 * If we have read enough bytes to form a CTF header and the magic
448 * string matches, attempt to interpret the file as raw CTF.
450 if (nbytes
>= sizeof (ctf_preamble_t
) &&
451 hdr
.ctf
.ctp_magic
== CTF_MAGIC
) {
453 return (ctf_set_open_errno(errp
, EINVAL
));
455 if (hdr
.ctf
.ctp_version
> CTF_VERSION
)
456 return (ctf_set_open_errno(errp
, ECTF_CTFVERS
));
458 ctfsect
.cts_data
= mmap64(NULL
, st
.st_size
, PROT_READ
,
461 if (ctfsect
.cts_data
== MAP_FAILED
)
462 return (ctf_set_open_errno(errp
, errno
));
464 ctfsect
.cts_name
= _CTF_SECTION
;
465 ctfsect
.cts_type
= SHT_PROGBITS
;
466 ctfsect
.cts_flags
= SHF_ALLOC
;
467 ctfsect
.cts_size
= (size_t)st
.st_size
;
468 ctfsect
.cts_entsize
= 1;
469 ctfsect
.cts_offset
= 0;
471 if ((fp
= ctf_bufopen(&ctfsect
, NULL
, NULL
, errp
)) == NULL
)
472 ctf_sect_munmap(&ctfsect
);
478 * If we have read enough bytes to form an ELF header and the magic
479 * string matches, attempt to interpret the file as an ELF file. We
480 * do our own largefile ELF processing, and convert everything to
481 * GElf structures so that clients can operate on any data model.
483 if (nbytes
>= sizeof (Elf32_Ehdr
) &&
484 bcmp(&hdr
.e32
.e_ident
[EI_MAG0
], ELFMAG
, SELFMAG
) == 0) {
486 uchar_t order
= ELFDATA2MSB
;
488 uchar_t order
= ELFDATA2LSB
;
493 size_t strs_mapsz
, i
;
496 if (hdr
.e32
.e_ident
[EI_DATA
] != order
)
497 return (ctf_set_open_errno(errp
, ECTF_ENDIAN
));
498 if (hdr
.e32
.e_version
!= EV_CURRENT
)
499 return (ctf_set_open_errno(errp
, ECTF_ELFVERS
));
501 if (hdr
.e32
.e_ident
[EI_CLASS
] == ELFCLASS64
) {
502 if (nbytes
< sizeof (GElf_Ehdr
))
503 return (ctf_set_open_errno(errp
, ECTF_FMT
));
505 Elf32_Ehdr e32
= hdr
.e32
;
506 ehdr_to_gelf(&e32
, &hdr
.e64
);
509 shnum
= hdr
.e64
.e_shnum
;
510 shstrndx
= hdr
.e64
.e_shstrndx
;
512 /* Extended ELF sections */
513 if ((shstrndx
== SHN_XINDEX
) || (shnum
== 0)) {
514 if (hdr
.e32
.e_ident
[EI_CLASS
] == ELFCLASS32
) {
517 if (pread64(fd
, &x32
, sizeof (x32
),
518 hdr
.e64
.e_shoff
) != sizeof (x32
))
519 return (ctf_set_open_errno(errp
,
523 shstrndx
= x32
.sh_link
;
527 if (pread64(fd
, &x64
, sizeof (x64
),
528 hdr
.e64
.e_shoff
) != sizeof (x64
))
529 return (ctf_set_open_errno(errp
,
533 shstrndx
= x64
.sh_link
;
537 if (shstrndx
>= shnum
)
538 return (ctf_set_open_errno(errp
, ECTF_CORRUPT
));
540 nbytes
= sizeof (GElf_Shdr
) * shnum
;
542 if ((sp
= malloc(nbytes
)) == NULL
)
543 return (ctf_set_open_errno(errp
, errno
));
546 * Read in and convert to GElf the array of Shdr structures
547 * from e_shoff so we can locate sections of interest.
549 if (hdr
.e32
.e_ident
[EI_CLASS
] == ELFCLASS32
) {
552 nbytes
= sizeof (Elf32_Shdr
) * shnum
;
554 if ((sp32
= malloc(nbytes
)) == NULL
|| pread64(fd
,
555 sp32
, nbytes
, hdr
.e64
.e_shoff
) != nbytes
) {
557 return (ctf_set_open_errno(errp
, errno
));
560 for (i
= 0; i
< shnum
; i
++)
561 shdr_to_gelf(&sp32
[i
], &sp
[i
]);
565 } else if (pread64(fd
, sp
, nbytes
, hdr
.e64
.e_shoff
) != nbytes
) {
567 return (ctf_set_open_errno(errp
, errno
));
571 * Now mmap the section header strings section so that we can
572 * perform string comparison on the section names.
574 strs_mapsz
= sp
[shstrndx
].sh_size
+
575 (sp
[shstrndx
].sh_offset
& ~_PAGEMASK
);
577 strs_map
= mmap64(NULL
, strs_mapsz
, PROT_READ
, MAP_PRIVATE
,
578 fd
, sp
[shstrndx
].sh_offset
& _PAGEMASK
);
580 strs
= (const char *)strs_map
+
581 (sp
[shstrndx
].sh_offset
& ~_PAGEMASK
);
583 if (strs_map
== MAP_FAILED
) {
585 return (ctf_set_open_errno(errp
, ECTF_MMAP
));
589 * Iterate over the section header array looking for the CTF
590 * section and symbol table. The strtab is linked to symtab.
592 for (i
= 0; i
< shnum
; i
++) {
593 const GElf_Shdr
*shp
= &sp
[i
];
594 const GElf_Shdr
*lhp
= &sp
[shp
->sh_link
];
596 if (shp
->sh_link
>= shnum
)
597 continue; /* corrupt sh_link field */
599 if (shp
->sh_name
>= sp
[shstrndx
].sh_size
||
600 lhp
->sh_name
>= sp
[shstrndx
].sh_size
)
601 continue; /* corrupt sh_name field */
603 if (shp
->sh_type
== SHT_PROGBITS
&&
604 strcmp(strs
+ shp
->sh_name
, _CTF_SECTION
) == 0 &&
606 ctfsect
.cts_name
= strs
+ shp
->sh_name
;
607 ctfsect
.cts_type
= shp
->sh_type
;
608 ctfsect
.cts_flags
= shp
->sh_flags
;
609 ctfsect
.cts_size
= shp
->sh_size
;
610 ctfsect
.cts_entsize
= shp
->sh_entsize
;
611 ctfsect
.cts_offset
= (off64_t
)shp
->sh_offset
;
613 } else if (shp
->sh_type
== SHT_SYMTAB
) {
614 symsect
.cts_name
= strs
+ shp
->sh_name
;
615 symsect
.cts_type
= shp
->sh_type
;
616 symsect
.cts_flags
= shp
->sh_flags
;
617 symsect
.cts_size
= shp
->sh_size
;
618 symsect
.cts_entsize
= shp
->sh_entsize
;
619 symsect
.cts_offset
= (off64_t
)shp
->sh_offset
;
621 strsect
.cts_name
= strs
+ lhp
->sh_name
;
622 strsect
.cts_type
= lhp
->sh_type
;
623 strsect
.cts_flags
= lhp
->sh_flags
;
624 strsect
.cts_size
= lhp
->sh_size
;
625 strsect
.cts_entsize
= lhp
->sh_entsize
;
626 strsect
.cts_offset
= (off64_t
)lhp
->sh_offset
;
630 free(sp
); /* free section header array */
633 if (ctfsect
.cts_type
== SHT_NULL
&& ctfp
== NULL
) {
634 (void) munmap(strs_map
, strs_mapsz
);
635 return (ctf_set_open_errno(errp
,
640 * Now mmap the CTF data, symtab, and strtab sections
641 * and call ctf_bufopen() to do the rest of the work.
643 if (ctf_sect_mmap(&ctfsect
, fd
) == MAP_FAILED
) {
644 (void) munmap(strs_map
, strs_mapsz
);
645 return (ctf_set_open_errno(errp
, ECTF_MMAP
));
650 if (symsect
.cts_type
!= SHT_NULL
&&
651 strsect
.cts_type
!= SHT_NULL
) {
652 if (ctf_sect_mmap(&symsect
, fd
) == MAP_FAILED
||
653 ctf_sect_mmap(&strsect
, fd
) == MAP_FAILED
) {
654 (void) ctf_set_open_errno(errp
, ECTF_MMAP
);
655 goto bad
; /* unmap all and abort */
657 fp
= ctf_bufopen(ctfp
, &symsect
, &strsect
, errp
);
659 fp
= ctf_bufopen(ctfp
, NULL
, NULL
, errp
);
663 ctf_sect_munmap(&ctfsect
);
664 ctf_sect_munmap(&symsect
);
665 ctf_sect_munmap(&strsect
);
667 fp
->ctf_flags
|= LCTF_MMAP
;
669 (void) munmap(strs_map
, strs_mapsz
);
673 return (ctf_set_open_errno(errp
, ECTF_FMT
));
677 ctf_fdopen(int fd
, int *errp
)
679 return (ctf_fdcreate_int(fd
, errp
, NULL
));
683 * Open the specified file and return a pointer to a CTF container. The file
684 * can be either an ELF file or raw CTF file. This is just a convenient
685 * wrapper around ctf_fdopen() for callers.
688 ctf_open(const char *filename
, int *errp
)
693 if ((fd
= open64(filename
, O_RDONLY
)) == -1) {
699 fp
= ctf_fdopen(fd
, errp
);
705 * Write the uncompressed CTF data stream to the specified file descriptor.
706 * This is useful for saving the results of dynamic CTF containers.
709 ctf_write(ctf_file_t
*fp
, int fd
)
711 const uchar_t
*buf
= fp
->ctf_base
;
712 ssize_t resid
= fp
->ctf_size
;
716 if ((len
= write(fd
, buf
, resid
)) <= 0)
717 return (ctf_set_errno(fp
, errno
));
726 * Set the CTF library client version to the specified version. If version is
727 * zero, we just return the default library version number.
730 ctf_version(int version
)
738 if (version
> CTF_VERSION
) {
742 ctf_dprintf("ctf_version: client using version %d\n", version
);
743 _libctf_version
= version
;
746 return (_libctf_version
);
750 * A utility function for folks debugging CTF conversion and merging.
753 ctf_phase_dump(ctf_file_t
*fp
, const char *phase
)
757 char path
[MAXPATHLEN
];
759 if (base
== NULL
&& (base
= getenv("LIBCTF_WRITE_PHASES")) == NULL
)
762 (void) snprintf(path
, sizeof (path
), "%s/libctf.%s.%d.ctf", base
,
763 phase
!= NULL
? phase
: "",
765 if ((fd
= open(path
, O_CREAT
| O_TRUNC
| O_RDWR
, 0777)) < 0)
767 (void) ctf_write(fp
, fd
);