summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Mustacchi <rm@joyent.com>2015-07-27 00:35:52 +0000
committerRobert Mustacchi <rm@joyent.com>2015-07-28 19:05:39 +0000
commit14c3d85bba96b10c225341f4c7f4af93c314b508 (patch)
tree8ef3a2513e220291581564d4db16bd5b4fbf127e
parent9211d9b4c9ccc64292132e8e87c92ad6084b29a8 (diff)
downloadillumos-joyent-14c3d85bba96b10c225341f4c7f4af93c314b508.tar.gz
OS-4548 CTF Everywhere: Phase 1
OS-4549 ctfconvert should be implemented in terms of libctf OS-4550 ctfconvert could convert multiple compilation units OS-4553 want multi-threaded ctfmerge OS-4552 Want general workq OS-4551 Want general mergeq OS-4554 ctfdiff doesn't properly handle unknown options OS-4555 ctfdiff's symbols could be more consistently prefixed OS-4048 new ctfmerge uses tmpfile after freeing it OS-4556 ctfdump should drive on when incomplete files exist OS-4557 ctf_add_encoded assigns() incorrect byte size to types OS-4558 ctf_add_{struct,union,enum} can reuse forwards OS-4559 ctf_add_{struct,union,enum} occasionally forget to dirty the ctf_file_t OS-4560 ctf_add_member could better handle bitfields OS-4561 ctf_type_size() reports wrong size for forwards OS-4563 diffing CTF typedefs needs to walk multiple definitions OS-4564 build scripts shouldn't hardcode CTF paths OS-4565 ctf_fdcreate could be more flexible OS-4566 Want libctf ctf_kind_name() function OS-4567 Want libctf function to set struct/union size OS-4568 Want ctfmerge altexec
-rw-r--r--exception_lists/cstyle100
-rw-r--r--exception_lists/hdrchk44
-rw-r--r--manifest4
-rw-r--r--usr/src/Makefile.master9
-rw-r--r--usr/src/cmd/Makefile1
-rw-r--r--usr/src/cmd/ctfconvert/Makefile33
-rw-r--r--usr/src/cmd/ctfconvert/ctfconvert.c389
-rw-r--r--usr/src/cmd/ctfdiff/ctfdiff.c76
-rw-r--r--usr/src/cmd/ctfdump/ctfdump.c64
-rw-r--r--usr/src/cmd/ctfmerge/ctfmerge.c81
-rw-r--r--usr/src/common/ctf/ctf_create.c243
-rw-r--r--usr/src/common/ctf/ctf_error.c5
-rw-r--r--usr/src/common/ctf/ctf_types.c43
-rw-r--r--usr/src/lib/Makefile5
-rw-r--r--usr/src/lib/libctf/Makefile.shared.com17
-rw-r--r--usr/src/lib/libctf/Makefile.shared.targ4
-rw-r--r--usr/src/lib/libctf/common/ctf_convert.c210
-rw-r--r--usr/src/lib/libctf/common/ctf_diff.c95
-rw-r--r--usr/src/lib/libctf/common/ctf_dwarf.c2952
-rw-r--r--usr/src/lib/libctf/common/ctf_elfwrite.c198
-rw-r--r--usr/src/lib/libctf/common/ctf_lib.c211
-rw-r--r--usr/src/lib/libctf/common/ctf_merge.c492
-rw-r--r--usr/src/lib/libctf/common/ctf_subr.c28
-rw-r--r--usr/src/lib/libctf/common/libctf.h9
-rw-r--r--usr/src/lib/libctf/common/libctf_impl.h59
-rw-r--r--usr/src/lib/libctf/common/mapfile-vers6
-rw-r--r--usr/src/lib/libdwarf/Makefile40
-rw-r--r--usr/src/lib/libdwarf/Makefile.com92
-rw-r--r--usr/src/lib/libdwarf/THIRDPARTYLICENSE (renamed from usr/src/tools/ctf/dwarf/THIRDPARTYLICENSE)0
-rw-r--r--usr/src/lib/libdwarf/THIRDPARTYLICENSE.descrip (renamed from usr/src/tools/ctf/dwarf/THIRDPARTYLICENSE.descrip)0
-rw-r--r--usr/src/lib/libdwarf/amd64/Makefile19
-rw-r--r--usr/src/lib/libdwarf/common/cmplrs/dwarf_addr_finder.h (renamed from usr/src/tools/ctf/dwarf/common/cmplrs/dwarf_addr_finder.h)0
-rw-r--r--usr/src/lib/libdwarf/common/config.h (renamed from usr/src/tools/ctf/dwarf/common/config.h)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf.h (renamed from usr/src/tools/ctf/dwarf/common/dwarf.h)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_abbrev.c (renamed from usr/src/tools/ctf/dwarf/common/dwarf_abbrev.c)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_abbrev.h (renamed from usr/src/tools/ctf/dwarf/common/dwarf_abbrev.h)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_addr_finder.c (renamed from usr/src/tools/ctf/dwarf/common/dwarf_addr_finder.c)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_alloc.c (renamed from usr/src/tools/ctf/dwarf/common/dwarf_alloc.c)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_alloc.h (renamed from usr/src/tools/ctf/dwarf/common/dwarf_alloc.h)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_arange.c (renamed from usr/src/tools/ctf/dwarf/common/dwarf_arange.c)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_arange.h (renamed from usr/src/tools/ctf/dwarf/common/dwarf_arange.h)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_base_types.h (renamed from usr/src/tools/ctf/dwarf/common/dwarf_base_types.h)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_die_deliv.c (renamed from usr/src/tools/ctf/dwarf/common/dwarf_die_deliv.c)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_die_deliv.h (renamed from usr/src/tools/ctf/dwarf/common/dwarf_die_deliv.h)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_elf_access.c (renamed from usr/src/tools/ctf/dwarf/common/dwarf_elf_access.c)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_elf_access.h (renamed from usr/src/tools/ctf/dwarf/common/dwarf_elf_access.h)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_error.c (renamed from usr/src/tools/ctf/dwarf/common/dwarf_error.c)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_error.h (renamed from usr/src/tools/ctf/dwarf/common/dwarf_error.h)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_form.c (renamed from usr/src/tools/ctf/dwarf/common/dwarf_form.c)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_frame.c (renamed from usr/src/tools/ctf/dwarf/common/dwarf_frame.c)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_frame.h (renamed from usr/src/tools/ctf/dwarf/common/dwarf_frame.h)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_frame2.c (renamed from usr/src/tools/ctf/dwarf/common/dwarf_frame2.c)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_frame3.c (renamed from usr/src/tools/ctf/dwarf/common/dwarf_frame3.c)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_funcs.c (renamed from usr/src/tools/ctf/dwarf/common/dwarf_funcs.c)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_funcs.h (renamed from usr/src/tools/ctf/dwarf/common/dwarf_funcs.h)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_global.c (renamed from usr/src/tools/ctf/dwarf/common/dwarf_global.c)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_global.h (renamed from usr/src/tools/ctf/dwarf/common/dwarf_global.h)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_harmless.c (renamed from usr/src/tools/ctf/dwarf/common/dwarf_harmless.c)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_harmless.h (renamed from usr/src/tools/ctf/dwarf/common/dwarf_harmless.h)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_incl.h (renamed from usr/src/tools/ctf/dwarf/common/dwarf_incl.h)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_init_finish.c (renamed from usr/src/tools/ctf/dwarf/common/dwarf_init_finish.c)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_leb.c (renamed from usr/src/tools/ctf/dwarf/common/dwarf_leb.c)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_line.c (renamed from usr/src/tools/ctf/dwarf/common/dwarf_line.c)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_line.h (renamed from usr/src/tools/ctf/dwarf/common/dwarf_line.h)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_line2.c (renamed from usr/src/tools/ctf/dwarf/common/dwarf_line2.c)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_loc.c (renamed from usr/src/tools/ctf/dwarf/common/dwarf_loc.c)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_loc.h (renamed from usr/src/tools/ctf/dwarf/common/dwarf_loc.h)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_macro.c (renamed from usr/src/tools/ctf/dwarf/common/dwarf_macro.c)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_macro.h (renamed from usr/src/tools/ctf/dwarf/common/dwarf_macro.h)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_names.c (renamed from usr/src/tools/ctf/dwarf/common/dwarf_names.c)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_names.h (renamed from usr/src/tools/ctf/dwarf/common/dwarf_names.h)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_opaque.h (renamed from usr/src/tools/ctf/dwarf/common/dwarf_opaque.h)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_original_elf_init.c (renamed from usr/src/tools/ctf/dwarf/common/dwarf_original_elf_init.c)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_print_lines.c (renamed from usr/src/tools/ctf/dwarf/common/dwarf_print_lines.c)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_pubtypes.c (renamed from usr/src/tools/ctf/dwarf/common/dwarf_pubtypes.c)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_query.c (renamed from usr/src/tools/ctf/dwarf/common/dwarf_query.c)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_ranges.c (renamed from usr/src/tools/ctf/dwarf/common/dwarf_ranges.c)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_sort_line.c (renamed from usr/src/tools/ctf/dwarf/common/dwarf_sort_line.c)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_string.c (renamed from usr/src/tools/ctf/dwarf/common/dwarf_string.c)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_stubs.c (renamed from usr/src/tools/ctf/dwarf/common/dwarf_stubs.c)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_types.c (renamed from usr/src/tools/ctf/dwarf/common/dwarf_types.c)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_types.h (renamed from usr/src/tools/ctf/dwarf/common/dwarf_types.h)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_util.c (renamed from usr/src/tools/ctf/dwarf/common/dwarf_util.c)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_util.h (renamed from usr/src/tools/ctf/dwarf/common/dwarf_util.h)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_vars.c (renamed from usr/src/tools/ctf/dwarf/common/dwarf_vars.c)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_vars.h (renamed from usr/src/tools/ctf/dwarf/common/dwarf_vars.h)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_weaks.c (renamed from usr/src/tools/ctf/dwarf/common/dwarf_weaks.c)0
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_weaks.h (renamed from usr/src/tools/ctf/dwarf/common/dwarf_weaks.h)0
-rw-r--r--usr/src/lib/libdwarf/common/libdwarf.h (renamed from usr/src/tools/ctf/dwarf/common/libdwarf.h)0
-rw-r--r--usr/src/lib/libdwarf/common/libdwarfdefs.h (renamed from usr/src/tools/ctf/dwarf/common/libdwarfdefs.h)0
-rw-r--r--usr/src/lib/libdwarf/common/malloc_check.c (renamed from usr/src/tools/ctf/dwarf/common/malloc_check.c)0
-rw-r--r--usr/src/lib/libdwarf/common/malloc_check.h (renamed from usr/src/tools/ctf/dwarf/common/malloc_check.h)0
-rw-r--r--usr/src/lib/libdwarf/common/mapfile-vers (renamed from usr/src/tools/ctf/dwarf/common/mapfile-vers)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_alloc.c (renamed from usr/src/tools/ctf/dwarf/common/pro_alloc.c)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_alloc.h (renamed from usr/src/tools/ctf/dwarf/common/pro_alloc.h)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_arange.c (renamed from usr/src/tools/ctf/dwarf/common/pro_arange.c)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_arange.h (renamed from usr/src/tools/ctf/dwarf/common/pro_arange.h)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_die.c (renamed from usr/src/tools/ctf/dwarf/common/pro_die.c)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_die.h (renamed from usr/src/tools/ctf/dwarf/common/pro_die.h)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_encode_nm.c (renamed from usr/src/tools/ctf/dwarf/common/pro_encode_nm.c)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_encode_nm.h (renamed from usr/src/tools/ctf/dwarf/common/pro_encode_nm.h)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_error.c (renamed from usr/src/tools/ctf/dwarf/common/pro_error.c)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_error.h (renamed from usr/src/tools/ctf/dwarf/common/pro_error.h)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_expr.c (renamed from usr/src/tools/ctf/dwarf/common/pro_expr.c)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_expr.h (renamed from usr/src/tools/ctf/dwarf/common/pro_expr.h)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_finish.c (renamed from usr/src/tools/ctf/dwarf/common/pro_finish.c)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_forms.c (renamed from usr/src/tools/ctf/dwarf/common/pro_forms.c)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_frame.c (renamed from usr/src/tools/ctf/dwarf/common/pro_frame.c)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_frame.h (renamed from usr/src/tools/ctf/dwarf/common/pro_frame.h)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_funcs.c (renamed from usr/src/tools/ctf/dwarf/common/pro_funcs.c)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_incl.h (renamed from usr/src/tools/ctf/dwarf/common/pro_incl.h)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_init.c (renamed from usr/src/tools/ctf/dwarf/common/pro_init.c)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_line.c (renamed from usr/src/tools/ctf/dwarf/common/pro_line.c)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_line.h (renamed from usr/src/tools/ctf/dwarf/common/pro_line.h)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_macinfo.c (renamed from usr/src/tools/ctf/dwarf/common/pro_macinfo.c)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_macinfo.h (renamed from usr/src/tools/ctf/dwarf/common/pro_macinfo.h)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_opaque.h (renamed from usr/src/tools/ctf/dwarf/common/pro_opaque.h)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_pubnames.c (renamed from usr/src/tools/ctf/dwarf/common/pro_pubnames.c)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_reloc.c (renamed from usr/src/tools/ctf/dwarf/common/pro_reloc.c)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_reloc.h (renamed from usr/src/tools/ctf/dwarf/common/pro_reloc.h)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_reloc_stream.c (renamed from usr/src/tools/ctf/dwarf/common/pro_reloc_stream.c)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_reloc_stream.h (renamed from usr/src/tools/ctf/dwarf/common/pro_reloc_stream.h)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_reloc_symbolic.c (renamed from usr/src/tools/ctf/dwarf/common/pro_reloc_symbolic.c)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_reloc_symbolic.h (renamed from usr/src/tools/ctf/dwarf/common/pro_reloc_symbolic.h)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_section.c (renamed from usr/src/tools/ctf/dwarf/common/pro_section.c)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_section.h (renamed from usr/src/tools/ctf/dwarf/common/pro_section.h)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_types.c (renamed from usr/src/tools/ctf/dwarf/common/pro_types.c)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_types.h (renamed from usr/src/tools/ctf/dwarf/common/pro_types.h)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_util.h (renamed from usr/src/tools/ctf/dwarf/common/pro_util.h)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_vars.c (renamed from usr/src/tools/ctf/dwarf/common/pro_vars.c)0
-rw-r--r--usr/src/lib/libdwarf/common/pro_weaks.c (renamed from usr/src/tools/ctf/dwarf/common/pro_weaks.c)0
-rw-r--r--usr/src/lib/libdwarf/i386/Makefile18
-rw-r--r--usr/src/lib/libdwarf/sparc/Makefile18
-rw-r--r--usr/src/lib/libdwarf/sparcv9/Makefile19
-rw-r--r--usr/src/lib/mergeq/mergeq.c606
-rw-r--r--usr/src/lib/mergeq/mergeq.h52
-rw-r--r--usr/src/lib/mergeq/workq.c311
-rw-r--r--usr/src/lib/mergeq/workq.h52
-rw-r--r--usr/src/test/util-tests/tests/Makefile2
-rw-r--r--usr/src/test/util-tests/tests/mergeq/Makefile71
-rw-r--r--usr/src/test/util-tests/tests/mergeq/mqt.c218
-rw-r--r--usr/src/test/util-tests/tests/workq/Makefile71
-rw-r--r--usr/src/test/util-tests/tests/workq/wqt.c196
-rw-r--r--usr/src/tools/ctf/Makefile3
-rw-r--r--usr/src/tools/ctf/common/ctf_headers.h13
-rw-r--r--usr/src/tools/ctf/ctfconvert/Makefile44
-rw-r--r--usr/src/tools/ctf/ctfconvert/Makefile.com44
-rw-r--r--usr/src/tools/ctf/ctfconvert/i386/Makefile27
-rw-r--r--usr/src/tools/ctf/ctfconvert/sparc/Makefile27
-rw-r--r--usr/src/tools/ctf/ctfmerge/Makefile.com2
-rw-r--r--usr/src/tools/ctf/cvt/Makefile.com2
-rw-r--r--usr/src/tools/ctf/dwarf/Makefile.com7
-rw-r--r--usr/src/tools/ctf/libctf/Makefile.com15
-rw-r--r--usr/src/tools/scripts/bldenv.sh3
-rw-r--r--usr/src/tools/scripts/nightly.sh7
-rw-r--r--usr/src/uts/Makefile.uts8
-rw-r--r--usr/src/uts/common/sys/ctf_api.h8
157 files changed, 7026 insertions, 347 deletions
diff --git a/exception_lists/cstyle b/exception_lists/cstyle
index aadf35d05e..51c6542ae9 100644
--- a/exception_lists/cstyle
+++ b/exception_lists/cstyle
@@ -132,6 +132,106 @@ usr/src/grub/grub-0.97/stage2/pc_slice.h
usr/src/grub/grub-0.97/stage2/gpt.h
usr/src/grub/grub-0.97/stage2/shared.h
usr/src/lib/libc/port/gen/arc4random_uniform.c
+usr/src/lib/libdwarf/common/cmplrs/dwarf_addr_finder.h
+usr/src/lib/libdwarf/common/config.h
+usr/src/lib/libdwarf/common/dwarf_abbrev.c
+usr/src/lib/libdwarf/common/dwarf_abbrev.h
+usr/src/lib/libdwarf/common/dwarf_addr_finder.c
+usr/src/lib/libdwarf/common/dwarf_alloc.c
+usr/src/lib/libdwarf/common/dwarf_alloc.h
+usr/src/lib/libdwarf/common/dwarf_arange.c
+usr/src/lib/libdwarf/common/dwarf_arange.h
+usr/src/lib/libdwarf/common/dwarf_base_types.h
+usr/src/lib/libdwarf/common/dwarf_die_deliv.c
+usr/src/lib/libdwarf/common/dwarf_die_deliv.h
+usr/src/lib/libdwarf/common/dwarf_elf_access.c
+usr/src/lib/libdwarf/common/dwarf_elf_access.h
+usr/src/lib/libdwarf/common/dwarf_error.c
+usr/src/lib/libdwarf/common/dwarf_error.h
+usr/src/lib/libdwarf/common/dwarf_form.c
+usr/src/lib/libdwarf/common/dwarf_frame.c
+usr/src/lib/libdwarf/common/dwarf_frame.h
+usr/src/lib/libdwarf/common/dwarf_frame2.c
+usr/src/lib/libdwarf/common/dwarf_frame3.c
+usr/src/lib/libdwarf/common/dwarf_funcs.c
+usr/src/lib/libdwarf/common/dwarf_funcs.h
+usr/src/lib/libdwarf/common/dwarf_global.c
+usr/src/lib/libdwarf/common/dwarf_global.h
+usr/src/lib/libdwarf/common/dwarf_harmless.c
+usr/src/lib/libdwarf/common/dwarf_harmless.h
+usr/src/lib/libdwarf/common/dwarf_incl.h
+usr/src/lib/libdwarf/common/dwarf_init_finish.c
+usr/src/lib/libdwarf/common/dwarf_leb.c
+usr/src/lib/libdwarf/common/dwarf_line.c
+usr/src/lib/libdwarf/common/dwarf_line.h
+usr/src/lib/libdwarf/common/dwarf_line2.c
+usr/src/lib/libdwarf/common/dwarf_loc.c
+usr/src/lib/libdwarf/common/dwarf_loc.h
+usr/src/lib/libdwarf/common/dwarf_macro.c
+usr/src/lib/libdwarf/common/dwarf_macro.h
+usr/src/lib/libdwarf/common/dwarf_names.c
+usr/src/lib/libdwarf/common/dwarf_names.h
+usr/src/lib/libdwarf/common/dwarf_opaque.h
+usr/src/lib/libdwarf/common/dwarf_original_elf_init.c
+usr/src/lib/libdwarf/common/dwarf_print_lines.c
+usr/src/lib/libdwarf/common/dwarf_pubtypes.c
+usr/src/lib/libdwarf/common/dwarf_query.c
+usr/src/lib/libdwarf/common/dwarf_ranges.c
+usr/src/lib/libdwarf/common/dwarf_sort_line.c
+usr/src/lib/libdwarf/common/dwarf_string.c
+usr/src/lib/libdwarf/common/dwarf_stubs.c
+usr/src/lib/libdwarf/common/dwarf_types.c
+usr/src/lib/libdwarf/common/dwarf_types.h
+usr/src/lib/libdwarf/common/dwarf_util.c
+usr/src/lib/libdwarf/common/dwarf_util.h
+usr/src/lib/libdwarf/common/dwarf_vars.c
+usr/src/lib/libdwarf/common/dwarf_vars.h
+usr/src/lib/libdwarf/common/dwarf_weaks.c
+usr/src/lib/libdwarf/common/dwarf_weaks.h
+usr/src/lib/libdwarf/common/dwarf.h
+usr/src/lib/libdwarf/common/libdwarf.h
+usr/src/lib/libdwarf/common/libdwarfdefs.h
+usr/src/lib/libdwarf/common/malloc_check.c
+usr/src/lib/libdwarf/common/malloc_check.h
+usr/src/lib/libdwarf/common/mapfile-vers
+usr/src/lib/libdwarf/common/pro_alloc.c
+usr/src/lib/libdwarf/common/pro_alloc.h
+usr/src/lib/libdwarf/common/pro_arange.c
+usr/src/lib/libdwarf/common/pro_arange.h
+usr/src/lib/libdwarf/common/pro_die.c
+usr/src/lib/libdwarf/common/pro_die.h
+usr/src/lib/libdwarf/common/pro_encode_nm.c
+usr/src/lib/libdwarf/common/pro_encode_nm.h
+usr/src/lib/libdwarf/common/pro_error.c
+usr/src/lib/libdwarf/common/pro_error.h
+usr/src/lib/libdwarf/common/pro_expr.c
+usr/src/lib/libdwarf/common/pro_expr.h
+usr/src/lib/libdwarf/common/pro_finish.c
+usr/src/lib/libdwarf/common/pro_forms.c
+usr/src/lib/libdwarf/common/pro_frame.c
+usr/src/lib/libdwarf/common/pro_frame.h
+usr/src/lib/libdwarf/common/pro_funcs.c
+usr/src/lib/libdwarf/common/pro_incl.h
+usr/src/lib/libdwarf/common/pro_init.c
+usr/src/lib/libdwarf/common/pro_line.c
+usr/src/lib/libdwarf/common/pro_line.h
+usr/src/lib/libdwarf/common/pro_macinfo.c
+usr/src/lib/libdwarf/common/pro_macinfo.h
+usr/src/lib/libdwarf/common/pro_opaque.h
+usr/src/lib/libdwarf/common/pro_pubnames.c
+usr/src/lib/libdwarf/common/pro_reloc_stream.c
+usr/src/lib/libdwarf/common/pro_reloc_stream.h
+usr/src/lib/libdwarf/common/pro_reloc_symbolic.c
+usr/src/lib/libdwarf/common/pro_reloc_symbolic.h
+usr/src/lib/libdwarf/common/pro_reloc.c
+usr/src/lib/libdwarf/common/pro_reloc.h
+usr/src/lib/libdwarf/common/pro_section.c
+usr/src/lib/libdwarf/common/pro_section.h
+usr/src/lib/libdwarf/common/pro_types.c
+usr/src/lib/libdwarf/common/pro_types.h
+usr/src/lib/libdwarf/common/pro_util.h
+usr/src/lib/libdwarf/common/pro_vars.c
+usr/src/lib/libdwarf/common/pro_weaks.c
usr/src/lib/gss_mechs/mech_krb5/crypto/aes/aes_s2k.c
usr/src/lib/gss_mechs/mech_krb5/crypto/cksumtype_to_string.c
usr/src/lib/gss_mechs/mech_krb5/crypto/coll_proof_cksum.c
diff --git a/exception_lists/hdrchk b/exception_lists/hdrchk
index 60c60a527e..42b91ffea6 100644
--- a/exception_lists/hdrchk
+++ b/exception_lists/hdrchk
@@ -31,6 +31,50 @@ usr/src/cmd/mandoc/out.h
usr/src/cmd/mandoc/term.h
usr/src/common/openssl/crypto/krb5/krb5_asn.h
usr/src/grub/grub-0.97/stage2/shared.h
+usr/src/lib/libdwarf/common/cmplrs/dwarf_addr_finder.h
+usr/src/lib/libdwarf/common/config.h
+usr/src/lib/libdwarf/common/dwarf_abbrev.h
+usr/src/lib/libdwarf/common/dwarf_alloc.h
+usr/src/lib/libdwarf/common/dwarf_arange.h
+usr/src/lib/libdwarf/common/dwarf_base_types.h
+usr/src/lib/libdwarf/common/dwarf_die_deliv.h
+usr/src/lib/libdwarf/common/dwarf_elf_access.h
+usr/src/lib/libdwarf/common/dwarf_error.h
+usr/src/lib/libdwarf/common/dwarf_frame.h
+usr/src/lib/libdwarf/common/dwarf_funcs.h
+usr/src/lib/libdwarf/common/dwarf_global.h
+usr/src/lib/libdwarf/common/dwarf_harmless.h
+usr/src/lib/libdwarf/common/dwarf_incl.h
+usr/src/lib/libdwarf/common/dwarf_line.h
+usr/src/lib/libdwarf/common/dwarf_loc.h
+usr/src/lib/libdwarf/common/dwarf_macro.h
+usr/src/lib/libdwarf/common/dwarf_names.h
+usr/src/lib/libdwarf/common/dwarf_opaque.h
+usr/src/lib/libdwarf/common/dwarf_types.h
+usr/src/lib/libdwarf/common/dwarf_util.h
+usr/src/lib/libdwarf/common/dwarf_vars.h
+usr/src/lib/libdwarf/common/dwarf_weaks.h
+usr/src/lib/libdwarf/common/dwarf.h
+usr/src/lib/libdwarf/common/libdwarf.h
+usr/src/lib/libdwarf/common/libdwarfdefs.h
+usr/src/lib/libdwarf/common/malloc_check.h
+usr/src/lib/libdwarf/common/pro_alloc.h
+usr/src/lib/libdwarf/common/pro_arange.h
+usr/src/lib/libdwarf/common/pro_die.h
+usr/src/lib/libdwarf/common/pro_encode_nm.h
+usr/src/lib/libdwarf/common/pro_error.h
+usr/src/lib/libdwarf/common/pro_expr.h
+usr/src/lib/libdwarf/common/pro_frame.h
+usr/src/lib/libdwarf/common/pro_incl.h
+usr/src/lib/libdwarf/common/pro_line.h
+usr/src/lib/libdwarf/common/pro_macinfo.h
+usr/src/lib/libdwarf/common/pro_opaque.h
+usr/src/lib/libdwarf/common/pro_reloc_stream.h
+usr/src/lib/libdwarf/common/pro_reloc_symbolic.h
+usr/src/lib/libdwarf/common/pro_reloc.h
+usr/src/lib/libdwarf/common/pro_section.h
+usr/src/lib/libdwarf/common/pro_types.h
+usr/src/lib/libdwarf/common/pro_util.h
usr/src/lib/gss_mechs/mech_krb5/et/error_table.h
usr/src/lib/gss_mechs/mech_krb5/et/internal.h
usr/src/lib/gss_mechs/mech_krb5/et/mit-sipb-copyright.h
diff --git a/manifest b/manifest
index 68f3c742e0..1a3eb315ad 100644
--- a/manifest
+++ b/manifest
@@ -1155,6 +1155,7 @@ f lib/amd64/libdlpi.so.1 0755 root bin
s lib/amd64/libdlpi.so=libdlpi.so.1
f lib/amd64/libdoor.so.1 0755 root bin
s lib/amd64/libdoor.so=libdoor.so.1
+f lib/amd64/libdwarf.so.1 0755 root bin
f lib/amd64/libefi.so.1 0755 root bin
s lib/amd64/libefi.so=libefi.so.1
f lib/amd64/libelf.so.1 0755 root bin
@@ -1338,6 +1339,7 @@ f lib/libdlpi.so.1 0755 root bin
s lib/libdlpi.so=libdlpi.so.1
f lib/libdoor.so.1 0755 root bin
s lib/libdoor.so=libdoor.so.1
+f lib/libdwarf.so.1 0755 root bin
f lib/libefi.so.1 0755 root bin
s lib/libefi.so=libefi.so.1
f lib/libelf.so.1 0755 root bin
@@ -4751,6 +4753,7 @@ f usr/lib/amd64/libdns_sd.so.1 0755 root bin
s usr/lib/amd64/libdns_sd.so=libdns_sd.so.1
s usr/lib/amd64/libdoor.so.1=../../../lib/amd64/libdoor.so.1
s usr/lib/amd64/libdoor.so=../../../lib/amd64/libdoor.so.1
+s usr/lib/amd64/libdwarf.so.1=../../../lib/amd64/libdwarf.so.1
f usr/lib/amd64/libdtrace.so.1 0755 root bin
s usr/lib/amd64/libdtrace.so=libdtrace.so.1
f usr/lib/amd64/libdtrace_jni.so.1 0755 root bin
@@ -6149,6 +6152,7 @@ f usr/lib/libdns_sd.so.1 0755 root bin
s usr/lib/libdns_sd.so=libdns_sd.so.1
s usr/lib/libdoor.so.1=../../lib/libdoor.so.1
s usr/lib/libdoor.so=../../lib/libdoor.so.1
+s usr/lib/libdwarf.so.1=../../lib/libdwarf.so.1
f usr/lib/libdscfg.so.1 0755 root bin
s usr/lib/libdscfg.so=libdscfg.so.1
f usr/lib/libdtrace.so.1 0755 root bin
diff --git a/usr/src/Makefile.master b/usr/src/Makefile.master
index ad515114f7..b664edd967 100644
--- a/usr/src/Makefile.master
+++ b/usr/src/Makefile.master
@@ -917,6 +917,15 @@ CTFCVTFLAGS= -i -L VERSION
#
CTFMRGFLAGS=
+#
+# Make the transition between old and new CTF Tools. The new ctf tools
+# do not support stabs (eg. Sun Studio), so if GNUC is not set, we don't
+# honor them.
+#
+ENABLE_NEW_CTF=$(POUND_SIGN)
+$(ENABLE_NEW_CTF)$(_GNUC)CTFCONVERT= $(ONBLD_TOOLS)/bin/$(MACH)/ctfconvert-altexec
+$(ENABLE_NEW_CTF)$(_GNUC)CTFMERGE= $(ONBLD_TOOLS)/bin/$(MACH)/ctfmerge-altexec
+
CTFCONVERT_O = $(CTFCONVERT) $(CTFCVTFLAGS) $@
ELFSIGN_O= $(TRUE)
diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile
index 4c984d1458..8bb04e9a29 100644
--- a/usr/src/cmd/Makefile
+++ b/usr/src/cmd/Makefile
@@ -103,6 +103,7 @@ COMMON_SUBDIRS= \
crypt \
csh \
csplit \
+ ctfconvert \
ctfdiff \
ctfdump \
ctfmerge \
diff --git a/usr/src/cmd/ctfconvert/Makefile b/usr/src/cmd/ctfconvert/Makefile
new file mode 100644
index 0000000000..688addd9d1
--- /dev/null
+++ b/usr/src/cmd/ctfconvert/Makefile
@@ -0,0 +1,33 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2015, Joyent, Inc.
+#
+
+PROG= ctfconvert
+
+include ../Makefile.cmd
+
+CFLAGS += $(CCVERBOSE)
+LDLIBS += -lctf -lelf
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all $(ROOTPROG)
+
+clean:
+
+lint: lint_PROG
+
+include ../Makefile.targ
diff --git a/usr/src/cmd/ctfconvert/ctfconvert.c b/usr/src/cmd/ctfconvert/ctfconvert.c
new file mode 100644
index 0000000000..dd3d40c6cc
--- /dev/null
+++ b/usr/src/cmd/ctfconvert/ctfconvert.c
@@ -0,0 +1,389 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright (c) 2015, Joyent, Inc.
+ */
+
+/*
+ * Create CTF from extant debugging information
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <libelf.h>
+#include <libctf.h>
+#include <string.h>
+#include <libgen.h>
+#include <limits.h>
+#include <strings.h>
+#include <sys/debug.h>
+
+#define CTFCONVERT_OK 0
+#define CTFCONVERT_FATAL 1
+#define CTFCONVERT_USAGE 2
+
+#define CTFCONVERT_DEFAULT_NTHREADS 4
+
+#define CTFCONVERT_ALTEXEC "CTFCONVERT_ALTEXEC"
+
+static char *ctfconvert_progname;
+
+static void
+ctfconvert_fatal(const char *fmt, ...)
+{
+ va_list ap;
+
+ (void) fprintf(stderr, "%s: ", ctfconvert_progname);
+ va_start(ap, fmt);
+ (void) vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ exit(CTFCONVERT_FATAL);
+}
+
+
+static void
+ctfconvert_usage(const char *fmt, ...)
+{
+ if (fmt != NULL) {
+ va_list ap;
+
+ (void) fprintf(stderr, "%s: ", ctfconvert_progname);
+ va_start(ap, fmt);
+ (void) vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ }
+
+ (void) fprintf(stderr, "Usage: %s [-is] [-j nthrs] [-l label | "
+ "-L labelenv] [-o outfile] input\n"
+ "\n"
+ "\t-i ignore files not built partially from C sources\n"
+ "\t-j use nthrs threads to perform the merge\n"
+ "\t-k keep around original input file on failure\n"
+ "\t-o copy input to outfile and add CTF\n"
+ "\t-l set output container's label to specified value\n"
+ "\t-L set output container's label to value from environment\n",
+ ctfconvert_progname);
+}
+
+/*
+ * This is a bit unfortunate. Traditionally we do type uniquification across all
+ * modules in the kernel, including ip and unix against genunix. However, when
+ * _MACHDEP is defined, then the cpu_t ends up having an additional member
+ * (cpu_m), thus changing the ability for us to uniquify against it. This in
+ * turn causes a lot of type sprawl, as there's a lot of things that end up
+ * referring to the cpu_t and it chains out from there.
+ *
+ * So, if we find that a cpu_t has been defined and it has a couple of useful
+ * sentinel members and it does *not* have the cpu_m member, then we will try
+ * and lookup or create a forward declaration to the machcpu, append it to the
+ * end, and update the file.
+ *
+ * This currently is only invoked if an undocumented option -X is passed. This
+ * value is private to illumos and it can be changed at any time inside of it,
+ * so if -X wants to be used for something, it should be. The ability to rely on
+ * -X for others is strictly not an interface in any way, shape, or form.
+ *
+ * The following struct contains most of the information that we care about and
+ * that we want to validate exists before we decide what to do.
+ */
+
+typedef struct ctfconvert_fixup {
+ boolean_t cf_cyclic; /* Do we have a cpu_cyclic member */
+ boolean_t cf_mcpu; /* We have a cpu_m member */
+ boolean_t cf_lastpad; /* Is the pad member the last entry */
+ ulong_t cf_padoff; /* offset of the pad */
+} ctfconvert_fixup_t;
+
+/* ARGSUSED */
+static int
+ctfconvert_fixup_genunix_cb(const char *name, ctf_id_t tid, ulong_t off,
+ void *arg)
+{
+ ctfconvert_fixup_t *cfp = arg;
+
+ cfp->cf_lastpad = B_FALSE;
+ if (strcmp(name, "cpu_cyclic") == 0) {
+ cfp->cf_cyclic = B_TRUE;
+ return (0);
+ }
+
+ if (strcmp(name, "cpu_m") == 0) {
+ cfp->cf_mcpu = B_TRUE;
+ return (0);
+ }
+
+ if (strcmp(name, "cpu_m_pad") == 0) {
+ cfp->cf_lastpad = B_TRUE;
+ cfp->cf_padoff = off;
+ return (0);
+ }
+
+ return (0);
+}
+
+static void
+ctfconvert_fixup_genunix(ctf_file_t *fp)
+{
+ ctf_id_t cpuid, mcpu;
+ ssize_t sz;
+ ctfconvert_fixup_t cf;
+ int model, ptrsz;
+
+ cpuid = ctf_lookup_by_name(fp, "struct cpu");
+ if (cpuid == CTF_ERR)
+ return;
+
+ if (ctf_type_kind(fp, cpuid) != CTF_K_STRUCT)
+ return;
+
+ if ((sz = ctf_type_size(fp, cpuid)) == CTF_ERR)
+ return;
+
+ model = ctf_getmodel(fp);
+ VERIFY(model == CTF_MODEL_ILP32 || model == CTF_MODEL_LP64);
+ ptrsz = model == CTF_MODEL_ILP32 ? 4 : 8;
+
+ bzero(&cf, sizeof (ctfconvert_fixup_t));
+ if (ctf_member_iter(fp, cpuid, ctfconvert_fixup_genunix_cb, &cf) ==
+ CTF_ERR)
+ return;
+
+ /*
+ * Finally, we want to verify that the cpu_m is actually the last member
+ * that we have here.
+ */
+ if (cf.cf_cyclic == B_FALSE || cf.cf_mcpu == B_TRUE ||
+ cf.cf_lastpad == B_FALSE) {
+ return;
+ }
+
+ if (cf.cf_padoff + ptrsz * NBBY != sz * NBBY) {
+ return;
+ }
+
+ /*
+ * Okay, we're going to do this, try to find a struct machcpu. We either
+ * want a forward or a struct. If we find something else, error. If we
+ * find nothing, add a forward and then add the member.
+ */
+ mcpu = ctf_lookup_by_name(fp, "struct machcpu");
+ if (mcpu == CTF_ERR) {
+ mcpu = ctf_add_forward(fp, CTF_ADD_NONROOT, "struct machcpu",
+ CTF_K_STRUCT);
+ if (mcpu == CTF_ERR) {
+ ctfconvert_fatal("failed to add 'struct machcpu' "
+ "forward: %s", ctf_errmsg(ctf_errno(fp)));
+ }
+ } else {
+ int kind;
+ if ((kind = ctf_type_kind(fp, mcpu)) == CTF_ERR) {
+ ctfconvert_fatal("failed to get the type kind for "
+ "the struct machcpu: %s",
+ ctf_errmsg(ctf_errno(fp)));
+ }
+
+ if (kind != CTF_K_STRUCT && kind != CTF_K_FORWARD)
+ ctfconvert_fatal("encountered a struct machcpu of the "
+ "wrong type, found type kind %d\n", kind);
+ }
+
+ if (ctf_update(fp) == CTF_ERR) {
+ ctfconvert_fatal("failed to update output file: %s\n",
+ ctf_errmsg(ctf_errno(fp)));
+ }
+
+ if (ctf_add_member(fp, cpuid, "cpu_m", mcpu, sz * NBBY) == CTF_ERR) {
+ ctfconvert_fatal("failed to add the m_cpu member: %s\n",
+ ctf_errmsg(ctf_errno(fp)));
+ }
+
+ if (ctf_update(fp) == CTF_ERR) {
+ ctfconvert_fatal("failed to update output file: %s\n",
+ ctf_errmsg(ctf_errno(fp)));
+ }
+
+ VERIFY(ctf_type_size(fp, cpuid) == sz);
+}
+
+static void
+ctfconvert_altexec(char **argv)
+{
+ const char *alt;
+ char *altexec;
+
+ alt = getenv(CTFCONVERT_ALTEXEC);
+ if (alt == NULL || *alt == '\0')
+ return;
+
+ altexec = strdup(alt);
+ if (altexec == NULL)
+ ctfconvert_fatal("failed to allocate memory for altexec\n");
+ if (unsetenv(CTFCONVERT_ALTEXEC) != 0)
+ ctfconvert_fatal("failed to unset %s from environment: %s\n",
+ CTFCONVERT_ALTEXEC, strerror(errno));
+
+ (void) execv(altexec, argv);
+ ctfconvert_fatal("failed to execute alternate program %s: %s",
+ altexec, strerror(errno));
+}
+
+int
+main(int argc, char *argv[])
+{
+ int c, ifd, err;
+ boolean_t keep = B_FALSE;
+ uint_t flags = 0;
+ uint_t nthreads = CTFCONVERT_DEFAULT_NTHREADS;
+ const char *outfile = NULL;
+ const char *label = NULL;
+ const char *infile = NULL;
+ char *tmpfile;
+ ctf_file_t *ofp;
+ long argj;
+ char *eptr;
+ char buf[4096];
+ boolean_t optx = B_FALSE;
+
+ ctfconvert_progname = basename(argv[0]);
+
+ ctfconvert_altexec(argv);
+
+ while ((c = getopt(argc, argv, ":j:kl:L:o:iX")) != -1) {
+ switch (c) {
+ case 'k':
+ keep = B_TRUE;
+ break;
+ case 'l':
+ label = optarg;
+ break;
+ case 'L':
+ label = getenv(optarg);
+ break;
+ case 'j':
+ errno = 0;
+ argj = strtol(optarg, &eptr, 10);
+ if (errno != 0 || argj == LONG_MAX ||
+ argj == LONG_MIN || argj <= 0 ||
+ argj > UINT_MAX || *eptr != '\0') {
+ ctfconvert_fatal("invalid argument for -j: "
+ "%s\n", optarg);
+ }
+ nthreads = (uint_t)argj;
+ break;
+ case 'o':
+ outfile = optarg;
+ break;
+ case 'i':
+ flags |= CTF_CONVERT_F_IGNNONC;
+ break;
+ case 'X':
+ optx = B_TRUE;
+ break;
+ case ':':
+ ctfconvert_usage("Option -%c requires an operand\n",
+ optopt);
+ return (CTFCONVERT_USAGE);
+ case '?':
+ ctfconvert_usage("Unknown option: -%c\n", optopt);
+ return (CTFCONVERT_USAGE);
+ }
+ }
+
+ argv += optind;
+ argc -= optind;
+
+ if (argc < 1) {
+ ctfconvert_usage("Missing required input file\n");
+ return (CTFCONVERT_USAGE);
+ }
+ infile = argv[0];
+
+ if (elf_version(EV_CURRENT) == EV_NONE)
+ ctfconvert_fatal("failed to initialize libelf: library is "
+ "out of date\n");
+
+ ifd = open(infile, O_RDONLY);
+ if (ifd < 0) {
+ ctfconvert_fatal("failed to open input file %s: %s\n", infile,
+ strerror(errno));
+ }
+
+ /*
+ * By default we remove the input file on failure unless we've been
+ * given an output file or -k has been specified.
+ */
+ if (outfile != NULL && strcmp(infile, outfile) != 0)
+ keep = B_TRUE;
+
+ ofp = ctf_fdconvert(ifd, label, nthreads, flags, &err, buf,
+ sizeof (buf));
+ if (ofp == NULL) {
+ /*
+ * -i says that we shouldn't concern ourselves with source files
+ * that weren't built from C source code in part. Because this
+ * has been traditionally used across all of illumos, we still
+ * honor it.
+ */
+ if ((flags & CTF_CONVERT_F_IGNNONC) != 0 &&
+ err == ECTF_CONVNOCSRC) {
+ exit(CTFCONVERT_OK);
+ }
+ if (keep == B_FALSE)
+ (void) unlink(infile);
+ ctfconvert_fatal("CTF conversion failed: %s\n",
+ err == ECTF_CONVBKERR ? buf : ctf_errmsg(err));
+ }
+
+ if (optx == B_TRUE)
+ ctfconvert_fixup_genunix(ofp);
+
+ tmpfile = NULL;
+ if (outfile == NULL || strcmp(infile, outfile) == 0) {
+ if (asprintf(&tmpfile, "%s.ctf", infile) == -1) {
+ if (keep == B_FALSE)
+ (void) unlink(infile);
+ ctfconvert_fatal("failed to allocate memory for "
+ "temporary file: %s\n", strerror(errno));
+ }
+ outfile = tmpfile;
+ }
+ err = ctf_elfwrite(ofp, infile, outfile, CTF_ELFWRITE_F_COMPRESS);
+ if (err == CTF_ERR) {
+ (void) unlink(outfile);
+ if (keep == B_FALSE)
+ (void) unlink(infile);
+ ctfconvert_fatal("failed to write CTF section to output file: "
+ "%s", ctf_errmsg(ctf_errno(ofp)));
+ }
+ ctf_close(ofp);
+
+ if (tmpfile != NULL) {
+ if (rename(tmpfile, infile) != 0) {
+ int e = errno;
+ (void) unlink(outfile);
+ if (keep == B_FALSE)
+ (void) unlink(infile);
+ ctfconvert_fatal("failed to rename temporary file: "
+ "%s\n", strerror(e));
+ }
+ }
+ free(tmpfile);
+
+ return (CTFCONVERT_OK);
+}
diff --git a/usr/src/cmd/ctfdiff/ctfdiff.c b/usr/src/cmd/ctfdiff/ctfdiff.c
index 2537c15bcb..8018761257 100644
--- a/usr/src/cmd/ctfdiff/ctfdiff.c
+++ b/usr/src/cmd/ctfdiff/ctfdiff.c
@@ -44,7 +44,7 @@ typedef enum ctf_diff_cmd {
typedef struct {
int dil_next;
const char **dil_labels;
-} diff_label_t;
+} ctfdiff_label_t;
static char *g_progname;
static const char *g_iname;
@@ -78,7 +78,7 @@ ctfdiff_fatal(const char *fmt, ...)
}
static const char *
-fp_to_name(ctf_file_t *fp)
+ctfdiff_fp_to_name(ctf_file_t *fp)
{
if (fp == g_ifp)
return (g_iname);
@@ -89,8 +89,8 @@ fp_to_name(ctf_file_t *fp)
/* ARGSUSED */
static void
-diff_func_cb(ctf_file_t *ifp, ulong_t iidx, boolean_t similar, ctf_file_t *ofp,
- ulong_t oidx, void *arg)
+ctfdiff_func_cb(ctf_file_t *ifp, ulong_t iidx, boolean_t similar,
+ ctf_file_t *ofp, ulong_t oidx, void *arg)
{
char namebuf[CTFDIFF_NAMELEN];
@@ -101,7 +101,7 @@ diff_func_cb(ctf_file_t *ifp, ulong_t iidx, boolean_t similar, ctf_file_t *ofp,
if (g_nextfunc != 0)
return;
(void) printf("ctf container %s function %ld is different\n",
- fp_to_name(ifp), iidx);
+ ctfdiff_fp_to_name(ifp), iidx);
} else {
if (g_nextfunc != 0) {
int i;
@@ -113,7 +113,7 @@ diff_func_cb(ctf_file_t *ifp, ulong_t iidx, boolean_t similar, ctf_file_t *ofp,
return;
}
(void) printf("ctf container %s function %s (%ld) is "
- "different\n", fp_to_name(ifp), namebuf, iidx);
+ "different\n", ctfdiff_fp_to_name(ifp), namebuf, iidx);
}
g_different = B_TRUE;
@@ -121,7 +121,7 @@ diff_func_cb(ctf_file_t *ifp, ulong_t iidx, boolean_t similar, ctf_file_t *ofp,
/* ARGSUSED */
static void
-diff_obj_cb(ctf_file_t *ifp, ulong_t iidx, ctf_id_t iid, boolean_t similar,
+ctfdiff_obj_cb(ctf_file_t *ifp, ulong_t iidx, ctf_id_t iid, boolean_t similar,
ctf_file_t *ofp, ulong_t oidx, ctf_id_t oid, void *arg)
{
char namebuf[CTFDIFF_NAMELEN];
@@ -133,7 +133,7 @@ diff_obj_cb(ctf_file_t *ifp, ulong_t iidx, ctf_id_t iid, boolean_t similar,
if (g_nextobj != 0)
return;
(void) printf("ctf container %s object %ld is different\n",
- fp_to_name(ifp), iidx);
+ ctfdiff_fp_to_name(ifp), iidx);
} else {
if (g_nextobj != 0) {
int i;
@@ -145,7 +145,7 @@ diff_obj_cb(ctf_file_t *ifp, ulong_t iidx, ctf_id_t iid, boolean_t similar,
return;
}
(void) printf("ctf container %s object %s (%ld) is different\n",
- fp_to_name(ifp), namebuf, iidx);
+ ctfdiff_fp_to_name(ifp), namebuf, iidx);
}
g_different = B_TRUE;
@@ -153,7 +153,7 @@ diff_obj_cb(ctf_file_t *ifp, ulong_t iidx, ctf_id_t iid, boolean_t similar,
/* ARGSUSED */
static void
-diff_cb(ctf_file_t *ifp, ctf_id_t iid, boolean_t similar, ctf_file_t *ofp,
+ctfdiff_cb(ctf_file_t *ifp, ctf_id_t iid, boolean_t similar, ctf_file_t *ofp,
ctf_id_t oid, void *arg)
{
if (similar == B_TRUE)
@@ -173,7 +173,7 @@ diff_cb(ctf_file_t *ifp, ctf_id_t iid, boolean_t similar, ctf_file_t *ofp,
NULL) {
ctfdiff_fatal("failed to obtain the name "
"of type %ld from %s: %s\n",
- iid, fp_to_name(ifp),
+ iid, ctfdiff_fp_to_name(ifp),
ctf_errmsg(ctf_errno(ifp)));
}
@@ -192,12 +192,12 @@ diff_cb(ctf_file_t *ifp, ctf_id_t iid, boolean_t similar, ctf_file_t *ofp,
return;
(void) printf("ctf container %s type %ld is different\n",
- fp_to_name(ifp), iid);
+ ctfdiff_fp_to_name(ifp), iid);
}
/* ARGSUSED */
static int
-diff_labels_count(const char *name, const ctf_lblinfo_t *li, void *arg)
+ctfdiff_labels_count(const char *name, const ctf_lblinfo_t *li, void *arg)
{
uint32_t *count = arg;
*count = *count + 1;
@@ -207,9 +207,9 @@ diff_labels_count(const char *name, const ctf_lblinfo_t *li, void *arg)
/* ARGSUSED */
static int
-diff_labels_fill(const char *name, const ctf_lblinfo_t *li, void *arg)
+ctfdiff_labels_fill(const char *name, const ctf_lblinfo_t *li, void *arg)
{
- diff_label_t *dil = arg;
+ ctfdiff_label_t *dil = arg;
dil->dil_labels[dil->dil_next] = name;
dil->dil_next++;
@@ -218,24 +218,25 @@ diff_labels_fill(const char *name, const ctf_lblinfo_t *li, void *arg)
}
static int
-diff_labels(ctf_file_t *ifp, ctf_file_t *ofp)
+ctfdiff_labels(ctf_file_t *ifp, ctf_file_t *ofp)
{
int ret;
uint32_t nilabel, nolabel, i, j;
- diff_label_t idl, odl;
+ ctfdiff_label_t idl, odl;
const char **ilptr, **olptr;
nilabel = nolabel = 0;
- ret = ctf_label_iter(ifp, diff_labels_count, &nilabel);
+ ret = ctf_label_iter(ifp, ctfdiff_labels_count, &nilabel);
if (ret == CTF_ERR)
return (ret);
- ret = ctf_label_iter(ofp, diff_labels_count, &nolabel);
+ ret = ctf_label_iter(ofp, ctfdiff_labels_count, &nolabel);
if (ret == CTF_ERR)
return (ret);
if (nilabel != nolabel) {
(void) printf("ctf container %s labels differ from ctf "
- "container %s\n", fp_to_name(ifp), fp_to_name(ofp));
+ "container %s\n", ctfdiff_fp_to_name(ifp),
+ ctfdiff_fp_to_name(ofp));
g_different = B_TRUE;
return (0);
}
@@ -255,9 +256,9 @@ diff_labels(ctf_file_t *ifp, ctf_file_t *ofp)
odl.dil_next = 0;
odl.dil_labels = olptr;
- if ((ret = ctf_label_iter(ifp, diff_labels_fill, &idl)) != 0)
+ if ((ret = ctf_label_iter(ifp, ctfdiff_labels_fill, &idl)) != 0)
goto out;
- if ((ret = ctf_label_iter(ofp, diff_labels_fill, &odl)) != 0)
+ if ((ret = ctf_label_iter(ofp, ctfdiff_labels_fill, &odl)) != 0)
goto out;
for (i = 0; i < nilabel; i++) {
@@ -268,7 +269,8 @@ diff_labels(ctf_file_t *ifp, ctf_file_t *ofp)
if (j == nolabel) {
(void) printf("ctf container %s labels differ from ctf "
- "container %s\n", fp_to_name(ifp), fp_to_name(ofp));
+ "container %s\n", ctfdiff_fp_to_name(ifp),
+ ctfdiff_fp_to_name(ofp));
g_different = B_TRUE;
break;
}
@@ -282,7 +284,7 @@ out:
}
static void
-diff_usage(const char *fmt, ...)
+ctfdiff_usage(const char *fmt, ...)
{
if (fmt != NULL) {
va_list ap;
@@ -324,7 +326,7 @@ main(int argc, char *argv[])
g_progname = basename(argv[0]);
- while ((c = getopt(argc, argv, "aqtfolIp:F:O:P:T:")) != -1) {
+ while ((c = getopt(argc, argv, ":aqtfolIp:F:O:P:T:")) != -1) {
switch (c) {
case 'a':
g_flag |= CTF_DIFF_ALL;
@@ -417,6 +419,14 @@ main(int argc, char *argv[])
}
g_typelist[g_nexttype] = optarg;
g_nexttype++;
+ break;
+ case ':':
+ ctfdiff_usage("Option -%c requires an operand\n",
+ optopt);
+ return (CTFDIFF_EXIT_USAGE);
+ case '?':
+ ctfdiff_usage("Unknown option: -%c\n", optopt);
+ return (CTFDIFF_EXIT_USAGE);
}
}
@@ -427,22 +437,22 @@ main(int argc, char *argv[])
g_flag = CTF_DIFF_DEFAULT;
if (argc != 3) {
- diff_usage(NULL);
+ ctfdiff_usage(NULL);
return (CTFDIFF_EXIT_USAGE);
}
if (g_nexttype != 0 && !(g_flag & CTF_DIFF_TYPES)) {
- diff_usage("-T cannot be used if not diffing types\n");
+ ctfdiff_usage("-T cannot be used if not diffing types\n");
return (CTFDIFF_EXIT_USAGE);
}
if (g_nextfunc != 0 && !(g_flag & CTF_DIFF_FUNCS)) {
- diff_usage("-F cannot be used if not diffing functions\n");
+ ctfdiff_usage("-F cannot be used if not diffing functions\n");
return (CTFDIFF_EXIT_USAGE);
}
if (g_nextobj != 0 && !(g_flag & CTF_DIFF_OBJS)) {
- diff_usage("-O cannot be used if not diffing objects\n");
+ ctfdiff_usage("-O cannot be used if not diffing objects\n");
return (CTFDIFF_EXIT_USAGE);
}
@@ -489,13 +499,13 @@ main(int argc, char *argv[])
err = 0;
if ((g_flag & CTF_DIFF_TYPES) && err != CTF_ERR)
- err = ctf_diff_types(cdp, diff_cb, NULL);
+ err = ctf_diff_types(cdp, ctfdiff_cb, NULL);
if ((g_flag & CTF_DIFF_FUNCS) && err != CTF_ERR)
- err = ctf_diff_functions(cdp, diff_func_cb, NULL);
+ err = ctf_diff_functions(cdp, ctfdiff_func_cb, NULL);
if ((g_flag & CTF_DIFF_OBJS) && err != CTF_ERR)
- err = ctf_diff_objects(cdp, diff_obj_cb, NULL);
+ err = ctf_diff_objects(cdp, ctfdiff_obj_cb, NULL);
if ((g_flag & CTF_DIFF_LABEL) && err != CTF_ERR)
- err = diff_labels(ifp, ofp);
+ err = ctfdiff_labels(ifp, ofp);
ctf_diff_fini(cdp);
if (err == CTF_ERR) {
diff --git a/usr/src/cmd/ctfdump/ctfdump.c b/usr/src/cmd/ctfdump/ctfdump.c
index 327da82d63..d0ff63a02a 100644
--- a/usr/src/cmd/ctfdump/ctfdump.c
+++ b/usr/src/cmd/ctfdump/ctfdump.c
@@ -74,6 +74,8 @@ static ctfdump_stat_t g_stats;
static ctf_id_t *g_fargc;
static int g_nfargc;
+static int g_exit = 0;
+
static const char *ctfdump_fpenc[] = {
NULL,
"SINGLE",
@@ -90,17 +92,6 @@ static const char *ctfdump_fpenc[] = {
"LDIMAGINARY"
};
-static const char *ctfdump_knames[CTF_K_MAX+1] = {
- "unknown", "integer", "float", "pointer",
- "array", "function", "struct", "union",
- "enum", "forward", "typedef", "volatile",
- "const", "restrict", "CTF_K_14", "CTF_K_15",
- "CTF_K_16", "CTF_K_17", "CTF_K_18", "CTF_K_19",
- "CTF_K_20", "CTF_K_21", "CTF_K_22", "CTF_K_23",
- "CTF_K_24", "CTF_K_25", "CTF_K_26", "CTF_K_27",
- "CTF_K_28", "CTF_K_29", "CTF_K_30", "CTF_K_MAX"
-};
-
/*
* When stats are requested, we have to go through everything. To make our lives
* easier, we'll just always allow the code to print everything out, but only
@@ -120,6 +111,17 @@ ctfdump_printf(ctfdump_arg_t arg, const char *fmt, ...)
}
static void
+ctfdump_warn(const char *fmt, ...)
+{
+ va_list ap;
+
+ (void) fprintf(stderr, "%s: ", g_progname);
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+
+static void
ctfdump_fatal(const char *fmt, ...)
{
va_list ap;
@@ -183,9 +185,11 @@ static void
ctfdump_objects(void)
{
ctfdump_title(CTFDUMP_OBJECTS, "Data Objects");
- if (ctf_object_iter(g_fp, ctfdump_objects_cb, NULL) == CTF_ERR)
- ctfdump_fatal("failed to dump objects: %s\n",
+ if (ctf_object_iter(g_fp, ctfdump_objects_cb, NULL) == CTF_ERR) {
+ ctfdump_warn("failed to dump objects: %s\n",
ctf_errmsg(ctf_errno(g_fp)));
+ g_exit = 1;
+ }
}
static void
@@ -236,9 +240,11 @@ ctfdump_functions(void)
{
ctfdump_title(CTFDUMP_FUNCTIONS, "Functions");
- if (ctf_function_iter(g_fp, ctfdump_functions_cb, NULL) == CTF_ERR)
- ctfdump_fatal("failed to dump functions: %s\n",
+ if (ctf_function_iter(g_fp, ctfdump_functions_cb, NULL) == CTF_ERR) {
+ ctfdump_warn("failed to dump functions: %s\n",
ctf_errmsg(ctf_errno(g_fp)));
+ g_exit = 1;
+ }
}
static void
@@ -286,9 +292,11 @@ static void
ctfdump_labels(void)
{
ctfdump_title(CTFDUMP_LABELS, "Label Table");
- if (ctf_label_iter(g_fp, ctfdump_labels_cb, NULL) == CTF_ERR)
- ctfdump_fatal("failed to dump labels: %s\n",
+ if (ctf_label_iter(g_fp, ctfdump_labels_cb, NULL) == CTF_ERR) {
+ ctfdump_warn("failed to dump labels: %s\n",
ctf_errmsg(ctf_errno(g_fp)));
+ g_exit = 1;
+ }
}
static int
@@ -311,9 +319,11 @@ ctfdump_strings(void)
ulong_t stroff = 0;
ctfdump_title(CTFDUMP_STRINGS, "String Table");
- if (ctf_string_iter(g_fp, ctfdump_strings_cb, &stroff) == CTF_ERR)
- ctfdump_fatal("failed to dump strings: %s\n",
+ if (ctf_string_iter(g_fp, ctfdump_strings_cb, &stroff) == CTF_ERR) {
+ ctfdump_warn("failed to dump strings: %s\n",
ctf_errmsg(ctf_errno(g_fp)));
+ g_exit = 1;
+ }
}
static void
@@ -504,7 +514,7 @@ ctfdump_types_cb(ctf_id_t id, boolean_t root, void *arg)
ctfdump_fatal("type %lu missing name: %s\n", id,
ctf_errmsg(ctf_errno(g_fp)));
(void) snprintf(name, sizeof (name), "(unknown %s)",
- ctfdump_knames[kind]);
+ ctf_kind_name(g_fp, kind));
}
g_stats.cs_ntypes[kind]++;
@@ -651,9 +661,11 @@ ctfdump_types(void)
{
ctfdump_title(CTFDUMP_TYPES, "Types");
- if (ctf_type_iter(g_fp, B_TRUE, ctfdump_types_cb, NULL) == CTF_ERR)
- ctfdump_fatal("failed to dump labels: %s\n",
+ if (ctf_type_iter(g_fp, B_TRUE, ctfdump_types_cb, NULL) == CTF_ERR) {
+ ctfdump_warn("failed to dump labels: %s\n",
ctf_errmsg(ctf_errno(g_fp)));
+ g_exit = 1;
+ }
}
static void
@@ -728,11 +740,11 @@ main(int argc, char *argv[])
ufile = optarg;
break;
case '?':
- ctfdump_usage("Option -%c requires an operand\n",
- optopt);
+ ctfdump_usage("Unknown option: -%c\n", optopt);
return (2);
case ':':
- ctfdump_usage("Unknown option: -%c\n", optopt);
+ ctfdump_usage("Option -%c requires an operand\n",
+ optopt);
return (2);
}
}
@@ -799,5 +811,5 @@ main(int argc, char *argv[])
if (g_dump & CTFDUMP_OUTPUT)
ctfdump_output(ufile);
- return (0);
+ return (g_exit);
}
diff --git a/usr/src/cmd/ctfmerge/ctfmerge.c b/usr/src/cmd/ctfmerge/ctfmerge.c
index a64936ccbe..5dea32b3aa 100644
--- a/usr/src/cmd/ctfmerge/ctfmerge.c
+++ b/usr/src/cmd/ctfmerge/ctfmerge.c
@@ -33,6 +33,7 @@
#include <sys/mman.h>
#include <libgen.h>
#include <stdarg.h>
+#include <limits.h>
static char *g_progname;
static char *g_unique;
@@ -40,6 +41,13 @@ static char *g_outfile;
static boolean_t g_req;
static uint_t g_nctf;
+#define CTFMERGE_OK 0
+#define CTFMERGE_FATAL 1
+#define CTFMERGE_USAGE 2
+
+#define CTFMERGE_DEFAULT_NTHREADS 8
+#define CTFMERGE_ALTEXEC "CTFMERGE_ALTEXEC"
+
static void
ctfmerge_fatal(const char *fmt, ...)
{
@@ -53,7 +61,7 @@ ctfmerge_fatal(const char *fmt, ...)
if (g_outfile != NULL)
(void) unlink(g_outfile);
- exit(1);
+ exit(CTFMERGE_FATAL);
}
static boolean_t
@@ -224,10 +232,9 @@ ctfmerge_elfopen(const char *name, Elf *elf, ctf_merge_t *cmh)
name, ctf_errmsg(err));
}
} else {
- if (ctf_merge_add(cmh, fp) != 0) {
+ if ((err = ctf_merge_add(cmh, fp)) != 0) {
ctfmerge_fatal("failed to add input %s: %s\n",
- name, ctf_errmsg(ctf_errno(fp)));
- exit(1);
+ name, ctf_errmsg(err));
}
g_nctf++;
}
@@ -294,10 +301,11 @@ ctfmerge_usage(const char *fmt, ...)
}
(void) fprintf(stderr, "Usage: %s [-gt] [-d uniqfile] [-l label] "
- "[-L labelenv] -o outfile file ...\n"
+ "[-L labelenv] [-j nthrs] -o outfile file ...\n"
"\n"
"\t-d uniquify merged output against uniqfile\n"
"\t-g do not remove source debug information (STABS, DWARF)\n"
+ "\t-j use nthrs threads to perform the merge\n"
"\t-l set output container's label to specified value\n"
"\t-L set output container's label to value from environment\n"
"\t-o file to add CTF data to\n"
@@ -305,22 +313,49 @@ ctfmerge_usage(const char *fmt, ...)
g_progname);
}
+static void
+ctfmerge_altexec(char **argv)
+{
+ const char *alt;
+ char *altexec;
+
+ alt = getenv(CTFMERGE_ALTEXEC);
+ if (alt == NULL || *alt == '\0')
+ return;
+
+ altexec = strdup(alt);
+ if (altexec == NULL)
+ ctfmerge_fatal("failed to allocate memory for altexec\n");
+ if (unsetenv(CTFMERGE_ALTEXEC) != 0)
+ ctfmerge_fatal("failed to unset %s from environment: %s\n",
+ CTFMERGE_ALTEXEC, strerror(errno));
+
+ (void) execv(altexec, argv);
+ ctfmerge_fatal("failed to execute alternate program %s: %s",
+ altexec, strerror(errno));
+}
+
int
main(int argc, char *argv[])
{
int err, i, c, ofd;
+ uint_t nthreads = CTFMERGE_DEFAULT_NTHREADS;
char *tmpfile = NULL, *label = NULL;
int wflags = CTF_ELFWRITE_F_COMPRESS;
ctf_file_t *ofp;
ctf_merge_t *cmh;
+ long argj;
+ char *eptr;
g_progname = basename(argv[0]);
+ ctfmerge_altexec(argv);
+
/*
* We support a subset of the old CTF merge flags, mostly for
* compatability.
*/
- while ((c = getopt(argc, argv, ":d:fgL:o:t")) != -1) {
+ while ((c = getopt(argc, argv, ":d:fgj:L:o:t")) != -1) {
switch (c) {
case 'd':
g_unique = optarg;
@@ -331,6 +366,17 @@ main(int argc, char *argv[])
case 'g':
/* Silently ignored for compatibility */
break;
+ case 'j':
+ errno = 0;
+ argj = strtol(optarg, &eptr, 10);
+ if (errno != 0 || argj == LONG_MAX ||
+ argj == LONG_MIN || argj <= 0 ||
+ argj > UINT_MAX || *eptr != '\0') {
+ ctfmerge_fatal("invalid argument for -j: %s\n",
+ optarg);
+ }
+ nthreads = (uint_t)argj;
+ break;
case 'l':
label = optarg;
break;
@@ -344,18 +390,18 @@ main(int argc, char *argv[])
g_req = B_TRUE;
break;
case ':':
- ctfmerge_usage("ctfmerge: Option -%c requires an "
- "operand\n", optopt);
- return (2);
+ ctfmerge_usage("Option -%c requires an operand\n",
+ optopt);
+ return (CTFMERGE_USAGE);
case '?':
ctfmerge_usage("Unknown option: -%c\n", optopt);
- return (2);
+ return (CTFMERGE_USAGE);
}
}
if (g_outfile == NULL) {
ctfmerge_usage("missing required -o output file\n");
- return (2);
+ return (CTFMERGE_USAGE);
}
(void) elf_version(EV_CURRENT);
@@ -373,7 +419,7 @@ main(int argc, char *argv[])
if (argc < 1) {
ctfmerge_usage("no input files specified");
- return (2);
+ return (CTFMERGE_USAGE);
}
cmh = ctf_merge_init(ofd, &err);
@@ -381,6 +427,10 @@ main(int argc, char *argv[])
ctfmerge_fatal("failed to create merge handle: %s\n",
ctf_errmsg(err));
+ if ((err = ctf_merge_set_nthreads(cmh, nthreads)) != 0)
+ ctfmerge_fatal("failed to set parallelism to %d: %s\n",
+ nthreads, ctf_errmsg(err));
+
for (i = 0; i < argc; i++) {
ctf_file_t *ifp;
int fd;
@@ -449,7 +499,6 @@ main(int argc, char *argv[])
if (ufp == NULL) {
ctfmerge_fatal("failed to open uniquify file %s: %s\n",
g_unique, ctf_errmsg(err));
- return (1);
}
base = basename(g_unique);
@@ -470,18 +519,20 @@ main(int argc, char *argv[])
if (asprintf(&tmpfile, "%s.ctf", g_outfile) == -1)
ctfmerge_fatal("ran out of memory for temporary file name\n");
err = ctf_elfwrite(ofp, g_outfile, tmpfile, wflags);
- free(tmpfile);
if (err == CTF_ERR) {
(void) unlink(tmpfile);
+ free(tmpfile);
ctfmerge_fatal("encountered a libctf error: %s!\n",
ctf_errmsg(ctf_errno(ofp)));
}
if (rename(tmpfile, g_outfile) != 0) {
(void) unlink(tmpfile);
+ free(tmpfile);
ctfmerge_fatal("failed to rename temporary file: %s\n",
strerror(errno));
}
+ free(tmpfile);
- return (0);
+ return (CTFMERGE_OK);
}
diff --git a/usr/src/common/ctf/ctf_create.c b/usr/src/common/ctf/ctf_create.c
index b51765dd0c..4857070db1 100644
--- a/usr/src/common/ctf/ctf_create.c
+++ b/usr/src/common/ctf/ctf_create.c
@@ -93,9 +93,14 @@ ctf_fdcreate(int fd, int *errp)
static const ctf_header_t hdr = { { CTF_MAGIC, CTF_VERSION, 0 } };
const ulong_t hashlen = 128;
- ctf_dtdef_t **hash = ctf_alloc(hashlen * sizeof (ctf_dtdef_t *));
+ ctf_dtdef_t **hash;
ctf_sect_t cts;
+ if (fd == -1)
+ return (ctf_create(errp));
+
+ hash = ctf_alloc(hashlen * sizeof (ctf_dtdef_t *));
+
if (hash == NULL)
return (ctf_set_open_errno(errp, EAGAIN));
@@ -287,6 +292,7 @@ ctf_update(ctf_file_t *fp)
int err;
ulong_t i;
const char *plabel;
+ const char *sname;
uintptr_t symbase = (uintptr_t)fp->ctf_symtab.cts_data;
uintptr_t strbase = (uintptr_t)fp->ctf_strtab.cts_data;
@@ -673,8 +679,20 @@ ctf_update(ctf_file_t *fp)
bzero(&fp->ctf_dsdefs, sizeof (ctf_list_t));
bzero(&fp->ctf_dldefs, sizeof (ctf_list_t));
+ /*
+ * Because the various containers share the data sections, we don't want
+ * to have ctf_close free it all. However, the name of the section is in
+ * fact unique to the ctf_sect_t. Thus we save the names of the symbol
+ * and string sections around the bzero() and restore them afterwards,
+ * ensuring that we don't result in a memory leak.
+ */
+ sname = fp->ctf_symtab.cts_name;
bzero(&fp->ctf_symtab, sizeof (ctf_sect_t));
+ fp->ctf_symtab.cts_name = sname;
+
+ sname = fp->ctf_strtab.cts_name;
bzero(&fp->ctf_strtab, sizeof (ctf_sect_t));
+ fp->ctf_strtab.cts_name = sname;
bcopy(fp, &ofp, sizeof (ctf_file_t));
bcopy(nfp, fp, sizeof (ctf_file_t));
@@ -833,7 +851,7 @@ ctf_dsd_insert(ctf_file_t *fp, ctf_dsdef_t *dsd)
void
ctf_dsd_delete(ctf_file_t *fp, ctf_dsdef_t *dsd)
{
- if (dsd->dts_argc != NULL)
+ if (dsd->dts_nargs > 0)
ctf_free(dsd->dts_argc,
sizeof (ctf_id_t) * dsd->dts_nargs);
ctf_list_delete(&fp->ctf_dsdefs, dsd);
@@ -962,25 +980,6 @@ ctf_add_generic(ctf_file_t *fp, uint_t flag, const char *name, ctf_dtdef_t **rp)
return (type);
}
-/*
- * When encoding integer sizes, we want to convert a byte count in the range
- * 1-8 to the closest power of 2 (e.g. 3->4, 5->8, etc). The clp2() function
- * is a clever implementation from "Hacker's Delight" by Henry Warren, Jr.
- */
-static size_t
-clp2(size_t x)
-{
- x--;
-
- x |= (x >> 1);
- x |= (x >> 2);
- x |= (x >> 4);
- x |= (x >> 8);
- x |= (x >> 16);
-
- return (x + 1);
-}
-
ctf_id_t
ctf_add_encoded(ctf_file_t *fp, uint_t flag,
const char *name, const ctf_encoding_t *ep, uint_t kind)
@@ -995,7 +994,14 @@ ctf_add_encoded(ctf_file_t *fp, uint_t flag,
return (CTF_ERR); /* errno is set for us */
dtd->dtd_data.ctt_info = CTF_TYPE_INFO(kind, flag, 0);
- dtd->dtd_data.ctt_size = clp2(P2ROUNDUP(ep->cte_bits, NBBY) / NBBY);
+
+ /*
+ * If the type's size is not an even number of bytes, then we should
+ * round up the type size to the nearest byte.
+ */
+ dtd->dtd_data.ctt_size = ep->cte_bits / NBBY;
+ if ((ep->cte_bits % NBBY) != 0)
+ dtd->dtd_data.ctt_size++;
dtd->dtd_u.dtu_enc = *ep;
return (type);
@@ -1054,13 +1060,17 @@ ctf_add_array(ctf_file_t *fp, uint_t flag, const ctf_arinfo_t *arp)
fpd = fp;
if (ctf_lookup_by_id(&fpd, arp->ctr_contents) == NULL &&
- ctf_dtd_lookup(fp, arp->ctr_contents) == NULL)
+ ctf_dtd_lookup(fp, arp->ctr_contents) == NULL) {
+ ctf_dprintf("bad contents for array: %d\n", arp->ctr_contents);
return (ctf_set_errno(fp, ECTF_BADID));
+ }
fpd = fp;
if (ctf_lookup_by_id(&fpd, arp->ctr_index) == NULL &&
- ctf_dtd_lookup(fp, arp->ctr_index) == NULL)
+ ctf_dtd_lookup(fp, arp->ctr_index) == NULL) {
+ ctf_dprintf("bad index for array: %d\n", arp->ctr_index);
return (ctf_set_errno(fp, ECTF_BADID));
+ }
if ((type = ctf_add_generic(fp, flag, NULL, &dtd)) == CTF_ERR)
return (CTF_ERR); /* errno is set for us */
@@ -1168,20 +1178,34 @@ ctf_add_struct(ctf_file_t *fp, uint_t flag, const char *name)
{
ctf_hash_t *hp = &fp->ctf_structs;
ctf_helem_t *hep = NULL;
- ctf_dtdef_t *dtd;
- ctf_id_t type;
+ ctf_dtdef_t *dtd = NULL;
+ ctf_id_t type = CTF_ERR;
if (name != NULL)
hep = ctf_hash_lookup(hp, fp, name, strlen(name));
- if (hep != NULL && ctf_type_kind(fp, hep->h_type) == CTF_K_FORWARD)
- dtd = ctf_dtd_lookup(fp, type = hep->h_type);
- else if ((type = ctf_add_generic(fp, flag, name, &dtd)) == CTF_ERR)
- return (CTF_ERR); /* errno is set for us */
+ if (hep != NULL && ctf_type_kind(fp, hep->h_type) == CTF_K_FORWARD) {
+ type = hep->h_type;
+ dtd = ctf_dtd_lookup(fp, type);
+ if (CTF_INFO_KIND(dtd->dtd_data.ctt_info) != CTF_K_FORWARD)
+ dtd = NULL;
+ }
+
+ if (dtd == NULL) {
+ type = ctf_add_generic(fp, flag, name, &dtd);
+ if (type == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+ }
+ VERIFY(type != CTF_ERR);
dtd->dtd_data.ctt_info = CTF_TYPE_INFO(CTF_K_STRUCT, flag, 0);
dtd->dtd_data.ctt_size = 0;
+ /*
+ * Always dirty in case we modified a forward.
+ */
+ fp->ctf_flags |= LCTF_DIRTY;
+
return (type);
}
@@ -1190,20 +1214,34 @@ ctf_add_union(ctf_file_t *fp, uint_t flag, const char *name)
{
ctf_hash_t *hp = &fp->ctf_unions;
ctf_helem_t *hep = NULL;
- ctf_dtdef_t *dtd;
- ctf_id_t type;
+ ctf_dtdef_t *dtd = NULL;
+ ctf_id_t type = CTF_ERR;
if (name != NULL)
hep = ctf_hash_lookup(hp, fp, name, strlen(name));
- if (hep != NULL && ctf_type_kind(fp, hep->h_type) == CTF_K_FORWARD)
- dtd = ctf_dtd_lookup(fp, type = hep->h_type);
- else if ((type = ctf_add_generic(fp, flag, name, &dtd)) == CTF_ERR)
- return (CTF_ERR); /* errno is set for us */
+ if (hep != NULL && ctf_type_kind(fp, hep->h_type) == CTF_K_FORWARD) {
+ type = hep->h_type;
+ dtd = ctf_dtd_lookup(fp, type);
+ if (CTF_INFO_KIND(dtd->dtd_data.ctt_info) != CTF_K_FORWARD)
+ dtd = NULL;
+ }
+
+ if (dtd == NULL) {
+ type = ctf_add_generic(fp, flag, name, &dtd);
+ if (type == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+ }
+ VERIFY(type != CTF_ERR);
dtd->dtd_data.ctt_info = CTF_TYPE_INFO(CTF_K_UNION, flag, 0);
dtd->dtd_data.ctt_size = 0;
+ /*
+ * Always dirty in case we modified a forward.
+ */
+ fp->ctf_flags |= LCTF_DIRTY;
+
return (type);
}
@@ -1212,20 +1250,34 @@ ctf_add_enum(ctf_file_t *fp, uint_t flag, const char *name)
{
ctf_hash_t *hp = &fp->ctf_enums;
ctf_helem_t *hep = NULL;
- ctf_dtdef_t *dtd;
- ctf_id_t type;
+ ctf_dtdef_t *dtd = NULL;
+ ctf_id_t type = CTF_ERR;
if (name != NULL)
hep = ctf_hash_lookup(hp, fp, name, strlen(name));
- if (hep != NULL && ctf_type_kind(fp, hep->h_type) == CTF_K_FORWARD)
- dtd = ctf_dtd_lookup(fp, type = hep->h_type);
- else if ((type = ctf_add_generic(fp, flag, name, &dtd)) == CTF_ERR)
- return (CTF_ERR); /* errno is set for us */
+ if (hep != NULL && ctf_type_kind(fp, hep->h_type) == CTF_K_FORWARD) {
+ type = hep->h_type;
+ dtd = ctf_dtd_lookup(fp, type);
+ if (CTF_INFO_KIND(dtd->dtd_data.ctt_info) != CTF_K_FORWARD)
+ dtd = NULL;
+ }
+ if (dtd == NULL) {
+ type = ctf_add_generic(fp, flag, name, &dtd);
+ if (type == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+ }
+
+ VERIFY(type != CTF_ERR);
dtd->dtd_data.ctt_info = CTF_TYPE_INFO(CTF_K_ENUM, flag, 0);
dtd->dtd_data.ctt_size = fp->ctf_dmodel->ctd_int;
+ /*
+ * Always dirty in case we modified a forward.
+ */
+ fp->ctf_flags |= LCTF_DIRTY;
+
return (type);
}
@@ -1338,8 +1390,10 @@ ctf_add_enumerator(ctf_file_t *fp, ctf_id_t enid, const char *name, int value)
for (dmd = ctf_list_next(&dtd->dtd_u.dtu_members);
dmd != NULL; dmd = ctf_list_next(dmd)) {
- if (strcmp(dmd->dmd_name, name) == 0)
+ if (strcmp(dmd->dmd_name, name) == 0) {
+ ctf_dprintf("encountered dupliacte member %s\n", name);
return (ctf_set_errno(fp, ECTF_DUPMEMBER));
+ }
}
if ((dmd = ctf_alloc(sizeof (ctf_dmdef_t))) == NULL)
@@ -1371,8 +1425,10 @@ ctf_add_member(ctf_file_t *fp, ctf_id_t souid, const char *name, ctf_id_t type,
ctf_dtdef_t *dtd = ctf_dtd_lookup(fp, souid);
ctf_dmdef_t *dmd;
+ ulong_t mbitsz;
ssize_t msize, malign, ssize;
uint_t kind, vlen, root;
+ int mkind;
char *s = NULL;
if (!(fp->ctf_flags & LCTF_RDWR))
@@ -1400,15 +1456,49 @@ ctf_add_member(ctf_file_t *fp, ctf_id_t souid, const char *name, ctf_id_t type,
for (dmd = ctf_list_next(&dtd->dtd_u.dtu_members);
dmd != NULL; dmd = ctf_list_next(dmd)) {
if (dmd->dmd_name != NULL &&
- strcmp(dmd->dmd_name, name) == 0)
+ strcmp(dmd->dmd_name, name) == 0) {
return (ctf_set_errno(fp, ECTF_DUPMEMBER));
+ }
}
}
if ((msize = ctf_type_size(fp, type)) == CTF_ERR ||
- (malign = ctf_type_align(fp, type)) == CTF_ERR)
+ (malign = ctf_type_align(fp, type)) == CTF_ERR ||
+ (mkind = ctf_type_kind(fp, type)) == CTF_ERR)
return (CTF_ERR); /* errno is set for us */
+ /*
+ * ctf_type_size returns sizes in bytes. However, for bitfields, that
+ * means that it may misrepresent and actually rounds it up to a power
+ * of two and store that in bytes. So instead we have to get the
+ * Integers encoding and rely on that.
+ */
+ if (mkind == CTF_K_INTEGER) {
+ ctf_encoding_t e;
+
+ if (ctf_type_encoding(fp, type, &e) == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+ mbitsz = e.cte_bits;
+ } else if (mkind == CTF_K_FORWARD) {
+ /*
+ * This is a rather rare case. In general one cannot add a
+ * forward to a structure. However, the CTF tools traditionally
+ * tried to add a forward to the struct cpu as the last member.
+ * Therefore, if we find one here, we're going to verify the
+ * size and make sure it's zero. It's certainly odd, but that's
+ * life.
+ *
+ * Further, if it's not an absolute position being specified,
+ * then we refuse to add it.
+ */
+ if (offset == ULONG_MAX)
+ return (ctf_set_errno(fp, EINVAL));
+ VERIFY(msize == 0);
+ mbitsz = msize;
+ } else {
+ mbitsz = msize * 8;
+ }
+
if ((dmd = ctf_alloc(sizeof (ctf_dmdef_t))) == NULL)
return (ctf_set_errno(fp, EAGAIN));
@@ -1452,7 +1542,7 @@ ctf_add_member(ctf_file_t *fp, ctf_id_t souid, const char *name, ctf_id_t type,
ssize = off + msize;
} else {
dmd->dmd_offset = offset;
- ssize = offset / NBBY + msize;
+ ssize = (offset + mbitsz) / NBBY;
}
} else {
dmd->dmd_offset = 0;
@@ -2042,3 +2132,64 @@ ctf_add_label(ctf_file_t *fp, const char *name, ctf_id_t type, uint_t position)
return (0);
}
+
+/*
+ * Update the size of a structure or union. Note that we don't allow this to
+ * shrink the size of a struct or union, only to increase it. This is useful for
+ * cases when you have a structure whose actual size is larger than the sum of
+ * its members due to padding for natuaral alignment.
+ */
+int
+ctf_set_size(ctf_file_t *fp, ctf_id_t id, const ulong_t newsz)
+{
+ ctf_dtdef_t *dtd = ctf_dtd_lookup(fp, id);
+ uint_t kind;
+ size_t oldsz;
+
+ if (!(fp->ctf_flags & LCTF_RDWR))
+ return (ctf_set_errno(fp, ECTF_RDONLY));
+
+ if (dtd == NULL)
+ return (ctf_set_errno(fp, ECTF_BADID));
+
+ kind = CTF_INFO_KIND(dtd->dtd_data.ctt_info);
+
+ if (kind != CTF_K_STRUCT && kind != CTF_K_UNION)
+ return (ctf_set_errno(fp, ECTF_NOTSOU));
+
+ if ((oldsz = dtd->dtd_data.ctt_size) == CTF_LSIZE_SENT)
+ oldsz = CTF_TYPE_LSIZE(&dtd->dtd_data);
+
+ if (newsz < oldsz)
+ return (ctf_set_errno(fp, EINVAL));
+
+ if (newsz > CTF_MAX_SIZE) {
+ dtd->dtd_data.ctt_size = CTF_LSIZE_SENT;
+ dtd->dtd_data.ctt_lsizehi = CTF_SIZE_TO_LSIZE_HI(newsz);
+ dtd->dtd_data.ctt_lsizelo = CTF_SIZE_TO_LSIZE_LO(newsz);
+ } else {
+ dtd->dtd_data.ctt_size = (ushort_t)newsz;
+ }
+
+ fp->ctf_flags |= LCTF_DIRTY;
+ return (0);
+}
+
+int
+ctf_set_root(ctf_file_t *fp, ctf_id_t id, const boolean_t vis)
+{
+ ctf_dtdef_t *dtd = ctf_dtd_lookup(fp, id);
+ uint_t kind, vlen;
+
+ if (!(fp->ctf_flags & LCTF_RDWR))
+ return (ctf_set_errno(fp, ECTF_RDONLY));
+
+ if (dtd == NULL)
+ return (ctf_set_errno(fp, ECTF_BADID));
+
+ kind = CTF_INFO_KIND(dtd->dtd_data.ctt_info);
+ vlen = CTF_INFO_VLEN(dtd->dtd_data.ctt_info);
+
+ dtd->dtd_data.ctt_info = CTF_TYPE_INFO(kind, vis, vlen);
+ return (0);
+}
diff --git a/usr/src/common/ctf/ctf_error.c b/usr/src/common/ctf/ctf_error.c
index e2eb12e9dd..1765b77b54 100644
--- a/usr/src/common/ctf/ctf_error.c
+++ b/usr/src/common/ctf/ctf_error.c
@@ -80,7 +80,10 @@ static const char *const _ctf_errlist[] = {
"Cannot merge child container", /* ECTF_MCHILD */
"Label already exists", /* ECTF_LABEL */
"Merged labels conflict", /* ECTF_LCONFLICT */
- "Zlib library failure" /* ECTF_ZLIB */
+ "Zlib library failure", /* ECTF_ZLIB */
+ "CTF conversion backend error", /* ECTF_CONVBKERR */
+ "No C source to convert from", /* ECTF_CONVNOCSRC */
+ "No applicable conversion backend" /* ECTF_NOCONVBKEND */
};
static const int _ctf_nerr = sizeof (_ctf_errlist) / sizeof (_ctf_errlist[0]);
diff --git a/usr/src/common/ctf/ctf_types.c b/usr/src/common/ctf/ctf_types.c
index b5dbb260a3..2ef4f42d6b 100644
--- a/usr/src/common/ctf/ctf_types.c
+++ b/usr/src/common/ctf/ctf_types.c
@@ -367,6 +367,9 @@ ctf_type_size(ctf_file_t *fp, ctf_id_t type)
case CTF_K_FUNCTION:
return (0); /* function size is only known by symtab */
+ case CTF_K_FORWARD:
+ return (0);
+
case CTF_K_ENUM:
return (fp->ctf_dmodel->ctd_int);
@@ -1142,3 +1145,43 @@ ctf_string_iter(ctf_file_t *fp, ctf_string_f *func, void *arg)
return (0);
}
+
+/*
+ * fp isn't strictly necesasry at the moment. However, if we ever rev the file
+ * format, the valid values for kind will change.
+ */
+const char *
+ctf_kind_name(ctf_file_t *fp, int kind)
+{
+ switch (kind) {
+ case CTF_K_INTEGER:
+ return ("integer");
+ case CTF_K_FLOAT:
+ return ("float");
+ case CTF_K_POINTER:
+ return ("pointer");
+ case CTF_K_ARRAY:
+ return ("array");
+ case CTF_K_FUNCTION:
+ return ("function");
+ case CTF_K_STRUCT:
+ return ("struct");
+ case CTF_K_UNION:
+ return ("union");
+ case CTF_K_ENUM:
+ return ("enum");
+ case CTF_K_FORWARD:
+ return ("forward");
+ case CTF_K_TYPEDEF:
+ return ("typedef");
+ case CTF_K_VOLATILE:
+ return ("volatile");
+ case CTF_K_CONST:
+ return ("const");
+ case CTF_K_RESTRICT:
+ return ("restrict");
+ case CTF_K_UNKNOWN:
+ default:
+ return ("unknown");
+ }
+}
diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile
index 3af3ca3ef0..8e8868b08e 100644
--- a/usr/src/lib/Makefile
+++ b/usr/src/lib/Makefile
@@ -166,7 +166,8 @@ SUBDIRS += \
libvnd \
libidspace \
varpd \
- libbunyan
+ libbunyan \
+ libdwarf
SUBDIRS += \
passwdutil \
@@ -391,6 +392,7 @@ HDRSUBDIRS= \
libdevid \
libdevinfo \
libdiskmgt \
+ libdwarf \
libdladm \
libdll \
libdlpi \
@@ -673,6 +675,7 @@ libstmf: libm
libvscan: libm
libidspace: libumem
libbunyan: libnvpair
+libctf: libdwarf
$(INTEL_BUILD)libdiskmgt:libfdisk
diff --git a/usr/src/lib/libctf/Makefile.shared.com b/usr/src/lib/libctf/Makefile.shared.com
index a0370fc944..55f090e7f8 100644
--- a/usr/src/lib/libctf/Makefile.shared.com
+++ b/usr/src/lib/libctf/Makefile.shared.com
@@ -35,6 +35,7 @@ VERS = .1
COMMON_OBJS = \
ctf_create.o \
ctf_decl.o \
+ ctf_dwarf.o \
ctf_error.o \
ctf_hash.o \
ctf_labels.o \
@@ -43,17 +44,22 @@ COMMON_OBJS = \
ctf_types.o \
ctf_util.o
+MERGEQ_OBJS = \
+ mergeq.o \
+ workq.o
+
LIST_OBJS = \
list.o
LIB_OBJS = \
+ ctf_convert.o \
ctf_elfwrite.o \
ctf_diff.o \
ctf_lib.o \
ctf_merge.o \
ctf_subr.o
-OBJECTS = $(COMMON_OBJS) $(LIB_OBJS) $(LIST_OBJS)
+OBJECTS = $(COMMON_OBJS) $(LIB_OBJS) $(LIST_OBJS) $(MERGEQ_OBJS)
MAPFILEDIR = $(SRC)/lib/libctf
include $(SRC)/lib/Makefile.lib
@@ -62,16 +68,21 @@ SRCS = \
$(COMMON_OBJS:%.o=$(SRC)/common/ctf/%.c) \
$(LIB_OBJS:%.o=$(SRC)/lib/libctf/common/%.c) \
$(LIST_OBJS:%.o=$(SRC)/common/list/%.c) \
+ $(MERGEQ_OBJS:%.o=$(SRC)/lib/mergeq/%.c)
LIBS = $(DYNLIB) $(LINTLIB)
-LDLIBS += -lc -lelf
+LDLIBS += -lc -lelf -ldwarf -lavl
C99MODE= -xc99=%all
C99LMODE= -Xc99=%all
SRCDIR = $(SRC)/lib/libctf/common
-CPPFLAGS += -I$(SRC)/lib/libctf/common -I$(SRC)/common/ctf -DCTF_OLD_VERSIONS
+CPPFLAGS += -I$(SRC)/lib/libctf/common \
+ -I$(SRC)/common/ctf \
+ -I$(SRC)/lib/libdwarf/common \
+ -I$(SRC)/lib/mergeq \
+ -DCTF_OLD_VERSIONS
CFLAGS += $(CCVERBOSE)
CERRWARN += -_gcc=-Wno-uninitialized
diff --git a/usr/src/lib/libctf/Makefile.shared.targ b/usr/src/lib/libctf/Makefile.shared.targ
index 282be86075..b6520f2366 100644
--- a/usr/src/lib/libctf/Makefile.shared.targ
+++ b/usr/src/lib/libctf/Makefile.shared.targ
@@ -24,3 +24,7 @@ pics/%.o: $(SRC)/common/ctf/%.c
pics/%.o: $(SRC)/common/list/%.c
$(COMPILE.c) -o $@ $<
$(POST_PROCESS_O)
+
+pics/%.o: $(SRC)/lib/mergeq/%.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
diff --git a/usr/src/lib/libctf/common/ctf_convert.c b/usr/src/lib/libctf/common/ctf_convert.c
new file mode 100644
index 0000000000..cbb4d48c76
--- /dev/null
+++ b/usr/src/lib/libctf/common/ctf_convert.c
@@ -0,0 +1,210 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2015 Joyent, Inc.
+ */
+
+/*
+ * Main conversion entry points. This has been designed such that there can be
+ * any number of different conversion backends. Currently we only have one that
+ * understands DWARFv2 (and bits of DWARFv4). Each backend should be placed in
+ * the ctf_converters list and each will be tried in turn.
+ */
+
+#include <libctf_impl.h>
+#include <gelf.h>
+
+ctf_convert_f ctf_converters[] = {
+ ctf_dwarf_convert
+};
+
+#define NCONVERTS (sizeof (ctf_converters) / sizeof (ctf_convert_f))
+
+typedef enum ctf_convert_source {
+ CTFCONV_SOURCE_NONE = 0x0,
+ CTFCONV_SOURCE_UNKNOWN = 0x01,
+ CTFCONV_SOURCE_C = 0x02,
+ CTFCONV_SOURCE_S = 0x04
+} ctf_convert_source_t;
+
+static void
+ctf_convert_ftypes(Elf *elf, ctf_convert_source_t *types)
+{
+ int i;
+ Elf_Scn *scn = NULL, *strscn;
+ *types = CTFCONV_SOURCE_NONE;
+ GElf_Shdr shdr;
+ Elf_Data *data, *strdata;
+
+ while ((scn = elf_nextscn(elf, scn)) != NULL) {
+
+ if (gelf_getshdr(scn, &shdr) == NULL)
+ return;
+
+ if (shdr.sh_type == SHT_SYMTAB)
+ break;
+ }
+
+ if (scn == NULL)
+ return;
+
+ if ((strscn = elf_getscn(elf, shdr.sh_link)) == NULL)
+ return;
+
+ if ((data = elf_getdata(scn, NULL)) == NULL)
+ return;
+
+ if ((strdata = elf_getdata(strscn, NULL)) == NULL)
+ return;
+
+ for (i = 0; i < shdr.sh_size / shdr.sh_entsize; i++) {
+ GElf_Sym sym;
+ const char *file;
+ size_t len;
+
+ if (gelf_getsym(data, i, &sym) == NULL)
+ return;
+
+ if (GELF_ST_TYPE(sym.st_info) != STT_FILE)
+ continue;
+
+ file = (const char *)((uintptr_t)strdata->d_buf + sym.st_name);
+ len = strlen(file);
+ if (len < 2 || file[len - 2] != '.') {
+ *types |= CTFCONV_SOURCE_UNKNOWN;
+ continue;
+ }
+
+ switch (file[len - 1]) {
+ case 'c':
+ *types |= CTFCONV_SOURCE_C;
+ break;
+ case 'h':
+ /* We traditionally ignore header files... */
+ break;
+ case 's':
+ *types |= CTFCONV_SOURCE_S;
+ break;
+ default:
+ *types |= CTFCONV_SOURCE_UNKNOWN;
+ break;
+ }
+ }
+}
+
+static ctf_file_t *
+ctf_elfconvert(int fd, Elf *elf, const char *label, uint_t nthrs, uint_t flags,
+ int *errp, char *errbuf, size_t errlen)
+{
+ int err, i;
+ ctf_file_t *fp = NULL;
+ boolean_t notsup = B_TRUE;
+ ctf_convert_source_t type;
+
+ if (errp == NULL)
+ errp = &err;
+
+ if (elf == NULL) {
+ *errp = EINVAL;
+ return (NULL);
+ }
+
+ if (flags & ~CTF_CONVERT_F_IGNNONC) {
+ *errp = EINVAL;
+ return (NULL);
+ }
+
+ if (elf_kind(elf) != ELF_K_ELF) {
+ *errp = ECTF_FMT;
+ return (NULL);
+ }
+
+ ctf_convert_ftypes(elf, &type);
+ ctf_dprintf("got types: %d\n", type);
+ if (flags & CTF_CONVERT_F_IGNNONC) {
+ if (type == CTFCONV_SOURCE_NONE ||
+ (type & CTFCONV_SOURCE_UNKNOWN)) {
+ *errp = ECTF_CONVNOCSRC;
+ return (NULL);
+ }
+ }
+
+ for (i = 0; i < NCONVERTS; i++) {
+ ctf_conv_status_t cs;
+
+ fp = NULL;
+ cs = ctf_converters[i](fd, elf, nthrs, errp, &fp, errbuf,
+ errlen);
+ if (cs == CTF_CONV_SUCCESS) {
+ notsup = B_FALSE;
+ break;
+ }
+ if (cs == CTF_CONV_ERROR) {
+ fp = NULL;
+ notsup = B_FALSE;
+ break;
+ }
+ }
+
+ if (notsup == B_TRUE) {
+ if ((flags & CTF_CONVERT_F_IGNNONC) != 0 &&
+ (type & CTFCONV_SOURCE_C) == 0) {
+ *errp = ECTF_CONVNOCSRC;
+ return (NULL);
+ }
+ *errp = ECTF_NOCONVBKEND;
+ return (NULL);
+ }
+
+ /*
+ * Succsesful conversion.
+ */
+ if (fp != NULL) {
+ if (label == NULL)
+ label = "";
+ if (ctf_add_label(fp, label, fp->ctf_typemax, 0) == CTF_ERR) {
+ *errp = ctf_errno(fp);
+ ctf_close(fp);
+ return (NULL);
+ }
+ if (ctf_update(fp) == CTF_ERR) {
+ *errp = ctf_errno(fp);
+ ctf_close(fp);
+ return (NULL);
+ }
+ }
+
+ return (fp);
+}
+
+ctf_file_t *
+ctf_fdconvert(int fd, const char *label, uint_t nthrs, uint_t flags, int *errp,
+ char *errbuf, size_t errlen)
+{
+ int err;
+ Elf *elf;
+ ctf_file_t *fp;
+
+ if (errp == NULL)
+ errp = &err;
+
+ elf = elf_begin(fd, ELF_C_READ, NULL);
+ if (elf == NULL) {
+ *errp = ECTF_FMT;
+ return (NULL);
+ }
+
+ fp = ctf_elfconvert(fd, elf, label, nthrs, flags, errp, errbuf, errlen);
+
+ (void) elf_end(elf);
+ return (fp);
+}
diff --git a/usr/src/lib/libctf/common/ctf_diff.c b/usr/src/lib/libctf/common/ctf_diff.c
index e819fe02cb..d070488bbb 100644
--- a/usr/src/lib/libctf/common/ctf_diff.c
+++ b/usr/src/lib/libctf/common/ctf_diff.c
@@ -72,6 +72,8 @@ struct ctf_diff {
ctf_file_t *cds_ofp;
ctf_id_t *cds_forward;
ctf_id_t *cds_reverse;
+ size_t cds_fsize;
+ size_t cds_rsize;
ctf_diff_type_f cds_func;
ctf_diff_guess_t *cds_guess;
void *cds_arg;
@@ -147,6 +149,39 @@ ctf_diff_number(ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp, ctf_id_t oid)
}
/*
+ * Two typedefs are equivalent, if after we resolve a chain of typedefs, they
+ * point to equivalent types. This means that if a size_t is defined as follows:
+ *
+ * size_t -> ulong_t -> unsigned long
+ * size_t -> unsigned long
+ *
+ * That we'll ultimately end up treating them the same.
+ */
+static int
+ctf_diff_typedef(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid,
+ ctf_file_t *ofp, ctf_id_t oid)
+{
+ ctf_id_t iref = CTF_ERR, oref = CTF_ERR;
+
+ while (ctf_type_kind(ifp, iid) == CTF_K_TYPEDEF) {
+ iref = ctf_type_reference(ifp, iid);
+ if (iref == CTF_ERR)
+ return (CTF_ERR);
+ iid = iref;
+ }
+
+ while (ctf_type_kind(ofp, oid) == CTF_K_TYPEDEF) {
+ oref = ctf_type_reference(ofp, oid);
+ if (oref == CTF_ERR)
+ return (CTF_ERR);
+ oid = oref;
+ }
+
+ VERIFY(iref != CTF_ERR && oref != CTF_ERR);
+ return (ctf_diff_type(cds, ifp, iref, ofp, oref));
+}
+
+/*
* Two qualifiers are equivalent iff they point to two equivalent types.
*/
static int
@@ -274,7 +309,8 @@ out:
/*
* Two structures are the same if every member is identical to its corresponding
- * type, at the same offset, and has the same name.
+ * type, at the same offset, and has the same name, as well as them having the
+ * same overall size.
*/
static int
ctf_diff_struct(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp,
@@ -296,6 +332,9 @@ ctf_diff_struct(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp,
if ((otp = ctf_lookup_by_id(&ofp, oid)) == NULL)
return (ctf_set_errno(oifp, ctf_errno(ofp)));
+ if (ctf_type_size(ifp, iid) != ctf_type_size(ofp, oid))
+ return (B_TRUE);
+
if (LCTF_INFO_VLEN(ifp, itp->ctt_info) !=
LCTF_INFO_VLEN(ofp, otp->ctt_info))
return (B_TRUE);
@@ -639,8 +678,10 @@ ctf_diff_type(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp,
case CTF_K_FORWARD:
ret = ctf_diff_forward(ifp, iid, ofp, oid);
break;
- case CTF_K_POINTER:
case CTF_K_TYPEDEF:
+ ret = ctf_diff_typedef(cds, ifp, iid, ofp, oid);
+ break;
+ case CTF_K_POINTER:
case CTF_K_VOLATILE:
case CTF_K_CONST:
case CTF_K_RESTRICT:
@@ -664,9 +705,12 @@ ctf_diff_type(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp,
/*
* Walk every type in the first container and try to find a match in the second.
* If there is a match, then update both the forward and reverse mapping tables.
+ *
+ * The self variable tells us whether or not we should be comparing the input
+ * ctf container with itself or not.
*/
static int
-ctf_diff_pass1(ctf_diff_t *cds)
+ctf_diff_pass1(ctf_diff_t *cds, boolean_t self)
{
int i, j, diff;
int istart, iend, jstart, jend;
@@ -689,6 +733,17 @@ ctf_diff_pass1(ctf_diff_t *cds)
for (i = istart; i <= iend; i++) {
diff = B_TRUE;
+
+ /*
+ * If we're doing a self diff for dedup purposes, then we want
+ * to ensure that we compare a type i with every type in the
+ * range, [ 1, i ). Yes, this does mean that when i equals 1,
+ * we won't compare anything.
+ */
+ if (self == B_TRUE) {
+ jstart = istart;
+ jend = i - 1;
+ }
for (j = jstart; j <= jend; j++) {
ctf_diff_guess_t *cdg, *tofree;
@@ -788,12 +843,14 @@ ctf_diff_init(ctf_file_t *ifp, ctf_file_t *ofp, ctf_diff_t **cdsp)
ctf_free(cds, sizeof (ctf_diff_t));
return (ctf_set_errno(ifp, ENOMEM));
}
+ cds->cds_fsize = fsize;
cds->cds_reverse = ctf_alloc(rsize);
if (cds->cds_reverse == NULL) {
ctf_free(cds->cds_forward, fsize);
ctf_free(cds, sizeof (ctf_diff_t));
return (ctf_set_errno(ifp, ENOMEM));
}
+ cds->cds_rsize = rsize;
bzero(cds->cds_forward, fsize);
bzero(cds->cds_reverse, rsize);
@@ -811,7 +868,7 @@ ctf_diff_types(ctf_diff_t *cds, ctf_diff_type_f cb, void *arg)
cds->cds_func = cb;
cds->cds_arg = arg;
- ret = ctf_diff_pass1(cds);
+ ret = ctf_diff_pass1(cds, B_FALSE);
if (ret == 0)
ret = ctf_diff_pass2(cds);
@@ -821,12 +878,36 @@ ctf_diff_types(ctf_diff_t *cds, ctf_diff_type_f cb, void *arg)
return (ret);
}
+/*
+ * Do a diff where we're comparing a container with itself. In other words we'd
+ * like to know what types are actually duplicates of existing types in the
+ * container.
+ *
+ * Note this should remain private to libctf and not be exported in the public
+ * mapfile for the time being.
+ */
+int
+ctf_diff_self(ctf_diff_t *cds, ctf_diff_type_f cb, void *arg)
+{
+ if (cds->cds_ifp != cds->cds_ofp)
+ return (EINVAL);
+
+ cds->cds_func = cb;
+ cds->cds_arg = arg;
+
+ return (ctf_diff_pass1(cds, B_TRUE));
+}
+
+
void
ctf_diff_fini(ctf_diff_t *cds)
{
ctf_diff_guess_t *cdg;
size_t fsize, rsize;
+ if (cds == NULL)
+ return;
+
cds->cds_ifp->ctf_refcnt--;
cds->cds_ofp->ctf_refcnt--;
@@ -855,6 +936,10 @@ ctf_diff_fini(ctf_diff_t *cds)
cdg = cdg->cdg_next;
ctf_free(tofree, sizeof (ctf_diff_guess_t));
}
+ if (cds->cds_forward != NULL)
+ ctf_free(cds->cds_forward, cds->cds_fsize);
+ if (cds->cds_reverse != NULL)
+ ctf_free(cds->cds_reverse, cds->cds_rsize);
ctf_free(cds, sizeof (ctf_diff_t));
}
@@ -867,7 +952,7 @@ ctf_diff_getflags(ctf_diff_t *cds)
int
ctf_diff_setflags(ctf_diff_t *cds, uint_t flags)
{
- if ((flags & ~CTF_DIFF_F_MASK) != 0)
+ if ((flags & ~CTF_DIFF_F_IGNORE_INTNAMES) != 0)
return (ctf_set_errno(cds->cds_ifp, EINVAL));
cds->cds_flags = flags;
diff --git a/usr/src/lib/libctf/common/ctf_dwarf.c b/usr/src/lib/libctf/common/ctf_dwarf.c
new file mode 100644
index 0000000000..72e5aa9bd9
--- /dev/null
+++ b/usr/src/lib/libctf/common/ctf_dwarf.c
@@ -0,0 +1,2952 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * Copyright 2012 Jason King. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright 2015 Joyent, Inc.
+ */
+
+/*
+ * CTF DWARF conversion theory.
+ *
+ * DWARF data contains a series of compilation units. Each compilation unit
+ * generally refers to an object file or what once was, in the case of linked
+ * binaries and shared objects. Each compilation unit has a series of what DWARF
+ * calls a DIE (Debugging Information Entry). The set of entries that we care
+ * about have type information stored in a series of attributes. Each DIE also
+ * has a tag that identifies the kind of attributes that it has.
+ *
+ * A given DIE may itself have children. For example, a DIE that represents a
+ * structure has children which represent members. Whenever we encounter a DIE
+ * that has children or other values or types associated with it, we recursively
+ * process those children first so that way we can then refer to the generated
+ * CTF type id while processing its parent. This reduces the amount of unknowns
+ * and fixups that we need. It also ensures that we don't accidentally add types
+ * that an overzealous compiler might add to the DWARF data but aren't used by
+ * anything in the system.
+ *
+ * Once we do a conversion, we store a mapping in an AVL tree that goes from the
+ * DWARF's die offset, which is relative to the given compilation unit), to a
+ * ctf_id_t.
+ *
+ * Unfortunately, some compilers actually will emit duplicate entries for a
+ * given type that look similar, but aren't quite. To that end, we go through
+ * and do a variant on a merge once we're done processing a single compilation
+ * unit which deduplicates all of the types that are in the unit.
+ *
+ * Finally, if we encounter an object that has multiple compilation units, then
+ * we'll convert all of the compilation units separately and then do a merge, so
+ * that way we can result in one single ctf_file_t that represents everything
+ * for the object.
+ *
+ * Conversion Steps
+ * ----------------
+ *
+ * Because a given object we've been given to convert may have multiple
+ * compilation units, we break the work into two halves. The first half
+ * processes each compilation unit (potentially in parallel) and then the second
+ * half optionally merges all of the dies in the first half. First, we'll cover
+ * what's involved in converting a single ctf_die_t's dwarf to CTF. This covers
+ * the work done in ctf_dwarf_convert_one().
+ *
+ * An individual ctf_die_t, which represents a compilation unit, is converted to
+ * CTF in a series of multiple passes.
+ *
+ * Pass 1: During the first pass we walk all of the dies and if we find a
+ * function, variable, struct, union, enum or typedef, we recursively transform
+ * all of its types. We don't recurse or process everything, because we don't
+ * want to add some of the types that compilers may add which are effectively
+ * unused.
+ *
+ * During pass 1, if we encounter any structures or unions we mark them for
+ * fixing up later. This is necessary because we may not be able to determine
+ * the full size of a structure at the beginning of time. This will happen if
+ * the DWARF attribute DW_AT_byte_size is not present for a member. Because of
+ * this possibility we defer adding members to structures or even converting
+ * them during pass 1 and save that for pass 2. Adding all of the base
+ * structures without any of their members helps deal with any circular
+ * dependencies that we might encounter.
+ *
+ * Pass 2: This pass is used to do the first half of fixing up structures and
+ * unions. Rather than walk the entire type space again, we actually walk the
+ * list of structures and unions that we marked for later fixing up. Here, we
+ * iterate over every structure and add members to the underlying ctf_file_t,
+ * but not to the structs themselves. One might wonder why we don't, and the
+ * main reason is that libctf requires a ctf_update() be done before adding the
+ * members to structures or unions.
+ *
+ * Pass 3: This pass is used to do the second half of fixing up structures and
+ * unions. During this part we always go through and add members to structures
+ * and unions that we added to the container in the previous pass. In addition,
+ * we set the structure and union's actual size, which may have additional
+ * padding added by the compiler, it isn't simply the last offset. DWARF always
+ * guarantees an attribute exists for this. Importantly no ctf_id_t's change
+ * during pass 2.
+ *
+ * Pass 4: The next phase is to add CTF entries for all of the symbols and
+ * variables that are present in this die. During pass 1 we added entries to a
+ * map for each variable and function. During this pass, we iterate over the
+ * symbol table and when we encounter a symbol that we have in our lists of
+ * translated information which matches, we then add it to the ctf_file_t.
+ *
+ * Pass 5: Here we go and look for any weak symbols and functions and see if
+ * they match anything that we recognize. If so, then we add type information
+ * for them at this point based on the matching type.
+ *
+ * Pass 6: This pass is actually a variant on a merge. The traditional merge
+ * process expects there to be no duplicate types. As such, at the end of
+ * conversion, we do a dedup on all of the types in the system. The
+ * deduplication process is described in lib/libctf/common/ctf_merge.c.
+ *
+ * Once pass 6 is done, we've finished processing the individual compilation
+ * unit.
+ *
+ * The following steps reflect the general process of doing a conversion.
+ *
+ * 1) Walk the dwarf section and determine the number of compilation units
+ * 2) Create a ctf_die_t for each compilation unit
+ * 3) Add all ctf_die_t's to a workq
+ * 4) Have the workq process each die with ctf_dwarf_convert_one. This itself
+ * is comprised of several steps, which were already enumerated.
+ * 5) If we have multiple dies, we do a ctf merge of all the dies. The mechanics
+ * of the merge are discussed in lib/libctf/common/ctf_merge.c.
+ * 6) Free everything up and return a ctf_file_t to the user. If we only had a
+ * single compilation unit, then we give that to the user. Otherwise, we
+ * return the merged ctf_file_t.
+ *
+ * Threading
+ * ---------
+ *
+ * The process has been designed to be amenable to threading. Each compilation
+ * unit has its own type stream, therefore the logical place to divide and
+ * conquer is at the compilation unit. Each ctf_die_t has been built to be able
+ * to be processed independently of the others. It has its own libdwarf handle,
+ * as a given libdwarf handle may only be used by a single thread at a time.
+ * This allows the various ctf_die_t's to be processed in parallel by different
+ * threads.
+ *
+ * All of the ctf_die_t's are loaded into a workq which allows for a number of
+ * threads to be specified and used as a thread pool to process all of the
+ * queued work. We set the number of threads to use in the workq equal to the
+ * number of threads that the user has specified.
+ *
+ * After all of the compilation units have been drained, we use the same number
+ * of threads when performing a merge of multiple compilation units, if they
+ * exist.
+ *
+ * While all of these different parts do support and allow for multiple threads,
+ * it's important that when only a single thread is specified, that it be the
+ * calling thread. This allows the conversion routines to be used in a context
+ * that doesn't allow additional threads, such as rtld.
+ *
+ * Common DWARF Mechanics and Notes
+ * --------------------------------
+ *
+ * At this time, we really only support DWARFv2, though support for DWARFv4 is
+ * mostly there. There is no intent to support DWARFv3.
+ *
+ * Generally types for something are stored in the DW_AT_type attribute. For
+ * example, a function's return type will be stored in the local DW_AT_type
+ * attribute while the arguments will be in child DIEs. There are also various
+ * times when we don't have any DW_AT_type. In that case, the lack of a type
+ * implies, at least for C, that it's C type is void. Because DWARF doesn't emit
+ * one, we have a synthetic void type that we create and manipulate instead and
+ * pass it off to consumers on an as-needed basis. If nothing has a void type,
+ * it will not be emitted.
+ *
+ * Architecture Specific Parts
+ * ---------------------------
+ *
+ * The CTF tooling encodes various information about the various architectures
+ * in the system. Importantly, the tool assumes that every architecture has a
+ * data model where long and pointer are the same size. This is currently the
+ * case, as the two data models illumos supports are ILP32 and LP64.
+ *
+ * In addition, we encode the mapping of various floating point sizes to various
+ * types for each architecture. If a new architecture is being added, it should
+ * be added to the list. The general design of the ctf conversion tools is to be
+ * architecture independent. eg. any of the tools here should be able to convert
+ * any architecture's DWARF into ctf; however, this has not been rigorously
+ * tested and more importantly, the ctf routines don't currently write out the
+ * data in an endian-aware form, they only use that of the currently running
+ * library.
+ */
+
+#include <libctf_impl.h>
+#include <sys/avl.h>
+#include <sys/debug.h>
+#include <gelf.h>
+#include <libdwarf.h>
+#include <dwarf.h>
+#include <libgen.h>
+#include <workq.h>
+#include <errno.h>
+
+#define DWARF_VERSION_TWO 2
+#define DWARF_VARARGS_NAME "..."
+
+/*
+ * Dwarf may refer recursively to other types that we've already processed. To
+ * see if we've already converted them, we look them up in an AVL tree that's
+ * sorted by the DWARF id.
+ */
+typedef struct ctf_dwmap {
+ avl_node_t cdm_avl;
+ Dwarf_Off cdm_off;
+ Dwarf_Die cdm_die;
+ ctf_id_t cdm_id;
+ boolean_t cdm_fix;
+} ctf_dwmap_t;
+
+typedef struct ctf_dwvar {
+ ctf_list_t cdv_list;
+ char *cdv_name;
+ ctf_id_t cdv_type;
+ boolean_t cdv_global;
+} ctf_dwvar_t;
+
+typedef struct ctf_dwfunc {
+ ctf_list_t cdf_list;
+ char *cdf_name;
+ ctf_funcinfo_t cdf_fip;
+ ctf_id_t *cdf_argv;
+ boolean_t cdf_global;
+} ctf_dwfunc_t;
+
+typedef struct ctf_dwbitf {
+ ctf_list_t cdb_list;
+ ctf_id_t cdb_base;
+ uint_t cdb_nbits;
+ ctf_id_t cdb_id;
+} ctf_dwbitf_t;
+
+/*
+ * The ctf_die_t represents a single top-level DWARF die unit. While generally,
+ * the typical object file hs only a single die, if we're asked to convert
+ * something that's been linked from multiple sources, multiple dies will exist.
+ */
+typedef struct ctf_die {
+ Elf *cd_elf; /* shared libelf handle */
+ char *cd_name; /* basename of the DIE */
+ ctf_merge_t *cd_cmh; /* merge handle */
+ ctf_list_t cd_vars; /* List of variables */
+ ctf_list_t cd_funcs; /* List of functions */
+ ctf_list_t cd_bitfields; /* Bit field members */
+ Dwarf_Debug cd_dwarf; /* shared libdwarf handle */
+ Dwarf_Die cd_cu; /* libdwarf compilation unit */
+ Dwarf_Off cd_cuoff; /* cu's offset */
+ Dwarf_Off cd_maxoff; /* maximum offset */
+ ctf_file_t *cd_ctfp; /* output CTF file */
+ avl_tree_t cd_map; /* map die offsets to CTF types */
+ char *cd_errbuf; /* error message buffer */
+ size_t cd_errlen; /* error message buffer length */
+ size_t cd_ptrsz; /* object's pointer size */
+ boolean_t cd_bigend; /* is it big endian */
+ uint_t cd_mach; /* machine type */
+ ctf_id_t cd_voidtid; /* void pointer */
+ ctf_id_t cd_longtid; /* id for a 'long' */
+} ctf_die_t;
+
+static int ctf_dwarf_offset(ctf_die_t *, Dwarf_Die, Dwarf_Off *);
+static int ctf_dwarf_convert_die(ctf_die_t *, Dwarf_Die);
+static int ctf_dwarf_convert_type(ctf_die_t *, Dwarf_Die, ctf_id_t *, int);
+
+static int ctf_dwarf_function_count(ctf_die_t *, Dwarf_Die, ctf_funcinfo_t *,
+ boolean_t);
+static int ctf_dwarf_convert_fargs(ctf_die_t *, Dwarf_Die, ctf_funcinfo_t *,
+ ctf_id_t *);
+
+typedef int (ctf_dwarf_symtab_f)(ctf_die_t *, const GElf_Sym *, ulong_t,
+ const char *, const char *, void *);
+
+/*
+ * This is a generic way to set a CTF Conversion backend error depending on what
+ * we were doing. Unless it was one of a specific set of errors that don't
+ * indicate a programming / translation bug, eg. ENOMEM, then we transform it
+ * into a CTF backend error and fill in the error buffer.
+ */
+static int
+ctf_dwarf_error(ctf_die_t *cdp, ctf_file_t *cfp, int err, const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+ size_t off = 0;
+ ssize_t rem = cdp->cd_errlen;
+ if (cfp != NULL)
+ err = ctf_errno(cfp);
+
+ if (err == ENOMEM)
+ return (err);
+
+ ret = snprintf(cdp->cd_errbuf, rem, "die %s: ", cdp->cd_name);
+ if (ret < 0)
+ goto err;
+ off += ret;
+ rem = MAX(rem - ret, 0);
+
+ va_start(ap, fmt);
+ ret = vsnprintf(cdp->cd_errbuf + off, rem, fmt, ap);
+ va_end(ap);
+ if (ret < 0)
+ goto err;
+
+ off += ret;
+ rem = MAX(rem - ret, 0);
+ if (fmt[strlen(fmt) - 1] != '\n') {
+ (void) snprintf(cdp->cd_errbuf + off, rem,
+ ": %s\n", ctf_errmsg(err));
+ }
+ va_end(ap);
+ return (ECTF_CONVBKERR);
+
+err:
+ cdp->cd_errbuf[0] = '\0';
+ return (ECTF_CONVBKERR);
+}
+
+/*
+ * DWARF often ops to put no explicit type to describe a void type. eg. if we
+ * have a reference type whose DW_AT_type member doesn't exist, then we should
+ * instead assume it points to void. Because this isn't represented, we
+ * instead cause it to come into existence.
+ */
+static ctf_id_t
+ctf_dwarf_void(ctf_die_t *cdp)
+{
+ if (cdp->cd_voidtid == CTF_ERR) {
+ ctf_encoding_t enc = { CTF_INT_SIGNED, 0, 0 };
+ cdp->cd_voidtid = ctf_add_integer(cdp->cd_ctfp, CTF_ADD_ROOT,
+ "void", &enc);
+ if (cdp->cd_voidtid == CTF_ERR) {
+ (void) snprintf(cdp->cd_errbuf, cdp->cd_errlen,
+ "failed to create void type: %s\n",
+ ctf_errmsg(ctf_errno(cdp->cd_ctfp)));
+ }
+ }
+
+ return (cdp->cd_voidtid);
+}
+
+/*
+ * There are many different forms that an array index may take. However, we just
+ * always force it to be of a type long no matter what. Therefore we use this to
+ * have a single instance of long across everything.
+ */
+static ctf_id_t
+ctf_dwarf_long(ctf_die_t *cdp)
+{
+ if (cdp->cd_longtid == CTF_ERR) {
+ ctf_encoding_t enc;
+
+ enc.cte_format = CTF_INT_SIGNED;
+ enc.cte_offset = 0;
+ /* All illumos systems are LP */
+ enc.cte_bits = cdp->cd_ptrsz * 8;
+ cdp->cd_longtid = ctf_add_integer(cdp->cd_ctfp, CTF_ADD_NONROOT,
+ "long", &enc);
+ if (cdp->cd_longtid == CTF_ERR) {
+ (void) snprintf(cdp->cd_errbuf, cdp->cd_errlen,
+ "failed to create long type: %s\n",
+ ctf_errmsg(ctf_errno(cdp->cd_ctfp)));
+ }
+
+ }
+
+ return (cdp->cd_longtid);
+}
+
+static int
+ctf_dwmap_comp(const void *a, const void *b)
+{
+ const ctf_dwmap_t *ca = a;
+ const ctf_dwmap_t *cb = b;
+
+ if (ca->cdm_off > cb->cdm_off)
+ return (1);
+ if (ca->cdm_off < cb->cdm_off)
+ return (-1);
+ return (0);
+}
+
+static int
+ctf_dwmap_add(ctf_die_t *cdp, ctf_id_t id, Dwarf_Die die, boolean_t fix)
+{
+ int ret;
+ avl_index_t index;
+ ctf_dwmap_t *dwmap;
+ Dwarf_Off off;
+
+ VERIFY(id > 0 && id < CTF_MAX_TYPE);
+
+ if ((ret = ctf_dwarf_offset(cdp, die, &off)) != 0)
+ return (ret);
+
+ if ((dwmap = ctf_alloc(sizeof (ctf_dwmap_t))) == NULL)
+ return (ENOMEM);
+
+ dwmap->cdm_die = die;
+ dwmap->cdm_off = off;
+ dwmap->cdm_id = id;
+ dwmap->cdm_fix = fix;
+
+ ctf_dprintf("dwmap: %p %x->%d\n", dwmap, (uint32_t)off, id);
+ VERIFY(avl_find(&cdp->cd_map, dwmap, &index) == NULL);
+ avl_insert(&cdp->cd_map, dwmap, index);
+ return (0);
+}
+
+static int
+ctf_dwarf_attribute(ctf_die_t *cdp, Dwarf_Die die, Dwarf_Half name,
+ Dwarf_Attribute *attrp)
+{
+ int ret;
+ Dwarf_Error derr;
+
+ if ((ret = dwarf_attr(die, name, attrp, &derr)) == DW_DLV_OK)
+ return (0);
+ if (ret == DW_DLV_NO_ENTRY) {
+ *attrp = NULL;
+ return (ENOENT);
+ }
+ (void) snprintf(cdp->cd_errbuf, cdp->cd_errlen,
+ "failed to get attribute for type: %s\n",
+ dwarf_errmsg(derr));
+ return (ECTF_CONVBKERR);
+}
+
+static int
+ctf_dwarf_ref(ctf_die_t *cdp, Dwarf_Die die, Dwarf_Half name, Dwarf_Off *refp)
+{
+ int ret;
+ Dwarf_Attribute attr;
+ Dwarf_Error derr;
+
+ if ((ret = ctf_dwarf_attribute(cdp, die, name, &attr)) != 0)
+ return (ret);
+
+ if (dwarf_formref(attr, refp, &derr) == DW_DLV_OK) {
+ dwarf_dealloc(cdp->cd_dwarf, attr, DW_DLA_ATTR);
+ return (0);
+ }
+
+ (void) snprintf(cdp->cd_errbuf, cdp->cd_errlen,
+ "failed to get unsigned attribute for type: %s\n",
+ dwarf_errmsg(derr));
+ return (ECTF_CONVBKERR);
+}
+
+static int
+ctf_dwarf_refdie(ctf_die_t *cdp, Dwarf_Die die, Dwarf_Half name,
+ Dwarf_Die *diep)
+{
+ int ret;
+ Dwarf_Off off;
+ Dwarf_Error derr;
+
+ if ((ret = ctf_dwarf_ref(cdp, die, DW_AT_type, &off)) != 0)
+ return (ret);
+
+ off += cdp->cd_cuoff;
+ if ((ret = dwarf_offdie(cdp->cd_dwarf, off, diep, &derr)) !=
+ DW_DLV_OK) {
+ (void) snprintf(cdp->cd_errbuf, cdp->cd_errlen,
+ "failed to get die from offset %llu: %s\n",
+ off, dwarf_errmsg(derr));
+ return (ECTF_CONVBKERR);
+ }
+
+ return (0);
+}
+
+static int
+ctf_dwarf_signed(ctf_die_t *cdp, Dwarf_Die die, Dwarf_Half name,
+ Dwarf_Signed *valp)
+{
+ int ret;
+ Dwarf_Attribute attr;
+ Dwarf_Error derr;
+
+ if ((ret = ctf_dwarf_attribute(cdp, die, name, &attr)) != 0)
+ return (ret);
+
+ if (dwarf_formsdata(attr, valp, &derr) == DW_DLV_OK) {
+ dwarf_dealloc(cdp->cd_dwarf, attr, DW_DLA_ATTR);
+ return (0);
+ }
+
+ (void) snprintf(cdp->cd_errbuf, cdp->cd_errlen,
+ "failed to get unsigned attribute for type: %s\n",
+ dwarf_errmsg(derr));
+ return (ECTF_CONVBKERR);
+}
+
+static int
+ctf_dwarf_unsigned(ctf_die_t *cdp, Dwarf_Die die, Dwarf_Half name,
+ Dwarf_Unsigned *valp)
+{
+ int ret;
+ Dwarf_Attribute attr;
+ Dwarf_Error derr;
+
+ if ((ret = ctf_dwarf_attribute(cdp, die, name, &attr)) != 0)
+ return (ret);
+
+ if (dwarf_formudata(attr, valp, &derr) == DW_DLV_OK) {
+ dwarf_dealloc(cdp->cd_dwarf, attr, DW_DLA_ATTR);
+ return (0);
+ }
+
+ (void) snprintf(cdp->cd_errbuf, cdp->cd_errlen,
+ "failed to get unsigned attribute for type: %s\n",
+ dwarf_errmsg(derr));
+ return (ECTF_CONVBKERR);
+}
+
+static int
+ctf_dwarf_boolean(ctf_die_t *cdp, Dwarf_Die die, Dwarf_Half name,
+ Dwarf_Bool *val)
+{
+ int ret;
+ Dwarf_Attribute attr;
+ Dwarf_Error derr;
+
+ if ((ret = ctf_dwarf_attribute(cdp, die, name, &attr)) != 0)
+ return (ret);
+
+ if (dwarf_formflag(attr, val, &derr) == DW_DLV_OK) {
+ dwarf_dealloc(cdp->cd_dwarf, attr, DW_DLA_ATTR);
+ return (0);
+ }
+
+ (void) snprintf(cdp->cd_errbuf, cdp->cd_errlen,
+ "failed to get boolean attribute for type: %s\n",
+ dwarf_errmsg(derr));
+
+ return (ECTF_CONVBKERR);
+}
+
+static int
+ctf_dwarf_string(ctf_die_t *cdp, Dwarf_Die die, Dwarf_Half name, char **strp)
+{
+ int ret;
+ char *s;
+ Dwarf_Attribute attr;
+ Dwarf_Error derr;
+
+ *strp = NULL;
+ if ((ret = ctf_dwarf_attribute(cdp, die, name, &attr)) != 0)
+ return (ret);
+
+ if (dwarf_formstring(attr, &s, &derr) == DW_DLV_OK) {
+ if ((*strp = ctf_strdup(s)) == NULL)
+ ret = ENOMEM;
+ else
+ ret = 0;
+ dwarf_dealloc(cdp->cd_dwarf, attr, DW_DLA_ATTR);
+ return (ret);
+ }
+
+ (void) snprintf(cdp->cd_errbuf, cdp->cd_errlen,
+ "failed to get string attribute for type: %s\n",
+ dwarf_errmsg(derr));
+ return (ECTF_CONVBKERR);
+}
+
+static int
+ctf_dwarf_member_location(ctf_die_t *cdp, Dwarf_Die die, Dwarf_Unsigned *valp)
+{
+ int ret;
+ Dwarf_Error derr;
+ Dwarf_Attribute attr;
+ Dwarf_Locdesc *loc;
+ Dwarf_Signed locnum;
+
+ if ((ret = ctf_dwarf_attribute(cdp, die, DW_AT_data_member_location,
+ &attr)) != 0)
+ return (ret);
+
+ if (dwarf_loclist(attr, &loc, &locnum, &derr) != DW_DLV_OK) {
+ (void) snprintf(cdp->cd_errbuf, cdp->cd_errlen,
+ "failed to obtain location list for member offset: %s",
+ dwarf_errmsg(derr));
+ dwarf_dealloc(cdp->cd_dwarf, attr, DW_DLA_ATTR);
+ return (ECTF_CONVBKERR);
+ }
+ dwarf_dealloc(cdp->cd_dwarf, attr, DW_DLA_ATTR);
+
+ if (locnum != 1 || loc->ld_s->lr_atom != DW_OP_plus_uconst) {
+ (void) snprintf(cdp->cd_errbuf, cdp->cd_errlen,
+ "failed to parse location structure for member");
+ dwarf_dealloc(cdp->cd_dwarf, loc->ld_s, DW_DLA_LOC_BLOCK);
+ dwarf_dealloc(cdp->cd_dwarf, loc, DW_DLA_LOCDESC);
+ return (ECTF_CONVBKERR);
+ }
+
+ *valp = loc->ld_s->lr_number;
+
+ dwarf_dealloc(cdp->cd_dwarf, loc->ld_s, DW_DLA_LOC_BLOCK);
+ dwarf_dealloc(cdp->cd_dwarf, loc, DW_DLA_LOCDESC);
+ return (0);
+}
+
+
+static int
+ctf_dwarf_offset(ctf_die_t *cdp, Dwarf_Die die, Dwarf_Off *offsetp)
+{
+ Dwarf_Error derr;
+
+ if (dwarf_dieoffset(die, offsetp, &derr) == DW_DLV_OK)
+ return (0);
+
+ (void) snprintf(cdp->cd_errbuf, cdp->cd_errlen,
+ "failed to get die offset: %s\n",
+ dwarf_errmsg(derr));
+ return (ECTF_CONVBKERR);
+}
+
+static int
+ctf_dwarf_tag(ctf_die_t *cdp, Dwarf_Die die, Dwarf_Half *tagp)
+{
+ Dwarf_Error derr;
+
+ if (dwarf_tag(die, tagp, &derr) == DW_DLV_OK)
+ return (0);
+
+ (void) snprintf(cdp->cd_errbuf, cdp->cd_errlen,
+ "failed to get tag type: %s\n",
+ dwarf_errmsg(derr));
+ return (ECTF_CONVBKERR);
+}
+
+static int
+ctf_dwarf_sib(ctf_die_t *cdp, Dwarf_Die base, Dwarf_Die *sibp)
+{
+ Dwarf_Error derr;
+ int ret;
+
+ *sibp = NULL;
+ ret = dwarf_siblingof(cdp->cd_dwarf, base, sibp, &derr);
+ if (ret == DW_DLV_OK || ret == DW_DLV_NO_ENTRY)
+ return (0);
+
+ (void) snprintf(cdp->cd_errbuf, cdp->cd_errlen,
+ "failed to sibling from die: %s\n",
+ dwarf_errmsg(derr));
+ return (ECTF_CONVBKERR);
+}
+
+static int
+ctf_dwarf_child(ctf_die_t *cdp, Dwarf_Die base, Dwarf_Die *childp)
+{
+ Dwarf_Error derr;
+ int ret;
+
+ *childp = NULL;
+ ret = dwarf_child(base, childp, &derr);
+ if (ret == DW_DLV_OK || ret == DW_DLV_NO_ENTRY)
+ return (0);
+
+ (void) snprintf(cdp->cd_errbuf, cdp->cd_errlen,
+ "failed to child from die: %s\n",
+ dwarf_errmsg(derr));
+ return (ECTF_CONVBKERR);
+}
+
+/*
+ * Compilers disagree on what to do to determine if something has global
+ * visiblity. Traditionally gcc has used DW_AT_external to indicate this while
+ * Studio has used DW_AT_visibility. We check DW_AT_visibility first and then
+ * fall back to DW_AT_external. Lack of DW_AT_external implies that it is not.
+ */
+static int
+ctf_dwarf_isglobal(ctf_die_t *cdp, Dwarf_Die die, boolean_t *igp)
+{
+ int ret;
+ Dwarf_Signed vis;
+ Dwarf_Bool ext;
+
+ if ((ret = ctf_dwarf_signed(cdp, die, DW_AT_visibility, &vis)) == 0) {
+ *igp = vis == DW_VIS_exported;
+ return (0);
+ } else if (ret != ENOENT) {
+ return (ret);
+ }
+
+ if ((ret = ctf_dwarf_boolean(cdp, die, DW_AT_external, &ext)) != 0) {
+ if (ret == ENOENT) {
+ *igp = B_FALSE;
+ return (0);
+ }
+ return (ret);
+ }
+ *igp = ext != 0 ? B_TRUE : B_FALSE;
+ return (0);
+}
+
+static int
+ctf_dwarf_die_elfenc(Elf *elf, ctf_die_t *cdp, char *errbuf, size_t errlen)
+{
+ GElf_Ehdr ehdr;
+
+ if (gelf_getehdr(elf, &ehdr) == NULL) {
+ (void) snprintf(errbuf, errlen,
+ "failed to get ELF header: %s\n",
+ elf_errmsg(elf_errno()));
+ return (ECTF_CONVBKERR);
+ }
+
+ cdp->cd_mach = ehdr.e_machine;
+
+ if (ehdr.e_ident[EI_CLASS] == ELFCLASS32) {
+ cdp->cd_ptrsz = 4;
+ VERIFY(ctf_setmodel(cdp->cd_ctfp, CTF_MODEL_ILP32) == 0);
+ } else if (ehdr.e_ident[EI_CLASS] == ELFCLASS64) {
+ cdp->cd_ptrsz = 8;
+ VERIFY(ctf_setmodel(cdp->cd_ctfp, CTF_MODEL_LP64) == 0);
+ } else {
+ (void) snprintf(errbuf, errlen,
+ "unknown ELF class %d", ehdr.e_ident[EI_CLASS]);
+ return (ECTF_CONVBKERR);
+ }
+
+ if (ehdr.e_ident[EI_DATA] == ELFDATA2LSB) {
+ cdp->cd_bigend = B_FALSE;
+ } else if (ehdr.e_ident[EI_DATA] == ELFDATA2MSB) {
+ cdp->cd_bigend = B_TRUE;
+ } else {
+ (void) snprintf(errbuf, errlen,
+ "unknown ELF data encoding: %d", ehdr.e_ident[EI_DATA]);
+ return (ECTF_CONVBKERR);
+ }
+
+ return (0);
+}
+
+typedef struct ctf_dwarf_fpent {
+ size_t cdfe_size;
+ uint_t cdfe_enc[3];
+} ctf_dwarf_fpent_t;
+
+typedef struct ctf_dwarf_fpmap {
+ uint_t cdf_mach;
+ ctf_dwarf_fpent_t cdf_ents[4];
+} ctf_dwarf_fpmap_t;
+
+static const ctf_dwarf_fpmap_t ctf_dwarf_fpmaps[] = {
+ { EM_SPARC, {
+ { 4, { CTF_FP_SINGLE, CTF_FP_CPLX, CTF_FP_IMAGRY } },
+ { 8, { CTF_FP_DOUBLE, CTF_FP_DCPLX, CTF_FP_DIMAGRY } },
+ { 16, { CTF_FP_LDOUBLE, CTF_FP_LDCPLX, CTF_FP_LDIMAGRY } },
+ { 0, { 0 } }
+ } },
+ { EM_SPARC32PLUS, {
+ { 4, { CTF_FP_SINGLE, CTF_FP_CPLX, CTF_FP_IMAGRY } },
+ { 8, { CTF_FP_DOUBLE, CTF_FP_DCPLX, CTF_FP_DIMAGRY } },
+ { 16, { CTF_FP_LDOUBLE, CTF_FP_LDCPLX, CTF_FP_LDIMAGRY } },
+ { 0, { 0 } }
+ } },
+ { EM_SPARCV9, {
+ { 4, { CTF_FP_SINGLE, CTF_FP_CPLX, CTF_FP_IMAGRY } },
+ { 8, { CTF_FP_DOUBLE, CTF_FP_DCPLX, CTF_FP_DIMAGRY } },
+ { 16, { CTF_FP_LDOUBLE, CTF_FP_LDCPLX, CTF_FP_LDIMAGRY } },
+ { 0, { 0 } }
+ } },
+ { EM_386, {
+ { 4, { CTF_FP_SINGLE, CTF_FP_CPLX, CTF_FP_IMAGRY } },
+ { 8, { CTF_FP_DOUBLE, CTF_FP_DCPLX, CTF_FP_DIMAGRY } },
+ { 12, { CTF_FP_LDOUBLE, CTF_FP_LDCPLX, CTF_FP_LDIMAGRY } },
+ { 0, { 0 } }
+ } },
+ { EM_X86_64, {
+ { 4, { CTF_FP_SINGLE, CTF_FP_CPLX, CTF_FP_IMAGRY } },
+ { 8, { CTF_FP_DOUBLE, CTF_FP_DCPLX, CTF_FP_DIMAGRY } },
+ { 16, { CTF_FP_LDOUBLE, CTF_FP_LDCPLX, CTF_FP_LDIMAGRY } },
+ { 0, { 0 } }
+ } },
+ { EM_NONE }
+};
+
+static int
+ctf_dwarf_float_base(ctf_die_t *cdp, Dwarf_Signed type, ctf_encoding_t *enc)
+{
+ const ctf_dwarf_fpmap_t *map = &ctf_dwarf_fpmaps[0];
+ const ctf_dwarf_fpent_t *ent;
+ uint_t col = 0, mult = 1;
+
+ for (map = &ctf_dwarf_fpmaps[0]; map->cdf_mach != EM_NONE; map++) {
+ if (map->cdf_mach == cdp->cd_mach)
+ break;
+ }
+
+ if (map->cdf_mach == EM_NONE) {
+ (void) snprintf(cdp->cd_errbuf, cdp->cd_errlen,
+ "Unsupported machine type: %d\n", cdp->cd_mach);
+ return (ENOTSUP);
+ }
+
+ if (type == DW_ATE_complex_float) {
+ mult = 2;
+ col = 1;
+ } else if (type == DW_ATE_imaginary_float ||
+ type == DW_ATE_SUN_imaginary_float) {
+ col = 2;
+ }
+
+ ent = &map->cdf_ents[0];
+ for (ent = &map->cdf_ents[0]; ent->cdfe_size != 0; ent++) {
+ if (ent->cdfe_size * mult * 8 == enc->cte_bits) {
+ enc->cte_format = ent->cdfe_enc[col];
+ return (0);
+ }
+ }
+
+ (void) snprintf(cdp->cd_errbuf, cdp->cd_errlen,
+ "failed to find valid fp mapping for encoding %d, size %d bits\n",
+ type, enc->cte_bits);
+ return (EINVAL);
+}
+
+static int
+ctf_dwarf_dwarf_base(ctf_die_t *cdp, Dwarf_Die die, int *kindp,
+ ctf_encoding_t *enc)
+{
+ int ret;
+ Dwarf_Signed type;
+
+ if ((ret = ctf_dwarf_signed(cdp, die, DW_AT_encoding, &type)) != 0)
+ return (ret);
+
+ switch (type) {
+ case DW_ATE_unsigned:
+ case DW_ATE_address:
+ *kindp = CTF_K_INTEGER;
+ enc->cte_format = 0;
+ break;
+ case DW_ATE_unsigned_char:
+ *kindp = CTF_K_INTEGER;
+ enc->cte_format = CTF_INT_CHAR;
+ break;
+ case DW_ATE_signed:
+ *kindp = CTF_K_INTEGER;
+ enc->cte_format = CTF_INT_SIGNED;
+ break;
+ case DW_ATE_signed_char:
+ *kindp = CTF_K_INTEGER;
+ enc->cte_format = CTF_INT_SIGNED | CTF_INT_CHAR;
+ break;
+ case DW_ATE_boolean:
+ *kindp = CTF_K_INTEGER;
+ enc->cte_format = CTF_INT_SIGNED | CTF_INT_BOOL;
+ break;
+ case DW_ATE_float:
+ case DW_ATE_complex_float:
+ case DW_ATE_imaginary_float:
+ case DW_ATE_SUN_imaginary_float:
+ case DW_ATE_SUN_interval_float:
+ *kindp = CTF_K_FLOAT;
+ if ((ret = ctf_dwarf_float_base(cdp, type, enc)) != 0)
+ return (ret);
+ break;
+ default:
+ (void) snprintf(cdp->cd_errbuf, cdp->cd_errlen,
+ "encountered unkown DWARF encoding: %d", type);
+ return (ECTF_CONVBKERR);
+ }
+
+ return (0);
+}
+
+/*
+ * Different compilers (at least GCC and Studio) use different names for types.
+ * This parses the types and attempts to unify them. If this fails, we just fall
+ * back to using the DWARF itself.
+ */
+static int
+ctf_dwarf_parse_base(const char *name, int *kindp, ctf_encoding_t *enc,
+ char **newnamep)
+{
+ char buf[256];
+ char *base, *c;
+ int nlong = 0, nshort = 0, nchar = 0, nint = 0;
+ int sign = 1;
+
+ if (strlen(name) + 1 > sizeof (buf))
+ return (EINVAL);
+
+ (void) strlcpy(buf, name, sizeof (buf));
+ for (c = strtok(buf, " "); c != NULL; c = strtok(NULL, " ")) {
+ if (strcmp(c, "signed") == 0) {
+ sign = 1;
+ } else if (strcmp(c, "unsigned") == 0) {
+ sign = 0;
+ } else if (strcmp(c, "long") == 0) {
+ nlong++;
+ } else if (strcmp(c, "char") == 0) {
+ nchar++;
+ } else if (strcmp(c, "short") == 0) {
+ nshort++;
+ } else if (strcmp(c, "int") == 0) {
+ nint++;
+ } else {
+ /*
+ * If we don't recognize any of the tokens, we'll tell
+ * the caller to fall back to the dwarf-provided
+ * encoding information.
+ */
+ return (EINVAL);
+ }
+ }
+
+ if (nchar > 1 || nshort > 1 || nint > 1 || nlong > 2)
+ return (EINVAL);
+
+ if (nchar > 0) {
+ if (nlong > 0 || nshort > 0 || nint > 0)
+ return (EINVAL);
+ base = "char";
+ } else if (nshort > 0) {
+ if (nlong > 0)
+ return (EINVAL);
+ base = "short";
+ } else if (nlong > 0) {
+ base = "long";
+ } else {
+ base = "int";
+ }
+
+ if (nchar > 0)
+ enc->cte_format = CTF_INT_CHAR;
+ else
+ enc->cte_format = 0;
+
+ if (sign > 0)
+ enc->cte_format |= CTF_INT_SIGNED;
+
+ (void) snprintf(buf, sizeof (buf), "%s%s%s",
+ (sign ? "" : "unsigned "),
+ (nlong > 1 ? "long " : ""),
+ base);
+
+ *newnamep = ctf_strdup(buf);
+ if (*newnamep == NULL)
+ return (ENOMEM);
+ *kindp = CTF_K_INTEGER;
+ return (0);
+}
+
+static int
+ctf_dwarf_create_base(ctf_die_t *cdp, Dwarf_Die die, ctf_id_t *idp, int isroot,
+ Dwarf_Off off)
+{
+ int ret;
+ char *name, *nname;
+ Dwarf_Unsigned sz;
+ int kind;
+ ctf_encoding_t enc;
+ ctf_id_t id;
+
+ if ((ret = ctf_dwarf_string(cdp, die, DW_AT_name, &name)) != 0)
+ return (ret);
+ if ((ret = ctf_dwarf_unsigned(cdp, die, DW_AT_byte_size, &sz)) != 0) {
+ goto out;
+ }
+ ctf_dprintf("Creating base type %s from off %llu, size: %d\n", name,
+ off, sz);
+
+ bzero(&enc, sizeof (ctf_encoding_t));
+ enc.cte_bits = sz * 8;
+ if ((ret = ctf_dwarf_parse_base(name, &kind, &enc, &nname)) == 0) {
+ ctf_free(name, strlen(name) + 1);
+ name = nname;
+ } else {
+ if (ret != EINVAL)
+ return (ret);
+ ctf_dprintf("falling back to dwarf for base type %s\n", name);
+ if ((ret = ctf_dwarf_dwarf_base(cdp, die, &kind, &enc)) != 0)
+ return (ret);
+ }
+
+ id = ctf_add_encoded(cdp->cd_ctfp, isroot, name, &enc, kind);
+ if (id == CTF_ERR) {
+ ret = ctf_errno(cdp->cd_ctfp);
+ } else {
+ *idp = id;
+ ret = ctf_dwmap_add(cdp, id, die, B_FALSE);
+ }
+out:
+ ctf_free(name, strlen(name) + 1);
+ return (ret);
+}
+
+/*
+ * Getting a member's offset is a surprisingly intricate dance. It works as
+ * follows:
+ *
+ * 1) If we're in DWARFv4, then we either have a DW_AT_data_bit_offset or we
+ * have a DW_AT_data_member_location. We won't have both. Thus we check first
+ * for DW_AT_data_bit_offset, and if it exists, we're set.
+ *
+ * Next, if we have a bitfield and we don't ahve a DW_AT_data_bit_offset, then
+ * we have to grab the data location and use the following dance:
+ *
+ * 2) Gather the set of DW_AT_byte_size, DW_AT_bit_offset, and DW_AT_bit_size.
+ * Of course, the DW_AT_byte_size may be omitted, even though it isn't always.
+ * When it's been omitted, we then have to say that the size is that of the
+ * underlying type, which forces that to be after a ctf_update(). Here, we have
+ * to do different things based on whether or not we're using big endian or
+ * little endian to obtain the proper offset.
+ */
+static int
+ctf_dwarf_member_offset(ctf_die_t *cdp, Dwarf_Die die, ctf_id_t mid,
+ ulong_t *offp)
+{
+ int ret;
+ Dwarf_Unsigned loc, bitsz, bytesz, bitoff;
+ size_t off, tsz;
+
+ if ((ret = ctf_dwarf_unsigned(cdp, die, DW_AT_data_bit_offset,
+ &loc)) == 0) {
+ *offp = loc;
+ return (0);
+ } else if (ret != ENOENT) {
+ return (ret);
+ }
+
+ if ((ret = ctf_dwarf_member_location(cdp, die, &loc)) != 0)
+ return (ret);
+ off = loc * 8;
+
+ if ((ret = ctf_dwarf_unsigned(cdp, die, DW_AT_bit_offset,
+ &bitoff)) != 0) {
+ if (ret != ENOENT)
+ return (ret);
+ *offp = off;
+ return (0);
+ }
+
+ /* At this point we have to have DW_AT_bit_size */
+ if ((ret = ctf_dwarf_unsigned(cdp, die, DW_AT_bit_size, &bitsz)) != 0)
+ return (ret);
+
+ if ((ret = ctf_dwarf_unsigned(cdp, die, DW_AT_byte_size,
+ &bytesz)) != 0) {
+ if (ret != ENOENT)
+ return (ret);
+ if ((tsz = ctf_type_size(cdp->cd_ctfp, mid)) == CTF_ERR) {
+ int e = ctf_errno(cdp->cd_ctfp);
+ (void) snprintf(cdp->cd_errbuf, cdp->cd_errlen,
+ "failed to get type size: %s", ctf_errmsg(e));
+ return (ECTF_CONVBKERR);
+ }
+ } else {
+ tsz = bytesz;
+ }
+ tsz *= 8;
+ if (cdp->cd_bigend == B_TRUE) {
+ *offp = off + bitoff;
+ } else {
+ *offp = off + tsz - bitoff - bitsz;
+ }
+
+ return (0);
+}
+
+/*
+ * We need to determine if the member in question is a bitfield. If it is, then
+ * we need to go through and create a new type that's based on the actual base
+ * type, but has a different size. We also rename the type as a result to help
+ * deal with future collisions.
+ *
+ * Here we need to look and see if we have a DW_AT_bit_size value. If we have a
+ * bit size member and it does not equal the byte size member, then we need to
+ * create a bitfield type based on this.
+ *
+ * Note: When we support DWARFv4, there may be a chance that we ned to also
+ * search for the DW_AT_byte_size if we don't have a DW_AT_bit_size member.
+ */
+static int
+ctf_dwarf_member_bitfield(ctf_die_t *cdp, Dwarf_Die die, ctf_id_t *idp)
+{
+ int ret;
+ Dwarf_Unsigned bitsz;
+ ctf_encoding_t e;
+ ctf_dwbitf_t *cdb;
+ ctf_dtdef_t *dtd;
+ ctf_id_t base = *idp;
+ int kind;
+
+ if ((ret = ctf_dwarf_unsigned(cdp, die, DW_AT_bit_size, &bitsz)) != 0) {
+ if (ret == ENOENT)
+ return (0);
+ return (ret);
+ }
+
+ ctf_dprintf("Trying to deal with bitfields on %d:%d\n", base, bitsz);
+ /*
+ * Given that we now have a bitsize, time to go do something about it.
+ * We're going to create a new type based on the current one, but first
+ * we need to find the base type. This means we need to traverse any
+ * typedef's, consts, and volatiles until we get to what should be
+ * something of type integer or enumeration.
+ */
+ VERIFY(bitsz < UINT32_MAX);
+ dtd = ctf_dtd_lookup(cdp->cd_ctfp, base);
+ VERIFY(dtd != NULL);
+ kind = CTF_INFO_KIND(dtd->dtd_data.ctt_info);
+ while (kind == CTF_K_TYPEDEF || kind == CTF_K_CONST ||
+ kind == CTF_K_VOLATILE) {
+ dtd = ctf_dtd_lookup(cdp->cd_ctfp, dtd->dtd_data.ctt_type);
+ VERIFY(dtd != NULL);
+ kind = CTF_INFO_KIND(dtd->dtd_data.ctt_info);
+ }
+ ctf_dprintf("got kind %d\n", kind);
+ VERIFY(kind == CTF_K_INTEGER || kind == CTF_K_ENUM);
+
+ /*
+ * As surprising as it may be, it is strictly possible to create a
+ * bitfield that is based on an enum. Of course, the C standard leaves
+ * enums sizing as an ABI concern more or less. To that effect, today on
+ * all illumos platforms the size of an enum is generally that of an
+ * int as our supported data models and ABIs all agree on that. So what
+ * we'll do is fake up a CTF enconding here to use. In this case, we'll
+ * treat it as an unsigned value of whatever size the underlying enum
+ * currently has (which is in the ctt_size member of its dynamic type
+ * data).
+ */
+ if (kind == CTF_K_INTEGER) {
+ e = dtd->dtd_u.dtu_enc;
+ } else {
+ bzero(&e, sizeof (ctf_encoding_t));
+ e.cte_bits = dtd->dtd_data.ctt_size * NBBY;
+ }
+
+ for (cdb = ctf_list_next(&cdp->cd_bitfields); cdb != NULL;
+ cdb = ctf_list_next(cdb)) {
+ if (cdb->cdb_base == base && cdb->cdb_nbits == bitsz)
+ break;
+ }
+
+ /*
+ * Create a new type if none exists. We name all types in a way that is
+ * guaranteed not to conflict with the corresponding C type. We do this
+ * by using the ':' operator.
+ */
+ if (cdb == NULL) {
+ size_t namesz;
+ char *name;
+
+ e.cte_bits = bitsz;
+ namesz = snprintf(NULL, 0, "%s:%d", dtd->dtd_name,
+ (uint32_t)bitsz);
+ name = ctf_alloc(namesz + 1);
+ if (name == NULL)
+ return (ENOMEM);
+ cdb = ctf_alloc(sizeof (ctf_dwbitf_t));
+ if (cdb == NULL) {
+ ctf_free(name, namesz + 1);
+ return (ENOMEM);
+ }
+ (void) snprintf(name, namesz + 1, "%s:%d", dtd->dtd_name,
+ (uint32_t)bitsz);
+
+ cdb->cdb_base = base;
+ cdb->cdb_nbits = bitsz;
+ cdb->cdb_id = ctf_add_integer(cdp->cd_ctfp, CTF_ADD_NONROOT,
+ name, &e);
+ if (cdb->cdb_id == CTF_ERR) {
+ (void) snprintf(cdp->cd_errbuf, cdp->cd_errlen,
+ "failed to get add bitfield type %s: %s", name,
+ ctf_errmsg(ctf_errno(cdp->cd_ctfp)));
+ ctf_free(name, namesz + 1);
+ ctf_free(cdb, sizeof (ctf_dwbitf_t));
+ return (ECTF_CONVBKERR);
+ }
+ ctf_free(name, namesz + 1);
+ ctf_list_append(&cdp->cd_bitfields, cdb);
+ }
+
+ *idp = cdb->cdb_id;
+
+ return (0);
+}
+
+static int
+ctf_dwarf_fixup_sou(ctf_die_t *cdp, Dwarf_Die die, ctf_id_t base, boolean_t add)
+{
+ int ret, kind;
+ Dwarf_Die child, memb;
+ Dwarf_Unsigned size;
+ ulong_t nsz;
+
+ kind = ctf_type_kind(cdp->cd_ctfp, base);
+ VERIFY(kind != CTF_ERR);
+ VERIFY(kind == CTF_K_STRUCT || kind == CTF_K_UNION);
+
+ /*
+ * Members are in children. However, gcc also allows empty ones.
+ */
+ if ((ret = ctf_dwarf_child(cdp, die, &child)) != 0)
+ return (ret);
+ if (child == NULL)
+ return (0);
+
+ memb = child;
+ while (memb != NULL) {
+ Dwarf_Die sib, tdie;
+ Dwarf_Half tag;
+ ctf_id_t mid;
+ char *mname;
+ ulong_t memboff = 0;
+
+ if ((ret = ctf_dwarf_tag(cdp, memb, &tag)) != 0)
+ return (ret);
+
+ if (tag != DW_TAG_member)
+ continue;
+
+ if ((ret = ctf_dwarf_refdie(cdp, memb, DW_AT_type, &tdie)) != 0)
+ return (ret);
+
+ if ((ret = ctf_dwarf_convert_type(cdp, tdie, &mid,
+ CTF_ADD_NONROOT)) != 0)
+ return (ret);
+ ctf_dprintf("Got back type id: %d\n", mid);
+
+ /*
+ * If we're not adding a member, just go ahead and return.
+ */
+ if (add == B_FALSE) {
+ if ((ret = ctf_dwarf_member_bitfield(cdp, memb,
+ &mid)) != 0)
+ return (ret);
+ goto next;
+ }
+
+ if ((ret = ctf_dwarf_string(cdp, memb, DW_AT_name,
+ &mname)) != 0 && ret != ENOENT)
+ return (ret);
+ if (ret == ENOENT)
+ mname = NULL;
+
+ if (kind == CTF_K_UNION) {
+ memboff = 0;
+ } else if ((ret = ctf_dwarf_member_offset(cdp, memb, mid,
+ &memboff)) != 0) {
+ if (mname != NULL)
+ ctf_free(mname, strlen(mname) + 1);
+ return (ret);
+ }
+
+ if ((ret = ctf_dwarf_member_bitfield(cdp, memb, &mid)) != 0)
+ return (ret);
+
+ ret = ctf_add_member(cdp->cd_ctfp, base, mname, mid, memboff);
+ if (ret == CTF_ERR) {
+ (void) snprintf(cdp->cd_errbuf, cdp->cd_errlen,
+ "failed to add member %s: %s",
+ mname, ctf_errmsg(ctf_errno(cdp->cd_ctfp)));
+ if (mname != NULL)
+ ctf_free(mname, strlen(mname) + 1);
+ return (ECTF_CONVBKERR);
+ }
+
+ if (mname != NULL)
+ ctf_free(mname, strlen(mname) + 1);
+
+next:
+ if ((ret = ctf_dwarf_sib(cdp, memb, &sib)) != 0)
+ return (ret);
+ memb = sib;
+ }
+
+ /*
+ * If we're not adding members, then we don't know the final size of the
+ * structure, so end here.
+ */
+ if (add == B_FALSE)
+ return (0);
+
+ /* Finally set the size of the structure to the actual byte size */
+ if ((ret = ctf_dwarf_unsigned(cdp, die, DW_AT_byte_size, &size)) != 0)
+ return (ret);
+ nsz = size;
+ if ((ctf_set_size(cdp->cd_ctfp, base, nsz)) == CTF_ERR) {
+ int e = ctf_errno(cdp->cd_ctfp);
+ (void) snprintf(cdp->cd_errbuf, cdp->cd_errlen,
+ "failed to set type size for %d to 0x%x: %s", base,
+ (uint32_t)size, ctf_errmsg(e));
+ return (ECTF_CONVBKERR);
+ }
+
+ return (0);
+}
+
+static int
+ctf_dwarf_create_sou(ctf_die_t *cdp, Dwarf_Die die, ctf_id_t *idp,
+ int kind, int isroot)
+{
+ int ret;
+ char *name;
+ ctf_id_t base;
+ Dwarf_Die child;
+ Dwarf_Bool decl;
+
+ /*
+ * Deal with the terribly annoying case of anonymous structs and unions.
+ * If they don't have a name, set the name to the empty string.
+ */
+ if ((ret = ctf_dwarf_string(cdp, die, DW_AT_name, &name)) != 0 &&
+ ret != ENOENT)
+ return (ret);
+ if (ret == ENOENT)
+ name = NULL;
+
+ /*
+ * We need to check if we just have a declaration here. If we do, then
+ * instead of creating an actual structure or union, we're just going to
+ * go ahead and create a forward. During a dedup or merge, the forward
+ * will be replaced with the real thing.
+ */
+ if ((ret = ctf_dwarf_boolean(cdp, die, DW_AT_declaration,
+ &decl)) != 0) {
+ if (ret != ENOENT)
+ return (ret);
+ decl = 0;
+ }
+
+ if (decl != 0) {
+ base = ctf_add_forward(cdp->cd_ctfp, isroot, name, kind);
+ } else if (kind == CTF_K_STRUCT) {
+ base = ctf_add_struct(cdp->cd_ctfp, isroot, name);
+ } else {
+ base = ctf_add_union(cdp->cd_ctfp, isroot, name);
+ }
+ ctf_dprintf("added sou %s (%d) (%d)\n", name, kind, base);
+ if (name != NULL)
+ ctf_free(name, strlen(name) + 1);
+ if (base == CTF_ERR)
+ return (ctf_errno(cdp->cd_ctfp));
+ *idp = base;
+
+ /*
+ * If it's just a declaration, we're not going to mark it for fix up or
+ * do anything else.
+ */
+ if (decl == B_TRUE)
+ return (ctf_dwmap_add(cdp, base, die, B_FALSE));
+ if ((ret = ctf_dwmap_add(cdp, base, die, B_TRUE)) != 0)
+ return (ret);
+
+ /*
+ * Members are in children. However, gcc also allows empty ones.
+ */
+ if ((ret = ctf_dwarf_child(cdp, die, &child)) != 0)
+ return (ret);
+ if (child == NULL)
+ return (0);
+
+ return (0);
+}
+
+static int
+ctf_dwarf_create_array_range(ctf_die_t *cdp, Dwarf_Die range, ctf_id_t *idp,
+ ctf_id_t base, int isroot)
+{
+ int ret;
+ Dwarf_Die sib;
+ Dwarf_Unsigned val;
+ Dwarf_Signed sval;
+ ctf_arinfo_t ar;
+
+ ctf_dprintf("creating array range\n");
+
+ if ((ret = ctf_dwarf_sib(cdp, range, &sib)) != 0)
+ return (ret);
+ if (sib != NULL) {
+ ctf_id_t id;
+ if ((ret = ctf_dwarf_create_array_range(cdp, sib, &id,
+ base, CTF_ADD_NONROOT)) != 0)
+ return (ret);
+ ar.ctr_contents = id;
+ } else {
+ ar.ctr_contents = base;
+ }
+
+ if ((ar.ctr_index = ctf_dwarf_long(cdp)) == CTF_ERR)
+ return (ctf_errno(cdp->cd_ctfp));
+
+ /*
+ * Array bounds can be signed or unsigned, but there are several kinds
+ * of signless forms (data1, data2, etc) that take their sign from the
+ * routine that is trying to interpret them. That is, data1 can be
+ * either signed or unsigned, depending on whether you use the signed or
+ * unsigned accessor function. GCC will use the signless forms to store
+ * unsigned values which have their high bit set, so we need to try to
+ * read them first as unsigned to get positive values. We could also
+ * try signed first, falling back to unsigned if we got a negative
+ * value.
+ */
+ if ((ret = ctf_dwarf_unsigned(cdp, range, DW_AT_upper_bound,
+ &val)) == 0) {
+ ar.ctr_nelems = val + 1;
+ } else if (ret != ENOENT) {
+ return (ret);
+ } else if ((ret = ctf_dwarf_signed(cdp, range, DW_AT_upper_bound,
+ &sval)) == 0) {
+ ar.ctr_nelems = sval + 1;
+ } else if (ret != ENOENT) {
+ return (ret);
+ } else {
+ ar.ctr_nelems = 0;
+ }
+
+ if ((*idp = ctf_add_array(cdp->cd_ctfp, isroot, &ar)) == CTF_ERR)
+ return (ctf_errno(cdp->cd_ctfp));
+
+ return (0);
+}
+
+/*
+ * Try and create an array type. First, the kind of the array is specified in
+ * the DW_AT_type entry. Next, the number of entries is stored in a more
+ * complicated form, we should have a child that has the DW_TAG_subrange type.
+ */
+static int
+ctf_dwarf_create_array(ctf_die_t *cdp, Dwarf_Die die, ctf_id_t *idp, int isroot)
+{
+ int ret;
+ Dwarf_Die tdie, rdie;
+ ctf_id_t tid;
+ Dwarf_Half rtag;
+ ctf_arinfo_t ar;
+
+ if ((ret = ctf_dwarf_refdie(cdp, die, DW_AT_type, &tdie)) != 0)
+ return (ret);
+ if ((ret = ctf_dwarf_convert_type(cdp, tdie, &tid,
+ CTF_ADD_NONROOT)) != 0)
+ return (ret);
+
+ ar.ctr_contents = tid;
+
+ if ((ret = ctf_dwarf_child(cdp, die, &rdie)) != 0)
+ return (ret);
+ if ((ret = ctf_dwarf_tag(cdp, rdie, &rtag)) != 0)
+ return (ret);
+ if (rtag != DW_TAG_subrange_type) {
+ (void) snprintf(cdp->cd_errbuf, cdp->cd_errlen,
+ "encountered array without DW_TAG_subrange_type child\n");
+ return (ECTF_CONVBKERR);
+ }
+
+ /*
+ * The compiler may opt to describe a multi-dimensional array as one
+ * giant array or it may opt to instead encode it as a series of
+ * subranges. If it's the latter, then for each subrange we introduce a
+ * type. We can always use the base type.
+ */
+ if ((ret = ctf_dwarf_create_array_range(cdp, rdie, idp, tid,
+ isroot)) != 0)
+ return (ret);
+ ctf_dprintf("Got back id %d\n", *idp);
+ return (ctf_dwmap_add(cdp, *idp, die, B_FALSE));
+}
+
+static int
+ctf_dwarf_create_reference(ctf_die_t *cdp, Dwarf_Die die, ctf_id_t *idp,
+ int kind, int isroot)
+{
+ int ret;
+ ctf_id_t id;
+ Dwarf_Die tdie;
+ char *name;
+ size_t namelen;
+
+ if ((ret = ctf_dwarf_string(cdp, die, DW_AT_name, &name)) != 0 &&
+ ret != ENOENT)
+ return (ret);
+ if (ret == ENOENT) {
+ name = NULL;
+ namelen = 0;
+ } else {
+ namelen = strlen(name);
+ }
+
+ ctf_dprintf("reference kind %d %s\n", kind, name != NULL ? name : "<>");
+
+ if ((ret = ctf_dwarf_refdie(cdp, die, DW_AT_type, &tdie)) != 0) {
+ if (ret != ENOENT) {
+ ctf_free(name, namelen);
+ return (ret);
+ }
+ if ((id = ctf_dwarf_void(cdp)) == CTF_ERR) {
+ ctf_free(name, namelen);
+ return (ctf_errno(cdp->cd_ctfp));
+ }
+ } else {
+ if ((ret = ctf_dwarf_convert_type(cdp, tdie, &id,
+ CTF_ADD_NONROOT)) != 0) {
+ ctf_free(name, namelen);
+ return (ret);
+ }
+ }
+
+ if ((*idp = ctf_add_reftype(cdp->cd_ctfp, isroot, name, id, kind)) ==
+ CTF_ERR) {
+ ctf_free(name, namelen);
+ return (ctf_errno(cdp->cd_ctfp));
+ }
+
+ ctf_free(name, namelen);
+ return (ctf_dwmap_add(cdp, *idp, die, B_FALSE));
+}
+
+static int
+ctf_dwarf_create_enum(ctf_die_t *cdp, Dwarf_Die die, ctf_id_t *idp, int isroot)
+{
+ int ret;
+ ctf_id_t id;
+ Dwarf_Die child;
+ char *name;
+
+ if ((ret = ctf_dwarf_string(cdp, die, DW_AT_name, &name)) != 0 &&
+ ret != ENOENT)
+ return (ret);
+ if (ret == ENOENT)
+ name = NULL;
+ id = ctf_add_enum(cdp->cd_ctfp, isroot, name);
+ ctf_dprintf("added enum %s (%d)\n", name, id);
+ if (name != NULL)
+ ctf_free(name, strlen(name) + 1);
+ if (id == CTF_ERR)
+ return (ctf_errno(cdp->cd_ctfp));
+ *idp = id;
+ if ((ret = ctf_dwmap_add(cdp, id, die, B_FALSE)) != 0)
+ return (ret);
+
+
+ if ((ret = ctf_dwarf_child(cdp, die, &child)) != 0) {
+ if (ret == ENOENT)
+ ret = 0;
+ return (ret);
+ }
+
+ while (child != NULL) {
+ Dwarf_Half tag;
+ Dwarf_Signed sval;
+ Dwarf_Unsigned uval;
+ Dwarf_Die arg = child;
+ int eval;
+
+ if ((ret = ctf_dwarf_sib(cdp, arg, &child)) != 0)
+ return (ret);
+
+ if ((ret = ctf_dwarf_tag(cdp, arg, &tag)) != 0)
+ return (ret);
+
+ if (tag != DW_TAG_enumerator) {
+ if ((ret = ctf_dwarf_convert_type(cdp, arg, NULL,
+ CTF_ADD_NONROOT)) != 0)
+ return (ret);
+ continue;
+ }
+
+ if ((ret = ctf_dwarf_signed(cdp, arg, DW_AT_const_value,
+ &sval)) == 0) {
+ eval = sval;
+ } else if (ret != ENOENT) {
+ return (ret);
+ } else if ((ret = ctf_dwarf_unsigned(cdp, arg,
+ DW_AT_const_value, &uval)) == 0) {
+ eval = (int)uval;
+ } else {
+ (void) snprintf(cdp->cd_errbuf, cdp->cd_errlen,
+ "encountered enumration without constant value\n");
+ return (ECTF_CONVBKERR);
+ }
+
+ /*
+ * DWARF v4 section 5.7 tells us we'll always have names.
+ */
+ if ((ret = ctf_dwarf_string(cdp, arg, DW_AT_name,
+ &name)) != 0)
+ return (ret);
+
+ ret = ctf_add_enumerator(cdp->cd_ctfp, id, name, eval);
+ if (ret == CTF_ERR) {
+ (void) snprintf(cdp->cd_errbuf, cdp->cd_errlen,
+ "failed to add enumarator %s (%d) to %d\n",
+ name, eval, id);
+ ctf_free(name, strlen(name) + 1);
+ return (ctf_errno(cdp->cd_ctfp));
+ }
+ ctf_free(name, strlen(name) + 1);
+ }
+
+ return (0);
+}
+
+/*
+ * For a function pointer, walk over and process all of its children, unless we
+ * encounter one that's just a declaration. In which case, we error on it.
+ */
+static int
+ctf_dwarf_create_fptr(ctf_die_t *cdp, Dwarf_Die die, ctf_id_t *idp, int isroot)
+{
+ int ret;
+ Dwarf_Bool b;
+ ctf_funcinfo_t fi;
+ Dwarf_Die retdie;
+ ctf_id_t *argv = NULL;
+
+ bzero(&fi, sizeof (ctf_funcinfo_t));
+
+ if ((ret = ctf_dwarf_boolean(cdp, die, DW_AT_declaration, &b)) != 0) {
+ if (ret != ENOENT)
+ return (ret);
+ } else {
+ if (b != 0)
+ return (EPROTOTYPE);
+ }
+
+ /*
+ * Return type is in DW_AT_type, if none, it returns void.
+ */
+ if ((ret = ctf_dwarf_refdie(cdp, die, DW_AT_type, &retdie)) != 0) {
+ if (ret != ENOENT)
+ return (ret);
+ if ((fi.ctc_return = ctf_dwarf_void(cdp)) == CTF_ERR)
+ return (ctf_errno(cdp->cd_ctfp));
+ } else {
+ if ((ret = ctf_dwarf_convert_type(cdp, retdie, &fi.ctc_return,
+ CTF_ADD_NONROOT)) != 0)
+ return (ret);
+ }
+
+ if ((ret = ctf_dwarf_function_count(cdp, die, &fi, B_TRUE)) != 0) {
+ return (ret);
+ }
+
+ if (fi.ctc_argc != 0) {
+ argv = ctf_alloc(sizeof (ctf_id_t) * fi.ctc_argc);
+ if (argv == NULL)
+ return (ENOMEM);
+
+ if ((ret = ctf_dwarf_convert_fargs(cdp, die, &fi, argv)) != 0) {
+ ctf_free(argv, sizeof (ctf_id_t) * fi.ctc_argc);
+ return (ret);
+ }
+ }
+
+ if ((*idp = ctf_add_funcptr(cdp->cd_ctfp, isroot, &fi, argv)) ==
+ CTF_ERR) {
+ ctf_free(argv, sizeof (ctf_id_t) * fi.ctc_argc);
+ return (ctf_errno(cdp->cd_ctfp));
+ }
+
+ ctf_free(argv, sizeof (ctf_id_t) * fi.ctc_argc);
+ return (ctf_dwmap_add(cdp, *idp, die, B_FALSE));
+}
+
+static int
+ctf_dwarf_convert_type(ctf_die_t *cdp, Dwarf_Die die, ctf_id_t *idp,
+ int isroot)
+{
+ int ret;
+ Dwarf_Off offset;
+ Dwarf_Half tag;
+ ctf_dwmap_t lookup, *map;
+ ctf_id_t id;
+
+ if (idp == NULL)
+ idp = &id;
+
+ if ((ret = ctf_dwarf_offset(cdp, die, &offset)) != 0)
+ return (ret);
+
+ if (offset > cdp->cd_maxoff) {
+ (void) snprintf(cdp->cd_errbuf, cdp->cd_errlen,
+ "die offset %llu beyond maximum for header %llu\n",
+ offset, cdp->cd_maxoff);
+ return (ECTF_CONVBKERR);
+ }
+
+ /*
+ * If we've already added an entry for this offset, then we're done.
+ */
+ lookup.cdm_off = offset;
+ if ((map = avl_find(&cdp->cd_map, &lookup, NULL)) != NULL) {
+ *idp = map->cdm_id;
+ return (0);
+ }
+
+ if ((ret = ctf_dwarf_tag(cdp, die, &tag)) != 0)
+ return (ret);
+
+ ret = ENOTSUP;
+ switch (tag) {
+ case DW_TAG_base_type:
+ ctf_dprintf("base\n");
+ ret = ctf_dwarf_create_base(cdp, die, idp, isroot, offset);
+ break;
+ case DW_TAG_array_type:
+ ctf_dprintf("array\n");
+ ret = ctf_dwarf_create_array(cdp, die, idp, isroot);
+ break;
+ case DW_TAG_enumeration_type:
+ ctf_dprintf("enum\n");
+ ret = ctf_dwarf_create_enum(cdp, die, idp, isroot);
+ break;
+ case DW_TAG_pointer_type:
+ ctf_dprintf("pointer\n");
+ ret = ctf_dwarf_create_reference(cdp, die, idp, CTF_K_POINTER,
+ isroot);
+ break;
+ case DW_TAG_structure_type:
+ ctf_dprintf("struct\n");
+ ret = ctf_dwarf_create_sou(cdp, die, idp, CTF_K_STRUCT,
+ isroot);
+ break;
+ case DW_TAG_subroutine_type:
+ ctf_dprintf("fptr\n");
+ ret = ctf_dwarf_create_fptr(cdp, die, idp, isroot);
+ break;
+ case DW_TAG_typedef:
+ ctf_dprintf("typedef\n");
+ ret = ctf_dwarf_create_reference(cdp, die, idp, CTF_K_TYPEDEF,
+ isroot);
+ break;
+ case DW_TAG_union_type:
+ ctf_dprintf("union\n");
+ ret = ctf_dwarf_create_sou(cdp, die, idp, CTF_K_UNION,
+ isroot);
+ break;
+ case DW_TAG_const_type:
+ ctf_dprintf("const\n");
+ ret = ctf_dwarf_create_reference(cdp, die, idp, CTF_K_CONST,
+ isroot);
+ break;
+ case DW_TAG_volatile_type:
+ ctf_dprintf("volatile\n");
+ ret = ctf_dwarf_create_reference(cdp, die, idp, CTF_K_VOLATILE,
+ isroot);
+ break;
+ case DW_TAG_restrict_type:
+ ctf_dprintf("restrict\n");
+ ret = ctf_dwarf_create_reference(cdp, die, idp, CTF_K_RESTRICT,
+ isroot);
+ break;
+ default:
+ ctf_dprintf("ignoring tag type %x\n", tag);
+ ret = 0;
+ break;
+ }
+ ctf_dprintf("ctf_dwarf_convert_type tag specific handler returned %d\n",
+ ret);
+
+ return (ret);
+}
+
+static int
+ctf_dwarf_walk_lexical(ctf_die_t *cdp, Dwarf_Die die)
+{
+ int ret;
+ Dwarf_Die child;
+
+ if ((ret = ctf_dwarf_child(cdp, die, &child)) != 0)
+ return (ret);
+
+ if (child == NULL)
+ return (0);
+
+ return (ctf_dwarf_convert_die(cdp, die));
+}
+
+static int
+ctf_dwarf_function_count(ctf_die_t *cdp, Dwarf_Die die, ctf_funcinfo_t *fip,
+ boolean_t fptr)
+{
+ int ret;
+ Dwarf_Die child, sib, arg;
+
+ if ((ret = ctf_dwarf_child(cdp, die, &child)) != 0)
+ return (ret);
+
+ arg = child;
+ while (arg != NULL) {
+ Dwarf_Half tag;
+
+ if ((ret = ctf_dwarf_tag(cdp, arg, &tag)) != 0)
+ return (ret);
+
+ /*
+ * We have to check for a varargs type decleration. This will
+ * happen in one of two ways. If we have a function pointer
+ * type, then it'll be done with a tag of type
+ * DW_TAG_unspecified_parameters. However, it only means we have
+ * a variable number of arguments, if we have more than one
+ * argument found so far. Otherwise, when we have a function
+ * type, it instead uses a formal parameter whose name is '...'
+ * to indicate a variable arguments member.
+ *
+ * Also, if we have a function pointer, then we have to expect
+ * that we might not get a name at all.
+ */
+ if (tag == DW_TAG_formal_parameter && fptr == B_FALSE) {
+ char *name;
+ if ((ret = ctf_dwarf_string(cdp, die, DW_AT_name,
+ &name)) != 0)
+ return (ret);
+ if (strcmp(name, DWARF_VARARGS_NAME) == 0)
+ fip->ctc_flags |= CTF_FUNC_VARARG;
+ else
+ fip->ctc_argc++;
+ ctf_free(name, strlen(name) + 1);
+ } else if (tag == DW_TAG_formal_parameter) {
+ fip->ctc_argc++;
+ } else if (tag == DW_TAG_unspecified_parameters &&
+ fip->ctc_argc > 0) {
+ fip->ctc_flags |= CTF_FUNC_VARARG;
+ }
+ if ((ret = ctf_dwarf_sib(cdp, arg, &sib)) != 0)
+ return (ret);
+ arg = sib;
+ }
+
+ return (0);
+}
+
+static int
+ctf_dwarf_convert_fargs(ctf_die_t *cdp, Dwarf_Die die, ctf_funcinfo_t *fip,
+ ctf_id_t *argv)
+{
+ int ret;
+ int i = 0;
+ Dwarf_Die child, sib, arg;
+
+ if ((ret = ctf_dwarf_child(cdp, die, &child)) != 0)
+ return (ret);
+
+ arg = child;
+ while (arg != NULL) {
+ Dwarf_Half tag;
+
+ if ((ret = ctf_dwarf_tag(cdp, arg, &tag)) != 0)
+ return (ret);
+ if (tag == DW_TAG_formal_parameter) {
+ Dwarf_Die tdie;
+
+ if ((ret = ctf_dwarf_refdie(cdp, arg, DW_AT_type,
+ &tdie)) != 0)
+ return (ret);
+
+ if ((ret = ctf_dwarf_convert_type(cdp, tdie, &argv[i],
+ CTF_ADD_ROOT)) != 0)
+ return (ret);
+ i++;
+
+ /*
+ * Once we hit argc entries, we're done. This ensures we
+ * don't accidentally hit a varargs which should be the
+ * least entry.
+ */
+ if (i == fip->ctc_argc)
+ break;
+ }
+
+ if ((ret = ctf_dwarf_sib(cdp, arg, &sib)) != 0)
+ return (ret);
+ arg = sib;
+ }
+
+ return (0);
+}
+
+static int
+ctf_dwarf_convert_function(ctf_die_t *cdp, Dwarf_Die die)
+{
+ int ret;
+ char *name;
+ ctf_dwfunc_t *cdf;
+ Dwarf_Die tdie;
+
+ /*
+ * Functions that don't have a name are generally functions that have
+ * been inlined and thus most information about them has been lost. If
+ * we can't get a name, then instead of returning ENOENT, we silently
+ * swallow the error.
+ */
+ if ((ret = ctf_dwarf_string(cdp, die, DW_AT_name, &name)) != 0) {
+ if (ret == ENOENT)
+ return (0);
+ return (ret);
+ }
+
+ ctf_dprintf("beginning work on function %s\n", name);
+ if ((cdf = ctf_alloc(sizeof (ctf_dwfunc_t))) == NULL) {
+ ctf_free(name, strlen(name) + 1);
+ return (ENOMEM);
+ }
+ bzero(cdf, sizeof (ctf_dwfunc_t));
+ cdf->cdf_name = name;
+
+ if ((ret = ctf_dwarf_refdie(cdp, die, DW_AT_type, &tdie)) == 0) {
+ if ((ret = ctf_dwarf_convert_type(cdp, tdie,
+ &(cdf->cdf_fip.ctc_return), CTF_ADD_ROOT)) != 0) {
+ ctf_free(name, strlen(name) + 1);
+ ctf_free(cdf, sizeof (ctf_dwfunc_t));
+ return (ret);
+ }
+ } else if (ret != ENOENT) {
+ ctf_free(name, strlen(name) + 1);
+ ctf_free(cdf, sizeof (ctf_dwfunc_t));
+ return (ret);
+ } else {
+ if ((cdf->cdf_fip.ctc_return = ctf_dwarf_void(cdp)) ==
+ CTF_ERR) {
+ ctf_free(name, strlen(name) + 1);
+ ctf_free(cdf, sizeof (ctf_dwfunc_t));
+ return (ctf_errno(cdp->cd_ctfp));
+ }
+ }
+
+ /*
+ * A function has a number of children, some of which may not be ones we
+ * care about. Children that we care about have a type of
+ * DW_TAG_formal_parameter. We're going to do two passes, the first to
+ * count the arguments, the second to process them. Afterwards, we
+ * should be good to go ahead and add this function.
+ *
+ * Note, we already got the return type by going in and grabbing it out
+ * of the DW_AT_type.
+ */
+ if ((ret = ctf_dwarf_function_count(cdp, die, &cdf->cdf_fip,
+ B_FALSE)) != 0) {
+ ctf_free(name, strlen(name) + 1);
+ ctf_free(cdf, sizeof (ctf_dwfunc_t));
+ return (ret);
+ }
+
+ ctf_dprintf("beginning to convert function arguments %s\n", name);
+ if (cdf->cdf_fip.ctc_argc != 0) {
+ uint_t argc = cdf->cdf_fip.ctc_argc;
+ cdf->cdf_argv = ctf_alloc(sizeof (ctf_id_t) * argc);
+ if (cdf->cdf_argv == NULL) {
+ ctf_free(name, strlen(name) + 1);
+ ctf_free(cdf, sizeof (ctf_dwfunc_t));
+ return (ENOMEM);
+ }
+ if ((ret = ctf_dwarf_convert_fargs(cdp, die,
+ &cdf->cdf_fip, cdf->cdf_argv)) != 0) {
+ ctf_free(cdf->cdf_argv, sizeof (ctf_id_t) * argc);
+ ctf_free(name, strlen(name) + 1);
+ ctf_free(cdf, sizeof (ctf_dwfunc_t));
+ return (ret);
+ }
+ } else {
+ cdf->cdf_argv = NULL;
+ }
+
+ if ((ret = ctf_dwarf_isglobal(cdp, die, &cdf->cdf_global)) != 0) {
+ ctf_free(cdf->cdf_argv, sizeof (ctf_id_t) *
+ cdf->cdf_fip.ctc_argc);
+ ctf_free(name, strlen(name) + 1);
+ ctf_free(cdf, sizeof (ctf_dwfunc_t));
+ return (ret);
+ }
+
+ ctf_list_append(&cdp->cd_funcs, cdf);
+ return (ret);
+}
+
+/*
+ * Convert variables, but only if they're not prototypes and have names.
+ */
+static int
+ctf_dwarf_convert_variable(ctf_die_t *cdp, Dwarf_Die die)
+{
+ int ret;
+ char *name;
+ Dwarf_Bool b;
+ Dwarf_Die tdie;
+ ctf_id_t id;
+ ctf_dwvar_t *cdv;
+
+ if ((ret = ctf_dwarf_boolean(cdp, die, DW_AT_declaration, &b)) != 0) {
+ if (ret != ENOENT)
+ return (ret);
+ } else if (b != 0) {
+ return (0);
+ }
+
+ if ((ret = ctf_dwarf_string(cdp, die, DW_AT_name, &name)) != 0 &&
+ ret != ENOENT)
+ return (ret);
+ if (ret == ENOENT)
+ return (0);
+
+ if ((ret = ctf_dwarf_refdie(cdp, die, DW_AT_type, &tdie)) != 0) {
+ ctf_free(name, strlen(name) + 1);
+ return (ret);
+ }
+
+ if ((ret = ctf_dwarf_convert_type(cdp, tdie, &id,
+ CTF_ADD_ROOT)) != 0)
+ return (ret);
+
+ if ((cdv = ctf_alloc(sizeof (ctf_dwvar_t))) == NULL) {
+ ctf_free(name, strlen(name) + 1);
+ return (ENOMEM);
+ }
+
+ cdv->cdv_name = name;
+ cdv->cdv_type = id;
+
+ if ((ret = ctf_dwarf_isglobal(cdp, die, &cdv->cdv_global)) != 0) {
+ ctf_free(cdv, sizeof (ctf_dwvar_t));
+ ctf_free(name, strlen(name) + 1);
+ return (ret);
+ }
+
+ ctf_list_append(&cdp->cd_vars, cdv);
+ return (0);
+}
+
+/*
+ * Walk through our set of top-level types and process them.
+ */
+static int
+ctf_dwarf_walk_toplevel(ctf_die_t *cdp, Dwarf_Die die)
+{
+ int ret;
+ Dwarf_Off offset;
+ Dwarf_Half tag;
+
+ if ((ret = ctf_dwarf_offset(cdp, die, &offset)) != 0)
+ return (ret);
+
+ if (offset > cdp->cd_maxoff) {
+ (void) snprintf(cdp->cd_errbuf, cdp->cd_errlen,
+ "die offset %llu beyond maximum for header %llu\n",
+ offset, cdp->cd_maxoff);
+ return (ECTF_CONVBKERR);
+ }
+
+ if ((ret = ctf_dwarf_tag(cdp, die, &tag)) != 0)
+ return (ret);
+
+ ret = 0;
+ switch (tag) {
+ case DW_TAG_subprogram:
+ ctf_dprintf("top level func\n");
+ ret = ctf_dwarf_convert_function(cdp, die);
+ break;
+ case DW_TAG_variable:
+ ctf_dprintf("top level var\n");
+ ret = ctf_dwarf_convert_variable(cdp, die);
+ break;
+ case DW_TAG_lexical_block:
+ ctf_dprintf("top level block\n");
+ ret = ctf_dwarf_walk_lexical(cdp, die);
+ break;
+ case DW_TAG_enumeration_type:
+ case DW_TAG_structure_type:
+ case DW_TAG_typedef:
+ case DW_TAG_union_type:
+ ctf_dprintf("top level type\n");
+ ret = ctf_dwarf_convert_type(cdp, die, NULL, B_TRUE);
+ break;
+ default:
+ break;
+ }
+
+ return (ret);
+}
+
+
+/*
+ * We're given a node. At this node we need to convert it and then proceed to
+ * convert any siblings that are associaed with this die.
+ */
+static int
+ctf_dwarf_convert_die(ctf_die_t *cdp, Dwarf_Die die)
+{
+ while (die != NULL) {
+ int ret;
+ Dwarf_Die sib;
+
+ if ((ret = ctf_dwarf_walk_toplevel(cdp, die)) != 0)
+ return (ret);
+
+ if ((ret = ctf_dwarf_sib(cdp, die, &sib)) != 0)
+ return (ret);
+ die = sib;
+ }
+ return (0);
+}
+
+static int
+ctf_dwarf_fixup_die(ctf_die_t *cdp, boolean_t addpass)
+{
+ ctf_dwmap_t *map;
+
+ for (map = avl_first(&cdp->cd_map); map != NULL;
+ map = AVL_NEXT(&cdp->cd_map, map)) {
+ int ret;
+ if (map->cdm_fix == B_FALSE)
+ continue;
+ if ((ret = ctf_dwarf_fixup_sou(cdp, map->cdm_die, map->cdm_id,
+ addpass)) != 0)
+ return (ret);
+ }
+
+ return (0);
+}
+
+static ctf_dwfunc_t *
+ctf_dwarf_match_func(ctf_die_t *cdp, const char *file, const char *name,
+ int bind)
+{
+ ctf_dwfunc_t *cdf;
+
+ if (bind == STB_WEAK)
+ return (NULL);
+
+ /* Nothing we can do if we can't find a name to compare it to. */
+ if (bind == STB_LOCAL && (file == NULL || cdp->cd_name == NULL))
+ return (NULL);
+
+ for (cdf = ctf_list_next(&cdp->cd_funcs); cdf != NULL;
+ cdf = ctf_list_next(cdf)) {
+ if (bind == STB_GLOBAL && cdf->cdf_global == B_FALSE)
+ continue;
+ if (bind == STB_LOCAL && cdf->cdf_global == B_TRUE)
+ continue;
+ if (strcmp(name, cdf->cdf_name) != 0)
+ continue;
+ if (bind == STB_LOCAL && strcmp(file, cdp->cd_name) != 0)
+ continue;
+ return (cdf);
+ }
+
+ return (NULL);
+}
+static ctf_dwvar_t *
+ctf_dwarf_match_var(ctf_die_t *cdp, const char *file, const char *name,
+ int bind)
+{
+ ctf_dwvar_t *cdv;
+
+ /* Nothing we can do if we can't find a name to compare it to. */
+ if (bind == STB_LOCAL && (file == NULL || cdp->cd_name == NULL))
+ return (NULL);
+ ctf_dprintf("Still considering %s\n", name);
+
+ for (cdv = ctf_list_next(&cdp->cd_vars); cdv != NULL;
+ cdv = ctf_list_next(cdv)) {
+ if (bind == STB_GLOBAL && cdv->cdv_global == B_FALSE)
+ continue;
+ if (bind == STB_LOCAL && cdv->cdv_global == B_TRUE)
+ continue;
+ if (strcmp(name, cdv->cdv_name) != 0)
+ continue;
+ if (bind == STB_LOCAL && strcmp(file, cdp->cd_name) != 0)
+ continue;
+ return (cdv);
+ }
+
+ return (NULL);
+}
+
+static int
+ctf_dwarf_symtab_iter(ctf_die_t *cdp, ctf_dwarf_symtab_f *func, void *arg)
+{
+ int ret;
+ ulong_t i;
+ ctf_file_t *fp = cdp->cd_ctfp;
+ const char *file = NULL;
+ uintptr_t symbase = (uintptr_t)fp->ctf_symtab.cts_data;
+ uintptr_t strbase = (uintptr_t)fp->ctf_strtab.cts_data;
+
+ for (i = 0; i < fp->ctf_nsyms; i++) {
+ const char *name;
+ int type;
+ GElf_Sym gsym;
+ const GElf_Sym *gsymp;
+
+ if (fp->ctf_symtab.cts_entsize == sizeof (Elf32_Sym)) {
+ const Elf32_Sym *symp = (Elf32_Sym *)symbase + i;
+ type = ELF32_ST_TYPE(symp->st_info);
+ if (type == STT_FILE) {
+ file = (char *)(strbase + symp->st_name);
+ continue;
+ }
+ if (type != STT_OBJECT && type != STT_FUNC)
+ continue;
+ if (ctf_sym_valid(strbase, type, symp->st_shndx,
+ symp->st_value, symp->st_name) == B_FALSE)
+ continue;
+ name = (char *)(strbase + symp->st_name);
+ gsym.st_name = symp->st_name;
+ gsym.st_value = symp->st_value;
+ gsym.st_size = symp->st_size;
+ gsym.st_info = symp->st_info;
+ gsym.st_other = symp->st_other;
+ gsym.st_shndx = symp->st_shndx;
+ gsymp = &gsym;
+ } else {
+ const Elf64_Sym *symp = (Elf64_Sym *)symbase + i;
+ type = ELF64_ST_TYPE(symp->st_info);
+ if (type == STT_FILE) {
+ file = (char *)(strbase + symp->st_name);
+ continue;
+ }
+ if (type != STT_OBJECT && type != STT_FUNC)
+ continue;
+ if (ctf_sym_valid(strbase, type, symp->st_shndx,
+ symp->st_value, symp->st_name) == B_FALSE)
+ continue;
+ name = (char *)(strbase + symp->st_name);
+ gsymp = symp;
+ }
+
+ ret = func(cdp, gsymp, i, file, name, arg);
+ if (ret != 0)
+ return (ret);
+ }
+
+ return (0);
+}
+
+static int
+ctf_dwarf_conv_funcvars_cb(ctf_die_t *cdp, const GElf_Sym *symp, ulong_t idx,
+ const char *file, const char *name, void *arg)
+{
+ int ret, bind, type;
+
+ bind = GELF_ST_BIND(symp->st_info);
+ type = GELF_ST_TYPE(symp->st_info);
+
+ /*
+ * Come back to weak symbols in another pass
+ */
+ if (bind == STB_WEAK)
+ return (0);
+
+ if (type == STT_OBJECT) {
+ ctf_dwvar_t *cdv = ctf_dwarf_match_var(cdp, file, name,
+ bind);
+ ctf_dprintf("match for %s (%d): %p\n", name, idx, cdv);
+ if (cdv == NULL)
+ return (0);
+ ret = ctf_add_object(cdp->cd_ctfp, idx, cdv->cdv_type);
+ ctf_dprintf("added object %s\n", name);
+ } else {
+ ctf_dwfunc_t *cdf = ctf_dwarf_match_func(cdp, file, name,
+ bind);
+ if (cdf == NULL)
+ return (0);
+ ret = ctf_add_function(cdp->cd_ctfp, idx, &cdf->cdf_fip,
+ cdf->cdf_argv);
+ }
+
+ if (ret == CTF_ERR) {
+ return (ctf_errno(cdp->cd_ctfp));
+ }
+
+ return (0);
+}
+
+static int
+ctf_dwarf_conv_funcvars(ctf_die_t *cdp)
+{
+ return (ctf_dwarf_symtab_iter(cdp, ctf_dwarf_conv_funcvars_cb, NULL));
+}
+
+/*
+ * Note, this comment comes from the original version of the CTF tools.
+ *
+ * If we have a weak symbol, attempt to find the strong symbol it will
+ * resolve to. Note: the code where this actually happens is in
+ * sym_process() in cmd/sgs/libld/common/syms.c
+ *
+ * Finding the matching symbol is unfortunately not trivial. For a
+ * symbol to be a candidate, it must:
+ *
+ * - have the same type (function, object)
+ * - have the same value (address)
+ * - have the same size
+ * - not be another weak symbol
+ * - belong to the same section (checked via section index)
+ *
+ * If such a candidate is global, then we assume we've found it. The
+ * linker generates the symbol table such that the curfile might be
+ * incorrect; this is OK for global symbols, since find_iidesc() doesn't
+ * need to check for the source file for the symbol.
+ *
+ * We might have found a strong local symbol, where the curfile is
+ * accurate and matches that of the weak symbol. We assume this is a
+ * reasonable match.
+ *
+ * If we've got a local symbol with a non-matching curfile, there are
+ * two possibilities. Either this is a completely different symbol, or
+ * it's a once-global symbol that was scoped to local via a mapfile. In
+ * the latter case, curfile is likely inaccurate since the linker does
+ * not preserve the needed curfile in the order of the symbol table (see
+ * the comments about locally scoped symbols in libld's update_osym()).
+ * As we can't tell this case from the former one, we use this symbol
+ * iff no other matching symbol is found.
+ *
+ * What we really need here is a SUNW section containing weak<->strong
+ * mappings that we can consume.
+ */
+typedef struct ctf_dwarf_weak_arg {
+ const GElf_Sym *cweak_symp;
+ const char *cweak_file;
+ boolean_t cweak_candidate;
+ ulong_t cweak_idx;
+} ctf_dwarf_weak_arg_t;
+
+static int
+ctf_dwarf_conv_check_weak(ctf_die_t *cdp, const GElf_Sym *symp,
+ ulong_t idx, const char *file, const char *name, void *arg)
+{
+ ctf_dwarf_weak_arg_t *cweak = arg;
+ const GElf_Sym *wsymp = cweak->cweak_symp;
+
+ ctf_dprintf("comparing weak to %s\n", name);
+
+ if (GELF_ST_BIND(symp->st_info) == STB_WEAK) {
+ return (0);
+ }
+
+ if (GELF_ST_TYPE(wsymp->st_info) != GELF_ST_TYPE(symp->st_info)) {
+ return (0);
+ }
+
+ if (wsymp->st_value != symp->st_value) {
+ return (0);
+ }
+
+ if (wsymp->st_size != symp->st_size) {
+ return (0);
+ }
+
+ if (wsymp->st_shndx != symp->st_shndx) {
+ return (0);
+ }
+
+ /*
+ * Check if it's a weak candidate.
+ */
+ if (GELF_ST_BIND(symp->st_info) == STB_LOCAL &&
+ (file == NULL || cweak->cweak_file == NULL ||
+ strcmp(file, cweak->cweak_file) != 0)) {
+ cweak->cweak_candidate = B_TRUE;
+ cweak->cweak_idx = idx;
+ return (0);
+ }
+
+ /*
+ * Found a match, break.
+ */
+ cweak->cweak_idx = idx;
+ return (1);
+}
+
+static int
+ctf_dwarf_duplicate_sym(ctf_die_t *cdp, ulong_t idx, ulong_t matchidx)
+{
+ ctf_id_t id = ctf_lookup_by_symbol(cdp->cd_ctfp, matchidx);
+
+ /*
+ * If we matched something that for some reason didn't have type data,
+ * we don't consider that a fatal error and silently swallow it.
+ */
+ if (id == CTF_ERR) {
+ if (ctf_errno(cdp->cd_ctfp) == ECTF_NOTYPEDAT)
+ return (0);
+ else
+ return (ctf_errno(cdp->cd_ctfp));
+ }
+
+ if (ctf_add_object(cdp->cd_ctfp, idx, id) == CTF_ERR)
+ return (ctf_errno(cdp->cd_ctfp));
+
+ return (0);
+}
+
+static int
+ctf_dwarf_duplicate_func(ctf_die_t *cdp, ulong_t idx, ulong_t matchidx)
+{
+ int ret;
+ ctf_funcinfo_t fip;
+ ctf_id_t *args = NULL;
+
+ if (ctf_func_info(cdp->cd_ctfp, matchidx, &fip) == CTF_ERR) {
+ if (ctf_errno(cdp->cd_ctfp) == ECTF_NOFUNCDAT)
+ return (0);
+ else
+ return (ctf_errno(cdp->cd_ctfp));
+ }
+
+ if (fip.ctc_argc != 0) {
+ args = ctf_alloc(sizeof (ctf_id_t) * fip.ctc_argc);
+ if (args == NULL)
+ return (ENOMEM);
+
+ if (ctf_func_args(cdp->cd_ctfp, matchidx, fip.ctc_argc, args) ==
+ CTF_ERR) {
+ ctf_free(args, sizeof (ctf_id_t) * fip.ctc_argc);
+ return (ctf_errno(cdp->cd_ctfp));
+ }
+ }
+
+ ret = ctf_add_function(cdp->cd_ctfp, idx, &fip, args);
+ if (args != NULL)
+ ctf_free(args, sizeof (ctf_id_t) * fip.ctc_argc);
+ if (ret == CTF_ERR)
+ return (ctf_errno(cdp->cd_ctfp));
+
+ return (0);
+}
+
+static int
+ctf_dwarf_conv_weaks_cb(ctf_die_t *cdp, const GElf_Sym *symp,
+ ulong_t idx, const char *file, const char *name, void *arg)
+{
+ int ret, type;
+ ctf_dwarf_weak_arg_t cweak;
+
+ /*
+ * We only care about weak symbols.
+ */
+ if (GELF_ST_BIND(symp->st_info) != STB_WEAK)
+ return (0);
+
+ type = GELF_ST_TYPE(symp->st_info);
+ ASSERT(type == STT_OBJECT || type == STT_FUNC);
+
+ /*
+ * For each weak symbol we encounter, we need to do a second iteration
+ * to try and find a match. We should probably think about other
+ * techniques to try and save us time in the future.
+ */
+ cweak.cweak_symp = symp;
+ cweak.cweak_file = file;
+ cweak.cweak_candidate = B_FALSE;
+ cweak.cweak_idx = 0;
+
+ ctf_dprintf("Trying to find weak equiv for %s\n", name);
+
+ ret = ctf_dwarf_symtab_iter(cdp, ctf_dwarf_conv_check_weak, &cweak);
+ VERIFY(ret == 0 || ret == 1);
+
+ /*
+ * Nothing was ever found, we're not going to add anything for this
+ * entry.
+ */
+ if (ret == 0 && cweak.cweak_candidate == B_FALSE) {
+ ctf_dprintf("found no weak match for %s\n", name);
+ return (0);
+ }
+
+ /*
+ * Now, finally go and add the type based on the match.
+ */
+ if (type == STT_OBJECT) {
+ ret = ctf_dwarf_duplicate_sym(cdp, idx, cweak.cweak_idx);
+ } else {
+ ret = ctf_dwarf_duplicate_func(cdp, idx, cweak.cweak_idx);
+ }
+
+ return (ret);
+}
+
+static int
+ctf_dwarf_conv_weaks(ctf_die_t *cdp)
+{
+ return (ctf_dwarf_symtab_iter(cdp, ctf_dwarf_conv_weaks_cb, NULL));
+}
+
+/* ARGSUSED */
+static int
+ctf_dwarf_convert_one(void *arg, void *unused)
+{
+ int ret;
+ ctf_file_t *dedup;
+ ctf_die_t *cdp = arg;
+
+ ctf_dprintf("converting die: %s\n", cdp->cd_name);
+ ctf_dprintf("max offset: %x\n", cdp->cd_maxoff);
+ VERIFY(cdp != NULL);
+
+ ret = ctf_dwarf_convert_die(cdp, cdp->cd_cu);
+ ctf_dprintf("ctf_dwarf_convert_die (%s) returned %d\n", cdp->cd_name,
+ ret);
+ if (ret != 0) {
+ return (ret);
+ }
+ if (ctf_update(cdp->cd_ctfp) != 0) {
+ return (ctf_dwarf_error(cdp, cdp->cd_ctfp, 0,
+ "failed to update output ctf container"));
+ }
+
+ ret = ctf_dwarf_fixup_die(cdp, B_FALSE);
+ ctf_dprintf("ctf_dwarf_fixup_die (%s) returned %d\n", cdp->cd_name,
+ ret);
+ if (ret != 0) {
+ return (ret);
+ }
+ if (ctf_update(cdp->cd_ctfp) != 0) {
+ return (ctf_dwarf_error(cdp, cdp->cd_ctfp, 0,
+ "failed to update output ctf container"));
+ }
+
+ ret = ctf_dwarf_fixup_die(cdp, B_TRUE);
+ ctf_dprintf("ctf_dwarf_fixup_die (%s) returned %d\n", cdp->cd_name,
+ ret);
+ if (ret != 0) {
+ return (ret);
+ }
+ if (ctf_update(cdp->cd_ctfp) != 0) {
+ return (ctf_dwarf_error(cdp, cdp->cd_ctfp, 0,
+ "failed to update output ctf container"));
+ }
+
+
+ if ((ret = ctf_dwarf_conv_funcvars(cdp)) != 0) {
+ return (ctf_dwarf_error(cdp, NULL, ret,
+ "failed to convert strong functions and variables"));
+ }
+
+ if (ctf_update(cdp->cd_ctfp) != 0) {
+ return (ctf_dwarf_error(cdp, cdp->cd_ctfp, 0,
+ "failed to update output ctf container"));
+ }
+
+ if ((ret = ctf_dwarf_conv_weaks(cdp)) != 0) {
+ return (ctf_dwarf_error(cdp, NULL, ret,
+ "failed to convert weak functions and variables"));
+ }
+
+ if (ctf_update(cdp->cd_ctfp) != 0) {
+ return (ctf_dwarf_error(cdp, cdp->cd_ctfp, 0,
+ "failed to update output ctf container"));
+ }
+
+ ctf_phase_dump(cdp->cd_ctfp, "pre-dedup");
+ ctf_dprintf("adding inputs for dedup\n");
+ if ((ret = ctf_merge_add(cdp->cd_cmh, cdp->cd_ctfp)) != 0) {
+ return (ctf_dwarf_error(cdp, NULL, ret,
+ "failed to add inputs for merge"));
+ }
+
+ ctf_dprintf("starting merge\n");
+ if ((ret = ctf_merge_dedup(cdp->cd_cmh, &dedup)) != 0) {
+ return (ctf_dwarf_error(cdp, NULL, ret,
+ "failed to deduplicate die"));
+ }
+ ctf_close(cdp->cd_ctfp);
+ cdp->cd_ctfp = dedup;
+
+ return (0);
+}
+
+/*
+ * Note, we expect that if we're returning a ctf_file_t from one of the dies,
+ * say in the single node case, it's been saved and the entry here has been set
+ * to NULL, which ctf_close happily ignores.
+ */
+static void
+ctf_dwarf_free_die(ctf_die_t *cdp)
+{
+ ctf_dwfunc_t *cdf, *ndf;
+ ctf_dwvar_t *cdv, *ndv;
+ ctf_dwbitf_t *cdb, *ndb;
+ ctf_dwmap_t *map;
+ void *cookie;
+ Dwarf_Error derr;
+
+ ctf_dprintf("Beginning to free die: %p\n", cdp);
+ cdp->cd_elf = NULL;
+ ctf_dprintf("Trying to free name: %p\n", cdp->cd_name);
+ if (cdp->cd_name != NULL)
+ ctf_free(cdp->cd_name, strlen(cdp->cd_name) + 1);
+ ctf_dprintf("Trying to free merge handle: %p\n", cdp->cd_cmh);
+ if (cdp->cd_cmh != NULL) {
+ ctf_merge_fini(cdp->cd_cmh);
+ cdp->cd_cmh = NULL;
+ }
+
+ ctf_dprintf("Trying to free functions\n");
+ for (cdf = ctf_list_next(&cdp->cd_funcs); cdf != NULL; cdf = ndf) {
+ ndf = ctf_list_next(cdf);
+ ctf_free(cdf->cdf_name, strlen(cdf->cdf_name) + 1);
+ if (cdf->cdf_fip.ctc_argc != 0) {
+ ctf_free(cdf->cdf_argv,
+ sizeof (ctf_id_t) * cdf->cdf_fip.ctc_argc);
+ }
+ ctf_free(cdf, sizeof (ctf_dwfunc_t));
+ }
+
+ ctf_dprintf("Trying to free variables\n");
+ for (cdv = ctf_list_next(&cdp->cd_vars); cdv != NULL; cdv = ndv) {
+ ndv = ctf_list_next(cdv);
+ ctf_free(cdv->cdv_name, strlen(cdv->cdv_name) + 1);
+ ctf_free(cdv, sizeof (ctf_dwvar_t));
+ }
+
+ ctf_dprintf("Trying to free bitfields\n");
+ for (cdb = ctf_list_next(&cdp->cd_bitfields); cdb != NULL; cdb = ndb) {
+ ndb = ctf_list_next(cdb);
+ ctf_free(cdb, sizeof (ctf_dwbitf_t));
+ }
+
+ /* How do we clean up die usage? */
+ ctf_dprintf("Trying to clean up dwarf_t: %p\n", cdp->cd_dwarf);
+ (void) dwarf_finish(cdp->cd_dwarf, &derr);
+ cdp->cd_dwarf = NULL;
+ ctf_close(cdp->cd_ctfp);
+
+ cookie = NULL;
+ while ((map = avl_destroy_nodes(&cdp->cd_map, &cookie)) != NULL) {
+ ctf_free(map, sizeof (ctf_dwmap_t));
+ }
+ avl_destroy(&cdp->cd_map);
+ cdp->cd_errbuf = NULL;
+}
+
+static void
+ctf_dwarf_free_dies(ctf_die_t *cdies, int ndies)
+{
+ int i;
+
+ ctf_dprintf("Beginning to free dies\n");
+ for (i = 0; i < ndies; i++) {
+ ctf_dwarf_free_die(&cdies[i]);
+ }
+
+ ctf_free(cdies, sizeof (ctf_die_t) * ndies);
+}
+
+static int
+ctf_dwarf_count_dies(Dwarf_Debug dw, Dwarf_Error *derr, int *ndies,
+ char *errbuf, size_t errlen)
+{
+ int ret;
+ Dwarf_Half vers;
+ Dwarf_Unsigned nexthdr;
+
+ while ((ret = dwarf_next_cu_header(dw, NULL, &vers, NULL, NULL,
+ &nexthdr, derr)) != DW_DLV_NO_ENTRY) {
+ if (ret != DW_DLV_OK) {
+ (void) snprintf(errbuf, errlen,
+ "file does not contain valid DWARF data: %s\n",
+ dwarf_errmsg(*derr));
+ return (ECTF_CONVBKERR);
+ }
+
+ if (vers != DWARF_VERSION_TWO) {
+ (void) snprintf(errbuf, errlen,
+ "unsupported DWARF version: %d\n", vers);
+ return (ECTF_CONVBKERR);
+ }
+ *ndies = *ndies + 1;
+ }
+
+ if (*ndies == 0) {
+ (void) snprintf(errbuf, errlen,
+ "file does not contain valid DWARF data: %s\n",
+ dwarf_errmsg(*derr));
+ return (ECTF_CONVBKERR);
+ }
+
+ return (0);
+}
+
+/*
+ * Iterate over all of the dies and create a ctf_die_t for each of them. This is
+ * used to determine if we have zero, one, or multiple dies to convert. If we
+ * have zero, that's an error. If there's only one die, that's the simple case.
+ * No merge needed and only a single Dwarf_Debug as well.
+ */
+static int
+ctf_dwarf_init_die(int fd, Elf *elf, ctf_die_t *cdp, int ndie, char *errbuf,
+ size_t errlen)
+{
+ int ret;
+ Dwarf_Unsigned hdrlen, abboff, nexthdr;
+ Dwarf_Half addrsz;
+ Dwarf_Unsigned offset = 0;
+ Dwarf_Error derr;
+
+ while ((ret = dwarf_next_cu_header(cdp->cd_dwarf, &hdrlen, NULL,
+ &abboff, &addrsz, &nexthdr, &derr)) != DW_DLV_NO_ENTRY) {
+ char *name;
+ Dwarf_Die cu, child;
+
+ /* Based on the counting above, we should be good to go */
+ VERIFY(ret == DW_DLV_OK);
+ if (ndie > 0) {
+ ndie--;
+ offset = nexthdr;
+ continue;
+ }
+
+ /*
+ * Compilers are apparently inconsistent. Some emit no DWARF for
+ * empty files and others emit empty compilation unit.
+ */
+ cdp->cd_voidtid = CTF_ERR;
+ cdp->cd_longtid = CTF_ERR;
+ cdp->cd_elf = elf;
+ cdp->cd_maxoff = nexthdr - 1;
+ cdp->cd_ctfp = ctf_fdcreate(fd, &ret);
+ if (cdp->cd_ctfp == NULL) {
+ ctf_free(cdp, sizeof (ctf_die_t));
+ return (ret);
+ }
+ avl_create(&cdp->cd_map, ctf_dwmap_comp, sizeof (ctf_dwmap_t),
+ offsetof(ctf_dwmap_t, cdm_avl));
+ cdp->cd_errbuf = errbuf;
+ cdp->cd_errlen = errlen;
+ bzero(&cdp->cd_vars, sizeof (ctf_list_t));
+ bzero(&cdp->cd_funcs, sizeof (ctf_list_t));
+ bzero(&cdp->cd_bitfields, sizeof (ctf_list_t));
+
+ if ((ret = ctf_dwarf_die_elfenc(elf, cdp, errbuf,
+ errlen)) != 0) {
+ avl_destroy(&cdp->cd_map);
+ ctf_free(cdp, sizeof (ctf_die_t));
+ return (ret);
+ }
+
+ if ((ret = ctf_dwarf_sib(cdp, NULL, &cu)) != 0) {
+ avl_destroy(&cdp->cd_map);
+ ctf_free(cdp, sizeof (ctf_die_t));
+ return (ret);
+ }
+ if (cu == NULL) {
+ (void) snprintf(errbuf, errlen,
+ "file does not contain DWARF data\n");
+ avl_destroy(&cdp->cd_map);
+ ctf_free(cdp, sizeof (ctf_die_t));
+ return (ECTF_CONVBKERR);
+ }
+
+ if ((ret = ctf_dwarf_child(cdp, cu, &child)) != 0) {
+ avl_destroy(&cdp->cd_map);
+ ctf_free(cdp, sizeof (ctf_die_t));
+ return (ret);
+ }
+ if (child == NULL) {
+ (void) snprintf(errbuf, errlen,
+ "file does not contain DWARF data\n");
+ avl_destroy(&cdp->cd_map);
+ ctf_free(cdp, sizeof (ctf_die_t));
+ return (ECTF_CONVBKERR);
+ }
+
+ cdp->cd_cuoff = offset;
+ cdp->cd_cu = child;
+
+ if ((cdp->cd_cmh = ctf_merge_init(fd, &ret)) == NULL) {
+ avl_destroy(&cdp->cd_map);
+ ctf_free(cdp, sizeof (ctf_die_t));
+ return (ret);
+ }
+
+ if (ctf_dwarf_string(cdp, cu, DW_AT_name, &name) == 0) {
+ size_t len = strlen(name) + 1;
+ char *b = basename(name);
+ cdp->cd_name = strdup(b);
+ ctf_free(name, len);
+ }
+ break;
+ }
+
+ return (0);
+}
+
+
+ctf_conv_status_t
+ctf_dwarf_convert(int fd, Elf *elf, uint_t nthrs, int *errp, ctf_file_t **fpp,
+ char *errmsg, size_t errlen)
+{
+ int err, ret, ndies, i;
+ Dwarf_Debug dw;
+ Dwarf_Error derr;
+ ctf_die_t *cdies = NULL, *cdp;
+ workq_t *wqp = NULL;
+
+ if (errp == NULL)
+ errp = &err;
+ *errp = 0;
+ *fpp = NULL;
+
+ ret = dwarf_elf_init(elf, DW_DLC_READ, NULL, NULL, &dw, &derr);
+ if (ret != DW_DLV_OK) {
+ /*
+ * The old CTF tools used to check if we expected DWARF data
+ * here. In this case, if we actually have some amount of DWARF,
+ * but no section, for now, just go ahead and create an empty
+ * CTF file.
+ */
+ if (ret == DW_DLV_NO_ENTRY ||
+ dwarf_errno(derr) == DW_DLE_DEBUG_INFO_NULL) {
+ *fpp = ctf_create(errp);
+ return (*fpp != NULL ? CTF_CONV_SUCCESS :
+ CTF_CONV_ERROR);
+ }
+ (void) snprintf(errmsg, errlen,
+ "failed to initialize DWARF: %s\n",
+ dwarf_errmsg(derr));
+ *errp = ECTF_CONVBKERR;
+ return (CTF_CONV_ERROR);
+ }
+
+ ndies = 0;
+ ret = ctf_dwarf_count_dies(dw, &derr, &ndies, errmsg, errlen);
+ if (ret != 0) {
+ *errp = ret;
+ goto out;
+ }
+
+ (void) dwarf_finish(dw, &derr);
+ cdies = ctf_alloc(sizeof (ctf_die_t) * ndies);
+ if (cdies == NULL) {
+ *errp = ENOMEM;
+ return (CTF_CONV_ERROR);
+ }
+
+ for (i = 0; i < ndies; i++) {
+ cdp = &cdies[i];
+ ret = dwarf_elf_init(elf, DW_DLC_READ, NULL, NULL,
+ &cdp->cd_dwarf, &derr);
+ if (ret != 0) {
+ ctf_free(cdies, sizeof (ctf_die_t) * ndies);
+ (void) snprintf(errmsg, errlen,
+ "failed to initialize DWARF: %s\n",
+ dwarf_errmsg(derr));
+ *errp = ECTF_CONVBKERR;
+ return (CTF_CONV_ERROR);
+ }
+
+ ret = ctf_dwarf_init_die(fd, elf, &cdies[i], i, errmsg, errlen);
+ if (ret != 0) {
+ *errp = ret;
+ goto out;
+ }
+ }
+
+ ctf_dprintf("found %d DWARF die(s)\n", ndies);
+
+ /*
+ * If we only have one die, there's no reason to use multiple threads,
+ * even if the user requested them. After all, they just gave us an
+ * upper bound.
+ */
+ if (ndies == 1)
+ nthrs = 1;
+
+ if (workq_init(&wqp, nthrs) == -1) {
+ *errp = errno;
+ goto out;
+ }
+
+ for (i = 0; i < ndies; i++) {
+ cdp = &cdies[i];
+ ctf_dprintf("adding die %s: %p, %x %x\n", cdp->cd_name,
+ cdp->cd_cu, cdp->cd_cuoff, cdp->cd_maxoff);
+ if (workq_add(wqp, cdp) == -1) {
+ *errp = errno;
+ goto out;
+ }
+ }
+
+ ret = workq_work(wqp, ctf_dwarf_convert_one, NULL, errp);
+ if (ret == WORKQ_ERROR) {
+ *errp = errno;
+ goto out;
+ } else if (ret == WORKQ_UERROR) {
+ ctf_dprintf("internal convert failed: %s\n",
+ ctf_errmsg(*errp));
+ goto out;
+ }
+
+ ctf_dprintf("Determining next phase: have %d dies\n", ndies);
+ if (ndies != 1) {
+ ctf_merge_t *cmp;
+
+ cmp = ctf_merge_init(fd, &ret);
+ if (cmp == NULL) {
+ *errp = ret;
+ goto out;
+ }
+
+ ctf_dprintf("setting threads\n");
+ if ((ret = ctf_merge_set_nthreads(cmp, nthrs)) != 0) {
+ ctf_merge_fini(cmp);
+ *errp = ret;
+ goto out;
+ }
+
+ ctf_dprintf("adding dies\n");
+ for (i = 0; i < ndies; i++) {
+ cdp = &cdies[i];
+ if ((ret = ctf_merge_add(cmp, cdp->cd_ctfp)) != 0) {
+ ctf_merge_fini(cmp);
+ *errp = ret;
+ goto out;
+ }
+ }
+
+ ctf_dprintf("performing merge\n");
+ ret = ctf_merge_merge(cmp, fpp);
+ if (ret != 0) {
+ ctf_dprintf("failed merge!\n");
+ *fpp = NULL;
+ ctf_merge_fini(cmp);
+ *errp = ret;
+ goto out;
+ }
+ ctf_merge_fini(cmp);
+ *errp = 0;
+ ctf_dprintf("successfully converted!\n");
+ } else {
+ *errp = 0;
+ *fpp = cdies->cd_ctfp;
+ cdies->cd_ctfp = NULL;
+ ctf_dprintf("successfully converted!\n");
+ }
+
+out:
+ workq_fini(wqp);
+ ctf_dwarf_free_dies(cdies, ndies);
+ return (*fpp != NULL ? CTF_CONV_SUCCESS : CTF_CONV_ERROR);
+}
diff --git a/usr/src/lib/libctf/common/ctf_elfwrite.c b/usr/src/lib/libctf/common/ctf_elfwrite.c
index c8a4d22cf7..4d7c10aeec 100644
--- a/usr/src/lib/libctf/common/ctf_elfwrite.c
+++ b/usr/src/lib/libctf/common/ctf_elfwrite.c
@@ -30,7 +30,7 @@
* Routines for writing ctf data to elf files, originally from the ctf tools.
*/
-#include <ctf_impl.h>
+#include <libctf_impl.h>
#include <libctf.h>
#include <gelf.h>
#include <sys/stat.h>
@@ -39,7 +39,6 @@
#include <errno.h>
#include <unistd.h>
#include <libelf.h>
-#include <sys/zmod.h>
static int
ctf_write_elf(ctf_file_t *fp, Elf *src, Elf *dst, int flags)
@@ -52,37 +51,54 @@ ctf_write_elf(ctf_file_t *fp, Elf *src, Elf *dst, int flags)
off_t new_offset = 0;
off_t ctfnameoff = 0;
int compress = (flags & CTF_ELFWRITE_F_COMPRESS);
- int *secxlate;
+ int *secxlate = NULL;
int srcidx, dstidx, pad, i;
int curnmoff = 0;
int changing = 0;
+ int ret;
size_t nshdr, nphdr, strndx;
+ void *strdatabuf = NULL, *symdatabuf = NULL;
+ size_t strdatasz = 0, symdatasz = 0;
void *cdata = NULL;
+ size_t elfsize, asize;
- if ((flags & ~(CTF_ELFWRITE_F_COMPRESS)) != 0)
- return (ctf_set_errno(fp, EINVAL));
-
- if (gelf_newehdr(dst, gelf_getclass(src)) == NULL)
- return (ctf_set_errno(fp, ECTF_ELF));
+ if ((flags & ~(CTF_ELFWRITE_F_COMPRESS)) != 0) {
+ ret = ctf_set_errno(fp, EINVAL);
+ goto out;
+ }
- if (gelf_getehdr(src, &sehdr) == NULL)
- return (ctf_set_errno(fp, ECTF_ELF));
+ if (gelf_newehdr(dst, gelf_getclass(src)) == NULL) {
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
+ }
+ if (gelf_getehdr(src, &sehdr) == NULL) {
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
+ }
(void) memcpy(&dehdr, &sehdr, sizeof (GElf_Ehdr));
- if (gelf_update_ehdr(dst, &dehdr) == 0)
- return (ctf_set_errno(fp, ECTF_ELF));
+ if (gelf_update_ehdr(dst, &dehdr) == 0) {
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
+ }
/*
* Use libelf to get the number of sections and the string section to
* deal with ELF files that may have a large number of sections. We just
* always use this to make our live easier.
*/
- if (elf_getphdrnum(src, &nphdr) != 0)
- return (ctf_set_errno(fp, ECTF_ELF));
- if (elf_getshdrnum(src, &nshdr) != 0)
- return (ctf_set_errno(fp, ECTF_ELF));
- if (elf_getshdrstrndx(src, &strndx) != 0)
- return (ctf_set_errno(fp, ECTF_ELF));
+ if (elf_getphdrnum(src, &nphdr) != 0) {
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
+ }
+ if (elf_getshdrnum(src, &nshdr) != 0) {
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
+ }
+ if (elf_getshdrstrndx(src, &strndx) != 0) {
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
+ }
/*
* Neither the existing debug sections nor the SUNW_ctf sections (new or
@@ -92,16 +108,22 @@ ctf_write_elf(ctf_file_t *fp, Elf *src, Elf *dst, int flags)
*/
if (nphdr != 0) {
(void) elf_flagelf(dst, ELF_C_SET, ELF_F_LAYOUT);
- if (gelf_newphdr(dst, nphdr) == NULL)
- return (ctf_set_errno(fp, ECTF_ELF));
+ if (gelf_newphdr(dst, nphdr) == NULL) {
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
+ }
for (i = 0; i < nphdr; i++) {
GElf_Phdr phdr;
- if (gelf_getphdr(src, i, &phdr) == NULL)
- return (ctf_set_errno(fp, ECTF_ELF));
- if (gelf_update_phdr(dst, i, &phdr) == 0)
- return (ctf_set_errno(fp, ECTF_ELF));
+ if (gelf_getphdr(src, i, &phdr) == NULL) {
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
+ }
+ if (gelf_update_phdr(dst, i, &phdr) == 0) {
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
+ }
}
}
@@ -112,13 +134,13 @@ ctf_write_elf(ctf_file_t *fp, Elf *src, Elf *dst, int flags)
char *sname;
if (gelf_getshdr(scn, &shdr) == NULL) {
- ctf_free(secxlate, sizeof (int) * nshdr);
- return (ctf_set_errno(fp, ECTF_ELF));
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
}
sname = elf_strptr(src, strndx, shdr.sh_name);
if (sname == NULL) {
- ctf_free(secxlate, sizeof (int) * nshdr);
- return (ctf_set_errno(fp, ECTF_ELF));
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
}
if (strcmp(sname, CTF_ELF_SCN_NAME) == 0) {
@@ -136,8 +158,8 @@ ctf_write_elf(ctf_file_t *fp, Elf *src, Elf *dst, int flags)
sscn = elf_getscn(src, srcidx);
if (gelf_getshdr(sscn, &shdr) == NULL) {
- ctf_free(secxlate, sizeof (int) * nshdr);
- return (ctf_set_errno(fp, ECTF_ELF));
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
}
if (secxlate[srcidx] == -1) {
@@ -147,8 +169,8 @@ ctf_write_elf(ctf_file_t *fp, Elf *src, Elf *dst, int flags)
dscn = elf_newscn(dst);
if (dscn == NULL) {
- ctf_free(secxlate, sizeof (int) * nshdr);
- return (ctf_set_errno(fp, ECTF_ELF));
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
}
/*
@@ -173,28 +195,28 @@ ctf_write_elf(ctf_file_t *fp, Elf *src, Elf *dst, int flags)
sname = elf_strptr(src, strndx, shdr.sh_name);
if (sname == NULL) {
- ctf_free(secxlate, sizeof (int) * nshdr);
- return (ctf_set_errno(fp, ECTF_ELF));
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
}
if ((sdata = elf_getdata(sscn, NULL)) == NULL) {
- ctf_free(secxlate, sizeof (int) * nshdr);
- return (ctf_set_errno(fp, ECTF_ELF));
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
}
if ((ddata = elf_newdata(dscn)) == NULL) {
- ctf_free(secxlate, sizeof (int) * nshdr);
- return (ctf_set_errno(fp, ECTF_ELF));
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
}
bcopy(sdata, ddata, sizeof (Elf_Data));
if (srcidx == strndx) {
char seclen = strlen(CTF_ELF_SCN_NAME);
- ddata->d_buf = ctf_alloc(ddata->d_size + shdr.sh_size +
- seclen + 1);
+ strdatasz = ddata->d_size + shdr.sh_size +
+ seclen + 1;
+ ddata->d_buf = strdatabuf = ctf_alloc(strdatasz);
if (ddata->d_buf == NULL) {
- ctf_free(secxlate,
- sizeof (int) * nshdr);
- return (ctf_set_errno(fp, ECTF_ELF));
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
}
bcopy(sdata->d_buf, ddata->d_buf, shdr.sh_size);
(void) strcpy((caddr_t)ddata->d_buf + shdr.sh_size,
@@ -212,11 +234,11 @@ ctf_write_elf(ctf_file_t *fp, Elf *src, Elf *dst, int flags)
symtab_idx = secxlate[srcidx];
- ddata->d_buf = ctf_alloc(shdr.sh_size);
+ symdatasz = shdr.sh_size;
+ ddata->d_buf = symdatabuf = ctf_alloc(symdatasz);
if (ddata->d_buf == NULL) {
- ctf_free(secxlate,
- sizeof (int) * nshdr);
- return (ctf_set_errno(fp, ECTF_ELF));
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
}
(void) bcopy(sdata->d_buf, ddata->d_buf, shdr.sh_size);
@@ -236,19 +258,17 @@ ctf_write_elf(ctf_file_t *fp, Elf *src, Elf *dst, int flags)
if (gelf_update_sym(ddata, i, &sym) ==
0) {
- ctf_free(secxlate,
- sizeof (int) *
- nshdr);
- return (ctf_set_errno(fp,
- ECTF_ELF));
+ ret = ctf_set_errno(fp,
+ ECTF_ELF);
+ goto out;
}
}
}
}
if (gelf_update_shdr(dscn, &shdr) == NULL) {
- ctf_free(secxlate, sizeof (int) * nshdr);
- return (ctf_set_errno(fp, ECTF_ELF));
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
}
new_offset = (off_t)shdr.sh_offset;
@@ -257,18 +277,18 @@ ctf_write_elf(ctf_file_t *fp, Elf *src, Elf *dst, int flags)
}
if (symtab_idx == -1) {
- ctf_free(secxlate, sizeof (int) * nshdr);
- return (ctf_set_errno(fp, ECTF_ELF));
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
}
/* Add the ctf section */
if ((dscn = elf_newscn(dst)) == NULL) {
- ctf_free(secxlate, sizeof (int) * nshdr);
- return (ctf_set_errno(fp, ECTF_ELF));
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
}
if (gelf_getshdr(dscn, &shdr) == NULL) {
- ctf_free(secxlate, sizeof (int) * nshdr);
- return (ctf_set_errno(fp, ECTF_ELF));
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
}
shdr.sh_name = ctfnameoff;
shdr.sh_type = SHT_PROGBITS;
@@ -286,36 +306,24 @@ ctf_write_elf(ctf_file_t *fp, Elf *src, Elf *dst, int flags)
}
if ((ddata = elf_newdata(dscn)) == NULL) {
- ctf_free(secxlate, sizeof (int) * nshdr);
- return (ctf_set_errno(fp, ECTF_ELF));
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
}
if (compress != 0) {
- size_t dlen;
- ctf_header_t *cthp;
int err;
if (ctf_zopen(&err) == NULL) {
- ctf_free(secxlate, sizeof (int) * nshdr);
- return (ctf_set_errno(fp, err));
+ ret = ctf_set_errno(fp, err);
+ goto out;
}
- dlen = fp->ctf_size;
- cdata = ctf_data_alloc(dlen);
- bcopy(fp->ctf_base, cdata, sizeof (ctf_header_t));
- cthp = cdata;
- cthp->cth_flags |= CTF_F_COMPRESS;
- dlen -= sizeof (ctf_header_t);
- if (z_compress((void *)((uintptr_t)cdata +
- sizeof (ctf_header_t)), &dlen,
- fp->ctf_base + sizeof (ctf_header_t),
- fp->ctf_size - sizeof (ctf_header_t)) != Z_OK) {
- ctf_data_free(cdata, fp->ctf_size);
- ctf_free(secxlate, sizeof (int) * nshdr);
- return (ctf_set_errno(fp, ECTF_ZLIB));
+ if ((err = ctf_compress(fp, &cdata, &asize, &elfsize)) != 0) {
+ ret = ctf_set_errno(fp, err);
+ goto out;
}
ddata->d_buf = cdata;
- ddata->d_size = dlen + sizeof (ctf_header_t);
+ ddata->d_size = elfsize;
} else {
ddata->d_buf = (void *)fp->ctf_base;
ddata->d_size = fp->ctf_size;
@@ -323,10 +331,8 @@ ctf_write_elf(ctf_file_t *fp, Elf *src, Elf *dst, int flags)
ddata->d_align = shdr.sh_addralign;
if (gelf_update_shdr(dscn, &shdr) == 0) {
- if (cdata != NULL)
- ctf_data_free(cdata, fp->ctf_size);
- ctf_free(secxlate, sizeof (int) * nshdr);
- return (ctf_set_errno(fp, ECTF_ELF));
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
}
/* update the section header location */
@@ -346,23 +352,27 @@ ctf_write_elf(ctf_file_t *fp, Elf *src, Elf *dst, int flags)
else
dehdr.e_shstrndx = secxlate[sehdr.e_shstrndx];
if (gelf_update_ehdr(dst, &dehdr) == NULL) {
- if (cdata != NULL)
- ctf_data_free(cdata, fp->ctf_size);
- ctf_free(secxlate, sizeof (int) * nshdr);
- return (ctf_set_errno(fp, ECTF_ELF));
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
}
if (elf_update(dst, ELF_C_WRITE) < 0) {
- if (cdata != NULL)
- ctf_data_free(cdata, fp->ctf_size);
- ctf_free(secxlate, sizeof (int) * nshdr);
- return (ctf_set_errno(fp, ECTF_ELF));
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
}
+ ret = 0;
+
+out:
+ if (strdatabuf != NULL)
+ ctf_free(strdatabuf, strdatasz);
+ if (symdatabuf != NULL)
+ ctf_free(symdatabuf, symdatasz);
if (cdata != NULL)
ctf_data_free(cdata, fp->ctf_size);
- ctf_free(secxlate, sizeof (int) * nshdr);
+ if (secxlate != NULL)
+ ctf_free(secxlate, sizeof (int) * nshdr);
- return (0);
+ return (ret);
}
int
diff --git a/usr/src/lib/libctf/common/ctf_lib.c b/usr/src/lib/libctf/common/ctf_lib.c
index 4658fc8ddc..6b637ba663 100644
--- a/usr/src/lib/libctf/common/ctf_lib.c
+++ b/usr/src/lib/libctf/common/ctf_lib.c
@@ -38,6 +38,7 @@
#include <gelf.h>
#include <zlib.h>
#include <zone.h>
+#include <sys/debug.h>
#ifdef _LP64
static const char *_libctf_zlib = "/usr/lib/64/libz.so.1";
@@ -57,6 +58,18 @@ static struct {
static size_t _PAGESIZE;
static size_t _PAGEMASK;
+static uint64_t ctf_phase = 0;
+
+#define CTF_COMPRESS_CHUNK (64*1024)
+
+typedef struct ctf_zdata {
+ void *czd_buf;
+ void *czd_next;
+ ctf_file_t *czd_ctfp;
+ size_t czd_allocsz;
+ z_stream czd_zstr;
+} ctf_zdata_t;
+
#pragma init(_libctf_init)
void
_libctf_init(void)
@@ -145,6 +158,182 @@ z_strerror(int err)
return (zlib.z_error(err));
}
+static int
+ctf_zdata_init(ctf_zdata_t *czd, ctf_file_t *fp)
+{
+ int err;
+ ctf_header_t *cthp;
+
+ bzero(czd, sizeof (ctf_zdata_t));
+
+ czd->czd_allocsz = fp->ctf_size;
+ czd->czd_buf = ctf_data_alloc(czd->czd_allocsz);
+ if (czd->czd_buf == MAP_FAILED)
+ return (ctf_set_errno(fp, ENOMEM));
+
+ bcopy(fp->ctf_base, czd->czd_buf, sizeof (ctf_header_t));
+ czd->czd_ctfp = fp;
+ cthp = czd->czd_buf;
+ cthp->cth_flags |= CTF_F_COMPRESS;
+ czd->czd_next = (void *)((uintptr_t)czd->czd_buf +
+ sizeof (ctf_header_t));
+
+ if ((err = zlib.z_initcomp(&czd->czd_zstr, Z_BEST_COMPRESSION,
+ ZLIB_VERSION, sizeof (z_stream))) != Z_OK)
+ return (ctf_set_errno(fp, ECTF_ZLIB));
+
+ return (0);
+}
+
+static int
+ctf_zdata_grow(ctf_zdata_t *czd)
+{
+ size_t off;
+ size_t newsz;
+ void *ndata;
+
+ off = (uintptr_t)czd->czd_next - (uintptr_t)czd->czd_buf;
+ newsz = czd->czd_allocsz + CTF_COMPRESS_CHUNK;
+ ndata = ctf_data_alloc(newsz);
+ if (ndata == MAP_FAILED) {
+ return (ctf_set_errno(czd->czd_ctfp, ENOMEM));
+ }
+
+ bcopy(czd->czd_buf, ndata, off);
+ ctf_data_free(czd->czd_buf, czd->czd_allocsz);
+ czd->czd_allocsz = newsz;
+ czd->czd_buf = ndata;
+ czd->czd_next = (void *)((uintptr_t)ndata + off);
+
+ czd->czd_zstr.next_out = (Bytef *)czd->czd_next;
+ czd->czd_zstr.avail_out = CTF_COMPRESS_CHUNK;
+ return (0);
+}
+
+static int
+ctf_zdata_compress_buffer(ctf_zdata_t *czd, const void *buf, size_t bufsize)
+{
+ int err;
+
+ czd->czd_zstr.next_out = czd->czd_next;
+ czd->czd_zstr.avail_out = czd->czd_allocsz -
+ (czd->czd_next - czd->czd_buf);
+ czd->czd_zstr.next_in = (Bytef *)buf;
+ czd->czd_zstr.avail_in = bufsize;
+
+ while (czd->czd_zstr.avail_in != 0) {
+ if (czd->czd_zstr.avail_out == 0) {
+ czd->czd_next = czd->czd_zstr.next_out;
+ if ((err = ctf_zdata_grow(czd)) != 0) {
+ return (err);
+ }
+ }
+
+ if ((err = zlib.z_compress(&czd->czd_zstr, Z_NO_FLUSH)) != Z_OK)
+ return (ctf_set_errno(czd->czd_ctfp, ECTF_ZLIB));
+ }
+ czd->czd_next = czd->czd_zstr.next_out;
+
+ return (0);
+}
+
+static int
+ctf_zdata_flush(ctf_zdata_t *czd, boolean_t finish)
+{
+ int err;
+ int flag = finish == B_TRUE ? Z_FINISH : Z_FULL_FLUSH;
+ int bret = finish == B_TRUE ? Z_STREAM_END : Z_BUF_ERROR;
+
+ for (;;) {
+ if (czd->czd_zstr.avail_out == 0) {
+ czd->czd_next = czd->czd_zstr.next_out;
+ if ((err = ctf_zdata_grow(czd)) != 0) {
+ return (err);
+ }
+ }
+
+ err = zlib.z_compress(&czd->czd_zstr, flag);
+ if (err == bret) {
+ break;
+ }
+ if (err != Z_OK)
+ return (ctf_set_errno(czd->czd_ctfp, ECTF_ZLIB));
+
+ }
+
+ czd->czd_next = czd->czd_zstr.next_out;
+
+ return (0);
+}
+
+static int
+ctf_zdata_end(ctf_zdata_t *czd)
+{
+ int ret;
+
+ if ((ret = ctf_zdata_flush(czd, B_TRUE)) != 0)
+ return (ret);
+
+ if ((ret = zlib.z_finicomp(&czd->czd_zstr)) != 0)
+ return (ctf_set_errno(czd->czd_ctfp, ECTF_ZLIB));
+
+ return (0);
+}
+
+static void
+ctf_zdata_cleanup(ctf_zdata_t *czd)
+{
+ ctf_data_free(czd->czd_buf, czd->czd_allocsz);
+ (void) zlib.z_finicomp(&czd->czd_zstr);
+}
+
+/*
+ * Compress our CTF data and return both the size of the compressed data and the
+ * size of the allocation. These may be different due to the nature of
+ * compression.
+ *
+ * In addition, we flush the compression inbetween our two phases such that we
+ * maintain a different dictionary bbetween the CTF data and the string section.
+ */
+int
+ctf_compress(ctf_file_t *fp, void **buf, size_t *allocsz, size_t *elfsize)
+{
+ int err;
+ ctf_zdata_t czd;
+ ctf_header_t *cthp = (ctf_header_t *)fp->ctf_base;
+
+ if ((err = ctf_zdata_init(&czd, fp)) != 0)
+ return (err);
+
+ if ((err = ctf_zdata_compress_buffer(&czd, fp->ctf_buf,
+ cthp->cth_stroff)) != 0) {
+ ctf_zdata_cleanup(&czd);
+ return (err);
+ }
+
+ if ((err = ctf_zdata_flush(&czd, B_FALSE)) != 0) {
+ ctf_zdata_cleanup(&czd);
+ return (err);
+ }
+
+ if ((err = ctf_zdata_compress_buffer(&czd,
+ fp->ctf_buf + cthp->cth_stroff, cthp->cth_strlen)) != 0) {
+ ctf_zdata_cleanup(&czd);
+ return (err);
+ }
+
+ if ((err = ctf_zdata_end(&czd)) != 0) {
+ ctf_zdata_cleanup(&czd);
+ return (err);
+ }
+
+ *buf = czd.czd_buf;
+ *allocsz = czd.czd_allocsz;
+ *elfsize = (uintptr_t)(czd.czd_next - czd.czd_buf);
+
+ return (0);
+}
+
int
z_compress(void *dst, size_t *dstlen, const void *src, size_t srclen)
{
@@ -576,3 +765,25 @@ ctf_version(int version)
return (_libctf_version);
}
+
+/*
+ * A utility function for folks debugging CTF conversion and merging.
+ */
+void
+ctf_phase_dump(ctf_file_t *fp, const char *phase)
+{
+ int fd;
+ static char *base;
+ char path[MAXPATHLEN];
+
+ if (base == NULL && (base = getenv("LIBCTF_WRITE_PHASES")) == NULL)
+ return;
+
+ (void) snprintf(path, sizeof (path), "%s/libctf.%s.%d.ctf", base,
+ phase != NULL ? phase : "",
+ ctf_phase);
+ if ((fd = open(path, O_CREAT | O_TRUNC | O_RDWR, 0777)) < 0)
+ return;
+ (void) ctf_write(fp, fd);
+ (void) close(fd);
+}
diff --git a/usr/src/lib/libctf/common/ctf_merge.c b/usr/src/lib/libctf/common/ctf_merge.c
index 70676bb0fd..9611c50acc 100644
--- a/usr/src/lib/libctf/common/ctf_merge.c
+++ b/usr/src/lib/libctf/common/ctf_merge.c
@@ -31,16 +31,15 @@
* we should take care to do the merge in the same way every time.
*/
-#include <ctf_impl.h>
-#include <libctf.h>
+#include <libctf_impl.h>
#include <sys/debug.h>
#include <sys/list.h>
#include <stddef.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
-
-#include <stdio.h>
+#include <mergeq.h>
+#include <errno.h>
typedef struct ctf_merge_tinfo {
uint16_t cmt_map; /* Map to the type in out */
@@ -56,6 +55,7 @@ typedef struct ctf_merge_types {
ctf_file_t *cm_out; /* Output CTF file */
ctf_file_t *cm_src; /* Input CTF file */
ctf_merge_tinfo_t *cm_tmap; /* Type state information */
+ boolean_t cm_dedup; /* Are we doing a dedup? */
} ctf_merge_types_t;
typedef struct ctf_merge_objmap {
@@ -80,11 +80,13 @@ typedef struct ctf_merge_input {
ctf_file_t *cmi_input;
list_t cmi_omap;
list_t cmi_fmap;
+ boolean_t cmi_created;
} ctf_merge_input_t;
struct ctf_merge_handle {
- ctf_file_t *cmh_output; /* Final output */
list_t cmh_inputs; /* Input list */
+ uint_t cmh_ninputs; /* Number of inputs */
+ uint_t cmh_nthreads; /* Number of threads to use */
ctf_file_t *cmh_unique; /* ctf to uniquify against */
boolean_t cmh_msyms; /* Should we merge symbols/funcs? */
int cmh_ofd; /* FD for output file */
@@ -95,6 +97,22 @@ struct ctf_merge_handle {
static int ctf_merge_add_type(ctf_merge_types_t *, ctf_id_t);
+static ctf_id_t
+ctf_merge_gettype(ctf_merge_types_t *cmp, ctf_id_t id)
+{
+ if (cmp->cm_dedup == B_FALSE) {
+ VERIFY(cmp->cm_tmap[id].cmt_map != 0);
+ return (cmp->cm_tmap[id].cmt_map);
+ }
+
+ while (cmp->cm_tmap[id].cmt_missing == B_FALSE) {
+ VERIFY(cmp->cm_tmap[id].cmt_map != 0);
+ id = cmp->cm_tmap[id].cmt_map;
+ }
+ VERIFY(cmp->cm_tmap[id].cmt_map != 0);
+ return (cmp->cm_tmap[id].cmt_map);
+}
+
static void
ctf_merge_diffcb(ctf_file_t *ifp, ctf_id_t iid, boolean_t same, ctf_file_t *ofp,
ctf_id_t oid, void *arg)
@@ -108,6 +126,8 @@ ctf_merge_diffcb(ctf_file_t *ifp, ctf_id_t iid, boolean_t same, ctf_file_t *ofp,
VERIFY(cmt[oid].cmt_map == 0);
cmt[oid].cmt_map = iid;
cmt[oid].cmt_forward = B_TRUE;
+ ctf_dprintf("merge diff forward mapped %d->%d\n", oid,
+ iid);
return;
}
@@ -119,9 +139,11 @@ ctf_merge_diffcb(ctf_file_t *ifp, ctf_id_t iid, boolean_t same, ctf_file_t *ofp,
if (cmt[oid].cmt_map != 0)
return;
cmt[oid].cmt_map = iid;
+ ctf_dprintf("merge diff mapped %d->%d\n", oid, iid);
} else if (ifp == cmp->cm_src) {
VERIFY(cmt[iid].cmt_map == 0);
cmt[iid].cmt_missing = B_TRUE;
+ ctf_dprintf("merge diff said %d is missing\n", iid);
}
}
@@ -176,7 +198,7 @@ ctf_merge_add_array(ctf_merge_types_t *cmp, ctf_id_t id)
return (ret);
ASSERT(cmp->cm_tmap[ar.ctr_contents].cmt_map != 0);
}
- ar.ctr_contents = cmp->cm_tmap[ar.ctr_contents].cmt_map;
+ ar.ctr_contents = ctf_merge_gettype(cmp, ar.ctr_contents);
if (cmp->cm_tmap[ar.ctr_index].cmt_map == 0) {
ret = ctf_merge_add_type(cmp, ar.ctr_index);
@@ -184,7 +206,7 @@ ctf_merge_add_array(ctf_merge_types_t *cmp, ctf_id_t id)
return (ret);
ASSERT(cmp->cm_tmap[ar.ctr_index].cmt_map != 0);
}
- ar.ctr_index = cmp->cm_tmap[ar.ctr_index].cmt_map;
+ ar.ctr_index = ctf_merge_gettype(cmp, ar.ctr_index);
ret = ctf_add_array(cmp->cm_out, flags, &ar);
if (ret == CTF_ERR)
@@ -221,7 +243,7 @@ ctf_merge_add_reftype(ctf_merge_types_t *cmp, ctf_id_t id)
return (ret);
ASSERT(cmp->cm_tmap[reftype].cmt_map != 0);
}
- reftype = cmp->cm_tmap[reftype].cmt_map;
+ reftype = ctf_merge_gettype(cmp, reftype);
ret = ctf_add_reftype(cmp->cm_out, flags, name, reftype,
ctf_type_kind(cmp->cm_src, id));
@@ -258,7 +280,7 @@ ctf_merge_add_typedef(ctf_merge_types_t *cmp, ctf_id_t id)
return (ret);
ASSERT(cmp->cm_tmap[reftype].cmt_map != 0);
}
- reftype = cmp->cm_tmap[reftype].cmt_map;
+ reftype = ctf_merge_gettype(cmp, reftype);
ret = ctf_add_typedef(cmp->cm_out, flags, name, reftype);
if (ret == CTF_ERR)
@@ -346,7 +368,7 @@ ctf_merge_add_func(ctf_merge_types_t *cmp, ctf_id_t id)
return (ret);
ASSERT(cmp->cm_tmap[ctc.ctc_return].cmt_map != 0);
}
- ctc.ctc_return = cmp->cm_tmap[ctc.ctc_return].cmt_map;
+ ctc.ctc_return = ctf_merge_gettype(cmp, ctc.ctc_return);
for (i = 0; i < ctc.ctc_argc; i++) {
if (cmp->cm_tmap[argv[i]].cmt_map == 0) {
@@ -355,7 +377,7 @@ ctf_merge_add_func(ctf_merge_types_t *cmp, ctf_id_t id)
return (ret);
ASSERT(cmp->cm_tmap[argv[i]].cmt_map != 0);
}
- argv[i] = cmp->cm_tmap[argv[i]].cmt_map;
+ argv[i] = ctf_merge_gettype(cmp, argv[i]);
}
ret = ctf_add_funcptr(cmp->cm_out, flags, &ctc, argv);
@@ -411,6 +433,7 @@ ctf_merge_add_member(const char *name, ctf_id_t type, ulong_t offset, void *arg)
VERIFY(cms->cms_cm->cm_tmap[type].cmt_map != 0);
type = cms->cms_cm->cm_tmap[type].cmt_map;
+ ctf_dprintf("Trying to add member %s to %d\n", name, cms->cms_id);
return (ctf_add_member(cms->cms_cm->cm_out, cms->cms_id, name,
type, offset) == CTF_ERR);
}
@@ -421,7 +444,7 @@ ctf_merge_add_member(const char *name, ctf_id_t type, ulong_t offset, void *arg)
* mark all structures and unions as needing to be fixed up.
*/
static int
-ctf_merge_add_su(ctf_merge_types_t *cmp, ctf_id_t id, boolean_t forward)
+ctf_merge_add_sou(ctf_merge_types_t *cmp, ctf_id_t id, boolean_t forward)
{
int flags, kind;
const ctf_type_t *tp;
@@ -451,6 +474,8 @@ ctf_merge_add_su(ctf_merge_types_t *cmp, ctf_id_t id, boolean_t forward)
if (forward == B_FALSE) {
VERIFY(cmp->cm_tmap[id].cmt_map == 0);
cmp->cm_tmap[id].cmt_map = suid;
+ ctf_dprintf("added sou \"%s\" as (%d) %d->%d\n", name, kind, id,
+ suid);
} else {
VERIFY(cmp->cm_tmap[id].cmt_map == suid);
}
@@ -501,7 +526,7 @@ ctf_merge_add_type(ctf_merge_types_t *cmp, ctf_id_t id)
break;
case CTF_K_STRUCT:
case CTF_K_UNION:
- ret = ctf_merge_add_su(cmp, id, B_FALSE);
+ ret = ctf_merge_add_sou(cmp, id, B_FALSE);
break;
case CTF_K_UNKNOWN:
/*
@@ -517,22 +542,29 @@ ctf_merge_add_type(ctf_merge_types_t *cmp, ctf_id_t id)
}
static int
-ctf_merge_fixup_su(ctf_merge_types_t *cmp, ctf_id_t id)
+ctf_merge_fixup_sou(ctf_merge_types_t *cmp, ctf_id_t id)
{
ctf_dtdef_t *dtd;
ctf_merge_su_t cms;
ctf_id_t mapid;
+ ssize_t size;
mapid = cmp->cm_tmap[id].cmt_map;
VERIFY(mapid != 0);
dtd = ctf_dtd_lookup(cmp->cm_out, mapid);
VERIFY(dtd != NULL);
+ ctf_dprintf("Trying to fix up sou %d\n", id);
cms.cms_cm = cmp;
cms.cms_id = mapid;
if (ctf_member_iter(cmp->cm_src, id, ctf_merge_add_member, &cms) != 0)
return (CTF_ERR);
+ if ((size = ctf_type_size(cmp->cm_src, id)) == CTF_ERR)
+ return (CTF_ERR);
+ if (ctf_set_size(cmp->cm_out, mapid, size) == CTF_ERR)
+ return (CTF_ERR);
+
return (0);
}
@@ -545,7 +577,7 @@ ctf_merge_fixup_type(ctf_merge_types_t *cmp, ctf_id_t id)
switch (kind) {
case CTF_K_STRUCT:
case CTF_K_UNION:
- ret = ctf_merge_fixup_su(cmp, id);
+ ret = ctf_merge_fixup_sou(cmp, id);
break;
default:
VERIFY(0);
@@ -555,6 +587,41 @@ ctf_merge_fixup_type(ctf_merge_types_t *cmp, ctf_id_t id)
return (ret);
}
+/*
+ * Now that we've successfully merged everything, we're going to clean
+ * up the merge type table. Traditionally if we had just two different
+ * files that we were working between, the types would be fully
+ * resolved. However, because we were comparing with ourself every step
+ * of the way and not our reduced self, we need to go through and update
+ * every mapped entry to what it now points to in the deduped file.
+ */
+static void
+ctf_merge_fixup_dedup_map(ctf_merge_types_t *cmp)
+{
+ int i;
+
+ for (i = 1; i < cmp->cm_src->ctf_typemax + 1; i++) {
+ ctf_id_t tid;
+
+ /*
+ * Missing types always have their id updated to exactly what it
+ * should be.
+ */
+ if (cmp->cm_tmap[i].cmt_missing == B_TRUE) {
+ VERIFY(cmp->cm_tmap[i].cmt_map != 0);
+ continue;
+ }
+
+ tid = i;
+ while (cmp->cm_tmap[tid].cmt_missing == B_FALSE) {
+ VERIFY(cmp->cm_tmap[tid].cmt_map != 0);
+ tid = cmp->cm_tmap[tid].cmt_map;
+ }
+ VERIFY(cmp->cm_tmap[tid].cmt_map != 0);
+ cmp->cm_tmap[i].cmt_map = cmp->cm_tmap[tid].cmt_map;
+ }
+}
+
/*
* We're going to do three passes over the containers.
@@ -571,19 +638,23 @@ ctf_merge_fixup_type(ctf_merge_types_t *cmp, ctf_id_t id)
*
* Importantly, we *must* call ctf_update between the second and third pass,
* otherwise several of the libctf functions will not properly find the data in
- * the container.
+ * the container. If we're doing a dedup we also fix up the type mapping.
*/
static int
ctf_merge_common(ctf_merge_types_t *cmp)
{
int ret, i;
+ ctf_phase_dump(cmp->cm_src, "merge-common-src");
+ ctf_phase_dump(cmp->cm_out, "merge-common-dest");
+
/* Pass 1 */
for (i = 1; i <= cmp->cm_src->ctf_typemax; i++) {
if (cmp->cm_tmap[i].cmt_forward == B_TRUE) {
- ret = ctf_merge_add_su(cmp, i, B_TRUE);
- if (ret != 0)
+ ret = ctf_merge_add_sou(cmp, i, B_TRUE);
+ if (ret != 0) {
return (ret);
+ }
}
}
@@ -591,8 +662,10 @@ ctf_merge_common(ctf_merge_types_t *cmp)
for (i = 1; i <= cmp->cm_src->ctf_typemax; i++) {
if (cmp->cm_tmap[i].cmt_missing == B_TRUE) {
ret = ctf_merge_add_type(cmp, i);
- if (ret != 0)
+ if (ret != 0) {
+ ctf_dprintf("Failed to merge type %d\n", i);
return (ret);
+ }
}
}
@@ -600,6 +673,11 @@ ctf_merge_common(ctf_merge_types_t *cmp)
if (ret != 0)
return (ret);
+ if (cmp->cm_dedup == B_TRUE) {
+ ctf_merge_fixup_dedup_map(cmp);
+ }
+
+ ctf_dprintf("Beginning merge pass 3\n");
/* Pass 3 */
for (i = 1; i <= cmp->cm_src->ctf_typemax; i++) {
if (cmp->cm_tmap[i].cmt_fixup == B_TRUE) {
@@ -609,6 +687,10 @@ ctf_merge_common(ctf_merge_types_t *cmp)
}
}
+ if (cmp->cm_dedup == B_TRUE) {
+ ctf_merge_fixup_dedup_map(cmp);
+ }
+
return (0);
}
@@ -666,18 +748,24 @@ ctf_merge_types_fini(ctf_merge_types_t *cmp)
}
/*
- * Merge types from targ into dest.
+ * Merge the types contained inside of two input files. The second input file is
+ * always going to be the destination. We're guaranteed that it's always
+ * writeable.
*/
static int
-ctf_merge(ctf_file_t **outp, ctf_merge_input_t *cmi)
+ctf_merge_types(void *arg, void *arg2, void **outp, void *unsued)
{
int ret;
ctf_merge_types_t cm;
ctf_diff_t *cdp;
ctf_merge_objmap_t *cmo;
ctf_merge_funcmap_t *cmf;
- ctf_file_t *out = *outp;
- ctf_file_t *source = cmi->cmi_input;
+ ctf_merge_input_t *scmi = arg;
+ ctf_merge_input_t *dcmi = arg2;
+ ctf_file_t *out = dcmi->cmi_input;
+ ctf_file_t *source = scmi->cmi_input;
+
+ ctf_dprintf("merging %p->%p\n", source, out);
if (!(out->ctf_flags & LCTF_RDWR))
return (ctf_set_errno(out, ECTF_RDONLY));
@@ -690,6 +778,7 @@ ctf_merge(ctf_file_t **outp, ctf_merge_input_t *cmi)
cm.cm_out = out;
cm.cm_src = source;
+ cm.cm_dedup = B_FALSE;
ret = ctf_merge_types_init(&cm);
if (ret != 0) {
ctf_diff_fini(cdp);
@@ -700,24 +789,27 @@ ctf_merge(ctf_file_t **outp, ctf_merge_input_t *cmi)
if (ret != 0)
goto cleanup;
ret = ctf_merge_common(&cm);
- if (ret == 0)
+ ctf_dprintf("merge common returned with %d\n", ret);
+ if (ret == 0) {
ret = ctf_update(out);
- else
+ ctf_dprintf("update returned with %d\n", ret);
+ } else {
goto cleanup;
+ }
/*
* Now we need to fix up the object and function maps.
*/
- for (cmo = list_head(&cmi->cmi_omap); cmo != NULL;
- cmo = list_next(&cmi->cmi_omap, cmo)) {
+ for (cmo = list_head(&scmi->cmi_omap); cmo != NULL;
+ cmo = list_next(&scmi->cmi_omap, cmo)) {
if (cmo->cmo_tid == 0)
continue;
VERIFY(cm.cm_tmap[cmo->cmo_tid].cmt_map != 0);
cmo->cmo_tid = cm.cm_tmap[cmo->cmo_tid].cmt_map;
}
- for (cmf = list_head(&cmi->cmi_fmap); cmf != NULL;
- cmf = list_next(&cmi->cmi_fmap, cmf)) {
+ for (cmf = list_head(&scmi->cmi_fmap); cmf != NULL;
+ cmf = list_next(&scmi->cmi_fmap, cmf)) {
int i;
VERIFY(cm.cm_tmap[cmf->cmf_rtid].cmt_map != 0);
@@ -728,28 +820,36 @@ ctf_merge(ctf_file_t **outp, ctf_merge_input_t *cmi)
}
}
+ /*
+ * Now that we've fixed things up, we need to give our function and
+ * object maps to the destination, such that it can continue to update
+ * them going forward.
+ */
+ list_move_tail(&dcmi->cmi_fmap, &scmi->cmi_fmap);
+ list_move_tail(&dcmi->cmi_omap, &scmi->cmi_omap);
+
cleanup:
+ if (ret == 0)
+ *outp = dcmi;
ctf_merge_types_fini(&cm);
ctf_diff_fini(cdp);
- return (ret);
+ if (ret != 0)
+ return (ctf_errno(out));
+ return (0);
}
static int
-ctf_uniquify_types(ctf_merge_t *cmh, ctf_file_t **outp)
+ctf_uniquify_types(ctf_merge_t *cmh, ctf_file_t *src, ctf_file_t **outp)
{
int err, ret;
ctf_file_t *out;
ctf_merge_types_t cm;
ctf_diff_t *cdp;
ctf_merge_input_t *cmi;
- ctf_file_t *src = cmh->cmh_output;
ctf_file_t *parent = cmh->cmh_unique;
*outp = NULL;
- if (cmh->cmh_ofd == -1)
- out = ctf_create(&err);
- else
- out = ctf_fdcreate(cmh->cmh_ofd, &err);
+ out = ctf_fdcreate(cmh->cmh_ofd, &err);
if (out == NULL)
return (ctf_set_errno(src, err));
@@ -773,6 +873,7 @@ ctf_uniquify_types(ctf_merge_t *cmh, ctf_file_t **outp)
cm.cm_out = parent;
cm.cm_src = src;
+ cm.cm_dedup = B_FALSE;
ret = ctf_merge_types_init(&cm);
if (ret != 0) {
ctf_close(out);
@@ -841,6 +942,9 @@ ctf_merge_fini_input(ctf_merge_input_t *cmi)
ctf_free(cmf, sizeof (ctf_merge_funcmap_t) +
sizeof (ctf_id_t) * cmf->cmf_argc);
+ if (cmi->cmi_created == B_TRUE && cmi->cmi_input != NULL)
+ ctf_close(cmi->cmi_input);
+
ctf_free(cmi, sizeof (ctf_merge_input_t));
}
@@ -850,9 +954,6 @@ ctf_merge_fini(ctf_merge_t *cmh)
size_t len;
ctf_merge_input_t *cmi;
- if (cmh->cmh_output != NULL)
- ctf_close(cmh->cmh_output);
-
if (cmh->cmh_label != NULL) {
len = strlen(cmh->cmh_label) + 1;
ctf_free(cmh->cmh_label, len);
@@ -891,22 +992,18 @@ ctf_merge_init(int fd, int *errp)
}
if (fd == -1) {
- out->cmh_output = ctf_create(errp);
out->cmh_msyms = B_FALSE;
} else {
- out->cmh_output = ctf_fdcreate(fd, errp);
out->cmh_msyms = B_TRUE;
}
- if (out->cmh_output == NULL) {
- ctf_free(out, sizeof (ctf_merge_t));
- return (NULL);
- }
-
list_create(&out->cmh_inputs, sizeof (ctf_merge_input_t),
offsetof(ctf_merge_input_t, cmi_node));
+ out->cmh_ninputs = 0;
+ out->cmh_nthreads = 1;
out->cmh_unique = NULL;
out->cmh_ofd = fd;
+ out->cmh_flags = 0;
out->cmh_label = NULL;
out->cmh_pname = NULL;
@@ -918,9 +1015,6 @@ ctf_merge_label(ctf_merge_t *cmh, const char *label)
{
char *dup;
- if (cmh->cmh_output == NULL)
- return (EINVAL);
-
if (label == NULL)
return (EINVAL);
@@ -983,14 +1077,17 @@ ctf_merge_add_objs(const char *name, ctf_id_t id, ulong_t idx, void *arg)
return (0);
}
+/*
+ * Whenever we create an entry to merge, we then go and add a second empty
+ * ctf_file_t which we use for the purposes of our merging. It's not the best,
+ * but it's the best that we've got at the moment.
+ */
int
ctf_merge_add(ctf_merge_t *cmh, ctf_file_t *input)
{
int ret;
ctf_merge_input_t *cmi;
-
- if (cmh->cmh_output == NULL)
- return (EINVAL);
+ ctf_file_t *empty;
if (input->ctf_flags & LCTF_CHILD)
return (ECTF_MCHILD);
@@ -999,6 +1096,7 @@ ctf_merge_add(ctf_merge_t *cmh, ctf_file_t *input)
if (cmi == NULL)
return (ENOMEM);
+ cmi->cmi_created = B_FALSE;
cmi->cmi_input = input;
list_create(&cmi->cmi_fmap, sizeof (ctf_merge_funcmap_t),
offsetof(ctf_merge_funcmap_t, cmf_node));
@@ -1020,6 +1118,30 @@ ctf_merge_add(ctf_merge_t *cmh, ctf_file_t *input)
}
list_insert_tail(&cmh->cmh_inputs, cmi);
+ cmh->cmh_ninputs++;
+
+ /* And now the empty one to merge into this */
+ cmi = ctf_alloc(sizeof (ctf_merge_input_t));
+ if (cmi == NULL)
+ return (ENOMEM);
+ list_create(&cmi->cmi_fmap, sizeof (ctf_merge_funcmap_t),
+ offsetof(ctf_merge_funcmap_t, cmf_node));
+ list_create(&cmi->cmi_omap, sizeof (ctf_merge_funcmap_t),
+ offsetof(ctf_merge_objmap_t, cmo_node));
+
+ empty = ctf_fdcreate(cmh->cmh_ofd, &ret);
+ if (empty == NULL)
+ return (ret);
+ cmi->cmi_input = empty;
+ cmi->cmi_created = B_TRUE;
+
+ if (ctf_setmodel(empty, ctf_getmodel(input)) == CTF_ERR) {
+ return (ctf_errno(empty));
+ }
+
+ list_insert_tail(&cmh->cmh_inputs, cmi);
+ cmh->cmh_ninputs++;
+ ctf_dprintf("added containers %p and %p\n", input, empty);
return (0);
}
@@ -1028,8 +1150,6 @@ ctf_merge_uniquify(ctf_merge_t *cmh, ctf_file_t *u, const char *pname)
{
char *dup;
- if (cmh->cmh_output == NULL)
- return (EINVAL);
if (u->ctf_flags & LCTF_CHILD)
return (ECTF_MCHILD);
if (pname == NULL)
@@ -1093,8 +1213,12 @@ found:
if (cmo != NULL) {
if (cmo->cmo_tid == 0)
continue;
- if ((err = ctf_add_object(fp, i, cmo->cmo_tid)) != 0)
+ if ((err = ctf_add_object(fp, i, cmo->cmo_tid)) != 0) {
+ ctf_dprintf("Failed to add symbol %s->%d: %s\n",
+ name, cmo->cmo_tid,
+ ctf_errmsg(ctf_errno(fp)));
return (err);
+ }
}
}
@@ -1161,12 +1285,14 @@ found:
}
int
-ctf_merge_merge(ctf_merge_t *cmh, ctf_file_t **out)
+ctf_merge_merge(ctf_merge_t *cmh, ctf_file_t **outp)
{
- int err;
+ int err, merr;
ctf_merge_input_t *cmi;
- boolean_t mset = B_FALSE;
ctf_id_t ltype;
+ mergeq_t *mqp;
+ ctf_merge_input_t *final;
+ ctf_file_t *out;
if (cmh->cmh_label != NULL && cmh->cmh_unique != NULL) {
const char *label = ctf_label_topmost(cmh->cmh_unique);
@@ -1176,56 +1302,248 @@ ctf_merge_merge(ctf_merge_t *cmh, ctf_file_t **out)
return (ECTF_LCONFLICT);
}
+ if (mergeq_init(&mqp, cmh->cmh_nthreads) == -1) {
+ return (errno);
+ }
+
/*
* We should consider doing a divide and conquer and parallel merge
* here. If we did, we'd want to use some number of threads to perform
* this operation.
*/
+ VERIFY(cmh->cmh_ninputs % 2 == 0);
for (cmi = list_head(&cmh->cmh_inputs); cmi != NULL;
cmi = list_next(&cmh->cmh_inputs, cmi)) {
- if (mset == B_FALSE) {
- if (ctf_setmodel(cmh->cmh_output,
- ctf_getmodel(cmi->cmi_input)) != 0) {
- return (ctf_errno(cmh->cmh_output));
- }
- mset = B_TRUE;
+ if (mergeq_add(mqp, cmi) == -1) {
+ err = errno;
+ mergeq_fini(mqp);
}
- err = ctf_merge(&cmh->cmh_output, cmi);
- if (err != 0)
- return (ctf_errno(cmh->cmh_output));
}
+ err = mergeq_merge(mqp, ctf_merge_types, NULL, (void **)&final, &merr);
+ mergeq_fini(mqp);
+
+ if (err == MERGEQ_ERROR) {
+ return (errno);
+ } else if (err == MERGEQ_UERROR) {
+ return (merr);
+ }
+
+ /*
+ * Disassociate the generated ctf_file_t from the original input. That
+ * way when the input gets cleaned up, we don't accidentally kill the
+ * final reference to the ctf_file_t. If it gets uniquified then we'll
+ * kill it.
+ */
+ VERIFY(final->cmi_input != NULL);
+ out = final->cmi_input;
+ final->cmi_input = NULL;
+
+ ctf_dprintf("preparing to uniquify against: %p\n", cmh->cmh_unique);
if (cmh->cmh_unique != NULL) {
- err = ctf_uniquify_types(cmh, out);
- if (err != 0)
- return (ctf_errno(cmh->cmh_output));
- ctf_close(cmh->cmh_output);
- } else {
- *out = cmh->cmh_output;
+ ctf_file_t *u;
+ err = ctf_uniquify_types(cmh, out, &u);
+ if (err != 0) {
+ err = ctf_errno(out);
+ ctf_close(out);
+ return (err);
+ }
+ ctf_close(out);
+ out = u;
}
- ltype = (*out)->ctf_typemax;
- if (((*out)->ctf_flags & LCTF_CHILD) && ltype != 0)
+ ltype = out->ctf_typemax;
+ if ((out->ctf_flags & LCTF_CHILD) && ltype != 0)
ltype += 0x8000;
- if (ctf_add_label(*out, cmh->cmh_label, ltype, 0) != 0) {
- return (ctf_errno(*out));
+ ctf_dprintf("trying to add the label\n");
+ if (cmh->cmh_label != NULL &&
+ ctf_add_label(out, cmh->cmh_label, ltype, 0) != 0) {
+ ctf_close(out);
+ return (ctf_errno(out));
}
+ ctf_dprintf("merging symbols and the like\n");
if (cmh->cmh_msyms == B_TRUE) {
- err = ctf_merge_symbols(cmh, *out);
- if (err != 0)
- return (ctf_errno(*out));
+ err = ctf_merge_symbols(cmh, out);
+ if (err != 0) {
+ ctf_close(out);
+ return (ctf_errno(out));
+ }
+
+ err = ctf_merge_functions(cmh, out);
+ if (err != 0) {
+ ctf_close(out);
+ return (ctf_errno(out));
+ }
+ }
+
+ err = ctf_update(out);
+ if (err != 0) {
+ ctf_close(out);
+ return (ctf_errno(out));
+ }
+
+ *outp = out;
+ return (0);
+}
+
+/*
+ * When we get told that something is unique, eg. same is B_FALSE, then that
+ * tells us that we need to add it to the output. If same is B_TRUE, then we'll
+ * want to record it in the mapping table so that we know how to redirect types
+ * to the extant ones.
+ */
+static void
+ctf_dedup_cb(ctf_file_t *ifp, ctf_id_t iid, boolean_t same, ctf_file_t *ofp,
+ ctf_id_t oid, void *arg)
+{
+ ctf_merge_types_t *cmp = arg;
+ ctf_merge_tinfo_t *cmt = cmp->cm_tmap;
- err = ctf_merge_functions(cmh, *out);
- if (err != 0)
- return (ctf_errno(*out));
+ if (same == B_TRUE) {
+ /*
+ * The output id here may itself map to something else.
+ * Therefore, we need to basically walk a chain and see what it
+ * points to until it itself points to a base type, eg. -1.
+ * Otherwise we'll dedup to something which no longer exists.
+ */
+ while (cmt[oid].cmt_missing == B_FALSE)
+ oid = cmt[oid].cmt_map;
+ cmt[iid].cmt_map = oid;
+ ctf_dprintf("%d->%d \n", iid, oid);
+ } else {
+ VERIFY(cmt[iid].cmt_map == 0);
+ cmt[iid].cmt_missing = B_TRUE;
+ ctf_dprintf("%d is missing\n", iid);
}
+}
- err = ctf_update(*out);
- if (err != 0)
- return (ctf_errno(*out));
+/*
+ * Dedup a CTF container.
+ *
+ * DWARF and other encoding formats that we use to create CTF data may create
+ * multiple copies of a given type. However, after doing a conversion, and
+ * before doing a merge, we'd prefer, if possible, to have every input container
+ * to be unique.
+ *
+ * Doing a deduplication is like a normal merge. However, when we diff the types
+ * in the container, rather than doing a normal diff, we instead want to diff
+ * against any already processed types. eg, for a given type i in a container,
+ * we want to diff it from 0 to i - 1.
+ */
+int
+ctf_merge_dedup(ctf_merge_t *cmp, ctf_file_t **outp)
+{
+ int ret;
+ ctf_diff_t *cdp = NULL;
+ ctf_merge_input_t *cmi, *cmc;
+ ctf_file_t *ifp, *ofp;
+ ctf_merge_objmap_t *cmo;
+ ctf_merge_funcmap_t *cmf;
+ ctf_merge_types_t cm;
- cmh->cmh_output = NULL;
+ if (cmp == NULL || outp == NULL)
+ return (EINVAL);
+
+ ctf_dprintf("encountered %d inputs\n", cmp->cmh_ninputs);
+ if (cmp->cmh_ninputs != 2)
+ return (EINVAL);
+
+ ctf_dprintf("passed argument sanity check\n");
+
+ cmi = list_head(&cmp->cmh_inputs);
+ VERIFY(cmi != NULL);
+ cmc = list_next(&cmp->cmh_inputs, cmi);
+ VERIFY(cmc != NULL);
+ ifp = cmi->cmi_input;
+ ofp = cmc->cmi_input;
+ VERIFY(ifp != NULL);
+ VERIFY(ofp != NULL);
+ cm.cm_src = ifp;
+ cm.cm_out = ofp;
+ cm.cm_dedup = B_TRUE;
+
+ if ((ret = ctf_merge_types_init(&cm)) != 0) {
+ return (ret);
+ }
+ if ((ret = ctf_diff_init(ifp, ifp, &cdp)) != 0)
+ goto err;
+
+ ctf_dprintf("Successfully initialized dedup\n");
+ if ((ret = ctf_diff_self(cdp, ctf_dedup_cb, &cm)) != 0)
+ goto err;
+
+ ctf_dprintf("Successfully diffed types\n");
+ ret = ctf_merge_common(&cm);
+ ctf_dprintf("deduping types result: %d\n", ret);
+ if (ret == 0)
+ ret = ctf_update(cm.cm_out);
+ if (ret != 0)
+ goto err;
+
+ ctf_dprintf("Successfully deduped types\n");
+ ctf_phase_dump(cm.cm_out, "dedup-pre-syms");
+
+
+ /*
+ * Now we need to fix up the object and function maps.
+ */
+ for (cmo = list_head(&cmi->cmi_omap); cmo != NULL;
+ cmo = list_next(&cmi->cmi_omap, cmo)) {
+ if (cmo->cmo_tid == 0)
+ continue;
+ ctf_dprintf("mapped %s %d->%d\n", cmo->cmo_name,
+ cmo->cmo_tid, cm.cm_tmap[cmo->cmo_tid].cmt_map);
+ cmo->cmo_tid = cm.cm_tmap[cmo->cmo_tid].cmt_map;
+ }
+
+ for (cmf = list_head(&cmi->cmi_fmap); cmf != NULL;
+ cmf = list_next(&cmi->cmi_fmap, cmf)) {
+ int i;
+
+ VERIFY(cm.cm_tmap[cmf->cmf_rtid].cmt_map != 0);
+ cmf->cmf_rtid = cm.cm_tmap[cmf->cmf_rtid].cmt_map;
+ for (i = 0; i < cmf->cmf_argc; i++) {
+ VERIFY(cm.cm_tmap[cmf->cmf_args[i]].cmt_map != 0);
+ cmf->cmf_args[i] = cm.cm_tmap[cmf->cmf_args[i]].cmt_map;
+ }
+ }
+
+ if (cmp->cmh_msyms == B_TRUE) {
+ ret = ctf_merge_symbols(cmp, cm.cm_out);
+ if (ret != 0) {
+ ret = ctf_errno(cm.cm_out);
+ ctf_dprintf("failed to dedup symbols: %s\n",
+ ctf_errmsg(ret));
+ goto err;
+ }
+
+ ret = ctf_merge_functions(cmp, cm.cm_out);
+ if (ret != 0) {
+ ret = ctf_errno(cm.cm_out);
+ ctf_dprintf("failed to dedup functions: %s\n",
+ ctf_errmsg(ret));
+ goto err;
+ }
+ }
+
+ ret = ctf_update(cm.cm_out);
+ if (ret == 0) {
+ cmc->cmi_input = NULL;
+ *outp = cm.cm_out;
+ }
+err:
+ ctf_merge_types_fini(&cm);
+ ctf_diff_fini(cdp);
+ return (ret);
+}
+
+int
+ctf_merge_set_nthreads(ctf_merge_t *cmp, const uint_t nthrs)
+{
+ if (nthrs == 0)
+ return (EINVAL);
+ cmp->cmh_nthreads = nthrs;
return (0);
}
diff --git a/usr/src/lib/libctf/common/ctf_subr.c b/usr/src/lib/libctf/common/ctf_subr.c
index 467b6a8181..26f7e8c4db 100644
--- a/usr/src/lib/libctf/common/ctf_subr.c
+++ b/usr/src/lib/libctf/common/ctf_subr.c
@@ -24,8 +24,6 @@
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <ctf_impl.h>
#include <libctf.h>
#include <sys/mman.h>
@@ -56,6 +54,18 @@ ctf_alloc(size_t size)
return (malloc(size));
}
+void *
+mergeq_alloc(size_t size)
+{
+ return (malloc(size));
+}
+
+void *
+workq_alloc(size_t size)
+{
+ return (malloc(size));
+}
+
/*ARGSUSED*/
void
ctf_free(void *buf, size_t size)
@@ -63,6 +73,20 @@ ctf_free(void *buf, size_t size)
free(buf);
}
+/*ARGSUSED*/
+void
+mergeq_free(void *buf, size_t size)
+{
+ free(buf);
+}
+
+/*ARGSUSED*/
+void
+workq_free(void *buf, size_t size)
+{
+ free(buf);
+}
+
const char *
ctf_strerror(int err)
{
diff --git a/usr/src/lib/libctf/common/libctf.h b/usr/src/lib/libctf/common/libctf.h
index 13c7e07e99..0951ae0606 100644
--- a/usr/src/lib/libctf/common/libctf.h
+++ b/usr/src/lib/libctf/common/libctf.h
@@ -55,8 +55,7 @@ extern "C" {
extern int _libctf_debug;
typedef enum ctf_diff_flag {
- CTF_DIFF_F_IGNORE_INTNAMES = 0x01,
- CTF_DIFF_F_MASK = 0x01
+ CTF_DIFF_F_IGNORE_INTNAMES = 0x01
} ctf_diff_flag_t;
typedef struct ctf_diff ctf_diff_t;
@@ -75,12 +74,18 @@ extern int ctf_diff_functions(ctf_diff_t *, ctf_diff_func_f, void *);
extern int ctf_diff_objects(ctf_diff_t *, ctf_diff_obj_f, void *);
extern void ctf_diff_fini(ctf_diff_t *);
+#define CTF_CONVERT_F_IGNNONC 0x01
+extern ctf_file_t *ctf_fdconvert(int, const char *, uint_t, uint_t, int *,
+ char *, size_t);
+
typedef struct ctf_merge_handle ctf_merge_t;
extern ctf_merge_t *ctf_merge_init(int, int *);
extern int ctf_merge_add(ctf_merge_t *, ctf_file_t *);
+extern int ctf_merge_set_nthreads(ctf_merge_t *, const uint_t);
extern int ctf_merge_label(ctf_merge_t *, const char *);
extern int ctf_merge_uniquify(ctf_merge_t *, ctf_file_t *, const char *);
extern int ctf_merge_merge(ctf_merge_t *, ctf_file_t **);
+extern int ctf_merge_dedup(ctf_merge_t *, ctf_file_t **);
extern void ctf_merge_fini(ctf_merge_t *);
#define CTF_ELFWRITE_F_COMPRESS 0x1
diff --git a/usr/src/lib/libctf/common/libctf_impl.h b/usr/src/lib/libctf/common/libctf_impl.h
new file mode 100644
index 0000000000..11193e97d0
--- /dev/null
+++ b/usr/src/lib/libctf/common/libctf_impl.h
@@ -0,0 +1,59 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2015 Joyent, Inc.
+ */
+
+#ifndef _LIBCTF_IMPL_H
+#define _LIBCTF_IMPL_H
+
+/*
+ * Portions of libctf implementations that are only suitable for CTF's userland
+ * library, eg. converting and merging related routines.
+ */
+
+#include <libelf.h>
+#include <libctf.h>
+#include <ctf_impl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum ctf_conv_status {
+ CTF_CONV_SUCCESS = 0,
+ CTF_CONV_ERROR = 1,
+ CTF_CONV_NOTSUP = 2
+} ctf_conv_status_t;
+
+typedef ctf_conv_status_t (*ctf_convert_f)(int, Elf *, uint_t, int *,
+ ctf_file_t **, char *, size_t);
+extern ctf_conv_status_t ctf_dwarf_convert(int, Elf *, uint_t, int *,
+ ctf_file_t **, char *, size_t);
+
+/*
+ * zlib compression routines
+ */
+extern int ctf_compress(ctf_file_t *fp, void **, size_t *, size_t *);
+
+extern int ctf_diff_self(ctf_diff_t *, ctf_diff_type_f, void *);
+
+/*
+ * Internal debugging aids
+ */
+extern void ctf_phase_dump(ctf_file_t *, const char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBCTF_IMPL_H */
diff --git a/usr/src/lib/libctf/common/mapfile-vers b/usr/src/lib/libctf/common/mapfile-vers
index 95ddfd5d6c..3c00c47a58 100644
--- a/usr/src/lib/libctf/common/mapfile-vers
+++ b/usr/src/lib/libctf/common/mapfile-vers
@@ -81,25 +81,31 @@ SYMBOL_VERSION SUNWprivate_1.2 {
ctf_elffdwrite;
ctf_elfwrite;
ctf_enum_value;
+ ctf_fdconvert;
ctf_flags;
ctf_func_args_by_id;
ctf_func_info_by_id;
ctf_function_iter;
+ ctf_kind_name;
ctf_label_info;
ctf_label_iter;
ctf_label_topmost;
ctf_member_info;
ctf_merge_add;
+ ctf_merge_dedup;
ctf_merge_fini;
ctf_merge_init;
ctf_merge_label;
ctf_merge_merge;
+ ctf_merge_set_nthreads;
ctf_merge_uniquify;
ctf_object_iter;
ctf_parent_file;
ctf_parent_label;
ctf_parent_name;
ctf_set_array;
+ ctf_set_root;
+ ctf_set_size;
ctf_string_iter;
ctf_symbol_name;
ctf_type_align;
diff --git a/usr/src/lib/libdwarf/Makefile b/usr/src/lib/libdwarf/Makefile
new file mode 100644
index 0000000000..6b7ff6244b
--- /dev/null
+++ b/usr/src/lib/libdwarf/Makefile
@@ -0,0 +1,40 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2015 Joyent, Inc.
+#
+
+include ../Makefile.lib
+
+SUBDIRS = $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+all := TARGET = all
+clean := TARGET = clean
+clobber := TARGET = clobber
+install := TARGET = install
+lint := TARGET = lint
+
+.KEEP_STATE:
+
+all clean clobber lint install_h: $(SUBDIRS)
+
+install: install_h $(SUBDIRS)
+
+check: $(CHECKHDRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include ../Makefile.targ
diff --git a/usr/src/lib/libdwarf/Makefile.com b/usr/src/lib/libdwarf/Makefile.com
new file mode 100644
index 0000000000..c737366af9
--- /dev/null
+++ b/usr/src/lib/libdwarf/Makefile.com
@@ -0,0 +1,92 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2015 Joyent, Inc.
+#
+
+LIBRARY= libdwarf.a
+VERS= .1
+
+OBJECTS=dwarf_abbrev.o \
+ dwarf_addr_finder.o \
+ dwarf_alloc.o \
+ dwarf_arange.o \
+ dwarf_die_deliv.o \
+ dwarf_elf_access.o \
+ dwarf_error.o \
+ dwarf_form.o \
+ dwarf_frame.o \
+ dwarf_frame2.o \
+ dwarf_frame3.o \
+ dwarf_funcs.o \
+ dwarf_global.o \
+ dwarf_harmless.o \
+ dwarf_init_finish.o \
+ dwarf_leb.o \
+ dwarf_line.o \
+ dwarf_line2.o \
+ dwarf_loc.o \
+ dwarf_macro.o \
+ dwarf_names.o \
+ dwarf_original_elf_init.o \
+ dwarf_print_lines.o \
+ dwarf_pubtypes.o \
+ dwarf_query.o \
+ dwarf_ranges.o \
+ dwarf_sort_line.o \
+ dwarf_string.o \
+ dwarf_stubs.o \
+ dwarf_types.o \
+ dwarf_util.o \
+ dwarf_vars.o \
+ dwarf_weaks.o \
+ malloc_check.o \
+ pro_alloc.o \
+ pro_arange.o \
+ pro_die.o \
+ pro_encode_nm.o \
+ pro_error.o \
+ pro_expr.o \
+ pro_finish.o \
+ pro_forms.o \
+ pro_frame.o \
+ pro_funcs.o \
+ pro_init.o \
+ pro_line.o \
+ pro_macinfo.o \
+ pro_pubnames.o \
+ pro_reloc.o \
+ pro_reloc_stream.o \
+ pro_reloc_symbolic.o \
+ pro_section.o \
+ pro_types.o \
+ pro_vars.o \
+ pro_weaks.o
+
+include ../../Makefile.lib
+include ../../Makefile.rootfs
+
+LIBS = $(DYNLIB)
+LDLIBS += -lelf -lc
+
+SRCDIR = ../common
+CPPFLAGS += -I$(SRCDIR) -DELF_TARGET_ALL=1
+CERRWARN += -_gcc=-Wno-unused
+CERRWARN += -_gcc=-Wno-implicit-function-declaration
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+lint: lintcheck
+
+include ../../Makefile.targ
diff --git a/usr/src/tools/ctf/dwarf/THIRDPARTYLICENSE b/usr/src/lib/libdwarf/THIRDPARTYLICENSE
index b9320c2d56..b9320c2d56 100644
--- a/usr/src/tools/ctf/dwarf/THIRDPARTYLICENSE
+++ b/usr/src/lib/libdwarf/THIRDPARTYLICENSE
diff --git a/usr/src/tools/ctf/dwarf/THIRDPARTYLICENSE.descrip b/usr/src/lib/libdwarf/THIRDPARTYLICENSE.descrip
index 73abaac973..73abaac973 100644
--- a/usr/src/tools/ctf/dwarf/THIRDPARTYLICENSE.descrip
+++ b/usr/src/lib/libdwarf/THIRDPARTYLICENSE.descrip
diff --git a/usr/src/lib/libdwarf/amd64/Makefile b/usr/src/lib/libdwarf/amd64/Makefile
new file mode 100644
index 0000000000..15a899f96f
--- /dev/null
+++ b/usr/src/lib/libdwarf/amd64/Makefile
@@ -0,0 +1,19 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2015 Joyent, Inc.
+#
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
diff --git a/usr/src/tools/ctf/dwarf/common/cmplrs/dwarf_addr_finder.h b/usr/src/lib/libdwarf/common/cmplrs/dwarf_addr_finder.h
index 0eda6d1c44..0eda6d1c44 100644
--- a/usr/src/tools/ctf/dwarf/common/cmplrs/dwarf_addr_finder.h
+++ b/usr/src/lib/libdwarf/common/cmplrs/dwarf_addr_finder.h
diff --git a/usr/src/tools/ctf/dwarf/common/config.h b/usr/src/lib/libdwarf/common/config.h
index 42b286cfda..42b286cfda 100644
--- a/usr/src/tools/ctf/dwarf/common/config.h
+++ b/usr/src/lib/libdwarf/common/config.h
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf.h b/usr/src/lib/libdwarf/common/dwarf.h
index b064c4d86b..b064c4d86b 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf.h
+++ b/usr/src/lib/libdwarf/common/dwarf.h
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_abbrev.c b/usr/src/lib/libdwarf/common/dwarf_abbrev.c
index c2ae361f33..c2ae361f33 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_abbrev.c
+++ b/usr/src/lib/libdwarf/common/dwarf_abbrev.c
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_abbrev.h b/usr/src/lib/libdwarf/common/dwarf_abbrev.h
index b525924c83..b525924c83 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_abbrev.h
+++ b/usr/src/lib/libdwarf/common/dwarf_abbrev.h
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_addr_finder.c b/usr/src/lib/libdwarf/common/dwarf_addr_finder.c
index 2fadefc1ea..2fadefc1ea 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_addr_finder.c
+++ b/usr/src/lib/libdwarf/common/dwarf_addr_finder.c
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_alloc.c b/usr/src/lib/libdwarf/common/dwarf_alloc.c
index ddb423e841..ddb423e841 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_alloc.c
+++ b/usr/src/lib/libdwarf/common/dwarf_alloc.c
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_alloc.h b/usr/src/lib/libdwarf/common/dwarf_alloc.h
index 3a61c692c6..3a61c692c6 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_alloc.h
+++ b/usr/src/lib/libdwarf/common/dwarf_alloc.h
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_arange.c b/usr/src/lib/libdwarf/common/dwarf_arange.c
index e7ad8acc5e..e7ad8acc5e 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_arange.c
+++ b/usr/src/lib/libdwarf/common/dwarf_arange.c
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_arange.h b/usr/src/lib/libdwarf/common/dwarf_arange.h
index d6c537c452..d6c537c452 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_arange.h
+++ b/usr/src/lib/libdwarf/common/dwarf_arange.h
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_base_types.h b/usr/src/lib/libdwarf/common/dwarf_base_types.h
index 00e2700a81..00e2700a81 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_base_types.h
+++ b/usr/src/lib/libdwarf/common/dwarf_base_types.h
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_die_deliv.c b/usr/src/lib/libdwarf/common/dwarf_die_deliv.c
index 4ba9f2aded..4ba9f2aded 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_die_deliv.c
+++ b/usr/src/lib/libdwarf/common/dwarf_die_deliv.c
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_die_deliv.h b/usr/src/lib/libdwarf/common/dwarf_die_deliv.h
index f1ecb153ba..f1ecb153ba 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_die_deliv.h
+++ b/usr/src/lib/libdwarf/common/dwarf_die_deliv.h
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_elf_access.c b/usr/src/lib/libdwarf/common/dwarf_elf_access.c
index 6caa64a758..6caa64a758 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_elf_access.c
+++ b/usr/src/lib/libdwarf/common/dwarf_elf_access.c
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_elf_access.h b/usr/src/lib/libdwarf/common/dwarf_elf_access.h
index fd52c17938..fd52c17938 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_elf_access.h
+++ b/usr/src/lib/libdwarf/common/dwarf_elf_access.h
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_error.c b/usr/src/lib/libdwarf/common/dwarf_error.c
index 7327529820..7327529820 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_error.c
+++ b/usr/src/lib/libdwarf/common/dwarf_error.c
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_error.h b/usr/src/lib/libdwarf/common/dwarf_error.h
index 27acf70db0..27acf70db0 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_error.h
+++ b/usr/src/lib/libdwarf/common/dwarf_error.h
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_form.c b/usr/src/lib/libdwarf/common/dwarf_form.c
index fcdd64230c..fcdd64230c 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_form.c
+++ b/usr/src/lib/libdwarf/common/dwarf_form.c
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_frame.c b/usr/src/lib/libdwarf/common/dwarf_frame.c
index 3a825ee925..3a825ee925 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_frame.c
+++ b/usr/src/lib/libdwarf/common/dwarf_frame.c
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_frame.h b/usr/src/lib/libdwarf/common/dwarf_frame.h
index ceb686335b..ceb686335b 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_frame.h
+++ b/usr/src/lib/libdwarf/common/dwarf_frame.h
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_frame2.c b/usr/src/lib/libdwarf/common/dwarf_frame2.c
index 01b9ec497b..01b9ec497b 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_frame2.c
+++ b/usr/src/lib/libdwarf/common/dwarf_frame2.c
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_frame3.c b/usr/src/lib/libdwarf/common/dwarf_frame3.c
index 7bd8ec86d5..7bd8ec86d5 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_frame3.c
+++ b/usr/src/lib/libdwarf/common/dwarf_frame3.c
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_funcs.c b/usr/src/lib/libdwarf/common/dwarf_funcs.c
index 8d725ae33f..8d725ae33f 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_funcs.c
+++ b/usr/src/lib/libdwarf/common/dwarf_funcs.c
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_funcs.h b/usr/src/lib/libdwarf/common/dwarf_funcs.h
index bf91c32157..bf91c32157 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_funcs.h
+++ b/usr/src/lib/libdwarf/common/dwarf_funcs.h
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_global.c b/usr/src/lib/libdwarf/common/dwarf_global.c
index d1c090fa43..d1c090fa43 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_global.c
+++ b/usr/src/lib/libdwarf/common/dwarf_global.c
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_global.h b/usr/src/lib/libdwarf/common/dwarf_global.h
index c2bc2cdcc3..c2bc2cdcc3 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_global.h
+++ b/usr/src/lib/libdwarf/common/dwarf_global.h
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_harmless.c b/usr/src/lib/libdwarf/common/dwarf_harmless.c
index 16dbe4bc97..16dbe4bc97 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_harmless.c
+++ b/usr/src/lib/libdwarf/common/dwarf_harmless.c
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_harmless.h b/usr/src/lib/libdwarf/common/dwarf_harmless.h
index 3d4d910ce9..3d4d910ce9 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_harmless.h
+++ b/usr/src/lib/libdwarf/common/dwarf_harmless.h
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_incl.h b/usr/src/lib/libdwarf/common/dwarf_incl.h
index df2fbf334c..df2fbf334c 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_incl.h
+++ b/usr/src/lib/libdwarf/common/dwarf_incl.h
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_init_finish.c b/usr/src/lib/libdwarf/common/dwarf_init_finish.c
index 1ab9d5fd38..1ab9d5fd38 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_init_finish.c
+++ b/usr/src/lib/libdwarf/common/dwarf_init_finish.c
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_leb.c b/usr/src/lib/libdwarf/common/dwarf_leb.c
index b3b5d262f5..b3b5d262f5 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_leb.c
+++ b/usr/src/lib/libdwarf/common/dwarf_leb.c
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_line.c b/usr/src/lib/libdwarf/common/dwarf_line.c
index e7e15e7c1a..e7e15e7c1a 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_line.c
+++ b/usr/src/lib/libdwarf/common/dwarf_line.c
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_line.h b/usr/src/lib/libdwarf/common/dwarf_line.h
index 66d6062754..66d6062754 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_line.h
+++ b/usr/src/lib/libdwarf/common/dwarf_line.h
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_line2.c b/usr/src/lib/libdwarf/common/dwarf_line2.c
index 634b848167..634b848167 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_line2.c
+++ b/usr/src/lib/libdwarf/common/dwarf_line2.c
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_loc.c b/usr/src/lib/libdwarf/common/dwarf_loc.c
index f28b27b630..f28b27b630 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_loc.c
+++ b/usr/src/lib/libdwarf/common/dwarf_loc.c
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_loc.h b/usr/src/lib/libdwarf/common/dwarf_loc.h
index 685d199f29..685d199f29 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_loc.h
+++ b/usr/src/lib/libdwarf/common/dwarf_loc.h
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_macro.c b/usr/src/lib/libdwarf/common/dwarf_macro.c
index e1ff976d8c..e1ff976d8c 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_macro.c
+++ b/usr/src/lib/libdwarf/common/dwarf_macro.c
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_macro.h b/usr/src/lib/libdwarf/common/dwarf_macro.h
index 31ea2e6e67..31ea2e6e67 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_macro.h
+++ b/usr/src/lib/libdwarf/common/dwarf_macro.h
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_names.c b/usr/src/lib/libdwarf/common/dwarf_names.c
index 417e025690..417e025690 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_names.c
+++ b/usr/src/lib/libdwarf/common/dwarf_names.c
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_names.h b/usr/src/lib/libdwarf/common/dwarf_names.h
index 6edafa5fdd..6edafa5fdd 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_names.h
+++ b/usr/src/lib/libdwarf/common/dwarf_names.h
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_opaque.h b/usr/src/lib/libdwarf/common/dwarf_opaque.h
index b235a9c7b4..b235a9c7b4 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_opaque.h
+++ b/usr/src/lib/libdwarf/common/dwarf_opaque.h
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_original_elf_init.c b/usr/src/lib/libdwarf/common/dwarf_original_elf_init.c
index a6d943da0a..a6d943da0a 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_original_elf_init.c
+++ b/usr/src/lib/libdwarf/common/dwarf_original_elf_init.c
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_print_lines.c b/usr/src/lib/libdwarf/common/dwarf_print_lines.c
index 30c4889ee5..30c4889ee5 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_print_lines.c
+++ b/usr/src/lib/libdwarf/common/dwarf_print_lines.c
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_pubtypes.c b/usr/src/lib/libdwarf/common/dwarf_pubtypes.c
index 330c1c6adc..330c1c6adc 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_pubtypes.c
+++ b/usr/src/lib/libdwarf/common/dwarf_pubtypes.c
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_query.c b/usr/src/lib/libdwarf/common/dwarf_query.c
index 3f21abd039..3f21abd039 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_query.c
+++ b/usr/src/lib/libdwarf/common/dwarf_query.c
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_ranges.c b/usr/src/lib/libdwarf/common/dwarf_ranges.c
index ae6d5cf9b5..ae6d5cf9b5 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_ranges.c
+++ b/usr/src/lib/libdwarf/common/dwarf_ranges.c
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_sort_line.c b/usr/src/lib/libdwarf/common/dwarf_sort_line.c
index 3576614129..3576614129 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_sort_line.c
+++ b/usr/src/lib/libdwarf/common/dwarf_sort_line.c
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_string.c b/usr/src/lib/libdwarf/common/dwarf_string.c
index fafa5a097c..fafa5a097c 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_string.c
+++ b/usr/src/lib/libdwarf/common/dwarf_string.c
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_stubs.c b/usr/src/lib/libdwarf/common/dwarf_stubs.c
index f2c1f7fd45..f2c1f7fd45 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_stubs.c
+++ b/usr/src/lib/libdwarf/common/dwarf_stubs.c
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_types.c b/usr/src/lib/libdwarf/common/dwarf_types.c
index d547805289..d547805289 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_types.c
+++ b/usr/src/lib/libdwarf/common/dwarf_types.c
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_types.h b/usr/src/lib/libdwarf/common/dwarf_types.h
index ebd31c6c79..ebd31c6c79 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_types.h
+++ b/usr/src/lib/libdwarf/common/dwarf_types.h
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_util.c b/usr/src/lib/libdwarf/common/dwarf_util.c
index 01e0dd755d..01e0dd755d 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_util.c
+++ b/usr/src/lib/libdwarf/common/dwarf_util.c
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_util.h b/usr/src/lib/libdwarf/common/dwarf_util.h
index 4046bb2478..4046bb2478 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_util.h
+++ b/usr/src/lib/libdwarf/common/dwarf_util.h
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_vars.c b/usr/src/lib/libdwarf/common/dwarf_vars.c
index 24105289ba..24105289ba 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_vars.c
+++ b/usr/src/lib/libdwarf/common/dwarf_vars.c
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_vars.h b/usr/src/lib/libdwarf/common/dwarf_vars.h
index bd5f967e48..bd5f967e48 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_vars.h
+++ b/usr/src/lib/libdwarf/common/dwarf_vars.h
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_weaks.c b/usr/src/lib/libdwarf/common/dwarf_weaks.c
index 425916e62e..425916e62e 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_weaks.c
+++ b/usr/src/lib/libdwarf/common/dwarf_weaks.c
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_weaks.h b/usr/src/lib/libdwarf/common/dwarf_weaks.h
index d38f5f118a..d38f5f118a 100644
--- a/usr/src/tools/ctf/dwarf/common/dwarf_weaks.h
+++ b/usr/src/lib/libdwarf/common/dwarf_weaks.h
diff --git a/usr/src/tools/ctf/dwarf/common/libdwarf.h b/usr/src/lib/libdwarf/common/libdwarf.h
index 78627a96a6..78627a96a6 100644
--- a/usr/src/tools/ctf/dwarf/common/libdwarf.h
+++ b/usr/src/lib/libdwarf/common/libdwarf.h
diff --git a/usr/src/tools/ctf/dwarf/common/libdwarfdefs.h b/usr/src/lib/libdwarf/common/libdwarfdefs.h
index a564655b23..a564655b23 100644
--- a/usr/src/tools/ctf/dwarf/common/libdwarfdefs.h
+++ b/usr/src/lib/libdwarf/common/libdwarfdefs.h
diff --git a/usr/src/tools/ctf/dwarf/common/malloc_check.c b/usr/src/lib/libdwarf/common/malloc_check.c
index 1c6e7738e4..1c6e7738e4 100644
--- a/usr/src/tools/ctf/dwarf/common/malloc_check.c
+++ b/usr/src/lib/libdwarf/common/malloc_check.c
diff --git a/usr/src/tools/ctf/dwarf/common/malloc_check.h b/usr/src/lib/libdwarf/common/malloc_check.h
index ba1ad3da71..ba1ad3da71 100644
--- a/usr/src/tools/ctf/dwarf/common/malloc_check.h
+++ b/usr/src/lib/libdwarf/common/malloc_check.h
diff --git a/usr/src/tools/ctf/dwarf/common/mapfile-vers b/usr/src/lib/libdwarf/common/mapfile-vers
index c1a652a591..c1a652a591 100644
--- a/usr/src/tools/ctf/dwarf/common/mapfile-vers
+++ b/usr/src/lib/libdwarf/common/mapfile-vers
diff --git a/usr/src/tools/ctf/dwarf/common/pro_alloc.c b/usr/src/lib/libdwarf/common/pro_alloc.c
index 1ca7806239..1ca7806239 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_alloc.c
+++ b/usr/src/lib/libdwarf/common/pro_alloc.c
diff --git a/usr/src/tools/ctf/dwarf/common/pro_alloc.h b/usr/src/lib/libdwarf/common/pro_alloc.h
index b4da65325f..b4da65325f 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_alloc.h
+++ b/usr/src/lib/libdwarf/common/pro_alloc.h
diff --git a/usr/src/tools/ctf/dwarf/common/pro_arange.c b/usr/src/lib/libdwarf/common/pro_arange.c
index 4e5c37795c..4e5c37795c 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_arange.c
+++ b/usr/src/lib/libdwarf/common/pro_arange.c
diff --git a/usr/src/tools/ctf/dwarf/common/pro_arange.h b/usr/src/lib/libdwarf/common/pro_arange.h
index f0e7e84dff..f0e7e84dff 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_arange.h
+++ b/usr/src/lib/libdwarf/common/pro_arange.h
diff --git a/usr/src/tools/ctf/dwarf/common/pro_die.c b/usr/src/lib/libdwarf/common/pro_die.c
index 948b641146..948b641146 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_die.c
+++ b/usr/src/lib/libdwarf/common/pro_die.c
diff --git a/usr/src/tools/ctf/dwarf/common/pro_die.h b/usr/src/lib/libdwarf/common/pro_die.h
index 01c00e79bd..01c00e79bd 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_die.h
+++ b/usr/src/lib/libdwarf/common/pro_die.h
diff --git a/usr/src/tools/ctf/dwarf/common/pro_encode_nm.c b/usr/src/lib/libdwarf/common/pro_encode_nm.c
index d6215dc56b..d6215dc56b 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_encode_nm.c
+++ b/usr/src/lib/libdwarf/common/pro_encode_nm.c
diff --git a/usr/src/tools/ctf/dwarf/common/pro_encode_nm.h b/usr/src/lib/libdwarf/common/pro_encode_nm.h
index d08e4d5148..d08e4d5148 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_encode_nm.h
+++ b/usr/src/lib/libdwarf/common/pro_encode_nm.h
diff --git a/usr/src/tools/ctf/dwarf/common/pro_error.c b/usr/src/lib/libdwarf/common/pro_error.c
index d408a391e2..d408a391e2 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_error.c
+++ b/usr/src/lib/libdwarf/common/pro_error.c
diff --git a/usr/src/tools/ctf/dwarf/common/pro_error.h b/usr/src/lib/libdwarf/common/pro_error.h
index c37035301b..c37035301b 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_error.h
+++ b/usr/src/lib/libdwarf/common/pro_error.h
diff --git a/usr/src/tools/ctf/dwarf/common/pro_expr.c b/usr/src/lib/libdwarf/common/pro_expr.c
index 4c701748a6..4c701748a6 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_expr.c
+++ b/usr/src/lib/libdwarf/common/pro_expr.c
diff --git a/usr/src/tools/ctf/dwarf/common/pro_expr.h b/usr/src/lib/libdwarf/common/pro_expr.h
index 202f2d30d5..202f2d30d5 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_expr.h
+++ b/usr/src/lib/libdwarf/common/pro_expr.h
diff --git a/usr/src/tools/ctf/dwarf/common/pro_finish.c b/usr/src/lib/libdwarf/common/pro_finish.c
index bc43a5f0f4..bc43a5f0f4 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_finish.c
+++ b/usr/src/lib/libdwarf/common/pro_finish.c
diff --git a/usr/src/tools/ctf/dwarf/common/pro_forms.c b/usr/src/lib/libdwarf/common/pro_forms.c
index fec9a39c60..fec9a39c60 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_forms.c
+++ b/usr/src/lib/libdwarf/common/pro_forms.c
diff --git a/usr/src/tools/ctf/dwarf/common/pro_frame.c b/usr/src/lib/libdwarf/common/pro_frame.c
index bd1ef6a637..bd1ef6a637 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_frame.c
+++ b/usr/src/lib/libdwarf/common/pro_frame.c
diff --git a/usr/src/tools/ctf/dwarf/common/pro_frame.h b/usr/src/lib/libdwarf/common/pro_frame.h
index df60d369ed..df60d369ed 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_frame.h
+++ b/usr/src/lib/libdwarf/common/pro_frame.h
diff --git a/usr/src/tools/ctf/dwarf/common/pro_funcs.c b/usr/src/lib/libdwarf/common/pro_funcs.c
index 8ff05500bb..8ff05500bb 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_funcs.c
+++ b/usr/src/lib/libdwarf/common/pro_funcs.c
diff --git a/usr/src/tools/ctf/dwarf/common/pro_incl.h b/usr/src/lib/libdwarf/common/pro_incl.h
index 10bce470c2..10bce470c2 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_incl.h
+++ b/usr/src/lib/libdwarf/common/pro_incl.h
diff --git a/usr/src/tools/ctf/dwarf/common/pro_init.c b/usr/src/lib/libdwarf/common/pro_init.c
index d696113a67..d696113a67 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_init.c
+++ b/usr/src/lib/libdwarf/common/pro_init.c
diff --git a/usr/src/tools/ctf/dwarf/common/pro_line.c b/usr/src/lib/libdwarf/common/pro_line.c
index 69d3e339f0..69d3e339f0 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_line.c
+++ b/usr/src/lib/libdwarf/common/pro_line.c
diff --git a/usr/src/tools/ctf/dwarf/common/pro_line.h b/usr/src/lib/libdwarf/common/pro_line.h
index eed941239d..eed941239d 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_line.h
+++ b/usr/src/lib/libdwarf/common/pro_line.h
diff --git a/usr/src/tools/ctf/dwarf/common/pro_macinfo.c b/usr/src/lib/libdwarf/common/pro_macinfo.c
index cfa820aee6..cfa820aee6 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_macinfo.c
+++ b/usr/src/lib/libdwarf/common/pro_macinfo.c
diff --git a/usr/src/tools/ctf/dwarf/common/pro_macinfo.h b/usr/src/lib/libdwarf/common/pro_macinfo.h
index 852a0cec1f..852a0cec1f 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_macinfo.h
+++ b/usr/src/lib/libdwarf/common/pro_macinfo.h
diff --git a/usr/src/tools/ctf/dwarf/common/pro_opaque.h b/usr/src/lib/libdwarf/common/pro_opaque.h
index befc69faa6..befc69faa6 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_opaque.h
+++ b/usr/src/lib/libdwarf/common/pro_opaque.h
diff --git a/usr/src/tools/ctf/dwarf/common/pro_pubnames.c b/usr/src/lib/libdwarf/common/pro_pubnames.c
index e07fe35943..e07fe35943 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_pubnames.c
+++ b/usr/src/lib/libdwarf/common/pro_pubnames.c
diff --git a/usr/src/tools/ctf/dwarf/common/pro_reloc.c b/usr/src/lib/libdwarf/common/pro_reloc.c
index 66f16acbd0..66f16acbd0 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_reloc.c
+++ b/usr/src/lib/libdwarf/common/pro_reloc.c
diff --git a/usr/src/tools/ctf/dwarf/common/pro_reloc.h b/usr/src/lib/libdwarf/common/pro_reloc.h
index d2e6c67357..d2e6c67357 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_reloc.h
+++ b/usr/src/lib/libdwarf/common/pro_reloc.h
diff --git a/usr/src/tools/ctf/dwarf/common/pro_reloc_stream.c b/usr/src/lib/libdwarf/common/pro_reloc_stream.c
index 459779ceda..459779ceda 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_reloc_stream.c
+++ b/usr/src/lib/libdwarf/common/pro_reloc_stream.c
diff --git a/usr/src/tools/ctf/dwarf/common/pro_reloc_stream.h b/usr/src/lib/libdwarf/common/pro_reloc_stream.h
index 892ea5baf3..892ea5baf3 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_reloc_stream.h
+++ b/usr/src/lib/libdwarf/common/pro_reloc_stream.h
diff --git a/usr/src/tools/ctf/dwarf/common/pro_reloc_symbolic.c b/usr/src/lib/libdwarf/common/pro_reloc_symbolic.c
index 22080a00cd..22080a00cd 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_reloc_symbolic.c
+++ b/usr/src/lib/libdwarf/common/pro_reloc_symbolic.c
diff --git a/usr/src/tools/ctf/dwarf/common/pro_reloc_symbolic.h b/usr/src/lib/libdwarf/common/pro_reloc_symbolic.h
index 3d03a47863..3d03a47863 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_reloc_symbolic.h
+++ b/usr/src/lib/libdwarf/common/pro_reloc_symbolic.h
diff --git a/usr/src/tools/ctf/dwarf/common/pro_section.c b/usr/src/lib/libdwarf/common/pro_section.c
index 6503c2cf09..6503c2cf09 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_section.c
+++ b/usr/src/lib/libdwarf/common/pro_section.c
diff --git a/usr/src/tools/ctf/dwarf/common/pro_section.h b/usr/src/lib/libdwarf/common/pro_section.h
index b37ade44dc..b37ade44dc 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_section.h
+++ b/usr/src/lib/libdwarf/common/pro_section.h
diff --git a/usr/src/tools/ctf/dwarf/common/pro_types.c b/usr/src/lib/libdwarf/common/pro_types.c
index 1f3f93280c..1f3f93280c 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_types.c
+++ b/usr/src/lib/libdwarf/common/pro_types.c
diff --git a/usr/src/tools/ctf/dwarf/common/pro_types.h b/usr/src/lib/libdwarf/common/pro_types.h
index 817609215b..817609215b 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_types.h
+++ b/usr/src/lib/libdwarf/common/pro_types.h
diff --git a/usr/src/tools/ctf/dwarf/common/pro_util.h b/usr/src/lib/libdwarf/common/pro_util.h
index 56bde8bda6..56bde8bda6 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_util.h
+++ b/usr/src/lib/libdwarf/common/pro_util.h
diff --git a/usr/src/tools/ctf/dwarf/common/pro_vars.c b/usr/src/lib/libdwarf/common/pro_vars.c
index 3e75a9d9af..3e75a9d9af 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_vars.c
+++ b/usr/src/lib/libdwarf/common/pro_vars.c
diff --git a/usr/src/tools/ctf/dwarf/common/pro_weaks.c b/usr/src/lib/libdwarf/common/pro_weaks.c
index 8c74bf08ca..8c74bf08ca 100644
--- a/usr/src/tools/ctf/dwarf/common/pro_weaks.c
+++ b/usr/src/lib/libdwarf/common/pro_weaks.c
diff --git a/usr/src/lib/libdwarf/i386/Makefile b/usr/src/lib/libdwarf/i386/Makefile
new file mode 100644
index 0000000000..4398507523
--- /dev/null
+++ b/usr/src/lib/libdwarf/i386/Makefile
@@ -0,0 +1,18 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2015 Joyent, Inc.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS)
diff --git a/usr/src/lib/libdwarf/sparc/Makefile b/usr/src/lib/libdwarf/sparc/Makefile
new file mode 100644
index 0000000000..4398507523
--- /dev/null
+++ b/usr/src/lib/libdwarf/sparc/Makefile
@@ -0,0 +1,18 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2015 Joyent, Inc.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS)
diff --git a/usr/src/lib/libdwarf/sparcv9/Makefile b/usr/src/lib/libdwarf/sparcv9/Makefile
new file mode 100644
index 0000000000..4e7833710f
--- /dev/null
+++ b/usr/src/lib/libdwarf/sparcv9/Makefile
@@ -0,0 +1,19 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2015 Joyent, Inc.
+#
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
diff --git a/usr/src/lib/mergeq/mergeq.c b/usr/src/lib/mergeq/mergeq.c
new file mode 100644
index 0000000000..fd9a9c32ea
--- /dev/null
+++ b/usr/src/lib/mergeq/mergeq.c
@@ -0,0 +1,606 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2015 Joyent, Inc.
+ */
+
+/*
+ * Merge queue
+ *
+ * A multi-threaded merging queue.
+ *
+ * The general constraint of the merge queue is that if a set of items are
+ * inserted into the queue in the same order, then no matter how many threads
+ * are on the scene, we will always process the items in the same order. The
+ * secondary constraint is that to support environments that must be
+ * single-threaded, we explicitly *must not* create a thread in the case where
+ * the number of requested threads is just one.
+ *
+ * To that end, we've designed our queue as a circular buffer. We will grow that
+ * buffer to contain enough space for all the input items, after which we'll
+ * then treat it as a circular buffer.
+ *
+ * Items will be issued to a processing function two at a time, until there is
+ * only one item remaining in the queue, at which point we will be doing doing
+ * any merging work.
+ *
+ * A given queue has three different entries that we care about tracking:
+ *
+ * o mq_nproc - What is the slot of the next item to process for something
+ * looking for work.
+ *
+ * o mq_next - What is the slot of the next item that should be inserted into
+ * the queue.
+ *
+ * o mq_ncommit - What is the slot of the next item that should be committed.
+ *
+ * When a thread comes and looks for work, we pop entries off of the queue based
+ * on the index provided by mq_nproc. At the same time, it also gets the slot
+ * that it should place the result in, which is mq_next. However, because we
+ * have multiple threads that are operating on the system, we want to make sure
+ * that we push things onto the queue in order. We do that by allocating a slot
+ * to each task and when it completes, it waits for its slot to be ready based
+ * on it being the value of mq_ncommit.
+ *
+ * In addition, we keep track of the number of items in the queue as well as the
+ * number of active workers. There's also a generation count that is used to
+ * figure out when the various values might lap one another.
+ *
+ * The following images show what happens when we have a queue with six items
+ * and whose capacity has been shrunk to six, to better fit in the screen.
+ *
+ *
+ * 1) This is the initial configuration of the queue right before any processing
+ * is done in the context of mergeq_merge(). Every box has an initial item for
+ * merging in it (represented by an 'x'). Here, the mq_nproc, mq_next, and
+ * mq_ncommit will all point at the initial entry. However, the mq_next has
+ * already lapped around the array and thus has a generation count of one.
+ *
+ * The '+' characters indicate which bucket the corresponding value of mq_nproc,
+ * mq_ncommit, and mq_nproc.
+ *
+ * +---++---++---++---++---++---+
+ * | X || X || X || X || X || X |
+ * +---++---++---++---++---++---+
+ * mq_next (g1) +
+ * mq_ncommit (g0) +
+ * mq_nproc (g0) +
+ *
+ * 2) This shows the state right as the first thread begins to process an entry.
+ * Note in this example we will have two threads processing this queue. Note,
+ * mq_ncommit has not advanced. This is because the first thread has started
+ * processing entries, but it has not finished, and thus we can't commit it.
+ * We've incremented mq_next by one because it has gone ahead and assigned a
+ * single entry. We've incremented mq_nproc by two, because we have removed two
+ * entries and thus will have another set available.
+ *
+ * +---++---++---++---++---++---+ t1 - slot 0
+ * | || || X || X || X || X | t2 - idle
+ * +---++---++---++---++---++---+
+ * mq_next (g1) +
+ * mq_ncommit (g0) +
+ * mq_nproc (g0) +
+ *
+ *
+ * 3) This shows the state right after the second thread begins to process an
+ * entry, note that the first thread has not finished. The changes are very
+ * similar to the previous state, we've advanced, mq_nproc and mq_next, but not
+ * mq_ncommit.
+ *
+ * +---++---++---++---++---++---+ t1 - slot 0
+ * | || || || || X || X | t2 - slot 1
+ * +---++---++---++---++---++---+
+ * mq_next (g1) +
+ * mq_ncommit (g0) +
+ * mq_nproc (g0) +
+ *
+ * 4) This shows the state after thread one has finished processing an item, but
+ * before it does anything else. Note that even if thread two finishes early, it
+ * cannot commit its item until thread one finishes. Here 'Y' refers to the
+ * result of merging the first two 'X's.
+ *
+ * +---++---++---++---++---++---+ t1 - idle
+ * | Y || || || || X || X | t2 - slot 1
+ * +---++---++---++---++---++---+
+ * mq_next (g1) +
+ * mq_ncommit (g0) +
+ * mq_nproc (g0) +
+ *
+ * 5) This shows the state after thread one has begun to process the next round
+ * and after thread two has committed, but before it begins processing the next
+ * item. Note that mq_nproc has wrapped around and we've bumped its generation
+ * counter.
+ *
+ * +---++---++---++---++---++---+ t1 - slot 2
+ * | Y || Y || || || || | t2 - idle
+ * +---++---++---++---++---++---+
+ * mq_next (g1) +
+ * mq_ncommit (g0) +
+ * mq_nproc (g0) +
+ *
+ * 6) Here, thread two, will take the next two Y values and thread 1 will commit
+ * its 'Y'. Thread one now must wait until thread two finishes such that it can
+ * do additional work.
+ *
+ * +---++---++---++---++---++---+ t1 - waiting
+ * | || || Y || || || | t2 - slot 3
+ * +---++---++---++---++---++---+
+ * mq_next (g1) +
+ * mq_ncommit (g0) +
+ * mq_nproc (g0) +
+ *
+ * 7) Here, thread two has committed and thread one is about to go process the
+ * final entry. The character 'Z' represents the results of merging two 'Y's.
+ *
+ * +---++---++---++---++---++---+ t1 - idle
+ * | || || Y || Z || || | t2 - idle
+ * +---++---++---++---++---++---+
+ * mq_next (g1) +
+ * mq_ncommit (g0) +
+ * mq_nproc (g0) +
+ *
+ * 8) Here, thread one is processing the final item. Thread two is waiting in
+ * mergeq_pop() for enough items to be available. In this case, it will never
+ * happen; however, once all threads have finished it will break out.
+ *
+ * +---++---++---++---++---++---+ t1 - slot 4
+ * | || || || || || | t2 - idle
+ * +---++---++---++---++---++---+
+ * mq_next (g1) +
+ * mq_ncommit (g0) +
+ * mq_nproc (g0) +
+ *
+ * 9) This is the final state of the queue, it has a single '*' item which is
+ * the final merge result. At this point, both thread one and thread two would
+ * stop processing and we'll return the result to the user.
+ *
+ * +---++---++---++---++---++---+ t1 - slot 4
+ * | || || || || * || | t2 - idle
+ * +---++---++---++---++---++---+
+ * mq_next (g1) +
+ * mq_ncommit (g0) +
+ * mq_nproc (g0) +
+ *
+ *
+ * Note, that if at any point in time the processing function fails, then all
+ * the merges will quiesce and that error will be propagated back to the user.
+ */
+
+#include <strings.h>
+#include <sys/debug.h>
+#include <thread.h>
+#include <synch.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#include "mergeq.h"
+
+struct mergeq {
+ mutex_t mq_lock; /* Protects items below */
+ cond_t mq_cond; /* Condition variable */
+ void **mq_items; /* Array of items to process */
+ size_t mq_nitems; /* Number of items in the queue */
+ size_t mq_cap; /* Capacity of the items */
+ size_t mq_next; /* Place to put next entry */
+ size_t mq_gnext; /* Generation for next */
+ size_t mq_nproc; /* Index of next thing to process */
+ size_t mq_gnproc; /* Generation for next proc */
+ size_t mq_ncommit; /* Index of the next thing to commit */
+ size_t mq_gncommit; /* Commit generation */
+ uint_t mq_nactthrs; /* Number of active threads */
+ uint_t mq_ndthreads; /* Desired number of threads */
+ thread_t *mq_thrs; /* Actual threads */
+ mergeq_proc_f *mq_func; /* Processing function */
+ void *mq_arg; /* Argument for processing */
+ boolean_t mq_working; /* Are we working on processing */
+ boolean_t mq_iserror; /* Have we encountered an error? */
+ int mq_error;
+};
+
+#define MERGEQ_DEFAULT_CAP 64
+
+static int
+mergeq_error(int err)
+{
+ errno = err;
+ return (MERGEQ_ERROR);
+}
+
+void
+mergeq_fini(mergeq_t *mqp)
+{
+ if (mqp == NULL)
+ return;
+
+ VERIFY(mqp->mq_working != B_TRUE);
+
+ if (mqp->mq_items != NULL)
+ mergeq_free(mqp->mq_items, sizeof (void *) * mqp->mq_cap);
+ if (mqp->mq_ndthreads > 0) {
+ mergeq_free(mqp->mq_thrs, sizeof (thread_t) *
+ mqp->mq_ndthreads);
+ }
+ VERIFY0(cond_destroy(&mqp->mq_cond));
+ VERIFY0(mutex_destroy(&mqp->mq_lock));
+ mergeq_free(mqp, sizeof (mergeq_t));
+}
+
+int
+mergeq_init(mergeq_t **outp, uint_t nthrs)
+{
+ int ret;
+ mergeq_t *mqp;
+
+ mqp = mergeq_alloc(sizeof (mergeq_t));
+ if (mqp == NULL)
+ return (mergeq_error(ENOMEM));
+
+ bzero(mqp, sizeof (mergeq_t));
+ mqp->mq_items = mergeq_alloc(sizeof (void *) * MERGEQ_DEFAULT_CAP);
+ if (mqp->mq_items == NULL) {
+ mergeq_free(mqp, sizeof (mergeq_t));
+ return (mergeq_error(ENOMEM));
+ }
+ bzero(mqp->mq_items, sizeof (void *) * MERGEQ_DEFAULT_CAP);
+
+ mqp->mq_ndthreads = nthrs - 1;
+ if (mqp->mq_ndthreads > 0) {
+ mqp->mq_thrs = mergeq_alloc(sizeof (thread_t) *
+ mqp->mq_ndthreads);
+ if (mqp->mq_thrs == NULL) {
+ mergeq_free(mqp->mq_items, sizeof (void *) *
+ MERGEQ_DEFAULT_CAP);
+ mergeq_free(mqp, sizeof (mergeq_t));
+ return (mergeq_error(ENOMEM));
+ }
+ }
+
+ if ((ret = mutex_init(&mqp->mq_lock, USYNC_THREAD | LOCK_ERRORCHECK,
+ NULL)) != 0) {
+ if (mqp->mq_ndthreads > 0) {
+ mergeq_free(mqp->mq_thrs,
+ sizeof (thread_t) * mqp->mq_ndthreads);
+ }
+ mergeq_free(mqp->mq_items, sizeof (void *) *
+ MERGEQ_DEFAULT_CAP);
+ mergeq_free(mqp, sizeof (mergeq_t));
+ return (mergeq_error(ret));
+ }
+
+ if ((ret = cond_init(&mqp->mq_cond, USYNC_THREAD, NULL)) != 0) {
+ VERIFY0(mutex_destroy(&mqp->mq_lock));
+ if (mqp->mq_ndthreads > 0) {
+ mergeq_free(mqp->mq_thrs,
+ sizeof (thread_t) * mqp->mq_ndthreads);
+ }
+ mergeq_free(mqp->mq_items, sizeof (void *) *
+ MERGEQ_DEFAULT_CAP);
+ mergeq_free(mqp, sizeof (mergeq_t));
+ return (mergeq_error(ret));
+ }
+
+ mqp->mq_cap = MERGEQ_DEFAULT_CAP;
+ *outp = mqp;
+ return (0);
+}
+
+static void
+mergeq_reset(mergeq_t *mqp)
+{
+ VERIFY(MUTEX_HELD(&mqp->mq_lock));
+ VERIFY(mqp->mq_working == B_FALSE);
+ if (mqp->mq_cap != 0)
+ bzero(mqp->mq_items, sizeof (void *) * mqp->mq_cap);
+ mqp->mq_nitems = 0;
+ mqp->mq_next = 0;
+ mqp->mq_gnext = 0;
+ mqp->mq_nproc = 0;
+ mqp->mq_gnproc = 0;
+ mqp->mq_ncommit = 0;
+ mqp->mq_gncommit = 0;
+ mqp->mq_func = NULL;
+ mqp->mq_arg = NULL;
+ mqp->mq_iserror = B_FALSE;
+ mqp->mq_error = 0;
+}
+
+static int
+mergeq_grow(mergeq_t *mqp)
+{
+ size_t ncap;
+ void **items;
+
+ VERIFY(MUTEX_HELD(&mqp->mq_lock));
+ VERIFY(mqp->mq_working == B_FALSE);
+
+ if (SIZE_MAX - mqp->mq_cap < MERGEQ_DEFAULT_CAP)
+ return (ENOSPC);
+
+ ncap = mqp->mq_cap + MERGEQ_DEFAULT_CAP;
+ items = mergeq_alloc(ncap * sizeof (void *));
+ if (items == NULL)
+ return (ENOMEM);
+
+ bzero(items, ncap * sizeof (void *));
+ bcopy(mqp->mq_items, items, mqp->mq_cap * sizeof (void *));
+ mergeq_free(mqp->mq_items, sizeof (mqp->mq_cap) * sizeof (void *));
+ mqp->mq_items = items;
+ mqp->mq_cap = ncap;
+ return (0);
+}
+
+int
+mergeq_add(mergeq_t *mqp, void *item)
+{
+ VERIFY0(mutex_lock(&mqp->mq_lock));
+ if (mqp->mq_working == B_TRUE) {
+ VERIFY0(mutex_unlock(&mqp->mq_lock));
+ return (mergeq_error(ENXIO));
+ }
+
+ if (mqp->mq_next == mqp->mq_cap) {
+ int ret;
+
+ if ((ret = mergeq_grow(mqp)) != 0) {
+ VERIFY0(mutex_unlock(&mqp->mq_lock));
+ return (mergeq_error(ret));
+ }
+ }
+ mqp->mq_items[mqp->mq_next] = item;
+ mqp->mq_next++;
+ mqp->mq_nitems++;
+
+ VERIFY0(mutex_unlock(&mqp->mq_lock));
+ return (0);
+}
+
+static size_t
+mergeq_slot(mergeq_t *mqp)
+{
+ size_t s;
+
+ VERIFY(MUTEX_HELD(&mqp->mq_lock));
+ VERIFY(mqp->mq_next < mqp->mq_cap);
+
+ /*
+ * This probably should be a cv / wait thing.
+ */
+ VERIFY(mqp->mq_nproc != (mqp->mq_next + 1) % mqp->mq_cap);
+
+ s = mqp->mq_next;
+ mqp->mq_next++;
+ if (mqp->mq_next == mqp->mq_cap) {
+ mqp->mq_next %= mqp->mq_cap;
+ mqp->mq_gnext++;
+ }
+
+ return (s);
+}
+
+/*
+ * Internal function to push items onto the queue which is now a circular
+ * buffer. This should only be used once we begin working on the queue.
+ */
+static void
+mergeq_push(mergeq_t *mqp, size_t slot, void *item)
+{
+ VERIFY(MUTEX_HELD(&mqp->mq_lock));
+ VERIFY(slot < mqp->mq_cap);
+
+ /*
+ * We need to verify that we don't push over something that exists.
+ * Based on the design, this should never happen. However, in the face
+ * of bugs, anything is possible.
+ */
+ while (mqp->mq_ncommit != slot && mqp->mq_iserror == B_FALSE)
+ (void) cond_wait(&mqp->mq_cond, &mqp->mq_lock);
+
+ if (mqp->mq_iserror == B_TRUE)
+ return;
+
+ mqp->mq_items[slot] = item;
+ mqp->mq_nitems++;
+ mqp->mq_ncommit++;
+ if (mqp->mq_ncommit == mqp->mq_cap) {
+ mqp->mq_ncommit %= mqp->mq_cap;
+ mqp->mq_gncommit++;
+ }
+ cond_broadcast(&mqp->mq_cond);
+}
+
+static void *
+mergeq_pop_one(mergeq_t *mqp)
+{
+ void *out;
+
+ /*
+ * We can't move mq_nproc beyond mq_next if they're on the same
+ * generation.
+ */
+ VERIFY(mqp->mq_gnext != mqp->mq_gnproc ||
+ mqp->mq_nproc != mqp->mq_next);
+
+ out = mqp->mq_items[mqp->mq_nproc];
+
+ mqp->mq_items[mqp->mq_nproc] = NULL;
+ mqp->mq_nproc++;
+ if (mqp->mq_nproc == mqp->mq_cap) {
+ mqp->mq_nproc %= mqp->mq_cap;
+ mqp->mq_gnproc++;
+ }
+ mqp->mq_nitems--;
+
+ return (out);
+}
+
+/*
+ * Pop a set of two entries from the queue. We may not have anything to process
+ * at the moment, eg. be waiting for someone to add something. In which case,
+ * we'll be sitting and waiting.
+ */
+static boolean_t
+mergeq_pop(mergeq_t *mqp, void **first, void **second)
+{
+ VERIFY(MUTEX_HELD(&mqp->mq_lock));
+ VERIFY(mqp->mq_nproc < mqp->mq_cap);
+
+ while (mqp->mq_nitems < 2 && mqp->mq_nactthrs > 0 &&
+ mqp->mq_iserror == B_FALSE)
+ (void) cond_wait(&mqp->mq_cond, &mqp->mq_lock);
+
+ if (mqp->mq_iserror == B_TRUE)
+ return (B_FALSE);
+
+ if (mqp->mq_nitems < 2 && mqp->mq_nactthrs == 0) {
+ VERIFY(mqp->mq_iserror == B_TRUE || mqp->mq_nitems == 1);
+ return (B_FALSE);
+ }
+ VERIFY(mqp->mq_nitems >= 2);
+
+ *first = mergeq_pop_one(mqp);
+ *second = mergeq_pop_one(mqp);
+
+ return (B_TRUE);
+}
+
+static void *
+mergeq_thr_merge(void *arg)
+{
+ mergeq_t *mqp = arg;
+
+ VERIFY0(mutex_lock(&mqp->mq_lock));
+
+ /*
+ * Check to make sure creation worked and if not, fail fast.
+ */
+ if (mqp->mq_iserror == B_TRUE) {
+ VERIFY0(mutex_unlock(&mqp->mq_lock));
+ return (NULL);
+ }
+
+ for (;;) {
+ void *first, *second, *out;
+ int ret;
+ size_t slot;
+
+ if (mqp->mq_nitems == 1 && mqp->mq_nactthrs == 0) {
+ VERIFY0(mutex_unlock(&mqp->mq_lock));
+ return (NULL);
+ }
+
+ if (mergeq_pop(mqp, &first, &second) == B_FALSE) {
+ VERIFY0(mutex_unlock(&mqp->mq_lock));
+ return (NULL);
+ }
+ slot = mergeq_slot(mqp);
+
+ mqp->mq_nactthrs++;
+
+ VERIFY0(mutex_unlock(&mqp->mq_lock));
+ ret = mqp->mq_func(first, second, &out, mqp->mq_arg);
+ VERIFY0(mutex_lock(&mqp->mq_lock));
+
+ if (ret != 0) {
+ if (mqp->mq_iserror == B_FALSE) {
+ mqp->mq_iserror = B_TRUE;
+ mqp->mq_error = ret;
+ cond_broadcast(&mqp->mq_cond);
+ }
+ mqp->mq_nactthrs--;
+ VERIFY0(mutex_unlock(&mqp->mq_lock));
+ return (NULL);
+ }
+ mergeq_push(mqp, slot, out);
+ mqp->mq_nactthrs--;
+ }
+}
+
+int
+mergeq_merge(mergeq_t *mqp, mergeq_proc_f *func, void *arg, void **outp,
+ int *errp)
+{
+ int ret, i;
+ boolean_t seterr = B_FALSE;
+
+ if (mqp == NULL || func == NULL || outp == NULL) {
+ return (mergeq_error(EINVAL));
+ }
+
+ VERIFY0(mutex_lock(&mqp->mq_lock));
+ if (mqp->mq_working == B_TRUE) {
+ VERIFY0(mutex_unlock(&mqp->mq_lock));
+ return (mergeq_error(EBUSY));
+ }
+
+ if (mqp->mq_nitems == 0) {
+ *outp = NULL;
+ mergeq_reset(mqp);
+ VERIFY0(mutex_unlock(&mqp->mq_lock));
+ return (0);
+ }
+
+ /*
+ * Now that we've finished adding items to the queue, turn it into a
+ * circular buffer.
+ */
+ mqp->mq_func = func;
+ mqp->mq_arg = arg;
+ mqp->mq_nproc = 0;
+ mqp->mq_working = B_TRUE;
+ if (mqp->mq_next == mqp->mq_cap) {
+ mqp->mq_next %= mqp->mq_cap;
+ mqp->mq_gnext++;
+ }
+ mqp->mq_ncommit = mqp->mq_next;
+
+ ret = 0;
+ for (i = 0; i < mqp->mq_ndthreads; i++) {
+ ret = thr_create(NULL, 0, mergeq_thr_merge, mqp, 0,
+ &mqp->mq_thrs[i]);
+ if (ret != 0) {
+ mqp->mq_iserror = B_TRUE;
+ break;
+ }
+ }
+
+ VERIFY0(mutex_unlock(&mqp->mq_lock));
+ if (ret == 0)
+ (void) mergeq_thr_merge(mqp);
+
+ for (i = 0; i < mqp->mq_ndthreads; i++) {
+ VERIFY0(thr_join(mqp->mq_thrs[i], NULL, NULL));
+ }
+
+ VERIFY0(mutex_lock(&mqp->mq_lock));
+
+ VERIFY(mqp->mq_nactthrs == 0);
+ mqp->mq_working = B_FALSE;
+ if (ret == 0 && mqp->mq_iserror == B_FALSE) {
+ VERIFY(mqp->mq_nitems == 1);
+ *outp = mergeq_pop_one(mqp);
+ } else if (ret == 0 && mqp->mq_iserror == B_TRUE) {
+ ret = MERGEQ_UERROR;
+ if (errp != NULL)
+ *errp = mqp->mq_error;
+ } else {
+ seterr = B_TRUE;
+ }
+
+ mergeq_reset(mqp);
+ VERIFY0(mutex_unlock(&mqp->mq_lock));
+
+ if (seterr == B_TRUE)
+ return (mergeq_error(ret));
+
+ return (ret);
+}
diff --git a/usr/src/lib/mergeq/mergeq.h b/usr/src/lib/mergeq/mergeq.h
new file mode 100644
index 0000000000..4c1a21d696
--- /dev/null
+++ b/usr/src/lib/mergeq/mergeq.h
@@ -0,0 +1,52 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2015 Joyent, Inc.
+ */
+
+#ifndef _MERGEQ_H
+#define _MERGEQ_H
+
+/*
+ * mergeq library routines
+ */
+
+#include <sys/types.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct mergeq mergeq_t;
+typedef int (mergeq_proc_f)(void *, void *, void **, void *);
+
+extern int mergeq_init(mergeq_t **, uint_t);
+extern void mergeq_fini(mergeq_t *);
+
+extern int mergeq_add(mergeq_t *, void *);
+
+#define MERGEQ_ERROR -1
+#define MERGEQ_UERROR -2
+extern int mergeq_merge(mergeq_t *, mergeq_proc_f *, void *, void **, int *);
+
+/*
+ * Routines consumers need to implement
+ */
+extern void *mergeq_alloc(size_t);
+extern void mergeq_free(void *, size_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MERGEQ_H */
diff --git a/usr/src/lib/mergeq/workq.c b/usr/src/lib/mergeq/workq.c
new file mode 100644
index 0000000000..b9f1f2aa1c
--- /dev/null
+++ b/usr/src/lib/mergeq/workq.c
@@ -0,0 +1,311 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2015 Joyent, Inc.
+ */
+
+/*
+ * Work queue
+ *
+ * A multi-threaded work queue.
+ *
+ * The general design of this is to add a fixed number of items to the queue and
+ * then drain them with the specified number of threads.
+ */
+
+#include <strings.h>
+#include <sys/debug.h>
+#include <thread.h>
+#include <synch.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#include "workq.h"
+
+struct workq {
+ mutex_t wq_lock; /* Protects below items */
+ cond_t wq_cond; /* Condition variable */
+ void **wq_items; /* Array of items to process */
+ size_t wq_nitems; /* Number of items in queue */
+ size_t wq_cap; /* Queue capacity */
+ size_t wq_next; /* Next item to process */
+ uint_t wq_ndthreads; /* Desired number of threads */
+ thread_t *wq_thrs; /* Actual threads */
+ workq_proc_f *wq_func; /* Processing function */
+ void *wq_arg; /* Argument for processing */
+ boolean_t wq_working; /* Are we actively using it? */
+ boolean_t wq_iserror; /* Have we encountered an error? */
+ int wq_error; /* Error value, if any */
+};
+
+#define WORKQ_DEFAULT_CAP 64
+
+static int
+workq_error(int err)
+{
+ VERIFY(err != 0);
+ errno = err;
+ return (WORKQ_ERROR);
+}
+
+void
+workq_fini(workq_t *wqp)
+{
+ if (wqp == NULL)
+ return;
+
+ VERIFY(wqp->wq_working != B_TRUE);
+ VERIFY0(mutex_destroy(&wqp->wq_lock));
+ VERIFY0(cond_destroy(&wqp->wq_cond));
+ if (wqp->wq_cap > 0)
+ workq_free(wqp->wq_items, sizeof (void *) * wqp->wq_cap);
+ if (wqp->wq_ndthreads > 0)
+ workq_free(wqp->wq_thrs, sizeof (thread_t) * wqp->wq_ndthreads);
+ workq_free(wqp, sizeof (workq_t));
+}
+
+int
+workq_init(workq_t **outp, uint_t nthrs)
+{
+ int ret;
+ workq_t *wqp;
+
+ wqp = workq_alloc(sizeof (workq_t));
+ if (wqp == NULL)
+ return (workq_error(ENOMEM));
+
+ bzero(wqp, sizeof (workq_t));
+ wqp->wq_items = workq_alloc(sizeof (void *) * WORKQ_DEFAULT_CAP);
+ if (wqp->wq_items == NULL) {
+ workq_free(wqp, sizeof (workq_t));
+ return (workq_error(ENOMEM));
+ }
+ bzero(wqp->wq_items, sizeof (void *) * WORKQ_DEFAULT_CAP);
+
+ wqp->wq_ndthreads = nthrs - 1;
+ if (wqp->wq_ndthreads > 0) {
+ wqp->wq_thrs = workq_alloc(sizeof (thread_t) *
+ wqp->wq_ndthreads);
+ if (wqp->wq_thrs == NULL) {
+ workq_free(wqp->wq_items, sizeof (void *) *
+ WORKQ_DEFAULT_CAP);
+ workq_free(wqp, sizeof (workq_t));
+ return (workq_error(ENOMEM));
+ }
+ }
+
+ if ((ret = mutex_init(&wqp->wq_lock, USYNC_THREAD | LOCK_ERRORCHECK,
+ NULL)) != 0) {
+ if (wqp->wq_ndthreads > 0) {
+ workq_free(wqp->wq_thrs,
+ sizeof (thread_t) * wqp->wq_ndthreads);
+ }
+ workq_free(wqp->wq_items, sizeof (void *) * WORKQ_DEFAULT_CAP);
+ workq_free(wqp, sizeof (workq_t));
+ return (workq_error(ret));
+ }
+
+ if ((ret = cond_init(&wqp->wq_cond, USYNC_THREAD, NULL)) != 0) {
+ VERIFY0(mutex_destroy(&wqp->wq_lock));
+ if (wqp->wq_ndthreads > 0) {
+ workq_free(wqp->wq_thrs,
+ sizeof (thread_t) * wqp->wq_ndthreads);
+ }
+ workq_free(wqp->wq_items, sizeof (void *) * WORKQ_DEFAULT_CAP);
+ workq_free(wqp, sizeof (workq_t));
+ return (workq_error(ret));
+ }
+
+ wqp->wq_cap = WORKQ_DEFAULT_CAP;
+ *outp = wqp;
+ return (0);
+}
+
+static void
+workq_reset(workq_t *wqp)
+{
+ VERIFY(MUTEX_HELD(&wqp->wq_lock));
+ VERIFY(wqp->wq_working == B_FALSE);
+ if (wqp->wq_cap > 0)
+ bzero(wqp->wq_items, sizeof (void *) * wqp->wq_cap);
+ wqp->wq_nitems = 0;
+ wqp->wq_next = 0;
+ wqp->wq_func = NULL;
+ wqp->wq_arg = NULL;
+ wqp->wq_iserror = B_FALSE;
+ wqp->wq_error = 0;
+}
+
+static int
+workq_grow(workq_t *wqp)
+{
+ size_t ncap;
+ void **items;
+
+ VERIFY(MUTEX_HELD(&wqp->wq_lock));
+ VERIFY(wqp->wq_working == B_FALSE);
+
+ if (SIZE_MAX - wqp->wq_cap < WORKQ_DEFAULT_CAP)
+ return (ENOSPC);
+
+ ncap = wqp->wq_cap + WORKQ_DEFAULT_CAP;
+ items = workq_alloc(ncap * sizeof (void *));
+ if (items == NULL)
+ return (ENOMEM);
+
+ bzero(items, ncap * sizeof (void *));
+ bcopy(wqp->wq_items, items, wqp->wq_cap * sizeof (void *));
+ workq_free(wqp->wq_items, sizeof (void *) * wqp->wq_cap);
+ wqp->wq_items = items;
+ wqp->wq_cap = ncap;
+ return (0);
+}
+
+int
+workq_add(workq_t *wqp, void *item)
+{
+ VERIFY0(mutex_lock(&wqp->wq_lock));
+ if (wqp->wq_working == B_TRUE) {
+ VERIFY0(mutex_unlock(&wqp->wq_lock));
+ return (workq_error(ENXIO));
+ }
+
+ if (wqp->wq_nitems == wqp->wq_cap) {
+ int ret;
+
+ if ((ret = workq_grow(wqp)) != 0) {
+ VERIFY0(mutex_unlock(&wqp->wq_lock));
+ return (workq_error(ret));
+ }
+ }
+
+ wqp->wq_items[wqp->wq_nitems] = item;
+ wqp->wq_nitems++;
+
+ VERIFY0(mutex_unlock(&wqp->wq_lock));
+
+ return (0);
+}
+
+static void *
+workq_pop(workq_t *wqp)
+{
+ void *out;
+
+ VERIFY(MUTEX_HELD(&wqp->wq_lock));
+ VERIFY(wqp->wq_next < wqp->wq_nitems);
+
+ out = wqp->wq_items[wqp->wq_next];
+ wqp->wq_items[wqp->wq_next] = NULL;
+ wqp->wq_next++;
+
+ return (out);
+}
+
+static void *
+workq_thr_work(void *arg)
+{
+ workq_t *wqp = arg;
+
+ VERIFY0(mutex_lock(&wqp->wq_lock));
+ VERIFY(wqp->wq_working == B_TRUE);
+
+ for (;;) {
+ int ret;
+ void *item;
+
+ if (wqp->wq_iserror == B_TRUE ||
+ wqp->wq_next == wqp->wq_nitems) {
+ VERIFY0(mutex_unlock(&wqp->wq_lock));
+ return (NULL);
+ }
+
+ item = workq_pop(wqp);
+
+ VERIFY0(mutex_unlock(&wqp->wq_lock));
+ ret = wqp->wq_func(item, wqp->wq_arg);
+ VERIFY0(mutex_lock(&wqp->wq_lock));
+
+ if (ret != 0) {
+ if (wqp->wq_iserror == B_FALSE) {
+ wqp->wq_iserror = B_TRUE;
+ wqp->wq_error = ret;
+ }
+ VERIFY0(mutex_unlock(&wqp->wq_lock));
+ return (NULL);
+ }
+ }
+}
+
+int
+workq_work(workq_t *wqp, workq_proc_f *func, void *arg, int *errp)
+{
+ int i, ret;
+ boolean_t seterr = B_FALSE;
+
+ if (wqp == NULL || func == NULL)
+ return (workq_error(EINVAL));
+
+ VERIFY0(mutex_lock(&wqp->wq_lock));
+ if (wqp->wq_working == B_TRUE) {
+ VERIFY0(mutex_unlock(&wqp->wq_lock));
+ return (workq_error(EBUSY));
+ }
+
+ if (wqp->wq_nitems == 0) {
+ workq_reset(wqp);
+ VERIFY0(mutex_unlock(&wqp->wq_lock));
+ return (0);
+ }
+
+ wqp->wq_func = func;
+ wqp->wq_arg = arg;
+ wqp->wq_next = 0;
+ wqp->wq_working = B_TRUE;
+
+ ret = 0;
+ for (i = 0; i < wqp->wq_ndthreads; i++) {
+ ret = thr_create(NULL, 0, workq_thr_work, wqp, 0,
+ &wqp->wq_thrs[i]);
+ if (ret != 0) {
+ wqp->wq_iserror = B_TRUE;
+ }
+ }
+
+ VERIFY0(mutex_unlock(&wqp->wq_lock));
+ if (ret == 0)
+ (void) workq_thr_work(wqp);
+
+ for (i = 0; i < wqp->wq_ndthreads; i++) {
+ VERIFY0(thr_join(wqp->wq_thrs[i], NULL, NULL));
+ }
+
+ VERIFY0(mutex_lock(&wqp->wq_lock));
+ wqp->wq_working = B_FALSE;
+ if (ret == 0 && wqp->wq_iserror == B_TRUE) {
+ ret = WORKQ_UERROR;
+ if (errp != NULL)
+ *errp = wqp->wq_error;
+ } else if (ret != 0) {
+ VERIFY(wqp->wq_iserror == B_FALSE);
+ seterr = B_TRUE;
+ }
+
+ workq_reset(wqp);
+ VERIFY0(mutex_unlock(&wqp->wq_lock));
+
+ if (seterr == B_TRUE)
+ return (workq_error(ret));
+
+ return (ret);
+}
diff --git a/usr/src/lib/mergeq/workq.h b/usr/src/lib/mergeq/workq.h
new file mode 100644
index 0000000000..20cfec4a95
--- /dev/null
+++ b/usr/src/lib/mergeq/workq.h
@@ -0,0 +1,52 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2015 Joyent, Inc.
+ */
+
+#ifndef _WORKQ_H
+#define _WORKQ_H
+
+/*
+ * workq library routines
+ */
+
+#include <sys/types.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct workq workq_t;
+typedef int (workq_proc_f)(void *, void *);
+
+extern int workq_init(workq_t **, uint_t);
+extern void workq_fini(workq_t *);
+
+extern int workq_add(workq_t *, void *);
+
+#define WORKQ_ERROR (-1)
+#define WORKQ_UERROR (-2)
+extern int workq_work(workq_t *, workq_proc_f *, void *, int *);
+
+/*
+ * Routines consumers need to implement
+ */
+extern void *workq_alloc(size_t);
+extern void workq_free(void *, size_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _WORKQ_H */
diff --git a/usr/src/test/util-tests/tests/Makefile b/usr/src/test/util-tests/tests/Makefile
index 151ff11b6a..921e48014c 100644
--- a/usr/src/test/util-tests/tests/Makefile
+++ b/usr/src/test/util-tests/tests/Makefile
@@ -14,6 +14,6 @@
# Copyright 2014 Garrett D'Amore <garrett@damore.org>
#
-SUBDIRS = dladm libnvpair_json printf xargs
+SUBDIRS = dladm libnvpair_json printf xargs mergeq
include $(SRC)/test/Makefile.com
diff --git a/usr/src/test/util-tests/tests/mergeq/Makefile b/usr/src/test/util-tests/tests/mergeq/Makefile
new file mode 100644
index 0000000000..168386324e
--- /dev/null
+++ b/usr/src/test/util-tests/tests/mergeq/Makefile
@@ -0,0 +1,71 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2015 Joyent, Inc.
+#
+
+include $(SRC)/Makefile.master
+
+ROOTOPTPKG = $(ROOT)/opt/util-tests
+TESTDIR = $(ROOTOPTPKG)/tests/mergeq
+ROOTBINDIR = $(ROOTOPTPKG)/bin
+
+PROG = mqt
+OBJS = mqt.o mergeq.o
+
+include $(SRC)/cmd/Makefile.cmd
+include $(SRC)/cmd/Makefile.ctf
+include $(SRC)/test/Makefile.com
+
+CMDS = $(PROG:%=$(ROOTBINDIR)/%) $(SCRIPTS:%=$(TESTDIR)/%)
+$(CMDS) := FILEMODE = 0555
+
+CPPFLAGS += -I$(SRC)/lib/mergeq -D_REENTRANT
+LDLIBS += -lumem
+
+all: $(PROG)
+
+install: all $(CMDS)
+
+lint: lint_SRCS
+
+clobber: clean
+ -$(RM) $(PROGS)
+
+clean:
+ -$(RM) *.o
+
+%.o: %.c
+ $(COMPILE.c) -o $@ -c $<
+ $(POST_PROCESS_O)
+
+%.o: $(SRC)/lib/mergeq/%.c
+ $(COMPILE.c) -o $@ -c $<
+ $(POST_PROCESS_O)
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+$(CMDS): $(TESTDIR) $(PROG)
+
+$(ROOTBINDIR):
+ $(INS.dir)
+
+$(ROOTBINDIR)/%: %
+ $(INS.file)
+
+$(TESTDIR):
+ $(INS.dir)
+
+$(TESTDIR)/%: %.ksh
+ $(INS.rename)
diff --git a/usr/src/test/util-tests/tests/mergeq/mqt.c b/usr/src/test/util-tests/tests/mergeq/mqt.c
new file mode 100644
index 0000000000..899970ba83
--- /dev/null
+++ b/usr/src/test/util-tests/tests/mergeq/mqt.c
@@ -0,0 +1,218 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2015 Joyent, Inc.
+ */
+
+/*
+ * mergeq testing routines
+ */
+
+#include <mergeq.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+const char *
+_umem_debug_init()
+{
+ return ("default,verbose");
+}
+
+const char *
+_umem_logging_init(void)
+{
+ return ("fail,contents");
+}
+
+void *
+mergeq_alloc(size_t size)
+{
+ return (malloc(size));
+}
+
+/*ARGSUSED*/
+void
+mergeq_free(void *buf, size_t size)
+{
+ free(buf);
+}
+
+static int
+mqt_int(void *first, void *second, void **outp, void *arg)
+{
+ uintptr_t a, b, c;
+ a = (uintptr_t)first;
+ b = (uintptr_t)second;
+ c = a + b;
+ *outp = (void *)c;
+
+ return (0);
+}
+
+static int
+mqt_append(void *first, void *second, void **outp, void *arg)
+{
+ int ret;
+ char *out;
+
+ /* Yes, this leaks, don't worry about it for the test */
+ if (asprintf(&out, "%s%s", first, second) != -1) {
+ *outp = out;
+ return (0);
+ }
+ return (-1);
+}
+
+static int
+mqt_fatal(void *first, void *second, void **outp, void *arg)
+{
+ return (-1);
+}
+
+/*
+ * Test structures and cases. We really want mq_args to be a flexible array
+ * member, but then we cant initialize it. Thus we set a fixed size number of
+ * entries.
+ */
+typedef struct mq_test {
+ const char *mq_desc; /* test description/name */
+ mergeq_proc_f *mq_proc; /* processing function */
+ int mq_rval; /* mergeq_merge return value */
+ int mq_uerr; /* user error, if any */
+ boolean_t mq_strcmp; /* use strcmp rather than == */
+ void *mq_result; /* expected result */
+ void **mq_args; /* argument array */
+} mq_test_t;
+
+static void *mqt_empty_args[] = { NULL };
+static void *mqt_single_args[] = { (void *)42, NULL };
+static void *mqt_double_args[] = { (void *)42, (void *)27, NULL };
+static void *mqt_wrap_args[] = {
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, NULL
+};
+static void *mqt_grow_args[] = {
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, NULL
+};
+static void *mqt_order_args[] = { "l", "e", "g", "e", "n", "d", " ", "o", "f",
+ " ", "z", "e", "l", "d", "a", NULL };
+
+
+static mq_test_t mq_tests[] = {
+ { "empty", mqt_int, 0, 0, B_FALSE, NULL, mqt_empty_args },
+ { "single", mqt_int, 0, 0, B_FALSE, (void *)42, mqt_single_args },
+ { "double", mqt_int, 0, 0, B_FALSE, (void *)69, mqt_double_args },
+ { "wrap", mqt_int, 0, 0, B_FALSE, (void *)64, mqt_wrap_args },
+ { "grow", mqt_int, 0, 0, B_FALSE, (void *)92, mqt_grow_args },
+ { "fatal", mqt_fatal, MERGEQ_UERROR, -1, B_FALSE, NULL,
+ mqt_double_args },
+ { "order", mqt_append, 0, 0, B_TRUE, "alegend of zeld", mqt_order_args }
+};
+
+#define NMQ_TESTS (sizeof (mq_tests) / sizeof (mq_test_t))
+
+static void
+mq_test_run(mergeq_t *mqp, mq_test_t *mqt)
+{
+ int ret, err;
+ void **itemp = mqt->mq_args;
+ void *out;
+
+ while (*itemp != NULL) {
+ if ((ret = mergeq_add(mqp, *itemp)) != 0) {
+ (void) fprintf(stderr,
+ "test %s: failed to add item: %s\n",
+ mqt->mq_desc, strerror(errno));
+ exit(1);
+ }
+ itemp++;
+ }
+
+ ret = mergeq_merge(mqp, mqt->mq_proc, NULL, &out, &err);
+ if (ret != mqt->mq_rval) {
+ (void) fprintf(stderr, "test %s: got incorrect rval. "
+ "Expected %d, got %d\n", mqt->mq_desc, mqt->mq_rval, ret);
+ exit(1);
+ }
+
+ if (ret == MERGEQ_UERROR && err != mqt->mq_uerr) {
+ (void) fprintf(stderr, "test %s: got incorrect user error. "
+ "Expected %d, got %d\n", mqt->mq_desc, mqt->mq_uerr, err);
+ exit(1);
+ }
+
+ if (ret == 0) {
+ if (mqt->mq_strcmp == B_TRUE &&
+ strcmp(out, mqt->mq_result) != 0) {
+ (void) fprintf(stderr, "test %s: got unexpected "
+ "result: %s, expected %s\n", mqt->mq_desc, out,
+ mqt->mq_result);
+ exit(1);
+ } else if (mqt->mq_strcmp == B_FALSE && out != mqt->mq_result) {
+ (void) fprintf(stderr, "test %s: got unexpected "
+ "result: %p, expected %p\n", mqt->mq_desc, out,
+ mqt->mq_result);
+ exit(1);
+ }
+ }
+}
+
+int
+main(void)
+{
+ int ret, i, t;
+ mergeq_t *mqp;
+ int nthreads[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, -1 };
+
+ for (t = 0; nthreads[t] != -1; t++) {
+ printf("Beginning tests with %d threads\n", nthreads[t]);
+ if ((ret = mergeq_init(&mqp, nthreads[t])) != 0) {
+ fprintf(stderr, "failed to init mergeq: %s\n",
+ strerror(errno));
+ return (1);
+ }
+
+ for (i = 0; i < NMQ_TESTS; i++) {
+ mq_test_run(mqp, &mq_tests[i]);
+ }
+
+ mergeq_fini(mqp);
+ }
+
+ return (0);
+}
diff --git a/usr/src/test/util-tests/tests/workq/Makefile b/usr/src/test/util-tests/tests/workq/Makefile
new file mode 100644
index 0000000000..1b17ea4f5b
--- /dev/null
+++ b/usr/src/test/util-tests/tests/workq/Makefile
@@ -0,0 +1,71 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2015 Joyent, Inc.
+#
+
+include $(SRC)/Makefile.master
+
+ROOTOPTPKG = $(ROOT)/opt/util-tests
+TESTDIR = $(ROOTOPTPKG)/tests/mergeq
+ROOTBINDIR = $(ROOTOPTPKG)/bin
+
+PROG = wqt
+OBJS = wqt.o workq.o
+
+include $(SRC)/cmd/Makefile.cmd
+include $(SRC)/cmd/Makefile.ctf
+include $(SRC)/test/Makefile.com
+
+CMDS = $(PROG:%=$(ROOTBINDIR)/%) $(SCRIPTS:%=$(TESTDIR)/%)
+$(CMDS) := FILEMODE = 0555
+
+CPPFLAGS += -I$(SRC)/lib/mergeq -D_REENTRANT
+LDLIBS += -lumem
+
+all: $(PROG)
+
+install: all $(CMDS)
+
+lint: lint_SRCS
+
+clobber: clean
+ -$(RM) $(PROG)
+
+clean:
+ -$(RM) $(OBJS)
+
+%.o: %.c
+ $(COMPILE.c) -o $@ -c $<
+ $(POST_PROCESS_O)
+
+%.o: $(SRC)/lib/mergeq/%.c
+ $(COMPILE.c) -o $@ -c $<
+ $(POST_PROCESS_O)
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+$(CMDS): $(TESTDIR) $(PROG)
+
+$(ROOTBINDIR):
+ $(INS.dir)
+
+$(ROOTBINDIR)/%: %
+ $(INS.file)
+
+$(TESTDIR):
+ $(INS.dir)
+
+$(TESTDIR)/%: %.ksh
+ $(INS.rename)
diff --git a/usr/src/test/util-tests/tests/workq/wqt.c b/usr/src/test/util-tests/tests/workq/wqt.c
new file mode 100644
index 0000000000..bda0b4a9e5
--- /dev/null
+++ b/usr/src/test/util-tests/tests/workq/wqt.c
@@ -0,0 +1,196 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2015 Joyent, Inc.
+ */
+
+/*
+ * workq testing routines
+ *
+ * What we want to guarantee is that every function is executed exactly once. To
+ * that end we have the callback function basically increment a global in the
+ * test around a mutex.
+ */
+
+#include <workq.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <thread.h>
+#include <synch.h>
+
+mutex_t wqt_lock = ERRORCHECKMUTEX;
+uintptr_t wqt_count;
+
+const char *
+_umem_debug_init()
+{
+ return ("default,verbose");
+}
+
+const char *
+_umem_logging_init(void)
+{
+ return ("fail,contents");
+}
+
+void *
+workq_alloc(size_t size)
+{
+ return (malloc(size));
+}
+
+/*ARGSUSED*/
+void
+workq_free(void *buf, size_t size)
+{
+ free(buf);
+}
+
+/*ARGSUSED*/
+int
+wqt_fatal(void *item, void *arg)
+{
+ return (-1);
+}
+
+int
+wqt_add(void *item, void *arg)
+{
+ uintptr_t a = (uintptr_t)item;
+
+ mutex_enter(&wqt_lock);
+ wqt_count += a;
+ mutex_exit(&wqt_lock);
+
+ return (0);
+}
+
+typedef struct wq_test {
+ const char *wq_desc; /* test description/name */
+ workq_proc_f *wq_proc; /* processing function */
+ int wq_rval; /* workq_work return value */
+ int wq_uerr; /* user error, if any */
+ uintptr_t wq_sum; /* expected sum */
+ void **wq_args; /* argument array */
+} wq_test_t;
+
+static void *wqt_empty_args[] = { NULL };
+static void *wqt_single_args[] = { (void *)42, NULL };
+static void *wqt_double_args[] = { (void *)42, (void *)27, NULL };
+static void *wqt_wrap_args[] = {
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, NULL
+};
+static void *wqt_grow_args[] = {
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, (void *)1, (void *)1, (void *)1, (void *)1,
+ (void *)1, (void *)1, NULL
+};
+
+static wq_test_t wq_tests[] = {
+ { "empty", wqt_add, 0, 0, NULL, wqt_empty_args },
+ { "single", wqt_add, 0, 0, 42, wqt_single_args },
+ { "double", wqt_add, 0, 0, 69, wqt_double_args },
+ { "wrap", wqt_add, 0, 0, 64, wqt_wrap_args },
+ { "grow", wqt_add, 0, 0, 92, wqt_grow_args },
+ { "fatal", wqt_fatal, WORKQ_UERROR, -1, -1, wqt_double_args }
+};
+
+#define NWQ_TESTS (sizeof (wq_tests) / sizeof (wq_test_t))
+
+static void
+wq_test_run(workq_t *wqp, wq_test_t *wqt)
+{
+ int ret, err;
+ void **itemp = wqt->wq_args;
+
+ while (*itemp != NULL) {
+ if ((ret = workq_add(wqp, *itemp)) != 0) {
+ (void) fprintf(stderr, "test %s: failed to add item: "
+ "%s\n", wqt->wq_desc, strerror(errno));
+ exit(1);
+ }
+ itemp++;
+ }
+
+ wqt_count = 0;
+ ret = workq_work(wqp, wqt->wq_proc, NULL, &err);
+ if (ret != wqt->wq_rval) {
+ (void) fprintf(stderr, "test %s: got incorrect rval. "
+ "Expected %d, got %d (%d)\n", wqt->wq_desc, wqt->wq_rval,
+ ret, errno);
+ exit(1);
+ }
+
+ if (ret == WORKQ_UERROR && err != wqt->wq_uerr) {
+ (void) fprintf(stderr, "test %s: got incorrect user error. "
+ "Expected %d, got %d\n", wqt->wq_desc, wqt->wq_uerr, err);
+ exit(1);
+ }
+
+ if (ret == 0 && wqt_count != wqt->wq_sum) {
+ (void) fprintf(stderr, "test %s: got unexpected "
+ "result: %d, expected %d\n", wqt->wq_desc, wqt_count,
+ wqt->wq_sum);
+ exit(1);
+ }
+}
+
+int
+main(void)
+{
+ int ret, i, t;
+ workq_t *wqp;
+ int nthreads[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, -1 };
+
+ for (t = 0; nthreads[t] != -1; t++) {
+ printf("Beginning tests with %d threads\n", nthreads[t]);
+ if ((ret = workq_init(&wqp, nthreads[t])) != 0) {
+ fprintf(stderr, "failed to init workq: %s\n",
+ strerror(errno));
+ return (1);
+ }
+
+ for (i = 0; i < NWQ_TESTS; i++) {
+ wq_test_run(wqp, &wq_tests[i]);
+ }
+
+ workq_fini(wqp);
+ }
+
+
+ return (0);
+}
diff --git a/usr/src/tools/ctf/Makefile b/usr/src/tools/ctf/Makefile
index e31268df97..b6679b2678 100644
--- a/usr/src/tools/ctf/Makefile
+++ b/usr/src/tools/ctf/Makefile
@@ -26,7 +26,7 @@
include ../Makefile.tools
-SUBDIRS = cvt stabs ctfstrip libctf ctfdiff ctfmerge ctfdump
+SUBDIRS = cvt stabs ctfstrip libctf ctfdiff ctfmerge ctfdump ctfconvert
.PARALLEL: $(SUBDIRS)
@@ -41,6 +41,7 @@ lint := TARGET= lint
ctfmerge: libctf
ctfdiff: libctf
ctfdump: libctf
+ctfconvert: libctf
all clean clobber install lint: dwarf .WAIT $(SUBDIRS)
diff --git a/usr/src/tools/ctf/common/ctf_headers.h b/usr/src/tools/ctf/common/ctf_headers.h
index 453c6450ed..a63690be77 100644
--- a/usr/src/tools/ctf/common/ctf_headers.h
+++ b/usr/src/tools/ctf/common/ctf_headers.h
@@ -35,6 +35,7 @@
* as part of Solaris. These include the following:
*
* $(SRC)/lib/libctf/common/libctf.h
+ * $(SRC)/lib/libctf/common/libctf_impl.h
* $(SRC)/uts/common/sys/ctf_api.h
* $(SRC)/uts/common/sys/ctf.h
*
@@ -67,16 +68,6 @@
#include <uts/common/sys/ctf_api.h>
#include <common/ctf/ctf_impl.h>
#include <lib/libctf/common/libctf.h>
-
-/*
- * XXX: This is hack to deal with GCC 4.x removing __builtin_stdarg_start
- *
- * We need to build on systems that don't have the fixed va_impl.h on the
- * system, to achieve that, we stub it out here and in all similar places to
- * give us a leg up.
- */
-#if __GNUC__ >= 4
-#define __builtin_stdarg_start(list, name) __builtin_va_start(list, name)
-#endif
+#include <lib/libctf/common/libctf_impl.h>
#endif /* _CTF_HEADERS_H */
diff --git a/usr/src/tools/ctf/ctfconvert/Makefile b/usr/src/tools/ctf/ctfconvert/Makefile
new file mode 100644
index 0000000000..07fadc5f8f
--- /dev/null
+++ b/usr/src/tools/ctf/ctfconvert/Makefile
@@ -0,0 +1,44 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+include ../../Makefile.tools
+
+SUBDIRS = $(MACH)
+
+all := TARGET= all
+install := TARGET= install
+clean := TARGET= clean
+clobber := TARGET= clobber
+lint := TARGET= lint
+
+.KEEP_STATE:
+
+install all clean clobber lint: $(SUBDIRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
diff --git a/usr/src/tools/ctf/ctfconvert/Makefile.com b/usr/src/tools/ctf/ctfconvert/Makefile.com
new file mode 100644
index 0000000000..d1cea479fb
--- /dev/null
+++ b/usr/src/tools/ctf/ctfconvert/Makefile.com
@@ -0,0 +1,44 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+PROG = ctfconvert-altexec
+SRCS = ctfconvert.c
+
+include ../../Makefile.ctf
+
+CFLAGS += $(CCVERBOSE)
+LDLIBS += -lctf -lelf
+
+LDFLAGS = \
+ -L$(ROOTONBLDLIBMACH) \
+ '-R$$ORIGIN/../../lib/$(MACH)' \
+
+CPPFLAGS += -include ../../common/ctf_headers.h
+
+OBJS = $(SRCS:%.c=%.o)
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+%.o: $(SRC)/cmd/ctfconvert/%.c
+ $(COMPILE.c) $<
+
+$(ROOTONBLDMACHPROG): $(PROG)
+
+install: $(ROOTONBLDMACHPROG)
+
+clean:
+ $(RM) $(OBJS) $(LINTFILES)
+
+include $(SRC)/tools/Makefile.targ
diff --git a/usr/src/tools/ctf/ctfconvert/i386/Makefile b/usr/src/tools/ctf/ctfconvert/i386/Makefile
new file mode 100644
index 0000000000..cd5462bee3
--- /dev/null
+++ b/usr/src/tools/ctf/ctfconvert/i386/Makefile
@@ -0,0 +1,27 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+include ../Makefile.com
diff --git a/usr/src/tools/ctf/ctfconvert/sparc/Makefile b/usr/src/tools/ctf/ctfconvert/sparc/Makefile
new file mode 100644
index 0000000000..cd5462bee3
--- /dev/null
+++ b/usr/src/tools/ctf/ctfconvert/sparc/Makefile
@@ -0,0 +1,27 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+include ../Makefile.com
diff --git a/usr/src/tools/ctf/ctfmerge/Makefile.com b/usr/src/tools/ctf/ctfmerge/Makefile.com
index b227e8b7b0..3f58feed9c 100644
--- a/usr/src/tools/ctf/ctfmerge/Makefile.com
+++ b/usr/src/tools/ctf/ctfmerge/Makefile.com
@@ -17,7 +17,7 @@ include ../../Makefile.ctf
CFLAGS += $(CCVERBOSE)
LDLIBS += -lctf -lelf
-LDFLAGS = \
+LDFLAGS += \
-L$(ROOTONBLDLIBMACH) \
'-R$$ORIGIN/../../lib/$(MACH)' \
diff --git a/usr/src/tools/ctf/cvt/Makefile.com b/usr/src/tools/ctf/cvt/Makefile.com
index d909031167..0eab68b9bd 100644
--- a/usr/src/tools/ctf/cvt/Makefile.com
+++ b/usr/src/tools/ctf/cvt/Makefile.com
@@ -72,7 +72,7 @@ DWARFLDFLAGS = \
-L$(ROOTONBLDLIBMACH) \
'-R$$ORIGIN/../../lib/$(MACH)' \
-ldwarf
-DWARFCPPFLAGS = -I../../dwarf/common
+DWARFCPPFLAGS = -I$(SRC)/lib/libdwarf/common
LDFLAGS += -L$(NATIVE_ADJUNCT)/lib
LDLIBS += -lz -lelf
diff --git a/usr/src/tools/ctf/dwarf/Makefile.com b/usr/src/tools/ctf/dwarf/Makefile.com
index 51e788ff04..d90e85b55a 100644
--- a/usr/src/tools/ctf/dwarf/Makefile.com
+++ b/usr/src/tools/ctf/dwarf/Makefile.com
@@ -67,11 +67,10 @@ OBJECTS=dwarf_abbrev.o \
include $(SRC)/lib/Makefile.lib
-SRCS= $(PICS:%.o=../common/%.c)
-FILEMODE = 0755
-
-SRCDIR = ../common/
+FILEMODE = 0755
+SRCDIR = $(SRC)/lib/libdwarf/common/
+SRCS = $(PICS:%.o=$(SRCDIR)/%.c)
CPPFLAGS += -I$(SRCDIR) -DELF_TARGET_ALL=1
CERRWARN += -_gcc=-Wno-unused
diff --git a/usr/src/tools/ctf/libctf/Makefile.com b/usr/src/tools/ctf/libctf/Makefile.com
index e47bde3e3b..0444b975d5 100644
--- a/usr/src/tools/ctf/libctf/Makefile.com
+++ b/usr/src/tools/ctf/libctf/Makefile.com
@@ -16,10 +16,21 @@
include $(SRC)/lib/libctf/Makefile.shared.com
include ../../Makefile.ctf
-CPPFLAGS += -include ../../common/ctf_headers.h \
+#
+# For some reason LDFLAGS doesn't seem to be taking effect at the
+# moment. Therefore add what we need to LDLIBS for now.
+#
+LDLIBS += \
+ -L$(ROOTONBLDLIBMACH) \
+ '-R$$ORIGIN/../../lib/$(MACH)' \
+
+CPPFLAGS += -I$(SRC)/lib/libctf/common/ \
+ -I$(SRC)/lib/libdwarf/common/ \
+ -I$(SRC)/lib/mergeq \
+ -include ../../common/ctf_headers.h \
-DCTF_OLD_VERSIONS \
-DCTF_TOOLS_BUILD
-LDLIBS += -lc -lelf
+LDLIBS += -lc -lelf -ldwarf -lavl
.KEEP_STATE:
diff --git a/usr/src/tools/scripts/bldenv.sh b/usr/src/tools/scripts/bldenv.sh
index f841a3cfc5..71987a44b5 100644
--- a/usr/src/tools/scripts/bldenv.sh
+++ b/usr/src/tools/scripts/bldenv.sh
@@ -291,9 +291,6 @@ if "${flags.t}" ; then
export CTFSTABS="${TOOLS_PROTO}/opt/onbld/bin/${MACH}/ctfstabs"
export GENOFFSETS="${TOOLS_PROTO}/opt/onbld/bin/genoffsets"
- export CTFCONVERT="${TOOLS_PROTO}/opt/onbld/bin/${MACH}/ctfconvert"
- export CTFMERGE="${TOOLS_PROTO}/opt/onbld/bin/${MACH}/ctfmerge"
-
PATH="${TOOLS_PROTO}/opt/onbld/bin/${MACH}:${PATH}"
PATH="${TOOLS_PROTO}/opt/onbld/bin:${PATH}"
export PATH
diff --git a/usr/src/tools/scripts/nightly.sh b/usr/src/tools/scripts/nightly.sh
index 30e9ba8706..842c63a295 100644
--- a/usr/src/tools/scripts/nightly.sh
+++ b/usr/src/tools/scripts/nightly.sh
@@ -518,11 +518,6 @@ function use_tools {
GENOFFSETS=${TOOLSROOT}/opt/onbld/bin/genoffsets
export GENOFFSETS
- CTFCONVERT=${TOOLSROOT}/opt/onbld/bin/${MACH}/ctfconvert
- export CTFCONVERT
- CTFMERGE=${TOOLSROOT}/opt/onbld/bin/${MACH}/ctfmerge
- export CTFMERGE
-
if [ "$VERIFY_ELFSIGN" = "y" ]; then
ELFSIGN=${TOOLSROOT}/opt/onbld/bin/elfsigncmp
else
@@ -542,8 +537,6 @@ function use_tools {
echo "\n==== New environment settings. ====\n" >> $LOGFILE
echo "STABS=${STABS}" >> $LOGFILE
echo "CTFSTABS=${CTFSTABS}" >> $LOGFILE
- echo "CTFCONVERT=${CTFCONVERT}" >> $LOGFILE
- echo "CTFMERGE=${CTFMERGE}" >> $LOGFILE
echo "ELFSIGN=${ELFSIGN}" >> $LOGFILE
echo "PATH=${PATH}" >> $LOGFILE
echo "ONBLD_TOOLS=${ONBLD_TOOLS}" >> $LOGFILE
diff --git a/usr/src/uts/Makefile.uts b/usr/src/uts/Makefile.uts
index 3ce261bc31..273949fb74 100644
--- a/usr/src/uts/Makefile.uts
+++ b/usr/src/uts/Makefile.uts
@@ -635,3 +635,11 @@ PRIVS_DEF = $(SRC)/uts/common/os/priv_defs
#
USBDEVS_AWK = $(SRC)/uts/common/io/usb/usbdevs2h.awk
USBDEVS_DATA = $(SRC)/uts/common/io/usb/usbdevs
+
+
+#
+# If we're using the newer CTF tools, then we need to make sure that we
+# are building with the private -X option to ctfconvert which allows us
+# to fixup the struct cpu to account for machcpu.
+#
+$(ENABLE_NEW_CTF)$(_GNUC)CTFCVTFLAGS += -X
diff --git a/usr/src/uts/common/sys/ctf_api.h b/usr/src/uts/common/sys/ctf_api.h
index 73beb2d244..bc99f67d3f 100644
--- a/usr/src/uts/common/sys/ctf_api.h
+++ b/usr/src/uts/common/sys/ctf_api.h
@@ -113,7 +113,10 @@ enum {
ECTF_MCHILD, /* cannot merge child container */
ECTF_LABELEXISTS, /* label already exists */
ECTF_LCONFLICT, /* merged labels conflict */
- ECTF_ZLIB /* zlib library failure */
+ ECTF_ZLIB, /* zlib library failure */
+ ECTF_CONVBKERR, /* CTF conversion backend error */
+ ECTF_CONVNOCSRC, /* No C source to convert from */
+ ECTF_NOCONVBKEND /* No applicable conversion backend */
};
/*
@@ -248,6 +251,7 @@ extern char *ctf_type_qname(ctf_file_t *, ctf_id_t, char *, size_t,
extern ssize_t ctf_type_size(ctf_file_t *, ctf_id_t);
extern ssize_t ctf_type_align(ctf_file_t *, ctf_id_t);
extern int ctf_type_kind(ctf_file_t *, ctf_id_t);
+extern const char *ctf_kind_name(ctf_file_t *, int);
extern ctf_id_t ctf_type_reference(ctf_file_t *, ctf_id_t);
extern ctf_id_t ctf_type_pointer(ctf_file_t *, ctf_id_t);
extern int ctf_type_encoding(ctf_file_t *, ctf_id_t, ctf_encoding_t *);
@@ -302,6 +306,8 @@ extern int ctf_add_object(ctf_file_t *, ulong_t, ctf_id_t);
extern int ctf_add_label(ctf_file_t *, const char *, ctf_id_t, uint_t);
extern int ctf_set_array(ctf_file_t *, ctf_id_t, const ctf_arinfo_t *);
+extern int ctf_set_root(ctf_file_t *, ctf_id_t, const boolean_t);
+extern int ctf_set_size(ctf_file_t *, ctf_id_t, const ulong_t);
extern int ctf_delete_type(ctf_file_t *, ctf_id_t);