summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/Makefile.lint1
-rw-r--r--usr/src/Makefile.master10
-rw-r--r--usr/src/Targetdirs10
-rw-r--r--usr/src/cmd/Makefile4
-rw-r--r--usr/src/cmd/ctfconvert/Makefile33
-rw-r--r--usr/src/cmd/ctfconvert/ctfconvert.c388
-rw-r--r--usr/src/cmd/ctfdiff/Makefile33
-rw-r--r--usr/src/cmd/ctfdiff/ctfdiff.c518
-rw-r--r--usr/src/cmd/ctfdump/Makefile36
-rw-r--r--usr/src/cmd/ctfdump/ctfdump.c1235
-rw-r--r--usr/src/cmd/ctfmerge/Makefile33
-rw-r--r--usr/src/cmd/ctfmerge/ctfmerge.c536
-rw-r--r--usr/src/cmd/mdb/Makefile.libstandctf5
-rw-r--r--usr/src/cmd/mdb/common/kmdb/kmdb_promif.c7
-rw-r--r--usr/src/cmd/mdb/common/libstandctf/ctf_subr.c9
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_ctf.c15
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_debug.c4
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_debug.h4
-rw-r--r--usr/src/common/ctf/ctf_create.c842
-rw-r--r--usr/src/common/ctf/ctf_error.c12
-rw-r--r--usr/src/common/ctf/ctf_hash.c3
-rw-r--r--usr/src/common/ctf/ctf_impl.h90
-rw-r--r--usr/src/common/ctf/ctf_open.c40
-rw-r--r--usr/src/common/ctf/ctf_types.c470
-rw-r--r--usr/src/common/ctf/ctf_util.c43
-rw-r--r--usr/src/lib/Makefile5
-rw-r--r--usr/src/lib/Makefile.lib13
-rw-r--r--usr/src/lib/Makefile.targ5
-rw-r--r--usr/src/lib/libctf/Makefile.com41
-rw-r--r--usr/src/lib/libctf/Makefile.shared.com90
-rw-r--r--usr/src/lib/libctf/Makefile.shared.targ30
-rw-r--r--usr/src/lib/libctf/common/ctf_convert.c210
-rw-r--r--usr/src/lib/libctf/common/ctf_diff.c1360
-rw-r--r--usr/src/lib/libctf/common/ctf_dwarf.c2995
-rw-r--r--usr/src/lib/libctf/common/ctf_elfwrite.c422
-rw-r--r--usr/src/lib/libctf/common/ctf_lib.c296
-rw-r--r--usr/src/lib/libctf/common/ctf_merge.c1551
-rw-r--r--usr/src/lib/libctf/common/ctf_subr.c28
-rw-r--r--usr/src/lib/libctf/common/libctf.h45
-rw-r--r--usr/src/lib/libctf/common/libctf_impl.h59
-rw-r--r--usr/src/lib/libctf/common/mapfile-vers38
-rw-r--r--usr/src/lib/libdtrace/common/dt_decl.c4
-rw-r--r--usr/src/lib/libdtrace/common/dt_open.c12
-rw-r--r--usr/src/lib/libdtrace/common/dt_parser.c4
-rw-r--r--usr/src/lib/libdwarf/Makefile40
-rw-r--r--usr/src/lib/libdwarf/Makefile.com94
-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/man/man1/Makefile2
-rw-r--r--usr/src/man/man1/ctfdiff.1348
-rw-r--r--usr/src/man/man1/ctfdump.1446
-rw-r--r--usr/src/pkg/manifests/developer-build-onbld.mf7
-rw-r--r--usr/src/pkg/manifests/developer-debug-ctf.mf32
-rw-r--r--usr/src/pkg/manifests/system-library.mf7
-rw-r--r--usr/src/pkg/manifests/system-test-utiltest.mf3
-rw-r--r--usr/src/test/util-tests/runfiles/default.run3
-rw-r--r--usr/src/test/util-tests/tests/Makefile2
-rw-r--r--usr/src/test/util-tests/tests/mergeq/Makefile64
-rw-r--r--usr/src/test/util-tests/tests/mergeq/mqt.c217
-rw-r--r--usr/src/test/util-tests/tests/workq/Makefile64
-rw-r--r--usr/src/test/util-tests/tests/workq/wqt.c196
-rw-r--r--usr/src/tools/ctf/Makefile9
-rw-r--r--usr/src/tools/ctf/Makefile.ctf7
-rw-r--r--usr/src/tools/ctf/common/ctf_headers.h18
-rw-r--r--usr/src/tools/ctf/ctfconvert/Makefile (renamed from usr/src/tools/ctf/dump/Makefile)1
-rw-r--r--usr/src/tools/ctf/ctfconvert/Makefile.com44
-rw-r--r--usr/src/tools/ctf/ctfconvert/i386/Makefile (renamed from usr/src/tools/ctf/dump/sparc/Makefile)1
-rw-r--r--usr/src/tools/ctf/ctfconvert/sparc/Makefile (renamed from usr/src/tools/ctf/dump/i386/Makefile)1
-rw-r--r--usr/src/tools/ctf/ctfdiff/Makefile44
-rw-r--r--usr/src/tools/ctf/ctfdiff/Makefile.com44
-rw-r--r--usr/src/tools/ctf/ctfdiff/i386/Makefile16
-rw-r--r--usr/src/tools/ctf/ctfdiff/sparc/Makefile16
-rw-r--r--usr/src/tools/ctf/ctfdump/Makefile33
-rw-r--r--usr/src/tools/ctf/ctfdump/Makefile.com47
-rw-r--r--usr/src/tools/ctf/ctfdump/i386/Makefile16
-rw-r--r--usr/src/tools/ctf/ctfdump/sparc/Makefile16
-rw-r--r--usr/src/tools/ctf/ctfmerge/Makefile44
-rw-r--r--usr/src/tools/ctf/ctfmerge/Makefile.com46
-rw-r--r--usr/src/tools/ctf/ctfmerge/i386/Makefile27
-rw-r--r--usr/src/tools/ctf/ctfmerge/sparc/Makefile27
-rw-r--r--usr/src/tools/ctf/cvt/Makefile.com3
-rw-r--r--usr/src/tools/ctf/cvt/altexec.c45
-rw-r--r--usr/src/tools/ctf/cvt/ctfconvert.c12
-rw-r--r--usr/src/tools/ctf/cvt/ctfmerge.c2
-rw-r--r--usr/src/tools/ctf/cvt/ctftools.h3
-rw-r--r--usr/src/tools/ctf/dump/dump.c1028
-rw-r--r--usr/src/tools/ctf/dwarf/Makefile.com7
-rw-r--r--usr/src/tools/ctf/libctf/Makefile (renamed from usr/src/tools/ctf/dump/Makefile.com)51
-rw-r--r--usr/src/tools/ctf/libctf/Makefile.com51
-rw-r--r--usr/src/tools/ctf/libctf/i386/Makefile18
-rw-r--r--usr/src/tools/ctf/libctf/sparc/Makefile18
-rw-r--r--usr/src/tools/make/lib/bsd/Makefile4
-rw-r--r--usr/src/tools/make/lib/makestate/Makefile.com4
-rw-r--r--usr/src/tools/make/lib/mksh/Makefile4
-rw-r--r--usr/src/tools/make/lib/vroot/Makefile4
-rw-r--r--usr/src/uts/Makefile.uts8
-rw-r--r--usr/src/uts/common/ctf/ctf_mod.c11
-rw-r--r--usr/src/uts/common/sys/ctf.h14
-rw-r--r--usr/src/uts/common/sys/ctf_api.h108
-rw-r--r--usr/src/uts/intel/ctf/Makefile4
-rw-r--r--usr/src/uts/sparc/ctf/Makefile4
209 files changed, 14707 insertions, 1362 deletions
diff --git a/usr/src/Makefile.lint b/usr/src/Makefile.lint
index 8cc78f94d9..cdacf2ddfd 100644
--- a/usr/src/Makefile.lint
+++ b/usr/src/Makefile.lint
@@ -95,6 +95,7 @@ COMMON_SUBDIRS = \
cmd/cpio \
cmd/crypt \
cmd/csplit \
+ cmd/ctfdump \
cmd/ctrun \
cmd/ctstat \
cmd/ctwatch \
diff --git a/usr/src/Makefile.master b/usr/src/Makefile.master
index 5b6cd53942..501853a6ed 100644
--- a/usr/src/Makefile.master
+++ b/usr/src/Makefile.master
@@ -182,6 +182,7 @@ JAVADOC= $(JAVA_ROOT)/bin/javadoc
RMIC= $(JAVA_ROOT)/bin/rmic
JAR= $(JAVA_ROOT)/bin/jar
CTFCONVERT= $(ONBLD_TOOLS)/bin/$(MACH)/ctfconvert
+CTFDIFF= $(ONBLD_TOOLS)/bin/$(MACH)/ctfdiff
CTFMERGE= $(ONBLD_TOOLS)/bin/$(MACH)/ctfmerge
CTFSTABS= $(ONBLD_TOOLS)/bin/$(MACH)/ctfstabs
CTFSTRIP= $(ONBLD_TOOLS)/bin/$(MACH)/ctfstrip
@@ -966,6 +967,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). By setting BUILD_OLD_CTF_TOOLS
+# here or in the environment file, the old ones will be built.
+#
+BUILD_NEW_CTF_TOOLS=
+BUILD_OLD_CTF_TOOLS=$(POUND_SIGN)
+$(BUILD_OLD_CTF_TOOLS)BUILD_NEW_CTF_TOOLS= $(POUND_SIGN)
+
CTFCONVERT_O = $(CTFCONVERT) $(CTFCVTFLAGS) $@
# Rules (normally from make.rules) and macros which are used for post
diff --git a/usr/src/Targetdirs b/usr/src/Targetdirs
index 25ee9bc38a..a056fd2d22 100644
--- a/usr/src/Targetdirs
+++ b/usr/src/Targetdirs
@@ -855,6 +855,8 @@ $(ROOT)/usr/lib/libdlpi.so.1:= REALPATH=../../lib/libdlpi.so.1
$(ROOT)/usr/lib/libdlpi.so:= REALPATH=../../lib/libdlpi.so.1
$(ROOT)/usr/lib/libdoor.so.1:= REALPATH=../../lib/libdoor.so.1
$(ROOT)/usr/lib/libdoor.so:= REALPATH=../../lib/libdoor.so.1
+$(ROOT)/usr/lib/libdwarf.so.1:= REALPATH=../../lib/libdwarf.so.1
+$(ROOT)/usr/lib/libdwarf.so:= REALPATH=../../lib/libdwarf.so.1
$(ROOT)/usr/lib/libefi.so.1:= REALPATH=../../lib/libefi.so.1
$(ROOT)/usr/lib/libefi.so:= REALPATH=../../lib/libefi.so.1
$(ROOT)/usr/lib/libelf.so.1:= REALPATH=../../lib/libelf.so.1
@@ -1136,6 +1138,10 @@ $(ROOT)/usr/lib/$(MACH64)/libdoor.so.1:= \
REALPATH=../../../lib/$(MACH64)/libdoor.so.1
$(ROOT)/usr/lib/$(MACH64)/libdoor.so:= \
REALPATH=../../../lib/$(MACH64)/libdoor.so.1
+$(ROOT)/usr/lib/$(MACH64)/libdwarf.so.1:= \
+ REALPATH=../../../lib/$(MACH64)/libdwarf.so.1
+$(ROOT)/usr/lib/$(MACH64)/libdwarf.so:= \
+ REALPATH=../../../lib/$(MACH64)/libdwarf.so.1
$(ROOT)/usr/lib/$(MACH64)/libefi.so.1:= \
REALPATH=../../../lib/$(MACH64)/libefi.so.1
$(ROOT)/usr/lib/$(MACH64)/libefi.so:= \
@@ -1478,6 +1484,8 @@ SYM.USRLIB= \
/usr/lib/libdlpi.so.1 \
/usr/lib/libdoor.so \
/usr/lib/libdoor.so.1 \
+ /usr/lib/libdwarf.so \
+ /usr/lib/libdwarf.so.1 \
/usr/lib/libefi.so \
/usr/lib/libefi.so.1 \
/usr/lib/libelf.so \
@@ -1721,6 +1729,8 @@ SYM.USRLIB64= \
/usr/lib/$(MACH64)/libdlpi.so.1 \
/usr/lib/$(MACH64)/libdoor.so \
/usr/lib/$(MACH64)/libdoor.so.1 \
+ /usr/lib/$(MACH64)/libdwarf.so \
+ /usr/lib/$(MACH64)/libdwarf.so.1 \
/usr/lib/$(MACH64)/libefi.so \
/usr/lib/$(MACH64)/libefi.so.1 \
/usr/lib/$(MACH64)/libelf.so \
diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile
index 300f92bf36..36ef810055 100644
--- a/usr/src/cmd/Makefile
+++ b/usr/src/cmd/Makefile
@@ -106,6 +106,10 @@ COMMON_SUBDIRS= \
crypt \
csh \
csplit \
+ ctfconvert \
+ ctfdiff \
+ ctfdump \
+ ctfmerge \
ctrun \
ctstat \
ctwatch \
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..3ff96c5089
--- /dev/null
+++ b/usr/src/cmd/ctfconvert/ctfconvert.c
@@ -0,0 +1,388 @@
+/*
+ * 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, "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 > 1024 || *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/Makefile b/usr/src/cmd/ctfdiff/Makefile
new file mode 100644
index 0000000000..268bf9f3ed
--- /dev/null
+++ b/usr/src/cmd/ctfdiff/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 2015, Joyent, Inc.
+#
+
+PROG= ctfdiff
+
+include ../Makefile.cmd
+
+CFLAGS += $(CCVERBOSE)
+LDLIBS += -lctf
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all $(ROOTPROG)
+
+clean:
+
+lint: lint_PROG
+
+include ../Makefile.targ
diff --git a/usr/src/cmd/ctfdiff/ctfdiff.c b/usr/src/cmd/ctfdiff/ctfdiff.c
new file mode 100644
index 0000000000..731fba4af8
--- /dev/null
+++ b/usr/src/cmd/ctfdiff/ctfdiff.c
@@ -0,0 +1,518 @@
+/*
+ * 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.
+ */
+
+/*
+ * diff two CTF containers
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <strings.h>
+#include <libctf.h>
+#include <libgen.h>
+#include <stdarg.h>
+
+#define CTFDIFF_NAMELEN 256
+
+#define CTFDIFF_EXIT_SIMILAR 0
+#define CTFDIFF_EXIT_DIFFERENT 1
+#define CTFDIFF_EXIT_USAGE 2
+#define CTFDIFF_EXIT_ERROR 3
+
+typedef enum ctf_diff_cmd {
+ CTF_DIFF_TYPES = 0x01,
+ CTF_DIFF_FUNCS = 0x02,
+ CTF_DIFF_OBJS = 0x04,
+ CTF_DIFF_DEFAULT = 0x07,
+ CTF_DIFF_LABEL = 0x08,
+ CTF_DIFF_ALL = 0x0f
+} ctf_diff_cmd_t;
+
+typedef struct {
+ int dil_next;
+ const char **dil_labels;
+} ctfdiff_label_t;
+
+static char *g_progname;
+static const char *g_iname;
+static ctf_file_t *g_ifp;
+static const char *g_oname;
+static ctf_file_t *g_ofp;
+static char **g_typelist = NULL;
+static int g_nexttype = 0;
+static int g_ntypes = 0;
+static char **g_objlist = NULL;
+static int g_nextfunc = 0;
+static int g_nfuncs = 0;
+static char **g_funclist = NULL;
+static int g_nextobj = 0;
+static int g_nobjs = 0;
+static boolean_t g_onlydiff = B_FALSE;
+static boolean_t g_different = B_FALSE;
+static ctf_diff_cmd_t g_flag = 0;
+
+static void
+ctfdiff_fatal(const char *fmt, ...)
+{
+ va_list ap;
+
+ (void) fprintf(stderr, "%s: ", g_progname);
+ va_start(ap, fmt);
+ (void) vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ exit(CTFDIFF_EXIT_ERROR);
+}
+
+static const char *
+ctfdiff_fp_to_name(ctf_file_t *fp)
+{
+ if (fp == g_ifp)
+ return (g_iname);
+ if (fp == g_ofp)
+ return (g_oname);
+ return (NULL);
+}
+
+/* ARGSUSED */
+static void
+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];
+
+ if (similar == B_TRUE)
+ return;
+
+ if (ctf_symbol_name(ifp, iidx, namebuf, sizeof (namebuf)) == NULL) {
+ if (g_nextfunc != 0)
+ return;
+ (void) printf("ctf container %s function %lu is different\n",
+ ctfdiff_fp_to_name(ifp), iidx);
+ } else {
+ if (g_nextfunc != 0) {
+ int i;
+ for (i = 0; i < g_nextfunc; i++) {
+ if (strcmp(g_funclist[i], namebuf) == 0)
+ break;
+ }
+ if (i == g_nextfunc)
+ return;
+ }
+ (void) printf("ctf container %s function %s (%lu) is "
+ "different\n", ctfdiff_fp_to_name(ifp), namebuf, iidx);
+ }
+
+ g_different = B_TRUE;
+}
+
+/* ARGSUSED */
+static void
+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];
+
+ if (similar == B_TRUE)
+ return;
+
+ if (ctf_symbol_name(ifp, iidx, namebuf, sizeof (namebuf)) == NULL) {
+ if (g_nextobj != 0)
+ return;
+ (void) printf("ctf container %s object %lu is different\n",
+ ctfdiff_fp_to_name(ifp), iidx);
+ } else {
+ if (g_nextobj != 0) {
+ int i;
+ for (i = 0; i < g_nextobj; i++) {
+ if (strcmp(g_objlist[i], namebuf) == 0)
+ break;
+ }
+ if (i == g_nextobj)
+ return;
+ }
+ (void) printf("ctf container %s object %s (%lu) is different\n",
+ ctfdiff_fp_to_name(ifp), namebuf, iidx);
+ }
+
+ g_different = B_TRUE;
+}
+
+/* ARGSUSED */
+static void
+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)
+ return;
+
+ if (ctf_type_kind(ifp, iid) == CTF_K_UNKNOWN)
+ return;
+
+ /*
+ * Check if it's the type the user cares about.
+ */
+ if (g_nexttype != 0) {
+ int i;
+ char namebuf[CTFDIFF_NAMELEN];
+
+ if (ctf_type_name(ifp, iid, namebuf, sizeof (namebuf)) ==
+ NULL) {
+ ctfdiff_fatal("failed to obtain the name "
+ "of type %ld from %s: %s\n",
+ iid, ctfdiff_fp_to_name(ifp),
+ ctf_errmsg(ctf_errno(ifp)));
+ }
+
+ for (i = 0; i < g_nexttype; i++) {
+ if (strcmp(g_typelist[i], namebuf) == 0)
+ break;
+ }
+
+ if (i == g_nexttype)
+ return;
+ }
+
+ g_different = B_TRUE;
+
+ if (g_onlydiff == B_TRUE)
+ return;
+
+ (void) printf("ctf container %s type %ld is different\n",
+ ctfdiff_fp_to_name(ifp), iid);
+}
+
+/* ARGSUSED */
+static int
+ctfdiff_labels_count(const char *name, const ctf_lblinfo_t *li, void *arg)
+{
+ uint32_t *count = arg;
+ *count = *count + 1;
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+ctfdiff_labels_fill(const char *name, const ctf_lblinfo_t *li, void *arg)
+{
+ ctfdiff_label_t *dil = arg;
+
+ dil->dil_labels[dil->dil_next] = name;
+ dil->dil_next++;
+
+ return (0);
+}
+
+static int
+ctfdiff_labels(ctf_file_t *ifp, ctf_file_t *ofp)
+{
+ int ret;
+ uint32_t nilabel, nolabel, i, j;
+ ctfdiff_label_t idl, odl;
+ const char **ilptr, **olptr;
+
+ nilabel = nolabel = 0;
+ ret = ctf_label_iter(ifp, ctfdiff_labels_count, &nilabel);
+ if (ret == CTF_ERR)
+ return (ret);
+ 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", ctfdiff_fp_to_name(ifp),
+ ctfdiff_fp_to_name(ofp));
+ g_different = B_TRUE;
+ return (0);
+ }
+
+ if (nilabel == 0)
+ return (0);
+
+ ilptr = malloc(sizeof (char *) * nilabel);
+ olptr = malloc(sizeof (char *) * nolabel);
+ if (ilptr == NULL || olptr == NULL) {
+ ctfdiff_fatal("failed to allocate memory for label "
+ "comparison\n");
+ }
+
+ idl.dil_next = 0;
+ idl.dil_labels = ilptr;
+ odl.dil_next = 0;
+ odl.dil_labels = olptr;
+
+ if ((ret = ctf_label_iter(ifp, ctfdiff_labels_fill, &idl)) != 0)
+ goto out;
+ if ((ret = ctf_label_iter(ofp, ctfdiff_labels_fill, &odl)) != 0)
+ goto out;
+
+ for (i = 0; i < nilabel; i++) {
+ for (j = 0; j < nolabel; j++) {
+ if (strcmp(ilptr[i], olptr[j]) == 0)
+ break;
+ }
+
+ if (j == nolabel) {
+ (void) printf("ctf container %s labels differ from ctf "
+ "container %s\n", ctfdiff_fp_to_name(ifp),
+ ctfdiff_fp_to_name(ofp));
+ g_different = B_TRUE;
+ break;
+ }
+ }
+
+ ret = 0;
+out:
+ free(ilptr);
+ free(olptr);
+ return (ret);
+}
+
+static void
+ctfdiff_usage(const char *fmt, ...)
+{
+ if (fmt != NULL) {
+ va_list ap;
+
+ (void) fprintf(stderr, "%s: ", g_progname);
+ va_start(ap, fmt);
+ (void) vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ }
+
+ (void) fprintf(stderr, "Usage: %s [-afIloqt] [-F function] [-O object]"
+ "[-p parent] [-P parent]\n"
+ "\t[-T type] file1 file2\n"
+ "\n"
+ "\t-a diff label, types, objects, and functions\n"
+ "\t-f diff function type information\n"
+ "\t-F when diffing functions, only consider those named\n"
+ "\t-I ignore the names of integral types\n"
+ "\t-l diff CTF labels\n"
+ "\t-o diff global object type information\n"
+ "\t-O when diffing objects, only consider those named\n"
+ "\t-p set the CTF parent for file1\n"
+ "\t-P set the CTF parent for file2\n"
+ "\t-q set quiet mode (no diff information sent to stdout)\n"
+ "\t-t diff CTF type information\n"
+ "\t-T when diffing types, only consider those named\n",
+ g_progname);
+}
+
+int
+main(int argc, char *argv[])
+{
+ ctf_diff_flag_t flags = 0;
+ int err, c;
+ ctf_file_t *ifp, *ofp;
+ ctf_diff_t *cdp;
+ ctf_file_t *pifp = NULL;
+ ctf_file_t *pofp = NULL;
+
+ g_progname = basename(argv[0]);
+
+ while ((c = getopt(argc, argv, ":aqtfolIp:F:O:P:T:")) != -1) {
+ switch (c) {
+ case 'a':
+ g_flag |= CTF_DIFF_ALL;
+ break;
+ case 't':
+ g_flag |= CTF_DIFF_TYPES;
+ break;
+ case 'f':
+ g_flag |= CTF_DIFF_FUNCS;
+ break;
+ case 'o':
+ g_flag |= CTF_DIFF_OBJS;
+ break;
+ case 'l':
+ g_flag |= CTF_DIFF_LABEL;
+ break;
+ case 'q':
+ g_onlydiff = B_TRUE;
+ break;
+ case 'p':
+ pifp = ctf_open(optarg, &err);
+ if (pifp == NULL) {
+ ctfdiff_fatal("failed to open parent input "
+ "container %s: %s\n", optarg,
+ ctf_errmsg(err));
+ }
+ break;
+ case 'F':
+ if (g_nextfunc == g_nfuncs) {
+ if (g_nfuncs == 0)
+ g_nfuncs = 16;
+ else
+ g_nfuncs *= 2;
+ g_funclist = realloc(g_funclist,
+ sizeof (char *) * g_nfuncs);
+ if (g_funclist == NULL) {
+ ctfdiff_fatal("failed to allocate "
+ "memory for the %dth -F option: "
+ "%s\n", g_nexttype + 1,
+ strerror(errno));
+ }
+ }
+ g_funclist[g_nextfunc] = optarg;
+ g_nextfunc++;
+ break;
+ case 'O':
+ if (g_nextobj == g_nobjs) {
+ if (g_nobjs == 0)
+ g_nobjs = 16;
+ else
+ g_nobjs *= 2;
+ g_objlist = realloc(g_objlist,
+ sizeof (char *) * g_nobjs);
+ if (g_objlist == NULL) {
+ ctfdiff_fatal("failed to allocate "
+ "memory for the %dth -F option: "
+ "%s\n", g_nexttype + 1,
+ strerror(errno));
+ return (CTFDIFF_EXIT_ERROR);
+ }
+ }
+ g_objlist[g_nextobj] = optarg;
+ g_nextobj++;
+ break;
+ case 'I':
+ flags |= CTF_DIFF_F_IGNORE_INTNAMES;
+ break;
+ case 'P':
+ pofp = ctf_open(optarg, &err);
+ if (pofp == NULL) {
+ ctfdiff_fatal("failed to open parent output "
+ "container %s: %s\n", optarg,
+ ctf_errmsg(err));
+ }
+ break;
+ case 'T':
+ if (g_nexttype == g_ntypes) {
+ if (g_ntypes == 0)
+ g_ntypes = 16;
+ else
+ g_ntypes *= 2;
+ g_typelist = realloc(g_typelist,
+ sizeof (char *) * g_ntypes);
+ if (g_typelist == NULL) {
+ ctfdiff_fatal("failed to allocate "
+ "memory for the %dth -T option: "
+ "%s\n", g_nexttype + 1,
+ strerror(errno));
+ }
+ }
+ 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);
+ }
+ }
+
+ argc -= optind - 1;
+ argv += optind - 1;
+
+ if (g_flag == 0)
+ g_flag = CTF_DIFF_DEFAULT;
+
+ if (argc != 3) {
+ ctfdiff_usage(NULL);
+ return (CTFDIFF_EXIT_USAGE);
+ }
+
+ if (g_nexttype != 0 && !(g_flag & CTF_DIFF_TYPES)) {
+ ctfdiff_usage("-T cannot be used if not diffing types\n");
+ return (CTFDIFF_EXIT_USAGE);
+ }
+
+ if (g_nextfunc != 0 && !(g_flag & CTF_DIFF_FUNCS)) {
+ ctfdiff_usage("-F cannot be used if not diffing functions\n");
+ return (CTFDIFF_EXIT_USAGE);
+ }
+
+ if (g_nextobj != 0 && !(g_flag & CTF_DIFF_OBJS)) {
+ ctfdiff_usage("-O cannot be used if not diffing objects\n");
+ return (CTFDIFF_EXIT_USAGE);
+ }
+
+ ifp = ctf_open(argv[1], &err);
+ if (ifp == NULL) {
+ ctfdiff_fatal("failed to open %s: %s\n", argv[1],
+ ctf_errmsg(err));
+ }
+ if (pifp != NULL) {
+ err = ctf_import(ifp, pifp);
+ if (err != 0) {
+ ctfdiff_fatal("failed to set parent container: %s\n",
+ ctf_errmsg(ctf_errno(pifp)));
+ }
+ }
+ g_iname = argv[1];
+ g_ifp = ifp;
+
+ ofp = ctf_open(argv[2], &err);
+ if (ofp == NULL) {
+ ctfdiff_fatal("failed to open %s: %s\n", argv[2],
+ ctf_errmsg(err));
+ }
+
+ if (pofp != NULL) {
+ err = ctf_import(ofp, pofp);
+ if (err != 0) {
+ ctfdiff_fatal("failed to set parent container: %s\n",
+ ctf_errmsg(ctf_errno(pofp)));
+ }
+ }
+ g_oname = argv[2];
+ g_ofp = ofp;
+
+ if (ctf_diff_init(ifp, ofp, &cdp) != 0) {
+ ctfdiff_fatal("failed to initialize libctf diff engine: %s\n",
+ ctf_errmsg(ctf_errno(ifp)));
+ }
+
+ if (ctf_diff_setflags(cdp, flags) != 0) {
+ ctfdiff_fatal("failed to set ctfdiff flags: %s\n",
+ ctf_errmsg(ctf_errno(ifp)));
+ }
+
+ err = 0;
+ if ((g_flag & CTF_DIFF_TYPES) && err != CTF_ERR)
+ err = ctf_diff_types(cdp, ctfdiff_cb, NULL);
+ if ((g_flag & CTF_DIFF_FUNCS) && err != CTF_ERR)
+ err = ctf_diff_functions(cdp, ctfdiff_func_cb, NULL);
+ if ((g_flag & CTF_DIFF_OBJS) && err != CTF_ERR)
+ err = ctf_diff_objects(cdp, ctfdiff_obj_cb, NULL);
+ if ((g_flag & CTF_DIFF_LABEL) && err != CTF_ERR)
+ err = ctfdiff_labels(ifp, ofp);
+
+ ctf_diff_fini(cdp);
+ if (err == CTF_ERR) {
+ ctfdiff_fatal("encountered a libctf error: %s!\n",
+ ctf_errmsg(ctf_errno(ifp)));
+ }
+
+ return (g_different == B_TRUE ? CTFDIFF_EXIT_DIFFERENT :
+ CTFDIFF_EXIT_SIMILAR);
+}
diff --git a/usr/src/cmd/ctfdump/Makefile b/usr/src/cmd/ctfdump/Makefile
new file mode 100644
index 0000000000..37a1ef4cc8
--- /dev/null
+++ b/usr/src/cmd/ctfdump/Makefile
@@ -0,0 +1,36 @@
+#
+# 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 2018 Joyent, Inc.
+#
+
+PROG= ctfdump
+
+include ../Makefile.cmd
+
+CFLAGS += $(CCVERBOSE)
+LDLIBS += -lctf
+
+CSTD = $(CSTD_GNU99)
+C99LMODE = -Xc99=%all
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all $(ROOTPROG)
+
+clean:
+
+lint: lint_PROG
+
+include ../Makefile.targ
diff --git a/usr/src/cmd/ctfdump/ctfdump.c b/usr/src/cmd/ctfdump/ctfdump.c
new file mode 100644
index 0000000000..e241c35b47
--- /dev/null
+++ b/usr/src/cmd/ctfdump/ctfdump.c
@@ -0,0 +1,1235 @@
+/*
+ * 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) 2018, Joyent, Inc.
+ */
+
+/*
+ * Dump information about CTF containers.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <libctf.h>
+#include <libgen.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/note.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <strings.h>
+#include <err.h>
+
+#define MAX_NAMELEN (512)
+
+typedef enum ctfdump_arg {
+ CTFDUMP_OBJECTS = 0x001,
+ CTFDUMP_FUNCTIONS = 0x002,
+ CTFDUMP_HEADER = 0x004,
+ CTFDUMP_LABELS = 0x008,
+ CTFDUMP_STRINGS = 0x010,
+ CTFDUMP_STATS = 0x020,
+ CTFDUMP_TYPES = 0x040,
+ CTFDUMP_DEFAULT = 0x07f,
+ CTFDUMP_OUTPUT = 0x080,
+ CTFDUMP_SOURCE = 0x100,
+} ctfdump_arg_t;
+
+typedef struct ctfdump_stat {
+ ulong_t cs_ndata; /* number of data objects */
+ ulong_t cs_nfuncs; /* number of functions */
+ ulong_t cs_nfuncargs; /* number of function args */
+ ulong_t cs_nfuncmax; /* largest number of args */
+ ulong_t cs_ntypes[CTF_K_MAX]; /* number of types */
+ ulong_t cs_nsmembs; /* number of struct members */
+ ulong_t cs_nsmax; /* largest number of members */
+ ulong_t cs_structsz; /* sum of structures sizes */
+ ulong_t cs_sszmax; /* largest structure */
+ ulong_t cs_numembs; /* number of union members */
+ ulong_t cs_numax; /* largest number of members */
+ ulong_t cs_unionsz; /* sum of unions sizes */
+ ulong_t cs_uszmax; /* largest union */
+ ulong_t cs_nemembs; /* number of enum members */
+ ulong_t cs_nemax; /* largest number of members */
+ ulong_t cs_nstrings; /* number of strings */
+ ulong_t cs_strsz; /* string size */
+ ulong_t cs_strmax; /* longest string */
+} ctfdump_stat_t;
+
+typedef struct {
+ char ci_name[MAX_NAMELEN];
+ ctf_id_t ci_id;
+ ulong_t ci_symidx;
+ ctf_funcinfo_t ci_funcinfo;
+} ctf_idname_t;
+
+static ctf_idname_t *idnames;
+static const char *g_progname;
+static ctfdump_arg_t g_dump;
+static ctf_file_t *g_fp;
+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",
+ "DOUBLE",
+ "COMPLEX",
+ "DCOMPLEX",
+ "LDCOMPLEX",
+ "LDOUBLE",
+ "INTERVAL",
+ "DINTERVAL",
+ "LDINTERVAL",
+ "IMAGINARY",
+ "DIMAGINARY",
+ "LDIMAGINARY"
+};
+
+/*
+ * 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
+ * output it if we have actually enabled that section.
+ */
+static void
+ctfdump_printf(ctfdump_arg_t arg, const char *fmt, ...)
+{
+ va_list ap;
+
+ if ((arg & g_dump) == 0)
+ return;
+
+ va_start(ap, fmt);
+ (void) vfprintf(stdout, fmt, ap);
+ va_end(ap);
+}
+
+static void
+ctfdump_fatal(const char *fmt, ...)
+{
+ va_list ap;
+
+ (void) fprintf(stderr, "%s: ", g_progname);
+ va_start(ap, fmt);
+ (void) vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ exit(1);
+}
+
+static void
+ctfdump_usage(const char *fmt, ...)
+{
+ if (fmt != NULL) {
+ va_list ap;
+ (void) fprintf(stderr, "%s: ", g_progname);
+ va_start(ap, fmt);
+ (void) vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ }
+
+ (void) fprintf(stderr, "Usage: %s [-cdfhlsSt] [-p parent] [-u outfile] "
+ "file\n"
+ "\n"
+ "\t-c dump C-style output\n"
+ "\t-d dump object data\n"
+ "\t-f dump function data\n"
+ "\t-h dump the CTF header\n"
+ "\t-l dump the label table\n"
+ "\t-p use parent to supply additional information\n"
+ "\t-s dump the string table\n"
+ "\t-S dump statistics about the CTF container\n"
+ "\t-t dump type information\n"
+ "\t-u dump uncompressed CTF data to outfile\n",
+ g_progname);
+}
+
+static void
+ctfdump_title(ctfdump_arg_t arg, const char *header)
+{
+ static const char line[] = "----------------------------------------"
+ "----------------------------------------";
+ ctfdump_printf(arg, "\n- %s %.*s\n\n", header, (int)78 - strlen(header),
+ line);
+}
+
+static int
+ctfdump_objects_cb(const char *name, ctf_id_t id, ulong_t symidx, void *arg)
+{
+ _NOTE(ARGUNUSED(arg));
+
+ int len;
+
+ len = snprintf(NULL, 0, " [%lu] %ld", g_stats.cs_ndata, id);
+ ctfdump_printf(CTFDUMP_OBJECTS, " [%lu] %ld %*s%s (%lu)\n",
+ g_stats.cs_ndata, id, MAX(15 - len, 0), "", name, symidx);
+ g_stats.cs_ndata++;
+ return (0);
+}
+
+static void
+ctfdump_objects(void)
+{
+ ctfdump_title(CTFDUMP_OBJECTS, "Data Objects");
+ if (ctf_object_iter(g_fp, ctfdump_objects_cb, NULL) == CTF_ERR) {
+ warnx("failed to dump objects: %s",
+ ctf_errmsg(ctf_errno(g_fp)));
+ g_exit = 1;
+ }
+}
+
+static void
+ctfdump_fargs_grow(int nargs)
+{
+ if (g_nfargc < nargs) {
+ g_fargc = realloc(g_fargc, sizeof (ctf_id_t) * nargs);
+ if (g_fargc == NULL)
+ ctfdump_fatal("failed to get memory for %d "
+ "ctf_id_t's\n", nargs);
+ g_nfargc = nargs;
+ }
+}
+
+static int
+ctfdump_functions_cb(const char *name, ulong_t symidx, ctf_funcinfo_t *ctc,
+ void *arg)
+{
+ _NOTE(ARGUNUSED(arg));
+ int i;
+
+ if (ctc->ctc_argc != 0) {
+ ctfdump_fargs_grow(ctc->ctc_argc);
+ if (ctf_func_args(g_fp, symidx, g_nfargc, g_fargc) == CTF_ERR)
+ ctfdump_fatal("failed to get arguments for function "
+ "%s: %s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+ }
+
+ ctfdump_printf(CTFDUMP_FUNCTIONS,
+ " [%lu] %s (%lu) returns: %ld args: (", g_stats.cs_nfuncs, name,
+ symidx, ctc->ctc_return);
+ for (i = 0; i < ctc->ctc_argc; i++)
+ ctfdump_printf(CTFDUMP_FUNCTIONS, "%ld%s", g_fargc[i],
+ i + 1 == ctc->ctc_argc ? "" : ", ");
+ if (ctc->ctc_flags & CTF_FUNC_VARARG)
+ ctfdump_printf(CTFDUMP_FUNCTIONS, "%s...",
+ ctc->ctc_argc == 0 ? "" : ", ");
+ ctfdump_printf(CTFDUMP_FUNCTIONS, ")\n");
+
+ g_stats.cs_nfuncs++;
+ g_stats.cs_nfuncargs += ctc->ctc_argc;
+ g_stats.cs_nfuncmax = MAX(ctc->ctc_argc, g_stats.cs_nfuncmax);
+
+ return (0);
+}
+
+static void
+ctfdump_functions(void)
+{
+ ctfdump_title(CTFDUMP_FUNCTIONS, "Functions");
+
+ if (ctf_function_iter(g_fp, ctfdump_functions_cb, NULL) == CTF_ERR) {
+ warnx("failed to dump functions: %s",
+ ctf_errmsg(ctf_errno(g_fp)));
+ g_exit = 1;
+ }
+}
+
+static void
+ctfdump_header(void)
+{
+ const ctf_header_t *hp;
+ const char *parname, *parlabel;
+
+ ctfdump_title(CTFDUMP_HEADER, "CTF Header");
+ ctf_dataptr(g_fp, (const void **)&hp, NULL);
+ ctfdump_printf(CTFDUMP_HEADER, " cth_magic = 0x%04x\n",
+ hp->cth_magic);
+ ctfdump_printf(CTFDUMP_HEADER, " cth_version = %u\n",
+ hp->cth_version);
+ ctfdump_printf(CTFDUMP_HEADER, " cth_flags = 0x%02x\n",
+ ctf_flags(g_fp));
+ parname = ctf_parent_name(g_fp);
+ parlabel = ctf_parent_label(g_fp);
+ ctfdump_printf(CTFDUMP_HEADER, " cth_parlabel = %s\n",
+ parlabel == NULL ? "(anon)" : parlabel);
+ ctfdump_printf(CTFDUMP_HEADER, " cth_parname = %s\n",
+ parname == NULL ? "(anon)" : parname);
+ ctfdump_printf(CTFDUMP_HEADER, " cth_lbloff = %u\n",
+ hp->cth_lbloff);
+ ctfdump_printf(CTFDUMP_HEADER, " cth_objtoff = %u\n",
+ hp->cth_objtoff);
+ ctfdump_printf(CTFDUMP_HEADER, " cth_funcoff = %u\n",
+ hp->cth_funcoff);
+ ctfdump_printf(CTFDUMP_HEADER, " cth_typeoff = %u\n",
+ hp->cth_typeoff);
+ ctfdump_printf(CTFDUMP_HEADER, " cth_stroff = %u\n",
+ hp->cth_stroff);
+ ctfdump_printf(CTFDUMP_HEADER, " cth_strlen = %u\n",
+ hp->cth_strlen);
+}
+
+static int
+ctfdump_labels_cb(const char *name, const ctf_lblinfo_t *li, void *arg)
+{
+ _NOTE(ARGUNUSED(arg));
+ ctfdump_printf(CTFDUMP_LABELS, " %5ld %s\n", li->ctb_typeidx, name);
+ return (0);
+}
+
+static void
+ctfdump_labels(void)
+{
+ ctfdump_title(CTFDUMP_LABELS, "Label Table");
+ if (ctf_label_iter(g_fp, ctfdump_labels_cb, NULL) == CTF_ERR) {
+ warnx("failed to dump labels: %s",
+ ctf_errmsg(ctf_errno(g_fp)));
+ g_exit = 1;
+ }
+}
+
+static int
+ctfdump_strings_cb(const char *s, void *arg)
+{
+ size_t len = strlen(s) + 1;
+ ulong_t *stroff = arg;
+ ctfdump_printf(CTFDUMP_STRINGS, " [%lu] %s\n", *stroff,
+ *s == '\0' ? "\\0" : s);
+ *stroff = *stroff + len;
+ g_stats.cs_nstrings++;
+ g_stats.cs_strsz += len;
+ g_stats.cs_strmax = MAX(g_stats.cs_strmax, len);
+ return (0);
+}
+
+static void
+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) {
+ warnx("failed to dump strings: %s",
+ ctf_errmsg(ctf_errno(g_fp)));
+ g_exit = 1;
+ }
+}
+
+static void
+ctfdump_stat_int(const char *name, ulong_t value)
+{
+ ctfdump_printf(CTFDUMP_STATS, " %-36s= %lu\n", name, value);
+}
+
+static void
+ctfdump_stat_fp(const char *name, float value)
+{
+ ctfdump_printf(CTFDUMP_STATS, " %-36s= %.2f\n", name, value);
+}
+
+static void
+ctfdump_stats(void)
+{
+ int i;
+ ulong_t sum;
+
+ ctfdump_title(CTFDUMP_STATS, "CTF Statistics");
+
+ ctfdump_stat_int("total number of data objects", g_stats.cs_ndata);
+ ctfdump_printf(CTFDUMP_STATS, "\n");
+ ctfdump_stat_int("total number of functions", g_stats.cs_nfuncs);
+ ctfdump_stat_int("total number of function arguments",
+ g_stats.cs_nfuncargs);
+ ctfdump_stat_int("maximum argument list length", g_stats.cs_nfuncmax);
+ if (g_stats.cs_nfuncs != 0)
+ ctfdump_stat_fp("average argument list length",
+ (float)g_stats.cs_nfuncargs / (float)g_stats.cs_nfuncs);
+ ctfdump_printf(CTFDUMP_STATS, "\n");
+
+ sum = 0;
+ for (i = 0; i < CTF_K_MAX; i++)
+ sum += g_stats.cs_ntypes[i];
+ ctfdump_stat_int("total number of types", sum);
+ ctfdump_stat_int("total number of integers",
+ g_stats.cs_ntypes[CTF_K_INTEGER]);
+ ctfdump_stat_int("total number of floats",
+ g_stats.cs_ntypes[CTF_K_FLOAT]);
+ ctfdump_stat_int("total number of pointers",
+ g_stats.cs_ntypes[CTF_K_POINTER]);
+ ctfdump_stat_int("total number of arrays",
+ g_stats.cs_ntypes[CTF_K_ARRAY]);
+ ctfdump_stat_int("total number of func types",
+ g_stats.cs_ntypes[CTF_K_FUNCTION]);
+ ctfdump_stat_int("total number of structs",
+ g_stats.cs_ntypes[CTF_K_STRUCT]);
+ ctfdump_stat_int("total number of unions",
+ g_stats.cs_ntypes[CTF_K_UNION]);
+ ctfdump_stat_int("total number of enums",
+ g_stats.cs_ntypes[CTF_K_ENUM]);
+ ctfdump_stat_int("total number of forward tags",
+ g_stats.cs_ntypes[CTF_K_FORWARD]);
+ ctfdump_stat_int("total number of typedefs",
+ g_stats.cs_ntypes[CTF_K_TYPEDEF]);
+ ctfdump_stat_int("total number of volatile types",
+ g_stats.cs_ntypes[CTF_K_VOLATILE]);
+ ctfdump_stat_int("total number of const types",
+ g_stats.cs_ntypes[CTF_K_CONST]);
+ ctfdump_stat_int("total number of restrict types",
+ g_stats.cs_ntypes[CTF_K_RESTRICT]);
+ ctfdump_stat_int("total number of unknowns (holes)",
+ g_stats.cs_ntypes[CTF_K_UNKNOWN]);
+
+ ctfdump_printf(CTFDUMP_STATS, "\n");
+ ctfdump_stat_int("total number of struct members", g_stats.cs_nsmembs);
+ ctfdump_stat_int("maximum number of struct members", g_stats.cs_nsmax);
+ ctfdump_stat_int("total size of all structs", g_stats.cs_structsz);
+ ctfdump_stat_int("maximum size of a struct", g_stats.cs_sszmax);
+ if (g_stats.cs_ntypes[CTF_K_STRUCT] != 0) {
+ ctfdump_stat_fp("average number of struct members",
+ (float)g_stats.cs_nsmembs /
+ (float)g_stats.cs_ntypes[CTF_K_STRUCT]);
+ ctfdump_stat_fp("average size of a struct",
+ (float)g_stats.cs_structsz /
+ (float)g_stats.cs_ntypes[CTF_K_STRUCT]);
+ }
+ ctfdump_printf(CTFDUMP_STATS, "\n");
+ ctfdump_stat_int("total number of union members", g_stats.cs_numembs);
+ ctfdump_stat_int("maximum number of union members", g_stats.cs_numax);
+ ctfdump_stat_int("total size of all unions", g_stats.cs_unionsz);
+ ctfdump_stat_int("maximum size of a union", g_stats.cs_uszmax);
+ if (g_stats.cs_ntypes[CTF_K_UNION] != 0) {
+ ctfdump_stat_fp("average number of union members",
+ (float)g_stats.cs_numembs /
+ (float)g_stats.cs_ntypes[CTF_K_UNION]);
+ ctfdump_stat_fp("average size of a union",
+ (float)g_stats.cs_unionsz /
+ (float)g_stats.cs_ntypes[CTF_K_UNION]);
+ }
+ ctfdump_printf(CTFDUMP_STATS, "\n");
+
+ ctfdump_stat_int("total number of enum members", g_stats.cs_nemembs);
+ ctfdump_stat_int("maximum number of enum members", g_stats.cs_nemax);
+ if (g_stats.cs_ntypes[CTF_K_ENUM] != 0) {
+ ctfdump_stat_fp("average number of enum members",
+ (float)g_stats.cs_nemembs /
+ (float)g_stats.cs_ntypes[CTF_K_ENUM]);
+ }
+ ctfdump_printf(CTFDUMP_STATS, "\n");
+
+ ctfdump_stat_int("total number of strings", g_stats.cs_nstrings);
+ ctfdump_stat_int("bytes of string data", g_stats.cs_strsz);
+ ctfdump_stat_int("maximum string length", g_stats.cs_strmax);
+ if (g_stats.cs_nstrings != 0)
+ ctfdump_stat_fp("average string length",
+ (float)g_stats.cs_strsz / (float)g_stats.cs_nstrings);
+ ctfdump_printf(CTFDUMP_STATS, "\n");
+}
+
+static void
+ctfdump_intenc_name(ctf_encoding_t *cte, char *buf, int len)
+{
+ int off = 0;
+ boolean_t space = B_FALSE;
+
+ if (cte->cte_format == 0 || (cte->cte_format &
+ ~(CTF_INT_SIGNED | CTF_INT_CHAR | CTF_INT_BOOL |
+ CTF_INT_VARARGS)) != 0) {
+ (void) snprintf(buf, len, "0x%x", cte->cte_format);
+ return;
+ }
+
+ if (cte->cte_format & CTF_INT_SIGNED) {
+ off += snprintf(buf + off, MAX(len - off, 0), "%sSIGNED",
+ space == B_TRUE ? " " : "");
+ space = B_TRUE;
+ }
+
+ if (cte->cte_format & CTF_INT_CHAR) {
+ off += snprintf(buf + off, MAX(len - off, 0), "%sCHAR",
+ space == B_TRUE ? " " : "");
+ space = B_TRUE;
+ }
+
+ if (cte->cte_format & CTF_INT_BOOL) {
+ off += snprintf(buf + off, MAX(len - off, 0), "%sBOOL",
+ space == B_TRUE ? " " : "");
+ space = B_TRUE;
+ }
+
+ if (cte->cte_format & CTF_INT_VARARGS) {
+ off += snprintf(buf + off, MAX(len - off, 0), "%sVARARGS",
+ space == B_TRUE ? " " : "");
+ space = B_TRUE;
+ }
+}
+
+static int
+ctfdump_member_cb(const char *member, ctf_id_t type, ulong_t off, void *arg)
+{
+ int *count = arg;
+ ctfdump_printf(CTFDUMP_TYPES, "\t%s type=%ld off=%lu\n", member, type,
+ off);
+ *count = *count + 1;
+ return (0);
+}
+
+static int
+ctfdump_enum_cb(const char *name, int value, void *arg)
+{
+ int *count = arg;
+ ctfdump_printf(CTFDUMP_TYPES, "\t%s = %d\n", name, value);
+ *count = *count + 1;
+ return (0);
+}
+
+static int
+ctfdump_types_cb(ctf_id_t id, boolean_t root, void *arg)
+{
+ _NOTE(ARGUNUSED(arg));
+ int kind, i, count;
+ ctf_id_t ref;
+ char name[MAX_NAMELEN], ienc[128];
+ const char *encn;
+ ctf_funcinfo_t ctc;
+ ctf_arinfo_t ar;
+ ctf_encoding_t cte;
+ ssize_t size;
+
+ if ((kind = ctf_type_kind(g_fp, id)) == CTF_ERR)
+ ctfdump_fatal("encountered malformed ctf, type %s does not "
+ "have a kind: %s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+
+ if (ctf_type_name(g_fp, id, name, sizeof (name)) == NULL) {
+ if (ctf_errno(g_fp) != ECTF_NOPARENT)
+ ctfdump_fatal("type %ld missing name: %s\n", id,
+ ctf_errmsg(ctf_errno(g_fp)));
+ (void) snprintf(name, sizeof (name), "(unknown %s)",
+ ctf_kind_name(g_fp, kind));
+ }
+
+ g_stats.cs_ntypes[kind]++;
+ if (root == B_TRUE)
+ ctfdump_printf(CTFDUMP_TYPES, " <%ld> ", id);
+ else
+ ctfdump_printf(CTFDUMP_TYPES, " [%ld] ", id);
+
+ switch (kind) {
+ case CTF_K_UNKNOWN:
+ break;
+ case CTF_K_INTEGER:
+ if (ctf_type_encoding(g_fp, id, &cte) == CTF_ERR)
+ ctfdump_fatal("failed to get encoding information "
+ "for %s: %s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+ ctfdump_intenc_name(&cte, ienc, sizeof (ienc));
+ ctfdump_printf(CTFDUMP_TYPES,
+ "%s encoding=%s offset=%u bits=%u",
+ name, ienc, cte.cte_offset, cte.cte_bits);
+ break;
+ case CTF_K_FLOAT:
+ if (ctf_type_encoding(g_fp, id, &cte) == CTF_ERR)
+ ctfdump_fatal("failed to get encoding information "
+ "for %s: %s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+ if (cte.cte_format < 1 || cte.cte_format > 12)
+ encn = "unknown";
+ else
+ encn = ctfdump_fpenc[cte.cte_format];
+ ctfdump_printf(CTFDUMP_TYPES, "%s encoding=%s offset=%u "
+ "bits=%u", name, encn, cte.cte_offset, cte.cte_bits);
+ break;
+ case CTF_K_POINTER:
+ if ((ref = ctf_type_reference(g_fp, id)) == CTF_ERR)
+ ctfdump_fatal("failed to get reference type for %s: "
+ "%s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+ ctfdump_printf(CTFDUMP_TYPES, "%s refers to %ld", name,
+ ref);
+ break;
+ case CTF_K_ARRAY:
+ if (ctf_array_info(g_fp, id, &ar) == CTF_ERR)
+ ctfdump_fatal("failed to get array information for "
+ "%s: %s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+ ctfdump_printf(CTFDUMP_TYPES, "%s contents: %ld, index: %ld",
+ name, ar.ctr_contents, ar.ctr_index);
+ break;
+ case CTF_K_FUNCTION:
+ if (ctf_func_info_by_id(g_fp, id, &ctc) == CTF_ERR)
+ ctfdump_fatal("failed to get function info for %s: "
+ "%s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+ if (ctc.ctc_argc > 0) {
+ ctfdump_fargs_grow(ctc.ctc_argc);
+ if (ctf_func_args_by_id(g_fp, id, g_nfargc, g_fargc) ==
+ CTF_ERR)
+ ctfdump_fatal("failed to get function "
+ "arguments for %s: %s\n", name,
+ ctf_errmsg(ctf_errno(g_fp)));
+ }
+ ctfdump_printf(CTFDUMP_TYPES,
+ "%s returns: %ld args: (", name, ctc.ctc_return);
+ for (i = 0; i < ctc.ctc_argc; i++) {
+ ctfdump_printf(CTFDUMP_TYPES, "%ld%s", g_fargc[i],
+ i + 1 == ctc.ctc_argc ? "" : ", ");
+ }
+ if (ctc.ctc_flags & CTF_FUNC_VARARG)
+ ctfdump_printf(CTFDUMP_TYPES, "%s...",
+ ctc.ctc_argc == 0 ? "" : ", ");
+ ctfdump_printf(CTFDUMP_TYPES, ")");
+ break;
+ case CTF_K_STRUCT:
+ case CTF_K_UNION:
+ size = ctf_type_size(g_fp, id);
+ if (size == CTF_ERR)
+ ctfdump_fatal("failed to get size of %s: %s\n", name,
+ ctf_errmsg(ctf_errno(g_fp)));
+ ctfdump_printf(CTFDUMP_TYPES, "%s (%zd bytes)\n", name, size);
+ count = 0;
+ if (ctf_member_iter(g_fp, id, ctfdump_member_cb, &count) != 0)
+ ctfdump_fatal("failed to iterate members of %s: %s\n",
+ name, ctf_errmsg(ctf_errno(g_fp)));
+ if (kind == CTF_K_STRUCT) {
+ g_stats.cs_nsmembs += count;
+ g_stats.cs_nsmax = MAX(count, g_stats.cs_nsmax);
+ g_stats.cs_structsz += size;
+ g_stats.cs_sszmax = MAX(size, g_stats.cs_sszmax);
+ } else {
+ g_stats.cs_numembs += count;
+ g_stats.cs_numax = MAX(count, g_stats.cs_numax);
+ g_stats.cs_unionsz += size;
+ g_stats.cs_uszmax = MAX(count, g_stats.cs_uszmax);
+ }
+ break;
+ case CTF_K_ENUM:
+ ctfdump_printf(CTFDUMP_TYPES, "%s\n", name);
+ count = 0;
+ if (ctf_enum_iter(g_fp, id, ctfdump_enum_cb, &count) != 0)
+ ctfdump_fatal("failed to iterate enumerators of %s: "
+ "%s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+ g_stats.cs_nemembs += count;
+ g_stats.cs_nemax = MAX(g_stats.cs_nemax, count);
+ break;
+ case CTF_K_FORWARD:
+ ctfdump_printf(CTFDUMP_TYPES, "forward %s\n", name);
+ break;
+ case CTF_K_TYPEDEF:
+ if ((ref = ctf_type_reference(g_fp, id)) == CTF_ERR)
+ ctfdump_fatal("failed to get reference type for %s: "
+ "%s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+ ctfdump_printf(CTFDUMP_TYPES, "typedef %s refers to %ld", name,
+ ref);
+ break;
+ case CTF_K_VOLATILE:
+ if ((ref = ctf_type_reference(g_fp, id)) == CTF_ERR)
+ ctfdump_fatal("failed to get reference type for %s: "
+ "%s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+ ctfdump_printf(CTFDUMP_TYPES, "%s refers to %ld", name,
+ ref);
+ break;
+ case CTF_K_CONST:
+ if ((ref = ctf_type_reference(g_fp, id)) == CTF_ERR)
+ ctfdump_fatal("failed to get reference type for %s: "
+ "%s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+ ctfdump_printf(CTFDUMP_TYPES, "%s refers to %ld", name,
+ ref);
+ break;
+ case CTF_K_RESTRICT:
+ if ((ref = ctf_type_reference(g_fp, id)) == CTF_ERR)
+ ctfdump_fatal("failed to get reference type for %s: "
+ "%s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+ ctfdump_printf(CTFDUMP_TYPES, "%s refers to %ld", name,
+ ref);
+ break;
+ default:
+ ctfdump_fatal("encountered unknown kind for type %s: %d\n",
+ name, kind);
+ }
+
+ ctfdump_printf(CTFDUMP_TYPES, "\n");
+
+ return (0);
+}
+
+static void
+ctfdump_types(void)
+{
+ ctfdump_title(CTFDUMP_TYPES, "Types");
+
+ if (ctf_type_iter(g_fp, B_TRUE, ctfdump_types_cb, NULL) == CTF_ERR) {
+ warnx("failed to dump types: %s",
+ ctf_errmsg(ctf_errno(g_fp)));
+ g_exit = 1;
+ }
+}
+
+/*
+ * C-style output. This is designed mainly for comparison purposes, and doesn't
+ * produce directly valid C:
+ *
+ * - the declarations are sorted alphabetically not semantically
+ * - anonymous enums without other users are elided (e.g. IDCS_PROBE_SENT)
+ * - doubly-pointed-to functions are wrong (e.g. in kiconv_ops_t)
+ * - anon unions declared within SOUs aren't expanded
+ * - function arguments aren't expanded recursively
+ */
+
+static void
+ctfsrc_refname(ctf_id_t id, char *buf, size_t bufsize)
+{
+ ctf_id_t ref;
+
+ if ((ref = ctf_type_reference(g_fp, id)) == CTF_ERR) {
+ ctfdump_fatal("failed to get reference type for %ld: "
+ "%s\n", id, ctf_errmsg(ctf_errno(g_fp)));
+ }
+
+ (void) ctf_type_name(g_fp, ref, buf, bufsize);
+}
+
+static int
+ctfsrc_member_cb(const char *member, ctf_id_t type, ulong_t off, void *arg)
+{
+ _NOTE(ARGUNUSED(arg));
+ char name[MAX_NAMELEN];
+
+ if (ctf_type_cname(g_fp, type, name, sizeof (name), member) == NULL) {
+ if (ctf_errno(g_fp) != ECTF_NOPARENT) {
+ ctfdump_fatal("type %ld missing name: %s\n", type,
+ ctf_errmsg(ctf_errno(g_fp)));
+ }
+
+ (void) snprintf(name, sizeof (name), "unknown_t %s", member);
+ }
+
+ /*
+ * A byte offset is friendlier, but we'll print bits too if it's not
+ * aligned (i.e. a bitfield).
+ */
+ if (off % NBBY != 0) {
+ (void) printf("\t%s; /* offset: %lu bytes (%lu bits) */\n",
+ name, off / NBBY, off);
+ } else {
+ (void) printf("\t%s; /* offset: %lu bytes */\n",
+ name, off / NBBY);
+ }
+ return (0);
+}
+
+static int
+ctfsrc_enum_cb(const char *name, int value, void *arg)
+{
+ _NOTE(ARGUNUSED(arg));
+ (void) printf("\t%s = %d,\n", name, value);
+ return (0);
+}
+
+static int
+is_anon_refname(const char *refname)
+{
+ return ((strcmp(refname, "struct ") == 0 ||
+ strcmp(refname, "union ") == 0 ||
+ strcmp(refname, "enum ") == 0));
+}
+
+static int
+ctfsrc_collect_types_cb(ctf_id_t id, boolean_t root, void *arg)
+{
+ _NOTE(ARGUNUSED(root, arg));
+ (void) ctf_type_name(g_fp, id, idnames[id].ci_name,
+ sizeof (idnames[id].ci_name));
+ idnames[id].ci_id = id;
+ return (0);
+}
+
+static void
+ctfsrc_type(ctf_id_t id, const char *name)
+{
+ char refname[MAX_NAMELEN];
+ ctf_id_t ref;
+ ssize_t size;
+ int kind;
+
+ if ((kind = ctf_type_kind(g_fp, id)) == CTF_ERR) {
+ ctfdump_fatal("encountered malformed ctf, type %s does not "
+ "have a kind: %s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+ }
+
+ switch (kind) {
+ case CTF_K_STRUCT:
+ case CTF_K_UNION:
+ /*
+ * Delay printing anonymous SOUs; a later typedef will usually
+ * pick them up.
+ */
+ if (is_anon_refname(name))
+ break;
+
+ if ((size = ctf_type_size(g_fp, id)) == CTF_ERR) {
+ ctfdump_fatal("failed to get size of %s: %s\n", name,
+ ctf_errmsg(ctf_errno(g_fp)));
+ }
+
+ (void) printf("%s { /* 0x%x bytes */\n", name, size);
+
+ if (ctf_member_iter(g_fp, id, ctfsrc_member_cb, NULL) != 0) {
+ ctfdump_fatal("failed to iterate members of %s: %s\n",
+ name, ctf_errmsg(ctf_errno(g_fp)));
+ }
+
+ (void) printf("};\n\n");
+ break;
+ case CTF_K_ENUM:
+ /*
+ * This will throw away any anon enum that isn't followed by a
+ * typedef...
+ */
+ if (is_anon_refname(name))
+ break;
+
+ (void) printf("%s {\n", name);
+
+ if (ctf_enum_iter(g_fp, id, ctfsrc_enum_cb, NULL) != 0) {
+ ctfdump_fatal("failed to iterate enumerators of %s: "
+ "%s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+ }
+
+ (void) printf("};\n\n");
+ break;
+ case CTF_K_TYPEDEF:
+ ctfsrc_refname(id, refname, sizeof (refname));
+
+ if (!is_anon_refname(refname)) {
+ (void) ctf_type_cname(g_fp,
+ ctf_type_reference(g_fp, id), refname,
+ sizeof (refname), name);
+
+ (void) printf("typedef %s;\n\n", refname);
+ break;
+ }
+
+ ref = ctf_type_reference(g_fp, id);
+
+ if (ctf_type_kind(g_fp, ref) == CTF_K_ENUM) {
+ (void) printf("typedef enum {\n");
+
+ if (ctf_enum_iter(g_fp, ref,
+ ctfsrc_enum_cb, NULL) != 0) {
+ ctfdump_fatal("failed to iterate enumerators "
+ "of %s: %s\n", refname,
+ ctf_errmsg(ctf_errno(g_fp)));
+ }
+
+ (void) printf("} %s;\n\n", name);
+ } else {
+ if ((size = ctf_type_size(g_fp, ref)) == CTF_ERR) {
+ ctfdump_fatal("failed to get size of %s: %s\n",
+ refname, ctf_errmsg(ctf_errno(g_fp)));
+ }
+
+ (void) printf("typedef %s{ /* 0x%zx bytes */\n",
+ refname, size);
+
+ if (ctf_member_iter(g_fp, ref,
+ ctfsrc_member_cb, NULL) != 0) {
+ ctfdump_fatal("failed to iterate members "
+ "of %s: %s\n", refname,
+ ctf_errmsg(ctf_errno(g_fp)));
+ }
+
+ (void) printf("} %s;\n\n", name);
+ }
+
+ break;
+ case CTF_K_FORWARD:
+ (void) printf("%s;\n\n", name);
+ break;
+ case CTF_K_UNKNOWN:
+ case CTF_K_INTEGER:
+ case CTF_K_FLOAT:
+ case CTF_K_POINTER:
+ case CTF_K_ARRAY:
+ case CTF_K_FUNCTION:
+ case CTF_K_VOLATILE:
+ case CTF_K_CONST:
+ case CTF_K_RESTRICT:
+ break;
+ default:
+ ctfdump_fatal("encountered unknown kind for type %s: %d\n",
+ name, kind);
+ break;
+ }
+}
+
+static int
+ctfsrc_collect_objects_cb(const char *name, ctf_id_t id,
+ ulong_t symidx, void *arg)
+{
+ size_t *count = arg;
+
+ /* local static vars can have an unknown ID */
+ if (id == 0)
+ return (0);
+
+ (void) strlcpy(idnames[*count].ci_name, name,
+ sizeof (idnames[*count].ci_name));
+ idnames[*count].ci_id = id;
+ idnames[*count].ci_symidx = symidx;
+ *count = *count + 1;
+ return (0);
+}
+
+static void
+ctfsrc_object(ctf_id_t id, const char *name)
+{
+ char tname[MAX_NAMELEN];
+
+ if (ctf_type_cname(g_fp, id, tname, sizeof (tname), name) == NULL) {
+ if (ctf_errno(g_fp) != ECTF_NOPARENT) {
+ ctfdump_fatal("type %ld missing name: %s\n", id,
+ ctf_errmsg(ctf_errno(g_fp)));
+ }
+ (void) snprintf(tname, sizeof (tname), "unknown_t %s", name);
+ }
+
+ (void) printf("extern %s;\n", tname);
+}
+
+static int
+ctfsrc_collect_functions_cb(const char *name, ulong_t symidx,
+ ctf_funcinfo_t *ctc, void *arg)
+{
+ size_t *count = arg;
+
+ (void) strlcpy(idnames[*count].ci_name, name,
+ sizeof (idnames[*count].ci_name));
+ bcopy(ctc, &idnames[*count].ci_funcinfo, sizeof (*ctc));
+ idnames[*count].ci_id = 0;
+ idnames[*count].ci_symidx = symidx;
+ *count = *count + 1;
+ return (0);
+}
+
+static void
+ctfsrc_function(ctf_idname_t *idn)
+{
+ ctf_funcinfo_t *cfi = &idn->ci_funcinfo;
+ char name[MAX_NAMELEN] = "unknown_t";
+
+ (void) ctf_type_name(g_fp, cfi->ctc_return, name, sizeof (name));
+
+ (void) printf("extern %s %s(", name, idn->ci_name);
+
+ if (cfi->ctc_argc != 0) {
+ ctfdump_fargs_grow(cfi->ctc_argc);
+ if (ctf_func_args(g_fp, idn->ci_symidx,
+ g_nfargc, g_fargc) == CTF_ERR) {
+ ctfdump_fatal("failed to get arguments for function "
+ "%s: %s\n", idn->ci_name,
+ ctf_errmsg(ctf_errno(g_fp)));
+ }
+
+ for (size_t i = 0; i < cfi->ctc_argc; i++) {
+ ctf_id_t aid = g_fargc[i];
+
+ name[0] = '\0';
+
+ (void) ctf_type_name(g_fp, aid, name, sizeof (name));
+
+ (void) printf("%s%s", name,
+ i + 1 == cfi->ctc_argc ? "" : ", ");
+ }
+ } else {
+ if (!(cfi->ctc_flags & CTF_FUNC_VARARG))
+ (void) printf("void");
+ }
+
+ if (cfi->ctc_flags & CTF_FUNC_VARARG)
+ (void) printf("%s...", cfi->ctc_argc == 0 ? "" : ", ");
+
+ (void) printf(");\n");
+}
+
+static int
+idname_compare(const void *lhs, const void *rhs)
+{
+ return (strcmp(((ctf_idname_t *)lhs)->ci_name,
+ ((ctf_idname_t *)rhs)->ci_name));
+}
+
+static void
+ctfdump_source(void)
+{
+ ulong_t nr_syms = ctf_nr_syms(g_fp);
+ ctf_id_t max_id = ctf_max_id(g_fp);
+ size_t count = 0;
+
+ (void) printf("/* Types */\n\n");
+
+ if ((idnames = calloc(max_id + 1, sizeof (idnames[0]))) == NULL) {
+ ctfdump_fatal("failed to alloc idnames: %s\n",
+ strerror(errno));
+ }
+
+ if (ctf_type_iter(g_fp, B_FALSE, ctfsrc_collect_types_cb,
+ idnames) == CTF_ERR) {
+ warnx("failed to collect types: %s",
+ ctf_errmsg(ctf_errno(g_fp)));
+ g_exit = 1;
+ }
+
+ qsort(idnames, max_id, sizeof (ctf_idname_t), idname_compare);
+
+ for (size_t i = 0; i < max_id; i++) {
+ if (idnames[i].ci_id != 0)
+ ctfsrc_type(idnames[i].ci_id, idnames[i].ci_name);
+ }
+
+ free(idnames);
+
+ (void) printf("\n\n/* Data Objects */\n\n");
+
+ if ((idnames = calloc(nr_syms, sizeof (idnames[0]))) == NULL) {
+ ctfdump_fatal("failed to alloc idnames: %s\n",
+ strerror(errno));
+ }
+
+ if (ctf_object_iter(g_fp, ctfsrc_collect_objects_cb,
+ &count) == CTF_ERR) {
+ warnx("failed to collect objects: %s",
+ ctf_errmsg(ctf_errno(g_fp)));
+ g_exit = 1;
+ }
+
+ qsort(idnames, count, sizeof (ctf_idname_t), idname_compare);
+
+ for (size_t i = 0; i < count; i++)
+ ctfsrc_object(idnames[i].ci_id, idnames[i].ci_name);
+
+ free(idnames);
+
+ (void) printf("\n\n/* Functions */\n\n");
+
+ if ((idnames = calloc(nr_syms, sizeof (idnames[0]))) == NULL) {
+ ctfdump_fatal("failed to alloc idnames: %s\n",
+ strerror(errno));
+ }
+
+ count = 0;
+
+ if (ctf_function_iter(g_fp, ctfsrc_collect_functions_cb,
+ &count) == CTF_ERR) {
+ warnx("failed to collect functions: %s",
+ ctf_errmsg(ctf_errno(g_fp)));
+ g_exit = 1;
+ }
+
+ qsort(idnames, count, sizeof (ctf_idname_t), idname_compare);
+
+ for (size_t i = 0; i < count; i++)
+ ctfsrc_function(&idnames[i]);
+
+ free(idnames);
+}
+
+static void
+ctfdump_output(const char *out)
+{
+ int fd, ret;
+ const void *data;
+ size_t len;
+
+ ctf_dataptr(g_fp, &data, &len);
+ if ((fd = open(out, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0)
+ ctfdump_fatal("failed to open output file %s: %s\n", out,
+ strerror(errno));
+
+ while (len > 0) {
+ ret = write(fd, data, len);
+ if (ret == -1 && errno == EINTR)
+ continue;
+ else if (ret == -1 && (errno == EFAULT || errno == EBADF))
+ abort();
+ else if (ret == -1)
+ ctfdump_fatal("failed to write to %s: %s\n", out,
+ strerror(errno));
+ data = ((char *)data) + ret;
+ len -= ret;
+ }
+
+ do {
+ ret = close(fd);
+ } while (ret == -1 && errno == EINTR);
+ if (ret != 0 && errno == EBADF)
+ abort();
+ if (ret != 0)
+ ctfdump_fatal("failed to close %s: %s\n", out, strerror(errno));
+}
+
+int
+main(int argc, char *argv[])
+{
+ int c, fd, err;
+ const char *ufile = NULL, *parent = NULL;
+
+ g_progname = basename(argv[0]);
+ while ((c = getopt(argc, argv, ":cdfhlp:sStu:")) != -1) {
+ switch (c) {
+ case 'c':
+ g_dump |= CTFDUMP_SOURCE;
+ break;
+ case 'd':
+ g_dump |= CTFDUMP_OBJECTS;
+ break;
+ case 'f':
+ g_dump |= CTFDUMP_FUNCTIONS;
+ break;
+ case 'h':
+ g_dump |= CTFDUMP_HEADER;
+ break;
+ case 'l':
+ g_dump |= CTFDUMP_LABELS;
+ break;
+ case 'p':
+ parent = optarg;
+ break;
+ case 's':
+ g_dump |= CTFDUMP_STRINGS;
+ break;
+ case 'S':
+ g_dump |= CTFDUMP_STATS;
+ break;
+ case 't':
+ g_dump |= CTFDUMP_TYPES;
+ break;
+ case 'u':
+ g_dump |= CTFDUMP_OUTPUT;
+ ufile = optarg;
+ break;
+ case '?':
+ ctfdump_usage("Unknown option: -%c\n", optopt);
+ return (2);
+ case ':':
+ ctfdump_usage("Option -%c requires an operand\n",
+ optopt);
+ return (2);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if ((g_dump & CTFDUMP_SOURCE) && !!(g_dump & ~CTFDUMP_SOURCE)) {
+ ctfdump_usage("-c must be specified on its own\n");
+ return (2);
+ }
+
+ /*
+ * Dump all information except C source by default.
+ */
+ if (g_dump == 0)
+ g_dump = CTFDUMP_DEFAULT;
+
+ if (argc != 1) {
+ ctfdump_usage("no file to dump\n");
+ return (2);
+ }
+
+ if ((fd = open(argv[0], O_RDONLY)) < 0)
+ ctfdump_fatal("failed to open file %s: %s\n", argv[0],
+ strerror(errno));
+
+ g_fp = ctf_fdopen(fd, &err);
+ if (g_fp == NULL)
+ ctfdump_fatal("failed to open file %s: %s\n", argv[0],
+ ctf_errmsg(err));
+
+ /*
+ * Check to see if this file needs a parent. If it does not and we were
+ * given one, that should be an error. If it does need one and the
+ * parent is not specified, that is fine, we just won't know how to
+ * find child types. If we are given a parent, check at least that the
+ * labels match.
+ */
+ if (ctf_parent_name(g_fp) == NULL) {
+ if (parent != NULL)
+ ctfdump_fatal("cannot use %s as a parent file, %s is "
+ "not a child\n", parent, argv[0]);
+ } else if (parent != NULL) {
+ const char *explabel, *label;
+ ctf_file_t *pfp = ctf_open(parent, &err);
+
+ if (pfp == NULL)
+ ctfdump_fatal("failed to open parent file %s: %s\n",
+ parent, ctf_errmsg(err));
+
+ /*
+ * Before we import the parent into the child, check that the
+ * labels match. While there is also the notion of the parent
+ * name, it's less straightforward to match that. Require that
+ * labels match.
+ */
+ explabel = ctf_parent_label(g_fp);
+ label = ctf_label_topmost(pfp);
+ if (explabel == NULL || label == NULL ||
+ strcmp(explabel, label) != 0) {
+ if (label == NULL)
+ label = "<missing>";
+ if (explabel == NULL)
+ explabel = "<missing>";
+ ctfdump_fatal("label mismatch between parent %s and "
+ "child %s, parent has %s, child expects %s\n",
+ parent, argv[0], label, explabel);
+ }
+
+ if (ctf_import(g_fp, pfp) != 0)
+ ctfdump_fatal("failed to import parent %s: %s\n",
+ parent, ctf_errmsg(ctf_errno(g_fp)));
+ }
+
+ if (g_dump & CTFDUMP_SOURCE) {
+ ctfdump_source();
+ return (0);
+ }
+
+ /*
+ * If stats is set, we must run through everything exect CTFDUMP_OUTPUT.
+ * We also do CTFDUMP_STATS last as a result.
+ */
+ if (g_dump & CTFDUMP_HEADER)
+ ctfdump_header();
+
+ if (g_dump & (CTFDUMP_LABELS | CTFDUMP_STATS))
+ ctfdump_labels();
+
+ if (g_dump & (CTFDUMP_OBJECTS | CTFDUMP_STATS))
+ ctfdump_objects();
+
+ if (g_dump & (CTFDUMP_FUNCTIONS | CTFDUMP_STATS))
+ ctfdump_functions();
+
+ if (g_dump & (CTFDUMP_TYPES | CTFDUMP_STATS))
+ ctfdump_types();
+
+ if (g_dump & (CTFDUMP_STRINGS | CTFDUMP_STATS))
+ ctfdump_strings();
+
+ if (g_dump & CTFDUMP_STATS)
+ ctfdump_stats();
+
+ if (g_dump & CTFDUMP_OUTPUT)
+ ctfdump_output(ufile);
+
+ return (g_exit);
+}
diff --git a/usr/src/cmd/ctfmerge/Makefile b/usr/src/cmd/ctfmerge/Makefile
new file mode 100644
index 0000000000..8f156c9c48
--- /dev/null
+++ b/usr/src/cmd/ctfmerge/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= ctfmerge
+
+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/ctfmerge/ctfmerge.c b/usr/src/cmd/ctfmerge/ctfmerge.c
new file mode 100644
index 0000000000..1257fd16c9
--- /dev/null
+++ b/usr/src/cmd/ctfmerge/ctfmerge.c
@@ -0,0 +1,536 @@
+/*
+ * 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) 2017, Joyent, Inc.
+ */
+
+/*
+ * merge CTF containers
+ */
+
+#include <stdio.h>
+#include <libctf.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <strings.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/fcntl.h>
+#include <stdlib.h>
+#include <libelf.h>
+#include <gelf.h>
+#include <sys/mman.h>
+#include <libgen.h>
+#include <stdarg.h>
+#include <limits.h>
+
+static char *g_progname;
+static char *g_unique;
+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, ...)
+{
+ va_list ap;
+
+ (void) fprintf(stderr, "%s: ", g_progname);
+ va_start(ap, fmt);
+ (void) vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ if (g_outfile != NULL)
+ (void) unlink(g_outfile);
+
+ exit(CTFMERGE_FATAL);
+}
+
+static boolean_t
+ctfmerge_expect_ctf(const char *name, Elf *elf)
+{
+ Elf_Scn *scn, *strscn;
+ Elf_Data *data, *strdata;
+ GElf_Shdr shdr;
+ ulong_t i;
+
+ if (g_req == B_FALSE)
+ return (B_FALSE);
+
+ scn = NULL;
+ while ((scn = elf_nextscn(elf, scn)) != NULL) {
+ if (gelf_getshdr(scn, &shdr) == NULL) {
+ ctfmerge_fatal("failed to get section header for file "
+ "%s: %s\n", name, elf_errmsg(elf_errno()));
+ }
+
+ if (shdr.sh_type == SHT_SYMTAB)
+ break;
+ }
+
+ if (scn == NULL)
+ return (B_FALSE);
+
+ if ((strscn = elf_getscn(elf, shdr.sh_link)) == NULL)
+ ctfmerge_fatal("failed to get section header for file %s: %s\n",
+ name, elf_errmsg(elf_errno()));
+
+ if ((data = elf_getdata(scn, NULL)) == NULL)
+ ctfmerge_fatal("failed to read symbol table for %s: %s\n",
+ name, elf_errmsg(elf_errno()));
+
+ if ((strdata = elf_getdata(strscn, NULL)) == NULL)
+ ctfmerge_fatal("failed to read string table for %s: %s\n",
+ name, elf_errmsg(elf_errno()));
+
+ 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)
+ ctfmerge_fatal("failed to read symbol table entry %lu "
+ "for %s: %s\n", i, name, elf_errmsg(elf_errno()));
+
+ 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 || name[len - 2] != '.')
+ continue;
+
+ if (name[len - 1] == 'c')
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+
+/*
+ * Go through and construct enough information for this Elf Object to try and do
+ * a ctf_bufopen().
+ */
+static void
+ctfmerge_elfopen(const char *name, Elf *elf, ctf_merge_t *cmh)
+{
+ GElf_Ehdr ehdr;
+ GElf_Shdr shdr;
+ Elf_Scn *scn;
+ Elf_Data *ctf_data, *str_data, *sym_data;
+ ctf_sect_t ctfsect, symsect, strsect;
+ ctf_file_t *fp;
+ int err;
+
+ if (gelf_getehdr(elf, &ehdr) == NULL)
+ ctfmerge_fatal("failed to get ELF header for %s: %s\n",
+ name, elf_errmsg(elf_errno()));
+
+ bzero(&ctfsect, sizeof (ctf_sect_t));
+ bzero(&symsect, sizeof (ctf_sect_t));
+ bzero(&strsect, sizeof (ctf_sect_t));
+
+ scn = NULL;
+ while ((scn = elf_nextscn(elf, scn)) != NULL) {
+ const char *sname;
+
+ if (gelf_getshdr(scn, &shdr) == NULL)
+ ctfmerge_fatal("failed to get section header for "
+ "file %s: %s\n", name, elf_errmsg(elf_errno()));
+
+ sname = elf_strptr(elf, ehdr.e_shstrndx, shdr.sh_name);
+ if (shdr.sh_type == SHT_PROGBITS &&
+ strcmp(sname, ".SUNW_ctf") == 0) {
+ ctfsect.cts_name = sname;
+ ctfsect.cts_type = shdr.sh_type;
+ ctfsect.cts_flags = shdr.sh_flags;
+ ctfsect.cts_size = shdr.sh_size;
+ ctfsect.cts_entsize = shdr.sh_entsize;
+ ctfsect.cts_offset = (off64_t)shdr.sh_offset;
+
+ ctf_data = elf_getdata(scn, NULL);
+ if (ctf_data == NULL)
+ ctfmerge_fatal("failed to get ELF CTF "
+ "data section for %s: %s\n", name,
+ elf_errmsg(elf_errno()));
+ ctfsect.cts_data = ctf_data->d_buf;
+ } else if (shdr.sh_type == SHT_SYMTAB) {
+ Elf_Scn *strscn;
+ GElf_Shdr strhdr;
+
+ symsect.cts_name = sname;
+ symsect.cts_type = shdr.sh_type;
+ symsect.cts_flags = shdr.sh_flags;
+ symsect.cts_size = shdr.sh_size;
+ symsect.cts_entsize = shdr.sh_entsize;
+ symsect.cts_offset = (off64_t)shdr.sh_offset;
+
+ if ((strscn = elf_getscn(elf, shdr.sh_link)) == NULL ||
+ gelf_getshdr(strscn, &strhdr) == NULL)
+ ctfmerge_fatal("failed to get "
+ "string table for file %s: %s\n", name,
+ elf_errmsg(elf_errno()));
+
+ strsect.cts_name = elf_strptr(elf, ehdr.e_shstrndx,
+ strhdr.sh_name);
+ strsect.cts_type = strhdr.sh_type;
+ strsect.cts_flags = strhdr.sh_flags;
+ strsect.cts_size = strhdr.sh_size;
+ strsect.cts_entsize = strhdr.sh_entsize;
+ strsect.cts_offset = (off64_t)strhdr.sh_offset;
+
+ sym_data = elf_getdata(scn, NULL);
+ if (sym_data == NULL)
+ ctfmerge_fatal("failed to get ELF CTF "
+ "data section for %s: %s\n", name,
+ elf_errmsg(elf_errno()));
+ symsect.cts_data = sym_data->d_buf;
+
+ str_data = elf_getdata(strscn, NULL);
+ if (str_data == NULL)
+ ctfmerge_fatal("failed to get ELF CTF "
+ "data section for %s: %s\n", name,
+ elf_errmsg(elf_errno()));
+ strsect.cts_data = str_data->d_buf;
+ }
+ }
+
+ if (ctfsect.cts_type == SHT_NULL) {
+ if (ctfmerge_expect_ctf(name, elf) == B_FALSE)
+ return;
+ ctfmerge_fatal("failed to open %s: %s\n", name,
+ ctf_errmsg(ECTF_NOCTFDATA));
+ }
+
+ if (symsect.cts_type != SHT_NULL && strsect.cts_type != SHT_NULL) {
+ fp = ctf_bufopen(&ctfsect, &symsect, &strsect, &err);
+ } else {
+ fp = ctf_bufopen(&ctfsect, NULL, NULL, &err);
+ }
+
+ if (fp == NULL) {
+ if (ctfmerge_expect_ctf(name, elf) == B_TRUE) {
+ ctfmerge_fatal("failed to open file %s: %s\n",
+ name, ctf_errmsg(err));
+ }
+ } else {
+ if ((err = ctf_merge_add(cmh, fp)) != 0) {
+ ctfmerge_fatal("failed to add input %s: %s\n",
+ name, ctf_errmsg(err));
+ }
+ g_nctf++;
+ }
+}
+
+static void
+ctfmerge_read_archive(const char *name, int fd, Elf *elf,
+ ctf_merge_t *cmh)
+{
+ Elf *aelf;
+ Elf_Cmd cmd = ELF_C_READ;
+ int cursec = 1;
+ char *nname;
+
+ while ((aelf = elf_begin(fd, cmd, elf)) != NULL) {
+ Elf_Arhdr *arhdr;
+ boolean_t leakelf = B_FALSE;
+
+ if ((arhdr = elf_getarhdr(aelf)) == NULL)
+ ctfmerge_fatal("failed to get archive header %d for "
+ "%s: %s\n", cursec, name, elf_errmsg(elf_errno()));
+
+ if (*(arhdr->ar_name) == '/')
+ goto next;
+
+ if (asprintf(&nname, "%s.%s.%d", name, arhdr->ar_name,
+ cursec) < 0)
+ ctfmerge_fatal("failed to allocate memory for archive "
+ "%d of file %s\n", cursec, name);
+
+ switch (elf_kind(aelf)) {
+ case ELF_K_AR:
+ ctfmerge_read_archive(nname, fd, aelf, cmh);
+ free(nname);
+ break;
+ case ELF_K_ELF:
+ ctfmerge_elfopen(nname, aelf, cmh);
+ free(nname);
+ leakelf = B_TRUE;
+ break;
+ default:
+ ctfmerge_fatal("unknown elf kind (%d) in archive %d "
+ "for %s\n", elf_kind(aelf), cursec, name);
+ }
+
+next:
+ cmd = elf_next(aelf);
+ if (leakelf == B_FALSE)
+ (void) elf_end(aelf);
+ cursec++;
+ }
+}
+
+static void
+ctfmerge_usage(const char *fmt, ...)
+{
+ if (fmt != NULL) {
+ va_list ap;
+
+ (void) fprintf(stderr, "%s: ", g_progname);
+ va_start(ap, fmt);
+ (void) vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ }
+
+ (void) fprintf(stderr, "Usage: %s [-t] [-d uniqfile] [-l label] "
+ "[-L labelenv] [-j nthrs] -o outfile file ...\n"
+ "\n"
+ "\t-d uniquify merged output against uniqfile\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"
+ "\t-t require CTF data from all inputs built from C sources\n",
+ 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:fgj:l:L:o:t")) != -1) {
+ switch (c) {
+ case 'd':
+ g_unique = optarg;
+ break;
+ case 'f':
+ /* Silently ignored for compatibility */
+ break;
+ case 'g':
+ /* Silently ignored for compatibility */
+ break;
+ case 'j':
+ errno = 0;
+ argj = strtol(optarg, &eptr, 10);
+ if (errno != 0 || argj == LONG_MAX ||
+ argj > 1024 || *eptr != '\0') {
+ ctfmerge_fatal("invalid argument for -j: %s\n",
+ optarg);
+ }
+ nthreads = (uint_t)argj;
+ break;
+ case 'l':
+ label = optarg;
+ break;
+ case 'L':
+ label = getenv(optarg);
+ break;
+ case 'o':
+ g_outfile = optarg;
+ break;
+ case 't':
+ g_req = B_TRUE;
+ break;
+ case ':':
+ ctfmerge_usage("Option -%c requires an operand\n",
+ optopt);
+ return (CTFMERGE_USAGE);
+ case '?':
+ ctfmerge_usage("Unknown option: -%c\n", optopt);
+ return (CTFMERGE_USAGE);
+ }
+ }
+
+ if (g_outfile == NULL) {
+ ctfmerge_usage("missing required -o output file\n");
+ return (CTFMERGE_USAGE);
+ }
+
+ (void) elf_version(EV_CURRENT);
+
+ /*
+ * Obviously this isn't atomic, but at least gives us a good starting
+ * point.
+ */
+ if ((ofd = open(g_outfile, O_RDWR)) < 0)
+ ctfmerge_fatal("cannot open output file %s: %s\n", g_outfile,
+ strerror(errno));
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ ctfmerge_usage("no input files specified");
+ return (CTFMERGE_USAGE);
+ }
+
+ cmh = ctf_merge_init(ofd, &err);
+ if (cmh == NULL)
+ 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 %u: %s\n",
+ nthreads, ctf_errmsg(err));
+
+ for (i = 0; i < argc; i++) {
+ ctf_file_t *ifp;
+ int fd;
+
+ if ((fd = open(argv[i], O_RDONLY)) < 0)
+ ctfmerge_fatal("failed to open file %s: %s\n",
+ argv[i], strerror(errno));
+ ifp = ctf_fdopen(fd, &err);
+ if (ifp == NULL) {
+ Elf *e;
+
+ if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
+ (void) close(fd);
+ ctfmerge_fatal("failed to open %s: %s\n",
+ argv[i], ctf_errmsg(err));
+ }
+
+ /*
+ * It's an ELF file, check if we have an archive or if
+ * we're expecting CTF here.
+ */
+ switch (elf_kind(e)) {
+ case ELF_K_AR:
+ break;
+ case ELF_K_ELF:
+ if (ctfmerge_expect_ctf(argv[i], e) == B_TRUE) {
+ (void) elf_end(e);
+ (void) close(fd);
+ ctfmerge_fatal("failed to "
+ "open %s: file was built from C "
+ "sources, but missing CTF\n",
+ argv[i]);
+ }
+ (void) elf_end(e);
+ (void) close(fd);
+ continue;
+ default:
+ (void) elf_end(e);
+ (void) close(fd);
+ ctfmerge_fatal("failed to open %s: "
+ "unsupported ELF file type", argv[i]);
+ }
+
+ ctfmerge_read_archive(argv[i], fd, e, cmh);
+ (void) elf_end(e);
+ (void) close(fd);
+ continue;
+ }
+ (void) close(fd);
+ if ((err = ctf_merge_add(cmh, ifp)) != 0)
+ ctfmerge_fatal("failed to add input %s: %s\n",
+ argv[i], ctf_errmsg(err));
+ g_nctf++;
+ }
+
+ if (g_nctf == 0) {
+ ctf_merge_fini(cmh);
+ return (0);
+ }
+
+ if (g_unique != NULL) {
+ ctf_file_t *ufp;
+ char *base;
+
+ ufp = ctf_open(g_unique, &err);
+ if (ufp == NULL) {
+ ctfmerge_fatal("failed to open uniquify file %s: %s\n",
+ g_unique, ctf_errmsg(err));
+ }
+
+ base = basename(g_unique);
+ (void) ctf_merge_uniquify(cmh, ufp, base);
+ }
+
+ if (label != NULL) {
+ if ((err = ctf_merge_label(cmh, label)) != 0)
+ ctfmerge_fatal("failed to add label %s: %s\n", label,
+ ctf_errmsg(err));
+ }
+
+ err = ctf_merge_merge(cmh, &ofp);
+ if (err != 0)
+ ctfmerge_fatal("failed to merge types: %s\n", ctf_errmsg(err));
+ ctf_merge_fini(cmh);
+
+ 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);
+ 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 (CTFMERGE_OK);
+}
diff --git a/usr/src/cmd/mdb/Makefile.libstandctf b/usr/src/cmd/mdb/Makefile.libstandctf
index caa621be0e..176291399c 100644
--- a/usr/src/cmd/mdb/Makefile.libstandctf
+++ b/usr/src/cmd/mdb/Makefile.libstandctf
@@ -22,6 +22,8 @@
# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright 2018 Joyent, Inc.
+#
.KEEP_STATE:
@@ -45,6 +47,9 @@ $(NOT_RELEASE_BUILD)CPPFLAGS += -DDEBUG
CPPFLAGS += -I$(SRC)/common/ctf -I../../../common -DCTF_OLD_VERSIONS -D_MDB \
-Dvsnprintf=ctf_vsnprintf -Dassfail=kmdb_prom_assfail
+CSTD = $(CSTD_GNU99)
+C99LMODE = -Xc99=%all
+
#
# kmdb is a kernel module, so we'll use the kernel's build flags.
CFLAGS64 += $(STAND_FLAGS_64)
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_promif.c b/usr/src/cmd/mdb/common/kmdb/kmdb_promif.c
index 637e685b06..b82ab3275e 100644
--- a/usr/src/cmd/mdb/common/kmdb/kmdb_promif.c
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_promif.c
@@ -742,10 +742,10 @@ kmdb_prom_debugger_exit(void)
mdb.m_pio = NULL;
}
-#ifdef DEBUG
/*
- * The prom_* files use ASSERT, which is #defined as assfail().
- * We need to redirect that to our assert function.
+ * The prom_* files use ASSERT, which is #defined as assfail(). We need to
+ * redirect that to our assert function. This is also used by the various STAND
+ * libraries.
*/
int
kmdb_prom_assfail(const char *assertion, const char *file, int line)
@@ -754,7 +754,6 @@ kmdb_prom_assfail(const char *assertion, const char *file, int line)
/*NOTREACHED*/
return (0);
}
-#endif
/*
* Begin the initialization of the debugger/PROM interface. Initialization is
diff --git a/usr/src/cmd/mdb/common/libstandctf/ctf_subr.c b/usr/src/cmd/mdb/common/libstandctf/ctf_subr.c
index 0a211fcd22..e8c703e754 100644
--- a/usr/src/cmd/mdb/common/libstandctf/ctf_subr.c
+++ b/usr/src/cmd/mdb/common/libstandctf/ctf_subr.c
@@ -24,8 +24,6 @@
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <mdb/mdb_debug.h>
#include <mdb/mdb.h>
@@ -67,6 +65,13 @@ ctf_fdopen(int fd, int *errp)
/*ARGSUSED*/
ctf_file_t *
+ctf_fdcreate_int(int fd, int *errp, ctf_sect_t *ctfp)
+{
+ return (ctf_set_open_errno(errp, ENOTSUP));
+}
+
+/*ARGSUSED*/
+ctf_file_t *
ctf_open(const char *filename, int *errp)
{
return (ctf_set_open_errno(errp, ENOTSUP));
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_ctf.c b/usr/src/cmd/mdb/common/mdb/mdb_ctf.c
index dde087206c..9af52917b4 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_ctf.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_ctf.c
@@ -24,7 +24,7 @@
*/
/*
* Copyright (c) 2013, 2016 by Delphix. All rights reserved.
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2015, Joyent, Inc. All rights reserved.
*/
#include <mdb/mdb_ctf.h>
@@ -37,6 +37,7 @@
#include <libctf.h>
#include <string.h>
+#include <limits.h>
typedef struct tnarg {
mdb_tgt_t *tn_tgt; /* target to use for lookup */
@@ -781,8 +782,9 @@ mdb_ctf_enum_iter(mdb_ctf_id_t id, mdb_ctf_enum_f *cb, void *data)
/*
* callback proxy for mdb_ctf_type_iter
*/
+/* ARGSUSED */
static int
-type_iter_cb(ctf_id_t type, void *data)
+type_iter_cb(ctf_id_t type, boolean_t root, void *data)
{
type_iter_t *tip = data;
mdb_ctf_id_t id;
@@ -812,7 +814,7 @@ mdb_ctf_type_iter(const char *object, mdb_ctf_type_f *cb, void *data)
ti.ti_arg = data;
ti.ti_fp = fp;
- if ((ret = ctf_type_iter(fp, type_iter_cb, &ti)) == CTF_ERR)
+ if ((ret = ctf_type_iter(fp, B_FALSE, type_iter_cb, &ti)) == CTF_ERR)
return (set_errno(ctf_to_errno(ctf_errno(fp))));
return (ret);
@@ -1980,7 +1982,7 @@ mdb_ctf_add_member(const mdb_ctf_id_t *p, const char *name,
return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
}
- id = ctf_add_member(mdb.m_synth, mcip->mci_id, name, mtid);
+ id = ctf_add_member(mdb.m_synth, mcip->mci_id, name, mtid, ULONG_MAX);
if (id == CTF_ERR) {
mdb_dprintf(MDB_DBG_CTF, "failed to add member %s: %s\n",
name, ctf_errmsg(ctf_errno(mdb.m_synth)));
@@ -2081,7 +2083,7 @@ mdb_ctf_add_pointer(const mdb_ctf_id_t *p, mdb_ctf_id_t *rid)
}
- id = ctf_add_pointer(mdb.m_synth, CTF_ADD_ROOT, id);
+ id = ctf_add_pointer(mdb.m_synth, CTF_ADD_ROOT, NULL, id);
if (id == CTF_ERR) {
mdb_dprintf(MDB_DBG_CTF, "failed to add pointer: %s\n",
ctf_errmsg(ctf_errno(mdb.m_synth)));
@@ -2129,6 +2131,7 @@ mdb_ctf_type_delete(const mdb_ctf_id_t *id)
return (0);
}
+/* ARGSUSED */
static int
mdb_ctf_synthetics_file_cb(mdb_ctf_id_t id, void *arg)
{
@@ -2166,7 +2169,7 @@ mdb_ctf_synthetics_from_file(const char *file)
ti.ti_fp = fp;
ti.ti_arg = syn;
ti.ti_cb = mdb_ctf_synthetics_file_cb;
- if (ctf_type_iter(fp, type_iter_cb, &ti) == CTF_ERR) {
+ if (ctf_type_iter(fp, B_FALSE, type_iter_cb, &ti) == CTF_ERR) {
ret = set_errno(ctf_to_errno(ctf_errno(fp)));
mdb_warn("failed to add types");
goto cleanup;
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_debug.c b/usr/src/cmd/mdb/common/mdb/mdb_debug.c
index d373d3726e..a158864a28 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_debug.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_debug.c
@@ -24,8 +24,6 @@
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <mdb/mdb_debug.h>
#include <mdb/mdb_err.h>
#include <mdb/mdb_io.h>
@@ -157,7 +155,6 @@ mdb_dmode(uint_t bits)
mdb.m_debug = bits;
}
-#ifdef DEBUG
int
mdb_dassert(const char *expr, const char *file, int line)
{
@@ -165,7 +162,6 @@ mdb_dassert(const char *expr, const char *file, int line)
/*NOTREACHED*/
return (0);
}
-#endif
/*
* Function to convert mdb longjmp codes (see <mdb/mdb.h>) into a string for
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_debug.h b/usr/src/cmd/mdb/common/mdb/mdb_debug.h
index cf3b97b499..f889238e4f 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_debug.h
+++ b/usr/src/cmd/mdb/common/mdb/mdb_debug.h
@@ -27,8 +27,6 @@
#ifndef _MDB_DEBUG_H
#define _MDB_DEBUG_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -66,8 +64,8 @@ extern void mdb_dmode(uint_t);
extern const char *mdb_err2str(int);
-#ifdef DEBUG
extern int mdb_dassert(const char *, const char *, int);
+#ifdef DEBUG
#define ASSERT(x) ((void)((x) || mdb_dassert(#x, __FILE__, __LINE__)))
#else
#define ASSERT(x)
diff --git a/usr/src/common/ctf/ctf_create.c b/usr/src/common/ctf/ctf_create.c
index 239d166f44..c0a4b15055 100644
--- a/usr/src/common/ctf/ctf_create.c
+++ b/usr/src/common/ctf/ctf_create.c
@@ -25,7 +25,7 @@
* Use is subject to license terms.
*/
/*
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2015, Joyent, Inc.
*/
#include <sys/sysmacros.h>
@@ -86,6 +86,48 @@ ctf_create(int *errp)
return (fp);
}
+ctf_file_t *
+ctf_fdcreate(int fd, int *errp)
+{
+ ctf_file_t *fp;
+ static const ctf_header_t hdr = { { CTF_MAGIC, CTF_VERSION, 0 } };
+
+ const ulong_t hashlen = 128;
+ 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));
+
+ cts.cts_name = _CTF_SECTION;
+ cts.cts_type = SHT_PROGBITS;
+ cts.cts_flags = 0;
+ cts.cts_data = &hdr;
+ cts.cts_size = sizeof (hdr);
+ cts.cts_entsize = 1;
+ cts.cts_offset = 0;
+
+ if ((fp = ctf_fdcreate_int(fd, errp, &cts)) == NULL) {
+ ctf_free(hash, hashlen * sizeof (ctf_dtdef_t *));
+ return (NULL);
+ }
+
+ fp->ctf_flags |= LCTF_RDWR;
+ fp->ctf_dthashlen = hashlen;
+ bzero(hash, hashlen * sizeof (ctf_dtdef_t *));
+ fp->ctf_dthash = hash;
+ fp->ctf_dtstrlen = sizeof (_CTF_STRTAB_TEMPLATE);
+ fp->ctf_dtnextid = 1;
+ fp->ctf_dtoldid = 0;
+
+ return (fp);
+}
+
static uchar_t *
ctf_copy_smembers(ctf_dtdef_t *dtd, uint_t soff, uchar_t *t)
{
@@ -236,14 +278,24 @@ int
ctf_update(ctf_file_t *fp)
{
ctf_file_t ofp, *nfp;
- ctf_header_t hdr;
+ ctf_header_t hdr, *bhdr;
ctf_dtdef_t *dtd;
- ctf_sect_t cts;
+ ctf_dsdef_t *dsd;
+ ctf_dldef_t *dld;
+ ctf_sect_t cts, *symp, *strp;
uchar_t *s, *s0, *t;
- size_t size;
+ ctf_lblent_t *label;
+ uint16_t *obj, *func;
+ size_t size, objsize, funcsize, labelsize, plen;
void *buf;
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;
if (!(fp->ctf_flags & LCTF_RDWR))
return (ctf_set_errno(fp, ECTF_RDONLY));
@@ -261,8 +313,26 @@ ctf_update(ctf_file_t *fp)
hdr.cth_magic = CTF_MAGIC;
hdr.cth_version = CTF_VERSION;
- if (fp->ctf_flags & LCTF_CHILD)
- hdr.cth_parname = 1; /* i.e. _CTF_STRTAB_TEMPLATE[1] */
+ if (fp->ctf_flags & LCTF_CHILD) {
+ if (fp->ctf_parname == NULL) {
+ plen = 0;
+ hdr.cth_parname = 1; /* i.e. _CTF_STRTAB_TEMPLATE[1] */
+ plabel = NULL;
+ } else {
+ plen = strlen(fp->ctf_parname) + 1;
+ plabel = ctf_label_topmost(fp->ctf_parent);
+ }
+ } else {
+ plabel = NULL;
+ plen = 0;
+ }
+
+ /*
+ * Iterate over the labels that we have.
+ */
+ for (labelsize = 0, dld = ctf_list_next(&fp->ctf_dldefs);
+ dld != NULL; dld = ctf_list_next(dld))
+ labelsize += sizeof (ctf_lblent_t);
/*
* Iterate through the dynamic type definition list and compute the
@@ -304,25 +374,121 @@ ctf_update(ctf_file_t *fp)
}
/*
+ * An entry for each object must exist in the data section. However, if
+ * the symbol is SHN_UNDEF, then it is skipped. For objects, the storage
+ * is just the size of the 2-byte id. For functions it's always 2 bytes,
+ * plus 2 bytes per argument and the return type.
+ */
+ dsd = ctf_list_next(&fp->ctf_dsdefs);
+ for (objsize = 0, funcsize = 0, i = 0; i < fp->ctf_nsyms; i++) {
+ int type;
+
+ 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 (ctf_sym_valid(strbase, type, symp->st_shndx,
+ symp->st_value, symp->st_name) == B_FALSE)
+ continue;
+ } else {
+ const Elf64_Sym *symp = (Elf64_Sym *)symbase + i;
+
+ type = ELF64_ST_TYPE(symp->st_info);
+ if (ctf_sym_valid(strbase, type, symp->st_shndx,
+ symp->st_value, symp->st_name) == B_FALSE)
+ continue;
+ }
+
+ while (dsd != NULL && i > dsd->dsd_symidx)
+ dsd = ctf_list_next(dsd);
+ if (type == STT_OBJECT) {
+ objsize += sizeof (uint16_t);
+ } else {
+ /* Every function has a uint16_t info no matter what */
+ if (dsd == NULL || i < dsd->dsd_symidx) {
+ funcsize += sizeof (uint16_t);
+ } else {
+ funcsize += sizeof (uint16_t) *
+ (dsd->dsd_nargs + 2);
+ }
+ }
+ }
+
+ /*
+ * The objtoff and funcoffset must be 2-byte aligned. We're guaranteed
+ * that this is always true for the objtoff because labels are always 8
+ * bytes large. Similarly, because objects are always two bytes of data,
+ * this will always be true for funcoff.
+ */
+ hdr.cth_objtoff = hdr.cth_lbloff + labelsize;
+ hdr.cth_funcoff = hdr.cth_objtoff + objsize;
+
+ /*
+ * The type offset must be 4 byte aligned.
+ */
+ hdr.cth_typeoff = hdr.cth_funcoff + funcsize;
+ if (hdr.cth_typeoff & 3)
+ hdr.cth_typeoff += 4 - (hdr.cth_typeoff & 3);
+ ASSERT((hdr.cth_typeoff & 3) == 0);
+
+ /*
* Fill in the string table offset and size, compute the size of the
* entire CTF buffer we need, and then allocate a new buffer and
* bcopy the finished header to the start of the buffer.
*/
hdr.cth_stroff = hdr.cth_typeoff + size;
- hdr.cth_strlen = fp->ctf_dtstrlen;
+ hdr.cth_strlen = fp->ctf_dtstrlen + plen;
size = sizeof (ctf_header_t) + hdr.cth_stroff + hdr.cth_strlen;
+ ctf_dprintf("lbloff: %u\nobjtoff: %u\nfuncoff: %u\n"
+ "typeoff: %u\nstroff: %u\nstrlen: %u\n",
+ hdr.cth_lbloff, hdr.cth_objtoff, hdr.cth_funcoff,
+ hdr.cth_typeoff, hdr.cth_stroff, hdr.cth_strlen);
if ((buf = ctf_data_alloc(size)) == MAP_FAILED)
return (ctf_set_errno(fp, EAGAIN));
bcopy(&hdr, buf, sizeof (ctf_header_t));
- t = (uchar_t *)buf + sizeof (ctf_header_t);
+ bhdr = buf;
+ label = (ctf_lblent_t *)((uintptr_t)buf + sizeof (ctf_header_t));
+ t = (uchar_t *)buf + sizeof (ctf_header_t) + hdr.cth_typeoff;
s = s0 = (uchar_t *)buf + sizeof (ctf_header_t) + hdr.cth_stroff;
+ obj = (uint16_t *)((uintptr_t)buf + sizeof (ctf_header_t) +
+ hdr.cth_objtoff);
+ func = (uint16_t *)((uintptr_t)buf + sizeof (ctf_header_t) +
+ hdr.cth_funcoff);
bcopy(_CTF_STRTAB_TEMPLATE, s, sizeof (_CTF_STRTAB_TEMPLATE));
s += sizeof (_CTF_STRTAB_TEMPLATE);
/*
+ * We have an actual parent name and we're a child container, therefore
+ * we should make sure to note our parent's name here.
+ */
+ if (plen != 0) {
+ VERIFY(s + plen - s0 <= hdr.cth_strlen);
+ bcopy(fp->ctf_parname, s, plen);
+ bhdr->cth_parname = s - s0;
+ s += plen;
+ }
+
+ /*
+ * First pass over the labels and copy them out.
+ */
+ for (dld = ctf_list_next(&fp->ctf_dldefs); dld != NULL;
+ dld = ctf_list_next(dld), label++) {
+ size_t len = strlen(dld->dld_name) + 1;
+
+ VERIFY(s + len - s0 <= hdr.cth_strlen);
+ bcopy(dld->dld_name, s, len);
+ label->ctl_typeidx = dld->dld_type;
+ label->ctl_label = s - s0;
+ s += len;
+
+ if (plabel != NULL && strcmp(plabel, dld->dld_name) == 0)
+ bhdr->cth_parlabel = label->ctl_label;
+ }
+
+ /*
* We now take a final lap through the dynamic type definition list and
* copy the appropriate type records and strings to the output buffer.
*/
@@ -339,6 +505,7 @@ ctf_update(ctf_file_t *fp)
if (dtd->dtd_name != NULL) {
dtd->dtd_data.ctt_name = (uint_t)(s - s0);
len = strlen(dtd->dtd_name) + 1;
+ VERIFY(s + len - s0 <= hdr.cth_strlen);
bcopy(dtd->dtd_name, s, len);
s += len;
} else
@@ -411,6 +578,61 @@ ctf_update(ctf_file_t *fp)
}
/*
+ * Now we fill in our dynamic data and function sections. We use the
+ * same criteria as above, but also consult the dsd list.
+ */
+ dsd = ctf_list_next(&fp->ctf_dsdefs);
+ for (i = 0; i < fp->ctf_nsyms; i++) {
+ int type;
+ 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 (ctf_sym_valid(strbase, type, symp->st_shndx,
+ symp->st_value, symp->st_name) == B_FALSE)
+ continue;
+ } else {
+ const Elf64_Sym *symp = (Elf64_Sym *)symbase + i;
+ type = ELF64_ST_TYPE(symp->st_info);
+ if (ctf_sym_valid(strbase, type, symp->st_shndx,
+ symp->st_value, symp->st_name) == B_FALSE)
+ continue;
+ }
+
+ while (dsd != NULL && i > dsd->dsd_symidx) {
+ dsd = ctf_list_next(dsd);
+ }
+ if (type == STT_OBJECT) {
+ if (dsd == NULL || i < dsd->dsd_symidx) {
+ *obj = 0;
+ } else {
+ *obj = dsd->dsd_tid;
+ }
+ obj++;
+ VERIFY((uintptr_t)obj <= (uintptr_t)func);
+ } else {
+ if (dsd == NULL || i < dsd->dsd_symidx) {
+ ushort_t data = CTF_TYPE_INFO(CTF_K_UNKNOWN,
+ 0, 0);
+ *func = data;
+ func++;
+ } else {
+ int j;
+ ushort_t data = CTF_TYPE_INFO(CTF_K_FUNCTION, 0,
+ dsd->dsd_nargs);
+
+ *func = data;
+ func++;
+ *func = dsd->dsd_tid;
+ func++;
+ for (j = 0; j < dsd->dsd_nargs; j++)
+ func[j] = dsd->dsd_argc[j];
+ func += dsd->dsd_nargs;
+ }
+ }
+ }
+
+ /*
* Finally, we are ready to ctf_bufopen() the new container. If this
* is successful, we then switch nfp and fp and free the old container.
*/
@@ -423,7 +645,15 @@ ctf_update(ctf_file_t *fp)
cts.cts_entsize = 1;
cts.cts_offset = 0;
- if ((nfp = ctf_bufopen(&cts, NULL, NULL, &err)) == NULL) {
+ if (fp->ctf_nsyms == 0) {
+ symp = NULL;
+ strp = NULL;
+ } else {
+ symp = &fp->ctf_symtab;
+ strp = &fp->ctf_strtab;
+ }
+
+ if ((nfp = ctf_bufopen(&cts, symp, strp, &err)) == NULL) {
ctf_data_free(buf, size);
return (ctf_set_errno(fp, err));
}
@@ -433,10 +663,11 @@ ctf_update(ctf_file_t *fp)
nfp->ctf_refcnt = fp->ctf_refcnt;
nfp->ctf_flags |= fp->ctf_flags & ~LCTF_DIRTY;
- nfp->ctf_data.cts_data = NULL; /* force ctf_data_free() on close */
nfp->ctf_dthash = fp->ctf_dthash;
nfp->ctf_dthashlen = fp->ctf_dthashlen;
nfp->ctf_dtdefs = fp->ctf_dtdefs;
+ nfp->ctf_dsdefs = fp->ctf_dsdefs;
+ nfp->ctf_dldefs = fp->ctf_dldefs;
nfp->ctf_dtstrlen = fp->ctf_dtstrlen;
nfp->ctf_dtnextid = fp->ctf_dtnextid;
nfp->ctf_dtoldid = fp->ctf_dtnextid - 1;
@@ -445,6 +676,23 @@ ctf_update(ctf_file_t *fp)
fp->ctf_dthash = NULL;
fp->ctf_dthashlen = 0;
bzero(&fp->ctf_dtdefs, sizeof (ctf_list_t));
+ 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));
@@ -563,6 +811,101 @@ ctf_dtd_lookup(ctf_file_t *fp, ctf_id_t type)
return (dtd);
}
+ctf_dsdef_t *
+ctf_dsd_lookup(ctf_file_t *fp, ulong_t idx)
+{
+ ctf_dsdef_t *dsd;
+
+ for (dsd = ctf_list_next(&fp->ctf_dsdefs); dsd != NULL;
+ dsd = ctf_list_next(dsd)) {
+ if (dsd->dsd_symidx == idx)
+ return (dsd);
+ }
+
+ return (NULL);
+}
+
+/*
+ * We order the ctf_dsdef_t by symbol index to make things better for updates.
+ */
+void
+ctf_dsd_insert(ctf_file_t *fp, ctf_dsdef_t *dsd)
+{
+ ctf_dsdef_t *i;
+
+ for (i = ctf_list_next(&fp->ctf_dsdefs); i != NULL;
+ i = ctf_list_next(i)) {
+ if (i->dsd_symidx > dsd->dsd_symidx)
+ break;
+ }
+
+ if (i == NULL) {
+ ctf_list_append(&fp->ctf_dsdefs, dsd);
+ return;
+ }
+
+ ctf_list_insert_before(&fp->ctf_dsdefs, i, dsd);
+}
+
+/* ARGSUSED */
+void
+ctf_dsd_delete(ctf_file_t *fp, ctf_dsdef_t *dsd)
+{
+ if (dsd->dsd_nargs > 0)
+ ctf_free(dsd->dsd_argc,
+ sizeof (ctf_id_t) * dsd->dsd_nargs);
+ ctf_list_delete(&fp->ctf_dsdefs, dsd);
+ ctf_free(dsd, sizeof (ctf_dsdef_t));
+}
+
+ctf_dldef_t *
+ctf_dld_lookup(ctf_file_t *fp, const char *name)
+{
+ ctf_dldef_t *dld;
+
+ for (dld = ctf_list_next(&fp->ctf_dldefs); dld != NULL;
+ dld = ctf_list_next(dld)) {
+ if (strcmp(name, dld->dld_name) == 0)
+ return (dld);
+ }
+
+ return (NULL);
+}
+
+void
+ctf_dld_insert(ctf_file_t *fp, ctf_dldef_t *dld, uint_t pos)
+{
+ ctf_dldef_t *l;
+
+ if (pos == 0) {
+ ctf_list_prepend(&fp->ctf_dldefs, dld);
+ return;
+ }
+
+ for (l = ctf_list_next(&fp->ctf_dldefs); pos != 0 && dld != NULL;
+ l = ctf_list_next(l), pos--)
+ ;
+
+ if (l == NULL)
+ ctf_list_append(&fp->ctf_dldefs, dld);
+ else
+ ctf_list_insert_before(&fp->ctf_dsdefs, l, dld);
+}
+
+void
+ctf_dld_delete(ctf_file_t *fp, ctf_dldef_t *dld)
+{
+ ctf_list_delete(&fp->ctf_dldefs, dld);
+
+ if (dld->dld_name != NULL) {
+ size_t len = strlen(dld->dld_name) + 1;
+ ctf_free(dld->dld_name, len);
+ fp->ctf_dtstrlen -= len;
+ }
+
+ ctf_free(dld, sizeof (ctf_dldef_t));
+}
+
/*
* Discard all of the dynamic type definitions that have been added to the
* container since the last call to ctf_update(). We locate such types by
@@ -583,10 +926,10 @@ ctf_discard(ctf_file_t *fp)
return (0); /* no update required */
for (dtd = ctf_list_prev(&fp->ctf_dtdefs); dtd != NULL; dtd = ntd) {
+ ntd = ctf_list_prev(dtd);
if (dtd->dtd_type <= fp->ctf_dtoldid)
continue; /* skip types that have been committed */
- ntd = ctf_list_prev(dtd);
ctf_dtd_delete(fp, dtd);
}
@@ -637,26 +980,7 @@ 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);
-}
-
-static ctf_id_t
+ctf_id_t
ctf_add_encoded(ctf_file_t *fp, uint_t flag,
const char *name, const ctf_encoding_t *ep, uint_t kind)
{
@@ -670,14 +994,22 @@ 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);
}
-static ctf_id_t
-ctf_add_reftype(ctf_file_t *fp, uint_t flag, ctf_id_t ref, uint_t kind)
+ctf_id_t
+ctf_add_reftype(ctf_file_t *fp, uint_t flag,
+ const char *name, ctf_id_t ref, uint_t kind)
{
ctf_dtdef_t *dtd;
ctf_id_t type;
@@ -685,7 +1017,7 @@ ctf_add_reftype(ctf_file_t *fp, uint_t flag, ctf_id_t ref, uint_t kind)
if (ref == CTF_ERR || ref < 0 || ref > CTF_MAX_TYPE)
return (ctf_set_errno(fp, EINVAL));
- if ((type = ctf_add_generic(fp, flag, NULL, &dtd)) == CTF_ERR)
+ if ((type = ctf_add_generic(fp, flag, name, &dtd)) == CTF_ERR)
return (CTF_ERR); /* errno is set for us */
ctf_ref_inc(fp, ref);
@@ -711,9 +1043,9 @@ ctf_add_float(ctf_file_t *fp, uint_t flag,
}
ctf_id_t
-ctf_add_pointer(ctf_file_t *fp, uint_t flag, ctf_id_t ref)
+ctf_add_pointer(ctf_file_t *fp, uint_t flag, const char *name, ctf_id_t ref)
{
- return (ctf_add_reftype(fp, flag, ref, CTF_K_POINTER));
+ return (ctf_add_reftype(fp, flag, name, ref, CTF_K_POINTER));
}
ctf_id_t
@@ -728,13 +1060,18 @@ 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: %ld\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: %ld\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 */
@@ -781,7 +1118,7 @@ ctf_set_array(ctf_file_t *fp, ctf_id_t type, const ctf_arinfo_t *arp)
}
ctf_id_t
-ctf_add_function(ctf_file_t *fp, uint_t flag,
+ctf_add_funcptr(ctf_file_t *fp, uint_t flag,
const ctf_funcinfo_t *ctc, const ctf_id_t *argv)
{
ctf_dtdef_t *dtd;
@@ -842,20 +1179,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);
}
@@ -864,20 +1215,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);
}
@@ -886,20 +1251,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);
}
@@ -965,21 +1344,21 @@ ctf_add_typedef(ctf_file_t *fp, uint_t flag, const char *name, ctf_id_t ref)
}
ctf_id_t
-ctf_add_volatile(ctf_file_t *fp, uint_t flag, ctf_id_t ref)
+ctf_add_volatile(ctf_file_t *fp, uint_t flag, const char *name, ctf_id_t ref)
{
- return (ctf_add_reftype(fp, flag, ref, CTF_K_VOLATILE));
+ return (ctf_add_reftype(fp, flag, name, ref, CTF_K_VOLATILE));
}
ctf_id_t
-ctf_add_const(ctf_file_t *fp, uint_t flag, ctf_id_t ref)
+ctf_add_const(ctf_file_t *fp, uint_t flag, const char *name, ctf_id_t ref)
{
- return (ctf_add_reftype(fp, flag, ref, CTF_K_CONST));
+ return (ctf_add_reftype(fp, flag, name, ref, CTF_K_CONST));
}
ctf_id_t
-ctf_add_restrict(ctf_file_t *fp, uint_t flag, ctf_id_t ref)
+ctf_add_restrict(ctf_file_t *fp, uint_t flag, const char *name, ctf_id_t ref)
{
- return (ctf_add_reftype(fp, flag, ref, CTF_K_RESTRICT));
+ return (ctf_add_reftype(fp, flag, name, ref, CTF_K_RESTRICT));
}
int
@@ -1012,8 +1391,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 duplicate member %s\n", name);
return (ctf_set_errno(fp, ECTF_DUPMEMBER));
+ }
}
if ((dmd = ctf_alloc(sizeof (ctf_dmdef_t))) == NULL)
@@ -1039,13 +1420,16 @@ ctf_add_enumerator(ctf_file_t *fp, ctf_id_t enid, const char *name, int value)
}
int
-ctf_add_member(ctf_file_t *fp, ctf_id_t souid, const char *name, ctf_id_t type)
+ctf_add_member(ctf_file_t *fp, ctf_id_t souid, const char *name, ctf_id_t type,
+ ulong_t offset)
{
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))
@@ -1064,19 +1448,58 @@ ctf_add_member(ctf_file_t *fp, ctf_id_t souid, const char *name, ctf_id_t type)
if (vlen == CTF_MAX_VLEN)
return (ctf_set_errno(fp, ECTF_DTFULL));
- if (name != NULL) {
+ /*
+ * Structures may have members which are anonymous. If they have two of
+ * these, then the duplicate member detection would find it due to the
+ * string of "", so we skip it.
+ */
+ if (name != NULL && *name != '\0') {
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));
@@ -1092,29 +1515,36 @@ ctf_add_member(ctf_file_t *fp, ctf_id_t souid, const char *name, ctf_id_t type)
if (kind == CTF_K_STRUCT && vlen != 0) {
ctf_dmdef_t *lmd = ctf_list_prev(&dtd->dtd_u.dtu_members);
ctf_id_t ltype = ctf_type_resolve(fp, lmd->dmd_type);
- size_t off = lmd->dmd_offset;
-
- ctf_encoding_t linfo;
- ssize_t lsize;
-
- if (ctf_type_encoding(fp, ltype, &linfo) != CTF_ERR)
- off += linfo.cte_bits;
- else if ((lsize = ctf_type_size(fp, ltype)) != CTF_ERR)
- off += lsize * NBBY;
-
- /*
- * Round up the offset of the end of the last member to the
- * next byte boundary, convert 'off' to bytes, and then round
- * it up again to the next multiple of the alignment required
- * by the new member. Finally, convert back to bits and store
- * the result in dmd_offset. Technically we could do more
- * efficient packing if the new member is a bit-field, but
- * we're the "compiler" and ANSI says we can do as we choose.
- */
- off = roundup(off, NBBY) / NBBY;
- off = roundup(off, MAX(malign, 1));
- dmd->dmd_offset = off * NBBY;
- ssize = off + msize;
+ size_t off;
+
+ if (offset == ULONG_MAX) {
+ ctf_encoding_t linfo;
+ ssize_t lsize;
+
+ off = lmd->dmd_offset;
+ if (ctf_type_encoding(fp, ltype, &linfo) != CTF_ERR)
+ off += linfo.cte_bits;
+ else if ((lsize = ctf_type_size(fp, ltype)) != CTF_ERR)
+ off += lsize * NBBY;
+
+ /*
+ * Round up the offset of the end of the last member to
+ * the next byte boundary, convert 'off' to bytes, and
+ * then round it up again to the next multiple of the
+ * alignment required by the new member. Finally,
+ * convert back to bits and store the result in
+ * dmd_offset. Technically we could do more efficient
+ * packing if the new member is a bit-field, but we're
+ * the "compiler" and ANSI says we can do as we choose.
+ */
+ off = roundup(off, NBBY) / NBBY;
+ off = roundup(off, MAX(malign, 1));
+ dmd->dmd_offset = off * NBBY;
+ ssize = off + msize;
+ } else {
+ dmd->dmd_offset = offset;
+ ssize = (offset + mbitsz) / NBBY;
+ }
} else {
dmd->dmd_offset = 0;
ssize = ctf_get_ctt_size(fp, &dtd->dtd_data, NULL, NULL);
@@ -1380,7 +1810,7 @@ ctf_add_type(ctf_file_t *dst_fp, ctf_file_t *src_fp, ctf_id_t src_type)
if (src_type == CTF_ERR)
return (CTF_ERR); /* errno is set for us */
- dst_type = ctf_add_reftype(dst_fp, flag, src_type, kind);
+ dst_type = ctf_add_reftype(dst_fp, flag, NULL, src_type, kind);
break;
case CTF_K_ARRAY:
@@ -1415,7 +1845,7 @@ ctf_add_type(ctf_file_t *dst_fp, ctf_file_t *src_fp, ctf_id_t src_type)
if (ctc.ctc_return == CTF_ERR)
return (CTF_ERR); /* errno is set for us */
- dst_type = ctf_add_function(dst_fp, flag, &ctc, NULL);
+ dst_type = ctf_add_funcptr(dst_fp, flag, &ctc, NULL);
break;
case CTF_K_STRUCT:
@@ -1540,3 +1970,227 @@ ctf_add_type(ctf_file_t *dst_fp, ctf_file_t *src_fp, ctf_id_t src_type)
return (dst_type);
}
+
+int
+ctf_add_function(ctf_file_t *fp, ulong_t idx, const ctf_funcinfo_t *fip,
+ const ctf_id_t *argc)
+{
+ int i;
+ ctf_dsdef_t *dsd;
+ ctf_file_t *afp;
+ uintptr_t symbase = (uintptr_t)fp->ctf_symtab.cts_data;
+
+ if (!(fp->ctf_flags & LCTF_RDWR))
+ return (ctf_set_errno(fp, ECTF_RDONLY));
+
+ if (ctf_dsd_lookup(fp, idx) != NULL)
+ return (ctf_set_errno(fp, ECTF_CONFLICT));
+
+ if (symbase == NULL)
+ return (ctf_set_errno(fp, ECTF_STRTAB));
+
+ if (idx > fp->ctf_nsyms)
+ return (ctf_set_errno(fp, ECTF_NOTDATA));
+
+ if (fp->ctf_symtab.cts_entsize == sizeof (Elf32_Sym)) {
+ const Elf32_Sym *symp = (Elf32_Sym *)symbase + idx;
+ if (ELF32_ST_TYPE(symp->st_info) != STT_FUNC)
+ return (ctf_set_errno(fp, ECTF_NOTFUNC));
+ } else {
+ const Elf64_Sym *symp = (Elf64_Sym *)symbase + idx;
+ if (ELF64_ST_TYPE(symp->st_info) != STT_FUNC)
+ return (ctf_set_errno(fp, ECTF_NOTFUNC));
+ }
+
+ afp = fp;
+ if (ctf_lookup_by_id(&afp, fip->ctc_return) == NULL)
+ return (CTF_ERR); /* errno is set for us */
+
+ for (i = 0; i < fip->ctc_argc; i++) {
+ afp = fp;
+ if (ctf_lookup_by_id(&afp, argc[i]) == NULL)
+ return (CTF_ERR); /* errno is set for us */
+ }
+
+ dsd = ctf_alloc(sizeof (ctf_dsdef_t));
+ if (dsd == NULL)
+ return (ctf_set_errno(fp, ENOMEM));
+ dsd->dsd_nargs = fip->ctc_argc;
+ if (fip->ctc_flags & CTF_FUNC_VARARG)
+ dsd->dsd_nargs++;
+ if (dsd->dsd_nargs != 0) {
+ dsd->dsd_argc = ctf_alloc(sizeof (ctf_id_t) * dsd->dsd_nargs);
+ if (dsd->dsd_argc == NULL) {
+ ctf_free(dsd, sizeof (ctf_dsdef_t));
+ return (ctf_set_errno(fp, ENOMEM));
+ }
+ bcopy(argc, dsd->dsd_argc, sizeof (ctf_id_t) * fip->ctc_argc);
+ if (fip->ctc_flags & CTF_FUNC_VARARG)
+ dsd->dsd_argc[fip->ctc_argc] = 0;
+ }
+ dsd->dsd_symidx = idx;
+ dsd->dsd_tid = fip->ctc_return;
+
+ ctf_dsd_insert(fp, dsd);
+ fp->ctf_flags |= LCTF_DIRTY;
+
+ return (0);
+}
+
+int
+ctf_add_object(ctf_file_t *fp, ulong_t idx, ctf_id_t type)
+{
+ ctf_dsdef_t *dsd;
+ ctf_file_t *afp;
+ uintptr_t symbase = (uintptr_t)fp->ctf_symtab.cts_data;
+
+ if (!(fp->ctf_flags & LCTF_RDWR))
+ return (ctf_set_errno(fp, ECTF_RDONLY));
+
+ if (!(fp->ctf_flags & LCTF_RDWR))
+ return (ctf_set_errno(fp, ECTF_RDONLY));
+
+ if (ctf_dsd_lookup(fp, idx) != NULL)
+ return (ctf_set_errno(fp, ECTF_CONFLICT));
+
+ if (symbase == NULL)
+ return (ctf_set_errno(fp, ECTF_STRTAB));
+
+ if (idx > fp->ctf_nsyms)
+ return (ctf_set_errno(fp, ECTF_NOTDATA));
+
+ if (fp->ctf_symtab.cts_entsize == sizeof (Elf32_Sym)) {
+ const Elf32_Sym *symp = (Elf32_Sym *)symbase + idx;
+ if (ELF32_ST_TYPE(symp->st_info) != STT_OBJECT)
+ return (ctf_set_errno(fp, ECTF_NOTDATA));
+ } else {
+ const Elf64_Sym *symp = (Elf64_Sym *)symbase + idx;
+ if (ELF64_ST_TYPE(symp->st_info) != STT_OBJECT)
+ return (ctf_set_errno(fp, ECTF_NOTDATA));
+ }
+
+ afp = fp;
+ if (ctf_lookup_by_id(&afp, type) == NULL)
+ return (CTF_ERR); /* errno is set for us */
+
+ dsd = ctf_alloc(sizeof (ctf_dsdef_t));
+ if (dsd == NULL)
+ return (ctf_set_errno(fp, ENOMEM));
+ dsd->dsd_symidx = idx;
+ dsd->dsd_tid = type;
+ dsd->dsd_argc = NULL;
+
+ ctf_dsd_insert(fp, dsd);
+ fp->ctf_flags |= LCTF_DIRTY;
+
+ return (0);
+}
+
+void
+ctf_dataptr(ctf_file_t *fp, const void **addrp, size_t *sizep)
+{
+ if (addrp != NULL)
+ *addrp = fp->ctf_base;
+ if (sizep != NULL)
+ *sizep = fp->ctf_size;
+}
+
+int
+ctf_add_label(ctf_file_t *fp, const char *name, ctf_id_t type, uint_t position)
+{
+ ctf_file_t *fpd;
+ ctf_dldef_t *dld;
+
+ if (name == NULL)
+ return (ctf_set_errno(fp, EINVAL));
+
+ if (!(fp->ctf_flags & LCTF_RDWR))
+ return (ctf_set_errno(fp, ECTF_RDONLY));
+
+ fpd = fp;
+ if (type != 0 && ctf_lookup_by_id(&fpd, type) == NULL)
+ return (CTF_ERR); /* errno is set for us */
+
+ if (type != 0 && (fp->ctf_flags & LCTF_CHILD) &&
+ CTF_TYPE_ISPARENT(type))
+ return (ctf_set_errno(fp, ECTF_NOPARENT));
+
+ if (ctf_dld_lookup(fp, name) != NULL)
+ return (ctf_set_errno(fp, ECTF_LABELEXISTS));
+
+ if ((dld = ctf_alloc(sizeof (ctf_dldef_t))) == NULL)
+ return (ctf_set_errno(fp, EAGAIN));
+
+ if ((dld->dld_name = ctf_strdup(name)) == NULL) {
+ ctf_free(dld, sizeof (ctf_dldef_t));
+ return (ctf_set_errno(fp, EAGAIN));
+ }
+
+ dld->dld_type = type;
+ fp->ctf_dtstrlen += strlen(name) + 1;
+ ctf_dld_insert(fp, dld, position);
+ fp->ctf_flags |= LCTF_DIRTY;
+
+ 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 natural 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 fe3d0de0cb..1765b77b54 100644
--- a/usr/src/common/ctf/ctf_error.c
+++ b/usr/src/common/ctf/ctf_error.c
@@ -24,7 +24,7 @@
* Use is subject to license terms.
*/
/*
- * Copyright (c) 2012, Joyent, Inc.
+ * Copyright (c) 2015, Joyent, Inc.
*/
#include <ctf_impl.h>
@@ -75,7 +75,15 @@ static const char *const _ctf_errlist[] = {
"Duplicate member name definition", /* ECTF_DUPMEMBER */
"Conflicting type is already defined", /* ECTF_CONFLICT */
"Type has outstanding references", /* ECTF_REFERENCED */
- "Type is not a dynamic type" /* ECTF_NOTDYN */
+ "Type is not a dynamic type", /* ECTF_NOTDYN */
+ "Elf library failure", /* ECTF_ELF */
+ "Cannot merge child container", /* ECTF_MCHILD */
+ "Label already exists", /* ECTF_LABEL */
+ "Merged labels conflict", /* ECTF_LCONFLICT */
+ "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_hash.c b/usr/src/common/ctf/ctf_hash.c
index b10a7618f6..0c5a71a5ac 100644
--- a/usr/src/common/ctf/ctf_hash.c
+++ b/usr/src/common/ctf/ctf_hash.c
@@ -25,9 +25,8 @@
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <ctf_impl.h>
+#include <sys/debug.h>
static const ushort_t _CTF_EMPTY[1] = { 0 };
diff --git a/usr/src/common/ctf/ctf_impl.h b/usr/src/common/ctf/ctf_impl.h
index f56fa6a005..93a85a8b4f 100644
--- a/usr/src/common/ctf/ctf_impl.h
+++ b/usr/src/common/ctf/ctf_impl.h
@@ -25,7 +25,7 @@
* Use is subject to license terms.
*/
/*
- * Copyright (c) 2012, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2015, Joyent, Inc. All rights reserved.
*/
#ifndef _CTF_IMPL_H
@@ -41,6 +41,8 @@
#include <sys/systm.h>
#include <sys/cmn_err.h>
#include <sys/varargs.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
#define isspace(c) \
((c) == ' ' || (c) == '\t' || (c) == '\n' || \
@@ -56,6 +58,7 @@
#include <stdio.h>
#include <limits.h>
#include <ctype.h>
+#include <stddef.h>
#endif /* _KERNEL */
@@ -77,6 +80,10 @@ typedef struct ctf_hash {
uint_t h_free; /* index of next free hash element */
} ctf_hash_t;
+struct ctf_idhash_iter {
+ int cii_id; /* Current iteration id */
+};
+
typedef struct ctf_strs {
const char *cts_strs; /* base address of string table */
size_t cts_len; /* size of string table in bytes */
@@ -159,6 +166,20 @@ typedef struct ctf_dtdef {
} dtd_u;
} ctf_dtdef_t;
+typedef struct ctf_dsdef {
+ ctf_list_t dsd_list; /* list forward/back pointers */
+ ulong_t dsd_symidx; /* symbol id */
+ ctf_id_t dsd_tid; /* type for obj, 0 if function */
+ uint_t dsd_nargs;
+ ctf_id_t *dsd_argc; /* function argv */
+} ctf_dsdef_t;
+
+typedef struct ctf_dldef {
+ ctf_list_t dld_list; /* list forward/back pointers */
+ char *dld_name; /* name of the label */
+ ctf_id_t dld_type; /* type ID associated with the label */
+} ctf_dldef_t;
+
typedef struct ctf_bundle {
ctf_file_t *ctb_file; /* CTF container handle */
ctf_id_t ctb_type; /* CTF type identifier */
@@ -211,6 +232,9 @@ struct ctf_file {
ulong_t ctf_dtnextid; /* next dynamic type id to assign */
ulong_t ctf_dtoldid; /* oldest id that has been committed */
void *ctf_specific; /* data for ctf_get/setspecific */
+ ctf_list_t ctf_dsdefs; /* list of dynamic obj/func definitions */
+ ctf_list_t ctf_dldefs; /* list of dynamic labels */
+ uint_t ctf_hflags; /* original flags on the header */
};
#define LCTF_INDEX_TO_TYPEPTR(fp, i) \
@@ -225,62 +249,15 @@ struct ctf_file {
#define LCTF_RDWR 0x0004 /* CTF container is writable */
#define LCTF_DIRTY 0x0008 /* CTF container has been modified */
-#define ECTF_BASE 1000 /* base value for libctf errnos */
-
-enum {
- ECTF_FMT = ECTF_BASE, /* file is not in CTF or ELF format */
- ECTF_ELFVERS, /* ELF version is more recent than libctf */
- ECTF_CTFVERS, /* CTF version is more recent than libctf */
- ECTF_ENDIAN, /* data is different endian-ness than lib */
- ECTF_SYMTAB, /* symbol table uses invalid entry size */
- ECTF_SYMBAD, /* symbol table data buffer invalid */
- ECTF_STRBAD, /* string table data buffer invalid */
- ECTF_CORRUPT, /* file data corruption detected */
- ECTF_NOCTFDATA, /* ELF file does not contain CTF data */
- ECTF_NOCTFBUF, /* buffer does not contain CTF data */
- ECTF_NOSYMTAB, /* symbol table data is not available */
- ECTF_NOPARENT, /* parent CTF container is not available */
- ECTF_DMODEL, /* data model mismatch */
- ECTF_MMAP, /* failed to mmap a data section */
- ECTF_ZMISSING, /* decompression library not installed */
- ECTF_ZINIT, /* failed to initialize decompression library */
- ECTF_ZALLOC, /* failed to allocate decompression buffer */
- ECTF_DECOMPRESS, /* failed to decompress CTF data */
- ECTF_STRTAB, /* string table for this string is missing */
- ECTF_BADNAME, /* string offset is corrupt w.r.t. strtab */
- ECTF_BADID, /* invalid type ID number */
- ECTF_NOTSOU, /* type is not a struct or union */
- ECTF_NOTENUM, /* type is not an enum */
- ECTF_NOTSUE, /* type is not a struct, union, or enum */
- ECTF_NOTINTFP, /* type is not an integer or float */
- ECTF_NOTARRAY, /* type is not an array */
- ECTF_NOTREF, /* type does not reference another type */
- ECTF_NAMELEN, /* buffer is too small to hold type name */
- ECTF_NOTYPE, /* no type found corresponding to name */
- ECTF_SYNTAX, /* syntax error in type name */
- ECTF_NOTFUNC, /* symtab entry does not refer to a function */
- ECTF_NOFUNCDAT, /* no func info available for function */
- ECTF_NOTDATA, /* symtab entry does not refer to a data obj */
- ECTF_NOTYPEDAT, /* no type info available for object */
- ECTF_NOLABEL, /* no label found corresponding to name */
- ECTF_NOLABELDATA, /* file does not contain any labels */
- ECTF_NOTSUP, /* feature not supported */
- ECTF_NOENUMNAM, /* enum element name not found */
- ECTF_NOMEMBNAM, /* member name not found */
- ECTF_RDONLY, /* CTF container is read-only */
- ECTF_DTFULL, /* CTF type is full (no more members allowed) */
- ECTF_FULL, /* CTF container is full */
- ECTF_DUPMEMBER, /* duplicate member name definition */
- ECTF_CONFLICT, /* conflicting type definition present */
- ECTF_REFERENCED, /* type has outstanding references */
- ECTF_NOTDYN /* type is not a dynamic type */
-};
+#define CTF_ELF_SCN_NAME ".SUNW_ctf"
extern ssize_t ctf_get_ctt_size(const ctf_file_t *, const ctf_type_t *,
ssize_t *, ssize_t *);
extern const ctf_type_t *ctf_lookup_by_id(ctf_file_t **, ctf_id_t);
+extern ctf_file_t *ctf_fdcreate_int(int, int *, ctf_sect_t *);
+
extern int ctf_hash_create(ctf_hash_t *, ulong_t);
extern int ctf_hash_insert(ctf_hash_t *, ctf_file_t *, ushort_t, uint_t);
extern int ctf_hash_define(ctf_hash_t *, ctf_file_t *, ushort_t, uint_t);
@@ -294,12 +271,16 @@ extern void ctf_hash_destroy(ctf_hash_t *);
extern void ctf_list_append(ctf_list_t *, void *);
extern void ctf_list_prepend(ctf_list_t *, void *);
+extern void ctf_list_insert_before(ctf_list_t *, void *, void *);
extern void ctf_list_delete(ctf_list_t *, void *);
extern void ctf_dtd_insert(ctf_file_t *, ctf_dtdef_t *);
extern void ctf_dtd_delete(ctf_file_t *, ctf_dtdef_t *);
extern ctf_dtdef_t *ctf_dtd_lookup(ctf_file_t *, ctf_id_t);
+extern void ctf_dsd_delete(ctf_file_t *, ctf_dsdef_t *);
+extern void ctf_dld_delete(ctf_file_t *, ctf_dldef_t *);
+
extern void ctf_decl_init(ctf_decl_t *, char *, size_t);
extern void ctf_decl_fini(ctf_decl_t *);
extern void ctf_decl_push(ctf_decl_t *, ctf_file_t *, ctf_id_t);
@@ -327,6 +308,13 @@ extern void ctf_dprintf(const char *, ...);
extern void *ctf_zopen(int *);
+extern ctf_id_t ctf_add_encoded(ctf_file_t *, uint_t, const char *,
+ const ctf_encoding_t *, uint_t);
+extern ctf_id_t ctf_add_reftype(ctf_file_t *, uint_t, const char *, ctf_id_t,
+ uint_t);
+extern boolean_t ctf_sym_valid(uintptr_t, int, uint16_t, uint64_t,
+ uint32_t);
+
extern const char _CTF_SECTION[]; /* name of CTF ELF section */
extern const char _CTF_NULLSTR[]; /* empty string */
diff --git a/usr/src/common/ctf/ctf_open.c b/usr/src/common/ctf/ctf_open.c
index 001cf5c591..82b396e825 100644
--- a/usr/src/common/ctf/ctf_open.c
+++ b/usr/src/common/ctf/ctf_open.c
@@ -25,7 +25,7 @@
* Use is subject to license terms.
*/
/*
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2015, Joyent, Inc. All rights reserved.
*/
#include <ctf_impl.h>
@@ -550,6 +550,7 @@ ctf_bufopen(const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
void *buf, *base;
size_t size, hdrsz;
int err;
+ uint_t hflags;
if (ctfsect == NULL || ((symsect == NULL) != (strsect == NULL)))
return (ctf_set_open_errno(errp, EINVAL));
@@ -631,6 +632,7 @@ ctf_bufopen(const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
* the CTF data buffer if it is compressed. Otherwise we just put
* the data section's buffer pointer into ctf_buf, below.
*/
+ hflags = hp.cth_flags;
if (hp.cth_flags & CTF_F_COMPRESS) {
size_t srclen, dstlen;
const void *src;
@@ -680,6 +682,7 @@ ctf_bufopen(const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
bzero(fp, sizeof (ctf_file_t));
fp->ctf_version = hp.cth_version;
fp->ctf_fileops = &ctf_fileops[hp.cth_version];
+ fp->ctf_hflags = hflags;
bcopy(ctfsect, &fp->ctf_data, sizeof (ctf_sect_t));
if (symsect != NULL) {
@@ -883,6 +886,8 @@ void
ctf_close(ctf_file_t *fp)
{
ctf_dtdef_t *dtd, *ntd;
+ ctf_dsdef_t *dsd, *nsd;
+ ctf_dldef_t *dld, *nld;
if (fp == NULL)
return; /* allow ctf_close(NULL) to simplify caller code */
@@ -906,10 +911,25 @@ ctf_close(ctf_file_t *fp)
ctf_dtd_delete(fp, dtd);
}
+ for (dsd = ctf_list_prev(&fp->ctf_dsdefs); dsd != NULL; dsd = nsd) {
+ nsd = ctf_list_prev(dsd);
+ ctf_dsd_delete(fp, dsd);
+ }
+
+ for (dld = ctf_list_prev(&fp->ctf_dldefs); dld != NULL; dld = nld) {
+ nld = ctf_list_prev(dld);
+ ctf_dld_delete(fp, dld);
+ }
+
ctf_free(fp->ctf_dthash, fp->ctf_dthashlen * sizeof (ctf_dtdef_t *));
if (fp->ctf_flags & LCTF_MMAP) {
- if (fp->ctf_data.cts_data != NULL)
+ /*
+ * Writeable containers shouldn't necessairily have the CTF
+ * section freed.
+ */
+ if (fp->ctf_data.cts_data != NULL &&
+ !(fp->ctf_flags & LCTF_RDWR))
ctf_sect_munmap(&fp->ctf_data);
if (fp->ctf_symtab.cts_data != NULL)
ctf_sect_munmap(&fp->ctf_symtab);
@@ -980,6 +1000,16 @@ ctf_parent_name(ctf_file_t *fp)
}
/*
+ * Return the label of the parent CTF container, if one exists. Otherwise return
+ * NULL.
+ */
+const char *
+ctf_parent_label(ctf_file_t *fp)
+{
+ return (fp->ctf_parlabel);
+}
+
+/*
* Import the types from the specified parent container by storing a pointer
* to it in ctf_parent and incrementing its reference count. Only one parent
* is allowed: if a parent already exists, it is replaced by the new parent.
@@ -1043,3 +1073,9 @@ ctf_getspecific(ctf_file_t *fp)
{
return (fp->ctf_specific);
}
+
+uint_t
+ctf_flags(ctf_file_t *fp)
+{
+ return (fp->ctf_hflags);
+}
diff --git a/usr/src/common/ctf/ctf_types.c b/usr/src/common/ctf/ctf_types.c
index ab1b9ff14b..18d2d3d88a 100644
--- a/usr/src/common/ctf/ctf_types.c
+++ b/usr/src/common/ctf/ctf_types.c
@@ -24,8 +24,12 @@
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
+/*
+ * Copyright 2018 Joyent, Inc.
+ */
#include <ctf_impl.h>
+#include <sys/debug.h>
ssize_t
ctf_get_ctt_size(const ctf_file_t *fp, const ctf_type_t *tp, ssize_t *sizep,
@@ -138,19 +142,21 @@ ctf_enum_iter(ctf_file_t *fp, ctf_id_t type, ctf_enum_f *func, void *arg)
}
/*
- * Iterate over every root (user-visible) type in the given CTF container.
- * We pass the type ID of each type to the specified callback function.
+ * Iterate over every type in the given CTF container. If the user doesn't ask
+ * for all types, then we only give them the user visible, aka root, types. We
+ * pass the type ID of each type to the specified callback function.
*/
int
-ctf_type_iter(ctf_file_t *fp, ctf_type_f *func, void *arg)
+ctf_type_iter(ctf_file_t *fp, boolean_t nonroot, ctf_type_f *func, void *arg)
{
ctf_id_t id, max = fp->ctf_typemax;
int rc, child = (fp->ctf_flags & LCTF_CHILD);
for (id = 1; id <= max; id++) {
const ctf_type_t *tp = LCTF_INDEX_TO_TYPEPTR(fp, id);
- if (CTF_INFO_ISROOT(tp->ctt_info) &&
- (rc = func(CTF_INDEX_TO_TYPE(id, child), arg)) != 0)
+ if ((nonroot || CTF_INFO_ISROOT(tp->ctt_info)) &&
+ (rc = func(CTF_INDEX_TO_TYPE(id, child),
+ CTF_INFO_ISROOT(tp->ctt_info), arg)) != 0)
return (rc);
}
@@ -194,13 +200,91 @@ ctf_type_resolve(ctf_file_t *fp, ctf_id_t type)
}
/*
- * Lookup the given type ID and print a string name for it into buf. Return
- * the actual number of bytes (not including \0) needed to format the name.
+ * Format an integer type; if a vname is specified, we need to insert it prior
+ * to any bitfield ":24" suffix. This works out far simpler than figuring it
+ * out from scratch.
+ */
+static const char *
+ctf_format_int(ctf_decl_t *cd, const char *vname, const char *qname,
+ const char *name)
+{
+ const char *c;
+
+ if (vname == NULL) {
+ if (qname != NULL)
+ ctf_decl_sprintf(cd, "%s`%s", qname, name);
+ else
+ ctf_decl_sprintf(cd, "%s", name);
+ return (NULL);
+ }
+
+ if ((c = strchr(name, ':')) == NULL) {
+ ctf_decl_sprintf(cd, "%s", name);
+ return (vname);
+ }
+
+ /* "unsigned int mybits:23" */
+ ctf_decl_sprintf(cd, "%.*s %s%s", c - name, name, vname, c);
+ return (NULL);
+}
+
+static void
+ctf_format_func(ctf_file_t *fp, ctf_decl_t *cd,
+ const char *vname, ctf_id_t id, int want_func_args)
+{
+ ctf_funcinfo_t fi;
+ /* We'll presume zone_create() is a bad example. */
+ ctf_id_t args[20];
+
+ ctf_decl_sprintf(cd, "%s(", vname == NULL ? "" : vname);
+
+ if (!want_func_args)
+ goto out;
+
+ if (ctf_func_info_by_id(fp, id, &fi) != 0)
+ goto out;
+
+ if (fi.ctc_argc > ARRAY_SIZE(args))
+ fi.ctc_argc = ARRAY_SIZE(args);
+
+ if (fi.ctc_argc == 0) {
+ ctf_decl_sprintf(cd, "void");
+ goto out;
+ }
+
+ if (ctf_func_args_by_id(fp, id, fi.ctc_argc, args) != 0)
+ goto out;
+
+ for (size_t i = 0; i < fi.ctc_argc; i++) {
+ char aname[512];
+
+ if (ctf_type_name(fp, args[i], aname, sizeof (aname)) == NULL)
+ (void) strlcpy(aname, "unknown_t", sizeof (aname));
+
+ ctf_decl_sprintf(cd, "%s%s", aname,
+ i + 1 == fi.ctc_argc ? "" : ", ");
+ }
+
+ if (fi.ctc_flags & CTF_FUNC_VARARG)
+ ctf_decl_sprintf(cd, "%s...", fi.ctc_argc == 0 ? "" : ", ");
+
+out:
+ ctf_decl_sprintf(cd, ")");
+}
+
+/*
+ * Lookup the given type ID and print a string name for it into buf. Return the
+ * actual number of bytes (not including \0) needed to format the name.
+ *
+ * "vname" is an optional variable name or similar, so array suffix formatting,
+ * bitfields, and functions are C-correct. (This is not perfect, as can be seen
+ * in kiconv_ops_t.)
*/
static ssize_t
ctf_type_qlname(ctf_file_t *fp, ctf_id_t type, char *buf, size_t len,
- const char *qname)
+ const char *vname, const char *qname)
{
+ int want_func_args = (vname != NULL);
ctf_decl_t cd;
ctf_decl_node_t *cdp;
ctf_decl_prec_t prec, lp, rp;
@@ -252,6 +336,8 @@ ctf_type_qlname(ctf_file_t *fp, ctf_id_t type, char *buf, size_t len,
switch (cdp->cd_kind) {
case CTF_K_INTEGER:
+ vname = ctf_format_int(&cd, vname, qname, name);
+ break;
case CTF_K_FLOAT:
case CTF_K_TYPEDEF:
if (qname != NULL)
@@ -262,10 +348,14 @@ ctf_type_qlname(ctf_file_t *fp, ctf_id_t type, char *buf, size_t len,
ctf_decl_sprintf(&cd, "*");
break;
case CTF_K_ARRAY:
- ctf_decl_sprintf(&cd, "[%u]", cdp->cd_n);
+ ctf_decl_sprintf(&cd, "%s[%u]",
+ vname != NULL ? vname : "", cdp->cd_n);
+ vname = NULL;
break;
case CTF_K_FUNCTION:
- ctf_decl_sprintf(&cd, "()");
+ ctf_format_func(fp, &cd, vname,
+ cdp->cd_type, want_func_args);
+ vname = NULL;
break;
case CTF_K_STRUCT:
case CTF_K_FORWARD:
@@ -300,10 +390,29 @@ ctf_type_qlname(ctf_file_t *fp, ctf_id_t type, char *buf, size_t len,
k = cdp->cd_kind;
}
- if (rp == prec)
+ if (rp == prec) {
+ /*
+ * Peek ahead: if we're going to hit a function,
+ * we want to insert its name now before this closing
+ * bracket.
+ */
+ if (vname != NULL && prec < CTF_PREC_FUNCTION) {
+ cdp = ctf_list_next(
+ &cd.cd_nodes[CTF_PREC_FUNCTION]);
+
+ if (cdp != NULL) {
+ ctf_decl_sprintf(&cd, "%s", vname);
+ vname = NULL;
+ }
+ }
+
ctf_decl_sprintf(&cd, ")");
+ }
}
+ if (vname != NULL)
+ ctf_decl_sprintf(&cd, " %s", vname);
+
if (cd.cd_len >= len)
(void) ctf_set_errno(fp, ECTF_NAMELEN);
@@ -314,7 +423,7 @@ ctf_type_qlname(ctf_file_t *fp, ctf_id_t type, char *buf, size_t len,
ssize_t
ctf_type_lname(ctf_file_t *fp, ctf_id_t type, char *buf, size_t len)
{
- return (ctf_type_qlname(fp, type, buf, len, NULL));
+ return (ctf_type_qlname(fp, type, buf, len, NULL, NULL));
}
/*
@@ -324,7 +433,7 @@ ctf_type_lname(ctf_file_t *fp, ctf_id_t type, char *buf, size_t len)
char *
ctf_type_name(ctf_file_t *fp, ctf_id_t type, char *buf, size_t len)
{
- ssize_t rv = ctf_type_qlname(fp, type, buf, len, NULL);
+ ssize_t rv = ctf_type_qlname(fp, type, buf, len, NULL, NULL);
return (rv >= 0 && rv < len ? buf : NULL);
}
@@ -332,10 +441,17 @@ char *
ctf_type_qname(ctf_file_t *fp, ctf_id_t type, char *buf, size_t len,
const char *qname)
{
- ssize_t rv = ctf_type_qlname(fp, type, buf, len, qname);
+ ssize_t rv = ctf_type_qlname(fp, type, buf, len, NULL, qname);
return (rv >= 0 && rv < len ? buf : NULL);
}
+char *
+ctf_type_cname(ctf_file_t *fp, ctf_id_t type, char *buf, size_t len,
+ const char *cname)
+{
+ ssize_t rv = ctf_type_qlname(fp, type, buf, len, cname, NULL);
+ return (rv >= 0 && rv < len ? buf : NULL);
+}
/*
* Resolve the type down to a base type node, and then return the size
@@ -361,6 +477,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);
@@ -380,7 +499,22 @@ ctf_type_size(ctf_file_t *fp, ctf_id_t type)
return (-1); /* errno is set for us */
return (size * ar.ctr_nelems);
-
+ case CTF_K_STRUCT:
+ case CTF_K_UNION:
+ /*
+ * If we have a zero size, we may be in the process of adding a
+ * structure or union but having not called ctf_update() to deal
+ * with the circular dependencies in such structures and unions.
+ * To handle that case, if we get a size of zero from the ctt,
+ * we look up the dtdef and use its size instead.
+ */
+ size = ctf_get_ctt_size(fp, tp, NULL, NULL);
+ if (size == 0) {
+ ctf_dtdef_t *dtd = ctf_dtd_lookup(fp, type);
+ if (dtd != NULL)
+ return (dtd->dtd_data.ctt_size);
+ }
+ return (size);
default:
return (ctf_get_ctt_size(fp, tp, NULL, NULL));
}
@@ -868,3 +1002,309 @@ ctf_type_visit(ctf_file_t *fp, ctf_id_t type, ctf_visit_f *func, void *arg)
{
return (ctf_type_rvisit(fp, type, func, arg, "", 0, 0));
}
+
+int
+ctf_func_info_by_id(ctf_file_t *fp, ctf_id_t type, ctf_funcinfo_t *fip)
+{
+ ctf_file_t *ofp = fp;
+ const ctf_type_t *tp;
+ const ushort_t *dp;
+ int nargs;
+ ssize_t increment;
+
+ if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
+ return (CTF_ERR); /* errno is set for us */
+
+ if (LCTF_INFO_KIND(fp, tp->ctt_info) != CTF_K_FUNCTION)
+ return (ctf_set_errno(ofp, ECTF_NOTFUNC));
+
+ fip->ctc_return = tp->ctt_type;
+ nargs = LCTF_INFO_VLEN(fp, tp->ctt_info);
+ fip->ctc_argc = nargs;
+ fip->ctc_flags = 0;
+
+ /* dp should now point to the first argument */
+ if (nargs != 0) {
+ (void) ctf_get_ctt_size(fp, tp, NULL, &increment);
+ dp = (ushort_t *)((uintptr_t)fp->ctf_buf +
+ fp->ctf_txlate[CTF_TYPE_TO_INDEX(type)] + increment);
+ if (dp[nargs - 1] == 0) {
+ fip->ctc_flags |= CTF_FUNC_VARARG;
+ fip->ctc_argc--;
+ }
+ }
+
+ return (0);
+}
+
+int
+ctf_func_args_by_id(ctf_file_t *fp, ctf_id_t type, uint_t argc, ctf_id_t *argv)
+{
+ ctf_file_t *ofp = fp;
+ const ctf_type_t *tp;
+ const ushort_t *dp;
+ int nargs;
+ ssize_t increment;
+
+ if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
+ return (CTF_ERR); /* errno is set for us */
+
+ if (LCTF_INFO_KIND(fp, tp->ctt_info) != CTF_K_FUNCTION)
+ return (ctf_set_errno(ofp, ECTF_NOTFUNC));
+
+ nargs = LCTF_INFO_VLEN(fp, tp->ctt_info);
+ (void) ctf_get_ctt_size(fp, tp, NULL, &increment);
+ dp = (ushort_t *)((uintptr_t)fp->ctf_buf +
+ fp->ctf_txlate[CTF_TYPE_TO_INDEX(type)] +
+ increment);
+ if (nargs != 0 && dp[nargs - 1] == 0)
+ nargs--;
+
+ for (nargs = MIN(argc, nargs); nargs != 0; nargs--)
+ *argv++ = *dp++;
+
+ return (0);
+}
+
+int
+ctf_object_iter(ctf_file_t *fp, ctf_object_f *func, void *arg)
+{
+ int i, ret;
+ ctf_id_t id;
+ uintptr_t symbase = (uintptr_t)fp->ctf_symtab.cts_data;
+ uintptr_t strbase = (uintptr_t)fp->ctf_strtab.cts_data;
+
+ if (fp->ctf_symtab.cts_data == NULL)
+ return (ctf_set_errno(fp, ECTF_NOSYMTAB));
+
+ for (i = 0; i < fp->ctf_nsyms; i++) {
+ char *name;
+ if (fp->ctf_sxlate[i] == -1u)
+ continue;
+ id = *(ushort_t *)((uintptr_t)fp->ctf_buf +
+ fp->ctf_sxlate[i]);
+
+ /*
+ * Validate whether or not we're looking at a data object as
+ * oposed to a function.
+ */
+ if (fp->ctf_symtab.cts_entsize == sizeof (Elf32_Sym)) {
+ const Elf32_Sym *symp = (Elf32_Sym *)symbase + i;
+ if (ELF32_ST_TYPE(symp->st_info) != STT_OBJECT)
+ continue;
+ if (fp->ctf_strtab.cts_data != NULL &&
+ symp->st_name != 0)
+ name = (char *)(strbase + symp->st_name);
+ else
+ name = NULL;
+ } else {
+ const Elf64_Sym *symp = (Elf64_Sym *)symbase + i;
+ if (ELF64_ST_TYPE(symp->st_info) != STT_OBJECT)
+ continue;
+ if (fp->ctf_strtab.cts_data != NULL &&
+ symp->st_name != 0)
+ name = (char *)(strbase + symp->st_name);
+ else
+ name = NULL;
+ }
+
+ if ((ret = func(name, id, i, arg)) != 0)
+ return (ret);
+ }
+
+ return (0);
+}
+
+int
+ctf_function_iter(ctf_file_t *fp, ctf_function_f *func, void *arg)
+{
+ int i, ret;
+ uintptr_t symbase = (uintptr_t)fp->ctf_symtab.cts_data;
+ uintptr_t strbase = (uintptr_t)fp->ctf_strtab.cts_data;
+
+ if (fp->ctf_symtab.cts_data == NULL)
+ return (ctf_set_errno(fp, ECTF_NOSYMTAB));
+
+ for (i = 0; i < fp->ctf_nsyms; i++) {
+ char *name;
+ ushort_t info, *dp;
+ ctf_funcinfo_t fi;
+ if (fp->ctf_sxlate[i] == -1u)
+ continue;
+
+ dp = (ushort_t *)((uintptr_t)fp->ctf_buf +
+ fp->ctf_sxlate[i]);
+ info = *dp;
+ if (info == 0)
+ continue;
+
+ /*
+ * This may be a function or it may be a data object. We have to
+ * consult the symbol table to be certain. Functions are encoded
+ * with their info, data objects with their actual type.
+ */
+ if (fp->ctf_symtab.cts_entsize == sizeof (Elf32_Sym)) {
+ const Elf32_Sym *symp = (Elf32_Sym *)symbase + i;
+ if (ELF32_ST_TYPE(symp->st_info) != STT_FUNC)
+ continue;
+ if (fp->ctf_strtab.cts_data != NULL)
+ name = (char *)(strbase + symp->st_name);
+ else
+ name = NULL;
+ } else {
+ const Elf64_Sym *symp = (Elf64_Sym *)symbase + i;
+ if (ELF64_ST_TYPE(symp->st_info) != STT_FUNC)
+ continue;
+ if (fp->ctf_strtab.cts_data != NULL)
+ name = (char *)(strbase + symp->st_name);
+ else
+ name = NULL;
+ }
+
+ if (LCTF_INFO_KIND(fp, info) != CTF_K_FUNCTION)
+ continue;
+ dp++;
+ fi.ctc_return = *dp;
+ dp++;
+ fi.ctc_argc = LCTF_INFO_VLEN(fp, info);
+ fi.ctc_flags = 0;
+
+ if (fi.ctc_argc != 0 && dp[fi.ctc_argc - 1] == 0) {
+ fi.ctc_flags |= CTF_FUNC_VARARG;
+ fi.ctc_argc--;
+ }
+
+ if ((ret = func(name, i, &fi, arg)) != 0)
+ return (ret);
+
+ }
+
+ return (0);
+}
+
+char *
+ctf_symbol_name(ctf_file_t *fp, ulong_t idx, char *buf, size_t len)
+{
+ const char *name;
+ uintptr_t symbase = (uintptr_t)fp->ctf_symtab.cts_data;
+ uintptr_t strbase = (uintptr_t)fp->ctf_strtab.cts_data;
+
+ if (fp->ctf_symtab.cts_data == NULL) {
+ (void) ctf_set_errno(fp, ECTF_NOSYMTAB);
+ return (NULL);
+ }
+
+ if (fp->ctf_strtab.cts_data == NULL) {
+ (void) ctf_set_errno(fp, ECTF_STRTAB);
+ return (NULL);
+ }
+
+ if (idx > fp->ctf_nsyms) {
+ (void) ctf_set_errno(fp, ECTF_NOTDATA);
+ return (NULL);
+ }
+
+ if (fp->ctf_symtab.cts_entsize == sizeof (Elf32_Sym)) {
+ const Elf32_Sym *symp = (Elf32_Sym *)symbase + idx;
+ if (ELF32_ST_TYPE(symp->st_info) != STT_OBJECT &&
+ ELF32_ST_TYPE(symp->st_info) != STT_FUNC) {
+ (void) ctf_set_errno(fp, ECTF_NOTDATA);
+ return (NULL);
+ }
+ if (symp->st_name == 0) {
+ (void) ctf_set_errno(fp, ENOENT);
+ return (NULL);
+ }
+ name = (const char *)(strbase + symp->st_name);
+ } else {
+ const Elf64_Sym *symp = (Elf64_Sym *)symbase + idx;
+ if (ELF64_ST_TYPE(symp->st_info) != STT_FUNC &&
+ ELF64_ST_TYPE(symp->st_info) != STT_OBJECT) {
+ (void) ctf_set_errno(fp, ECTF_NOTDATA);
+ return (NULL);
+ }
+ if (symp->st_name == 0) {
+ (void) ctf_set_errno(fp, ENOENT);
+ return (NULL);
+ }
+ name = (const char *)(strbase + symp->st_name);
+ }
+
+ (void) strlcpy(buf, name, len);
+
+ return (buf);
+}
+
+int
+ctf_string_iter(ctf_file_t *fp, ctf_string_f *func, void *arg)
+{
+ int rc;
+ const char *strp = fp->ctf_str[CTF_STRTAB_0].cts_strs;
+ size_t strl = fp->ctf_str[CTF_STRTAB_0].cts_len;
+
+ while (strl > 0) {
+ size_t len;
+
+ if ((rc = func(strp, arg)) != 0)
+ return (rc);
+
+ len = strlen(strp) + 1;
+ strl -= len;
+ strp += len;
+ }
+
+ return (0);
+}
+
+/*
+ * fp isn't strictly necessary 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");
+ }
+}
+
+ctf_id_t
+ctf_max_id(ctf_file_t *fp)
+{
+ int child = (fp->ctf_flags & LCTF_CHILD);
+ return (fp->ctf_typemax + (child ? CTF_CHILD_START : 0));
+}
+
+ulong_t
+ctf_nr_syms(ctf_file_t *fp)
+{
+ return (fp->ctf_nsyms);
+}
diff --git a/usr/src/common/ctf/ctf_util.c b/usr/src/common/ctf/ctf_util.c
index 740d403e8c..550195b5e1 100644
--- a/usr/src/common/ctf/ctf_util.c
+++ b/usr/src/common/ctf/ctf_util.c
@@ -23,10 +23,12 @@
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-
-#pragma ident "%Z%%M% %I% %E% SMI"
+/*
+ * Copyright (c) 2015, Joyent, Inc.
+ */
#include <ctf_impl.h>
+#include <sys/debug.h>
/*
* Simple doubly-linked list append routine. This implementation assumes that
@@ -71,6 +73,24 @@ ctf_list_prepend(ctf_list_t *lp, void *new)
lp->l_prev = p;
}
+void
+ctf_list_insert_before(ctf_list_t *head, void *item, void *nitem)
+{
+ ctf_list_t *lp = item;
+ ctf_list_t *new = nitem;
+ ctf_list_t *prev = lp->l_prev;
+
+ lp->l_prev = new;
+ new->l_next = lp;
+ new->l_prev = prev;
+ if (prev != NULL) {
+ prev->l_next = new;
+ } else {
+ ASSERT(head->l_next == lp);
+ head->l_next = new;
+ }
+}
+
/*
* Delete the specified existing element from the given ctf_list_t. The
* existing pointer should be pointing at a struct with embedded ctf_list_t.
@@ -150,3 +170,22 @@ ctf_set_errno(ctf_file_t *fp, int err)
fp->ctf_errno = err;
return (CTF_ERR);
}
+
+boolean_t
+ctf_sym_valid(uintptr_t strbase, int type, uint16_t shndx, uint64_t val,
+ uint32_t noff)
+{
+ const char *name;
+
+ if (type != STT_OBJECT && type != STT_FUNC)
+ return (B_FALSE);
+ if (shndx == SHN_UNDEF || noff == 0)
+ return (B_FALSE);
+ if (type == STT_OBJECT && shndx == SHN_ABS && val == 0)
+ return (B_FALSE);
+ name = (char *)(strbase + noff);
+ if (strcmp(name, "_START_") == 0 || strcmp(name, "_END_") == 0)
+ return (B_FALSE);
+
+ return (B_TRUE);
+}
diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile
index 7463954834..7c461eb3fc 100644
--- a/usr/src/lib/Makefile
+++ b/usr/src/lib/Makefile
@@ -22,7 +22,7 @@
#
# Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2012 by Delphix. All rights reserved.
-# Copyright (c) 2012, Joyent, Inc. All rights reserved.
+# Copyright 2015, Joyent, Inc.
# Copyright (c) 2013 Gary Mills
# Copyright 2014 Garrett D'Amore <garrett@damore.org>
# Copyright (c) 2015 Gary Mills
@@ -123,6 +123,7 @@ SUBDIRS += \
libdoor \
libdtrace \
libdtrace_jni \
+ libdwarf \
libefi \
libelfsign \
libeti \
@@ -390,6 +391,7 @@ HDRSUBDIRS= \
libdhcputil \
libdisasm \
libdiskmgt \
+ libdwarf \
libdladm \
libdll \
libdlpi \
@@ -590,6 +592,7 @@ libcmd: libsum libast
libcmdutils: libavl
libcpc: libpctx
libcrypt: libgen
+libctf: libdwarf
libdevid: libdevinfo
libdevinfo: libsec libgen
libdhcpagent: libdhcputil libuuid libdlpi libcontract
diff --git a/usr/src/lib/Makefile.lib b/usr/src/lib/Makefile.lib
index 86cc330f41..384c579cfc 100644
--- a/usr/src/lib/Makefile.lib
+++ b/usr/src/lib/Makefile.lib
@@ -21,6 +21,7 @@
# Copyright 2015 Gary Mills
# Copyright 2015 Igor Kozhukhov <ikozhukhov@gmail.com>
# Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2015, Joyent, Inc.
#
#
# Definitions common to libraries.
@@ -248,3 +249,15 @@ TARGETMACH= $(MACH)
# shouldn't override this - they should override $(CLOBBERFILES) instead.
#
CLOBBERTARGFILES= $(LIBS) $(DYNLIB) $(CLOBBERFILES)
+
+#
+# Define the default ctfdiff invocation used to check a list of types
+# supplied by a user of a library. The goal is to validate that a given
+# series of types is the same in both a 32-bit and 64-bit artifact. This
+# is only defined if we have a 64-bit build to do.
+#
+TYPECHECK_LIB32 = $(TYPECHECK_LIB:%=$(MACH)/%)
+TYPECHECK_LIB64 = $(TYPECHECK_LIB:%=$(MACH64)/%)
+TYPECHECK_LIST= $(TYPELIST:%=-T %)
+$(BUILD64)TYPECHECK.lib = $(CTFDIFF) -t -I $(TYPECHECK_LIST) $(TYPECHECK_LIB32) $(TYPECHECK_LIB64)
+TYPECHECK = $(TYPECHECK_LIB:%=%.typecheck)
diff --git a/usr/src/lib/Makefile.targ b/usr/src/lib/Makefile.targ
index 4769c64d54..0744402619 100644
--- a/usr/src/lib/Makefile.targ
+++ b/usr/src/lib/Makefile.targ
@@ -99,11 +99,14 @@ $(DYNLIBCCC): pics .WAIT $$(PICS) $$(ALTPICS) $$(EXTPICS)
$(POST_PROCESS_SO)
$(LINTLIB): $$(SRCS)
- $(LINT.c) -o $(LIBNAME) $(SRCS) > $(LINTOUT) 2>&1
+ $(LINT.c) -o $(LIBNAME) $(SRCS) > $(LINTOUT) 2>&1
lintcheck: $$(SRCS)
$(LINT.c) $(LINTCHECKFLAGS) $(SRCS) $(LDLIBS)
+$(TYPECHECK): $(TYPECHECK_LIB32) $(TYPECHECK_LIB64)
+ $(TYPECHECK.lib)
+
clobber: clean
-$(RM) $(CLOBBERTARGFILES)
diff --git a/usr/src/lib/libctf/Makefile.com b/usr/src/lib/libctf/Makefile.com
index 4d1e01d4eb..0169c2a367 100644
--- a/usr/src/lib/libctf/Makefile.com
+++ b/usr/src/lib/libctf/Makefile.com
@@ -23,43 +23,9 @@
# Use is subject to license terms.
#
-LIBRARY = libctf.a
-VERS = .1
-
-COMMON_OBJS = \
- ctf_create.o \
- ctf_decl.o \
- ctf_error.o \
- ctf_hash.o \
- ctf_labels.o \
- ctf_lookup.o \
- ctf_open.o \
- ctf_types.o \
- ctf_util.o
-
-LIB_OBJS = \
- ctf_lib.o \
- ctf_subr.o
-
-OBJECTS = $(COMMON_OBJS) $(LIB_OBJS)
-
-include ../../Makefile.lib
+include ../Makefile.shared.com
include ../../Makefile.rootfs
-SRCS = $(COMMON_OBJS:%.o=../../../common/ctf/%.c) $(LIB_OBJS:%.o=../common/%.c)
-LIBS = $(DYNLIB) $(LINTLIB)
-
-SRCDIR = ../common
-
-CPPFLAGS += -I../common -I../../../common/ctf -DCTF_OLD_VERSIONS
-CFLAGS += $(CCVERBOSE)
-
-CERRWARN += -_gcc=-Wno-uninitialized
-
-LDLIBS += -lc
-
-$(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC)
-
.KEEP_STATE:
all: $(LIBS)
@@ -67,7 +33,4 @@ all: $(LIBS)
lint: lintcheck
include ../../Makefile.targ
-
-objs/%.o pics/%.o: ../../../common/ctf/%.c
- $(COMPILE.c) -o $@ $<
- $(POST_PROCESS_O)
+include ../Makefile.shared.targ
diff --git a/usr/src/lib/libctf/Makefile.shared.com b/usr/src/lib/libctf/Makefile.shared.com
new file mode 100644
index 0000000000..3f2603a29a
--- /dev/null
+++ b/usr/src/lib/libctf/Makefile.shared.com
@@ -0,0 +1,90 @@
+#
+# 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 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# Copyright 2018 Joyent, Inc.
+#
+
+#
+# This Makefile is shared between the libctf native build in tools and
+# the libctf build here for the system.
+#
+LIBRARY = libctf.a
+VERS = .1
+
+COMMON_OBJS = \
+ ctf_create.o \
+ ctf_decl.o \
+ ctf_dwarf.o \
+ ctf_error.o \
+ ctf_hash.o \
+ ctf_labels.o \
+ ctf_lookup.o \
+ ctf_open.o \
+ 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) $(MERGEQ_OBJS)
+MAPFILEDIR = $(SRC)/lib/libctf
+
+include $(SRC)/lib/Makefile.lib
+
+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 -ldwarf -lavl
+
+CSTD = $(CSTD_GNU99)
+C99LMODE = -Xc99=%all
+
+SRCDIR = $(SRC)/lib/libctf/common
+
+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
+
+$(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC)
diff --git a/usr/src/lib/libctf/Makefile.shared.targ b/usr/src/lib/libctf/Makefile.shared.targ
new file mode 100644
index 0000000000..b6520f2366
--- /dev/null
+++ b/usr/src/lib/libctf/Makefile.shared.targ
@@ -0,0 +1,30 @@
+#
+# 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. All rights reserved.
+#
+
+#
+# This Makefile is shared between both the tools and the normal library build.
+#
+
+pics/%.o: $(SRC)/common/ctf/%.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
+
+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
new file mode 100644
index 0000000000..c8facbc728
--- /dev/null
+++ b/usr/src/lib/libctf/common/ctf_diff.c
@@ -0,0 +1,1360 @@
+/*
+ * 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. All rights reserved.
+ */
+
+/*
+ * The following is a basic overview of how we diff types in containers (the
+ * generally interesting part of diff, and what's used by merge). We maintain
+ * two mapping tables, a table of forward mappings (src->dest), and a reverse
+ * mapping (dest->src). Both are initialized to contain no mapping, and can also
+ * be updated to contain a negative mapping.
+ *
+ * What we do first is iterate over each type in the src container, and compare
+ * it with a type in the destination container. This may involve doing recursive
+ * comparisons -- which can involve cycles. To deal with this, whenever we
+ * encounter something which may be cyclic, we insert a guess. In other words,
+ * we assume that it may be true. This is necessary for the classic case of the
+ * following structure:
+ *
+ * struct foo {
+ * struct foo *foo_next;
+ * };
+ *
+ * If it turns out that we were wrong, we discard our guesses.
+ *
+ * If we find that a given type in src has no corresponding entry in dst, we
+ * then mark its map as CTF_ERR (-1) to indicate that it has *no* match, as
+ * opposed to the default value of 0, which indicates an unknown match.
+ * Once we've done the first iteration through src, we know at that point in
+ * time whether everything in dst is similar or not and can simply walk over it
+ * and don't have to do any additional checks.
+ */
+
+#include <libctf.h>
+#include <ctf_impl.h>
+#include <sys/debug.h>
+
+typedef struct ctf_diff_func {
+ const char *cdf_name;
+ ulong_t cdf_symidx;
+ ulong_t cdf_matchidx;
+} ctf_diff_func_t;
+
+typedef struct ctf_diff_obj {
+ const char *cdo_name;
+ ulong_t cdo_symidx;
+ ctf_id_t cdo_id;
+ ulong_t cdo_matchidx;
+} ctf_diff_obj_t;
+
+typedef struct ctf_diff_guess {
+ struct ctf_diff_guess *cdg_next;
+ ctf_id_t cdg_iid;
+ ctf_id_t cdg_oid;
+} ctf_diff_guess_t;
+
+/* typedef in libctf.h */
+struct ctf_diff {
+ uint_t cds_flags;
+ boolean_t cds_tvalid; /* types valid */
+ ctf_file_t *cds_ifp;
+ 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;
+ uint_t cds_nifuncs;
+ uint_t cds_nofuncs;
+ uint_t cds_nextifunc;
+ uint_t cds_nextofunc;
+ ctf_diff_func_t *cds_ifuncs;
+ ctf_diff_func_t *cds_ofuncs;
+ boolean_t cds_ffillip;
+ boolean_t cds_fvalid;
+ uint_t cds_niobj;
+ uint_t cds_noobj;
+ uint_t cds_nextiobj;
+ uint_t cds_nextoobj;
+ ctf_diff_obj_t *cds_iobj;
+ ctf_diff_obj_t *cds_oobj;
+ boolean_t cds_ofillip;
+ boolean_t cds_ovalid;
+};
+
+#define TINDEX(tid) (tid - 1)
+
+/*
+ * Team Diff
+ */
+static int ctf_diff_type(ctf_diff_t *, ctf_file_t *, ctf_id_t, ctf_file_t *,
+ ctf_id_t);
+
+static int
+ctf_diff_name(ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp, ctf_id_t oid)
+{
+ const char *iname, *oname;
+ const ctf_type_t *itp, *otp;
+
+ if ((itp = ctf_lookup_by_id(&ifp, iid)) == NULL)
+ return (CTF_ERR);
+
+ if ((otp = ctf_lookup_by_id(&ofp, oid)) == NULL)
+ return (ctf_set_errno(ifp, iid));
+
+ iname = ctf_strptr(ifp, itp->ctt_name);
+ oname = ctf_strptr(ofp, otp->ctt_name);
+
+ if ((iname == NULL || oname == NULL) && (iname != oname))
+ return (B_TRUE);
+
+ /* Two anonymous names are the same */
+ if (iname == NULL && oname == NULL)
+ return (B_FALSE);
+
+ return (strcmp(iname, oname) == 0 ? B_FALSE: B_TRUE);
+}
+
+/*
+ * For floats and ints
+ */
+static int
+ctf_diff_number(ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp, ctf_id_t oid)
+{
+ ctf_encoding_t ien, den;
+
+ if (ctf_type_encoding(ifp, iid, &ien) != 0)
+ return (CTF_ERR);
+
+ if (ctf_type_encoding(ofp, oid, &den) != 0)
+ return (ctf_set_errno(ifp, iid));
+
+ if (bcmp(&ien, &den, sizeof (ctf_encoding_t)) != 0)
+ return (B_TRUE);
+
+ return (B_FALSE);
+}
+
+/*
+ * 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
+ctf_diff_qualifier(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid,
+ ctf_file_t *ofp, ctf_id_t oid)
+{
+ ctf_id_t iref, oref;
+
+ iref = ctf_type_reference(ifp, iid);
+ if (iref == CTF_ERR)
+ return (CTF_ERR);
+
+ oref = ctf_type_reference(ofp, oid);
+ if (oref == CTF_ERR)
+ return (ctf_set_errno(ifp, ctf_errno(ofp)));
+
+ return (ctf_diff_type(cds, ifp, iref, ofp, oref));
+}
+
+/*
+ * Two arrays are the same iff they have the same type for contents, the same
+ * type for the index, and the same number of elements.
+ */
+static int
+ctf_diff_array(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp,
+ ctf_id_t oid)
+{
+ int ret;
+ ctf_arinfo_t iar, oar;
+
+ if (ctf_array_info(ifp, iid, &iar) == CTF_ERR)
+ return (CTF_ERR);
+
+ if (ctf_array_info(ofp, oid, &oar) == CTF_ERR)
+ return (ctf_set_errno(ifp, ctf_errno(ofp)));
+
+ ret = ctf_diff_type(cds, ifp, iar.ctr_contents, ofp, oar.ctr_contents);
+ if (ret != B_FALSE)
+ return (ret);
+
+ if (iar.ctr_nelems != oar.ctr_nelems)
+ return (B_TRUE);
+
+ /*
+ * If we're ignoring integer types names, then we're trying to do a bit
+ * of a logical diff and we don't really care about the fact that the
+ * index element might not be the same here, what we care about are the
+ * number of elements and that they're the same type.
+ */
+ if ((cds->cds_flags & CTF_DIFF_F_IGNORE_INTNAMES) == 0) {
+ ret = ctf_diff_type(cds, ifp, iar.ctr_index, ofp,
+ oar.ctr_index);
+ if (ret != B_FALSE)
+ return (ret);
+ }
+
+ return (B_FALSE);
+}
+
+/*
+ * Two function pointers are the same if the following is all true:
+ *
+ * o They have the same return type
+ * o They have the same number of arguments
+ * o The arguments are of the same type
+ * o They have the same flags
+ */
+static int
+ctf_diff_fptr(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp,
+ ctf_id_t oid)
+{
+ int ret, i;
+ ctf_funcinfo_t ifunc, ofunc;
+ ctf_id_t *iids, *oids;
+
+ if (ctf_func_info_by_id(ifp, iid, &ifunc) == CTF_ERR)
+ return (CTF_ERR);
+
+ if (ctf_func_info_by_id(ofp, oid, &ofunc) == CTF_ERR)
+ return (ctf_set_errno(ifp, ctf_errno(ofp)));
+
+ if (ifunc.ctc_argc != ofunc.ctc_argc)
+ return (B_TRUE);
+
+ if (ifunc.ctc_flags != ofunc.ctc_flags)
+ return (B_TRUE);
+
+ ret = ctf_diff_type(cds, ifp, ifunc.ctc_return, ofp, ofunc.ctc_return);
+ if (ret != B_FALSE)
+ return (ret);
+
+ iids = ctf_alloc(sizeof (ctf_id_t) * ifunc.ctc_argc);
+ if (iids == NULL)
+ return (ctf_set_errno(ifp, ENOMEM));
+
+ oids = ctf_alloc(sizeof (ctf_id_t) * ifunc.ctc_argc);
+ if (oids == NULL) {
+ ctf_free(iids, sizeof (ctf_id_t) * ifunc.ctc_argc);
+ return (ctf_set_errno(ifp, ENOMEM));
+ }
+
+ if (ctf_func_args_by_id(ifp, iid, ifunc.ctc_argc, iids) == CTF_ERR) {
+ ret = CTF_ERR;
+ goto out;
+ }
+
+ if (ctf_func_args_by_id(ofp, oid, ofunc.ctc_argc, oids) == CTF_ERR) {
+ ret = ctf_set_errno(ifp, ctf_errno(ofp));
+ goto out;
+ }
+
+ ret = B_TRUE;
+ for (i = 0; i < ifunc.ctc_argc; i++) {
+ ret = ctf_diff_type(cds, ifp, iids[i], ofp, oids[i]);
+ if (ret != B_FALSE)
+ goto out;
+ }
+ ret = B_FALSE;
+
+out:
+ ctf_free(iids, sizeof (ctf_id_t) * ifunc.ctc_argc);
+ ctf_free(oids, sizeof (ctf_id_t) * ofunc.ctc_argc);
+ return (ret);
+}
+
+/*
+ * Two structures are the same if every member is identical to its corresponding
+ * 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,
+ ctf_id_t oid)
+{
+ ctf_file_t *oifp;
+ const ctf_type_t *itp, *otp;
+ ssize_t isize, iincr, osize, oincr;
+ const ctf_member_t *imp, *omp;
+ const ctf_lmember_t *ilmp, *olmp;
+ int n;
+ ctf_diff_guess_t *cdg;
+
+ oifp = ifp;
+
+ if ((itp = ctf_lookup_by_id(&ifp, iid)) == NULL)
+ return (CTF_ERR);
+
+ 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);
+
+ (void) ctf_get_ctt_size(ifp, itp, &isize, &iincr);
+ (void) ctf_get_ctt_size(ofp, otp, &osize, &oincr);
+
+ if (ifp->ctf_version == CTF_VERSION_1 || isize < CTF_LSTRUCT_THRESH) {
+ imp = (const ctf_member_t *)((uintptr_t)itp + iincr);
+ ilmp = NULL;
+ } else {
+ imp = NULL;
+ ilmp = (const ctf_lmember_t *)((uintptr_t)itp + iincr);
+ }
+
+ if (ofp->ctf_version == CTF_VERSION_1 || osize < CTF_LSTRUCT_THRESH) {
+ omp = (const ctf_member_t *)((uintptr_t)otp + oincr);
+ olmp = NULL;
+ } else {
+ omp = NULL;
+ olmp = (const ctf_lmember_t *)((uintptr_t)otp + oincr);
+ }
+
+ /*
+ * Insert our assumption that they're equal for the moment.
+ */
+ cdg = ctf_alloc(sizeof (ctf_diff_guess_t));
+ if (cdg == NULL)
+ return (ctf_set_errno(ifp, ENOMEM));
+ cdg->cdg_iid = iid;
+ cdg->cdg_oid = oid;
+ cdg->cdg_next = cds->cds_guess;
+ cds->cds_guess = cdg;
+ cds->cds_forward[TINDEX(iid)] = oid;
+ cds->cds_reverse[TINDEX(oid)] = iid;
+
+ for (n = LCTF_INFO_VLEN(ifp, itp->ctt_info); n != 0; n--) {
+ const char *iname, *oname;
+ ulong_t ioff, ooff;
+ ctf_id_t itype, otype;
+ int ret;
+
+ if (imp != NULL) {
+ iname = ctf_strptr(ifp, imp->ctm_name);
+ ioff = imp->ctm_offset;
+ itype = imp->ctm_type;
+ } else {
+ iname = ctf_strptr(ifp, ilmp->ctlm_name);
+ ioff = CTF_LMEM_OFFSET(ilmp);
+ itype = ilmp->ctlm_type;
+ }
+
+ if (omp != NULL) {
+ oname = ctf_strptr(ofp, omp->ctm_name);
+ ooff = omp->ctm_offset;
+ otype = omp->ctm_type;
+ } else {
+ oname = ctf_strptr(ofp, olmp->ctlm_name);
+ ooff = CTF_LMEM_OFFSET(olmp);
+ otype = olmp->ctlm_type;
+ }
+
+ if (ioff != ooff) {
+ return (B_TRUE);
+ }
+ if (strcmp(iname, oname) != 0) {
+ return (B_TRUE);
+ }
+ ret = ctf_diff_type(cds, ifp, itype, ofp, otype);
+ if (ret != B_FALSE) {
+ return (ret);
+ }
+
+ /* Advance our pointers */
+ if (imp != NULL)
+ imp++;
+ if (ilmp != NULL)
+ ilmp++;
+ if (omp != NULL)
+ omp++;
+ if (olmp != NULL)
+ olmp++;
+ }
+
+ return (B_FALSE);
+}
+
+/*
+ * Two unions are the same if they have the same set of members. This is similar
+ * to, but slightly different from a struct. The offsets of members don't
+ * matter. However, there is no guarantee of ordering so we have to fall back to
+ * doing an O(N^2) scan.
+ */
+typedef struct ctf_diff_union_member {
+ ctf_diff_t *cdum_cds;
+ ctf_file_t *cdum_fp;
+ ctf_file_t *cdum_iterfp;
+ const char *cdum_name;
+ ctf_id_t cdum_type;
+ int cdum_ret;
+} ctf_diff_union_member_t;
+
+typedef struct ctf_diff_union_fp {
+ ctf_diff_t *cduf_cds;
+ ctf_file_t *cduf_curfp;
+ ctf_file_t *cduf_altfp;
+ ctf_id_t cduf_type;
+ int cduf_ret;
+} ctf_diff_union_fp_t;
+
+/* ARGSUSED */
+static int
+ctf_diff_union_check_member(const char *name, ctf_id_t id, ulong_t off,
+ void *arg)
+{
+ int ret;
+ ctf_diff_union_member_t *cdump = arg;
+
+ if (strcmp(name, cdump->cdum_name) != 0)
+ return (0);
+
+ ret = ctf_diff_type(cdump->cdum_cds, cdump->cdum_fp, cdump->cdum_type,
+ cdump->cdum_iterfp, id);
+ if (ret == CTF_ERR) {
+ cdump->cdum_ret = CTF_ERR;
+ return (1);
+ }
+
+ if (ret == B_FALSE) {
+ cdump->cdum_ret = B_FALSE;
+ /* Return non-zero to stop iteration as we have a match */
+ return (1);
+ }
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+ctf_diff_union_check_fp(const char *name, ctf_id_t id, ulong_t off, void *arg)
+{
+ int ret;
+ ctf_diff_union_member_t cdum;
+ ctf_diff_union_fp_t *cdufp = arg;
+
+ cdum.cdum_cds = cdufp->cduf_cds;
+ cdum.cdum_fp = cdufp->cduf_curfp;
+ cdum.cdum_iterfp = cdufp->cduf_altfp;
+ cdum.cdum_name = name;
+ cdum.cdum_type = id;
+ cdum.cdum_ret = B_TRUE;
+
+ ret = ctf_member_iter(cdum.cdum_iterfp, cdufp->cduf_type,
+ ctf_diff_union_check_member, &cdum);
+ if (ret == 0 || cdum.cdum_ret == CTF_ERR) {
+ /* No match found or error, terminate now */
+ cdufp->cduf_ret = cdum.cdum_ret;
+ return (1);
+ } else if (ret == CTF_ERR) {
+ (void) ctf_set_errno(cdum.cdum_fp, ctf_errno(cdum.cdum_iterfp));
+ cdufp->cduf_ret = CTF_ERR;
+ return (1);
+ } else {
+ ASSERT(cdum.cdum_ret == B_FALSE);
+ cdufp->cduf_ret = cdum.cdum_ret;
+ return (0);
+ }
+}
+
+static int
+ctf_diff_union(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp,
+ ctf_id_t oid)
+{
+ ctf_file_t *oifp;
+ const ctf_type_t *itp, *otp;
+ ctf_diff_union_fp_t cduf;
+ ctf_diff_guess_t *cdg;
+ int ret;
+
+ oifp = ifp;
+ if ((itp = ctf_lookup_by_id(&ifp, iid)) == NULL)
+ return (CTF_ERR);
+ if ((otp = ctf_lookup_by_id(&ofp, oid)) == NULL)
+ return (ctf_set_errno(oifp, ctf_errno(ofp)));
+
+ if (LCTF_INFO_VLEN(ifp, itp->ctt_info) !=
+ LCTF_INFO_VLEN(ofp, otp->ctt_info))
+ return (B_TRUE);
+
+ cdg = ctf_alloc(sizeof (ctf_diff_guess_t));
+ if (cdg == NULL)
+ return (ctf_set_errno(ifp, ENOMEM));
+ cdg->cdg_iid = iid;
+ cdg->cdg_oid = oid;
+ cdg->cdg_next = cds->cds_guess;
+ cds->cds_guess = cdg;
+ cds->cds_forward[TINDEX(iid)] = oid;
+ cds->cds_reverse[TINDEX(oid)] = iid;
+
+ cduf.cduf_cds = cds;
+ cduf.cduf_curfp = ifp;
+ cduf.cduf_altfp = ofp;
+ cduf.cduf_type = oid;
+ cduf.cduf_ret = B_TRUE;
+ ret = ctf_member_iter(ifp, iid, ctf_diff_union_check_fp, &cduf);
+ if (ret != CTF_ERR)
+ ret = cduf.cduf_ret;
+
+ return (ret);
+}
+
+/*
+ * Two enums are equivalent if they share the same underlying type and they have
+ * the same set of members.
+ */
+static int
+ctf_diff_enum(ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp, ctf_id_t oid)
+{
+ ctf_file_t *oifp;
+ const ctf_type_t *itp, *otp;
+ ssize_t iincr, oincr;
+ const ctf_enum_t *iep, *oep;
+ int n;
+
+ oifp = ifp;
+ if ((itp = ctf_lookup_by_id(&ifp, iid)) == NULL)
+ return (CTF_ERR);
+ if ((otp = ctf_lookup_by_id(&ofp, oid)) == NULL)
+ return (ctf_set_errno(oifp, ctf_errno(ofp)));
+
+ if (LCTF_INFO_VLEN(ifp, itp->ctt_info) !=
+ LCTF_INFO_VLEN(ofp, otp->ctt_info))
+ return (B_TRUE);
+
+ (void) ctf_get_ctt_size(ifp, itp, NULL, &iincr);
+ (void) ctf_get_ctt_size(ofp, otp, NULL, &oincr);
+ iep = (const ctf_enum_t *)((uintptr_t)itp + iincr);
+ oep = (const ctf_enum_t *)((uintptr_t)otp + oincr);
+
+ for (n = LCTF_INFO_VLEN(ifp, itp->ctt_info); n != 0;
+ n--, iep++, oep++) {
+ if (strcmp(ctf_strptr(ifp, iep->cte_name),
+ ctf_strptr(ofp, oep->cte_name)) != 0)
+ return (B_TRUE);
+
+ if (iep->cte_value != oep->cte_value)
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+
+/*
+ * Two forwards are equivalent in one of two cases. If both are forwards, than
+ * they are the same. Otherwise, they're equivalent if one is a struct or union
+ * and the other is a forward.
+ */
+static int
+ctf_diff_forward(ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp, ctf_id_t oid)
+{
+ int ikind, okind;
+
+ ikind = ctf_type_kind(ifp, iid);
+ okind = ctf_type_kind(ofp, oid);
+
+ if (ikind == okind) {
+ ASSERT(ikind == CTF_K_FORWARD);
+ return (B_FALSE);
+ } else if (ikind == CTF_K_FORWARD) {
+ return (okind != CTF_K_UNION && okind != CTF_K_STRUCT);
+ } else {
+ return (ikind != CTF_K_UNION && ikind != CTF_K_STRUCT);
+ }
+}
+
+/*
+ * Are two types equivalent?
+ */
+int
+ctf_diff_type(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp,
+ ctf_id_t oid)
+{
+ int ret, ikind, okind;
+
+ /* Do a quick short circuit */
+ if (ifp == ofp && iid == oid)
+ return (B_FALSE);
+
+ /*
+ * Check if it's something we've already encountered in a forward
+ * reference or forward negative table. Also double check the reverse
+ * table.
+ */
+ if (cds->cds_forward[TINDEX(iid)] == oid)
+ return (B_FALSE);
+ if (cds->cds_forward[TINDEX(iid)] != 0)
+ return (B_TRUE);
+ if (cds->cds_reverse[TINDEX(oid)] == iid)
+ return (B_FALSE);
+ if ((cds->cds_flags & CTF_DIFF_F_IGNORE_INTNAMES) == 0 &&
+ cds->cds_reverse[TINDEX(oid)] != 0)
+ return (B_TRUE);
+
+ ikind = ctf_type_kind(ifp, iid);
+ okind = ctf_type_kind(ofp, oid);
+
+ if (ikind != okind &&
+ ikind != CTF_K_FORWARD && okind != CTF_K_FORWARD)
+ return (B_TRUE);
+
+ /* Check names */
+ if ((ret = ctf_diff_name(ifp, iid, ofp, oid)) != B_FALSE) {
+ if (ikind != okind || ikind != CTF_K_INTEGER ||
+ (cds->cds_flags & CTF_DIFF_F_IGNORE_INTNAMES) == 0)
+ return (ret);
+ }
+
+ if (ikind == CTF_K_FORWARD || okind == CTF_K_FORWARD)
+ return (ctf_diff_forward(ifp, iid, ofp, oid));
+
+ switch (ikind) {
+ case CTF_K_INTEGER:
+ case CTF_K_FLOAT:
+ ret = ctf_diff_number(ifp, iid, ofp, oid);
+ break;
+ case CTF_K_ARRAY:
+ ret = ctf_diff_array(cds, ifp, iid, ofp, oid);
+ break;
+ case CTF_K_FUNCTION:
+ ret = ctf_diff_fptr(cds, ifp, iid, ofp, oid);
+ break;
+ case CTF_K_STRUCT:
+ ret = ctf_diff_struct(cds, ifp, iid, ofp, oid);
+ break;
+ case CTF_K_UNION:
+ ret = ctf_diff_union(cds, ifp, iid, ofp, oid);
+ break;
+ case CTF_K_ENUM:
+ ret = ctf_diff_enum(ifp, iid, ofp, oid);
+ break;
+ case CTF_K_FORWARD:
+ ret = ctf_diff_forward(ifp, iid, ofp, oid);
+ break;
+ 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:
+ ret = ctf_diff_qualifier(cds, ifp, iid, ofp, oid);
+ break;
+ case CTF_K_UNKNOWN:
+ /*
+ * The current CTF tools use CTF_K_UNKNOWN as a padding type. We
+ * always declare two instances of CTF_K_UNKNOWN as different,
+ * even though this leads to additional diff noise.
+ */
+ ret = B_TRUE;
+ break;
+ default:
+ abort();
+ }
+
+ return (ret);
+}
+
+/*
+ * 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, boolean_t self)
+{
+ int i, j, diff;
+ int istart, iend, jstart, jend;
+
+ istart = 1;
+ iend = cds->cds_ifp->ctf_typemax;
+ if (cds->cds_ifp->ctf_flags & LCTF_CHILD) {
+ istart += CTF_CHILD_START;
+ iend += CTF_CHILD_START;
+ }
+
+ jstart = 1;
+ jend = cds->cds_ofp->ctf_typemax;
+ if (cds->cds_ofp->ctf_flags & LCTF_CHILD) {
+ jstart += CTF_CHILD_START;
+ jend += CTF_CHILD_START;
+ }
+
+ 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;
+
+ ASSERT(cds->cds_guess == NULL);
+ diff = ctf_diff_type(cds, cds->cds_ifp, i,
+ cds->cds_ofp, j);
+ if (diff == CTF_ERR)
+ return (CTF_ERR);
+
+ /* Clean up our guesses */
+ cdg = cds->cds_guess;
+ cds->cds_guess = NULL;
+ while (cdg != NULL) {
+ if (diff == B_TRUE) {
+ cds->cds_forward[TINDEX(cdg->cdg_iid)] =
+ 0;
+ cds->cds_reverse[TINDEX(cdg->cdg_oid)] =
+ 0;
+ }
+ tofree = cdg;
+ cdg = cdg->cdg_next;
+ ctf_free(tofree, sizeof (ctf_diff_guess_t));
+ }
+
+ /* Found a hit, update the tables */
+ if (diff == B_FALSE) {
+ cds->cds_forward[TINDEX(i)] = j;
+ if (cds->cds_reverse[TINDEX(j)] == 0)
+ cds->cds_reverse[TINDEX(j)] = i;
+ break;
+ }
+ }
+
+ /* Call the callback at this point */
+ if (diff == B_TRUE) {
+ cds->cds_forward[TINDEX(i)] = CTF_ERR;
+ cds->cds_func(cds->cds_ifp, i, B_FALSE, NULL, CTF_ERR,
+ cds->cds_arg);
+ } else {
+ cds->cds_func(cds->cds_ifp, i, B_TRUE, cds->cds_ofp, j,
+ cds->cds_arg);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Now we need to walk the second container and emit anything that we didn't
+ * find as common in the first pass.
+ */
+static int
+ctf_diff_pass2(ctf_diff_t *cds)
+{
+ int i, start, end;
+
+ start = 0x1;
+ end = cds->cds_ofp->ctf_typemax;
+ if (cds->cds_ofp->ctf_flags & LCTF_CHILD) {
+ start += CTF_CHILD_START;
+ end += CTF_CHILD_START;
+ }
+
+ for (i = start; i <= end; i++) {
+ if (cds->cds_reverse[TINDEX(i)] != 0)
+ continue;
+ cds->cds_func(cds->cds_ofp, i, B_FALSE, NULL, CTF_ERR,
+ cds->cds_arg);
+ }
+
+ return (0);
+}
+
+int
+ctf_diff_init(ctf_file_t *ifp, ctf_file_t *ofp, ctf_diff_t **cdsp)
+{
+ ctf_diff_t *cds;
+ size_t fsize, rsize;
+
+ cds = ctf_alloc(sizeof (ctf_diff_t));
+ if (cds == NULL)
+ return (ctf_set_errno(ifp, ENOMEM));
+
+ bzero(cds, sizeof (ctf_diff_t));
+ cds->cds_ifp = ifp;
+ cds->cds_ofp = ofp;
+
+ fsize = sizeof (ctf_id_t) * ifp->ctf_typemax;
+ rsize = sizeof (ctf_id_t) * ofp->ctf_typemax;
+ if (ifp->ctf_flags & LCTF_CHILD)
+ fsize += CTF_CHILD_START * sizeof (ctf_id_t);
+ if (ofp->ctf_flags & LCTF_CHILD)
+ rsize += CTF_CHILD_START * sizeof (ctf_id_t);
+
+ cds->cds_forward = ctf_alloc(fsize);
+ if (cds->cds_forward == NULL) {
+ 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);
+
+ cds->cds_ifp->ctf_refcnt++;
+ cds->cds_ofp->ctf_refcnt++;
+ *cdsp = cds;
+ return (0);
+}
+
+int
+ctf_diff_types(ctf_diff_t *cds, ctf_diff_type_f cb, void *arg)
+{
+ int ret;
+
+ cds->cds_func = cb;
+ cds->cds_arg = arg;
+
+ ret = ctf_diff_pass1(cds, B_FALSE);
+ if (ret == 0)
+ ret = ctf_diff_pass2(cds);
+
+ cds->cds_func = NULL;
+ cds->cds_arg = NULL;
+ cds->cds_tvalid = B_TRUE;
+ 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--;
+
+ fsize = sizeof (ctf_id_t) * cds->cds_ifp->ctf_typemax;
+ rsize = sizeof (ctf_id_t) * cds->cds_ofp->ctf_typemax;
+ if (cds->cds_ifp->ctf_flags & LCTF_CHILD)
+ fsize += CTF_CHILD_START * sizeof (ctf_id_t);
+ if (cds->cds_ofp->ctf_flags & LCTF_CHILD)
+ rsize += CTF_CHILD_START * sizeof (ctf_id_t);
+
+ if (cds->cds_ifuncs != NULL)
+ ctf_free(cds->cds_ifuncs,
+ sizeof (ctf_diff_func_t) * cds->cds_nifuncs);
+ if (cds->cds_ofuncs != NULL)
+ ctf_free(cds->cds_ofuncs,
+ sizeof (ctf_diff_func_t) * cds->cds_nofuncs);
+ if (cds->cds_iobj != NULL)
+ ctf_free(cds->cds_iobj,
+ sizeof (ctf_diff_obj_t) * cds->cds_niobj);
+ if (cds->cds_oobj != NULL)
+ ctf_free(cds->cds_oobj,
+ sizeof (ctf_diff_obj_t) * cds->cds_noobj);
+ cdg = cds->cds_guess;
+ while (cdg != NULL) {
+ ctf_diff_guess_t *tofree = cdg;
+ 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));
+}
+
+uint_t
+ctf_diff_getflags(ctf_diff_t *cds)
+{
+ return (cds->cds_flags);
+}
+
+int
+ctf_diff_setflags(ctf_diff_t *cds, uint_t flags)
+{
+ if ((flags & ~CTF_DIFF_F_IGNORE_INTNAMES) != 0)
+ return (ctf_set_errno(cds->cds_ifp, EINVAL));
+
+ cds->cds_flags = flags;
+ return (0);
+}
+
+static boolean_t
+ctf_diff_symid(ctf_diff_t *cds, ctf_id_t iid, ctf_id_t oid)
+{
+ ctf_file_t *ifp, *ofp;
+
+ ifp = cds->cds_ifp;
+ ofp = cds->cds_ofp;
+
+ /*
+ * If we have parent containers on the scene here, we need to go through
+ * and do a full diff check because a diff for types will not actually
+ * go through and check types in the parent container.
+ */
+ if (iid == 0 || oid == 0)
+ return (iid == oid ? B_FALSE: B_TRUE);
+
+ if (!(ifp->ctf_flags & LCTF_CHILD) && !(ofp->ctf_flags & LCTF_CHILD)) {
+ if (cds->cds_forward[TINDEX(iid)] != oid)
+ return (B_TRUE);
+ return (B_FALSE);
+ }
+
+ return (ctf_diff_type(cds, ifp, iid, ofp, oid));
+}
+
+/* ARGSUSED */
+static void
+ctf_diff_void_cb(ctf_file_t *ifp, ctf_id_t iid, boolean_t same, ctf_file_t *ofp,
+ ctf_id_t oid, void *arg)
+{
+}
+
+/* ARGSUSED */
+static int
+ctf_diff_func_count(const char *name, ulong_t symidx, ctf_funcinfo_t *fip,
+ void *arg)
+{
+ uint32_t *ip = arg;
+
+ *ip = *ip + 1;
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+ctf_diff_func_fill_cb(const char *name, ulong_t symidx, ctf_funcinfo_t *fip,
+ void *arg)
+{
+ uint_t *next, max;
+ ctf_diff_func_t *funcptr;
+ ctf_diff_t *cds = arg;
+
+ if (cds->cds_ffillip == B_TRUE) {
+ max = cds->cds_nifuncs;
+ next = &cds->cds_nextifunc;
+ funcptr = cds->cds_ifuncs + *next;
+ } else {
+ max = cds->cds_nofuncs;
+ next = &cds->cds_nextofunc;
+ funcptr = cds->cds_ofuncs + *next;
+
+ }
+
+ VERIFY(*next < max);
+ funcptr->cdf_name = name;
+ funcptr->cdf_symidx = symidx;
+ funcptr->cdf_matchidx = ULONG_MAX;
+ *next = *next + 1;
+
+ return (0);
+}
+
+int
+ctf_diff_func_fill(ctf_diff_t *cds)
+{
+ int ret;
+ uint32_t ifcount, ofcount, idcnt, cti;
+ ulong_t i, j;
+ ctf_id_t *iids, *oids;
+
+ ifcount = 0;
+ ofcount = 0;
+ idcnt = 0;
+ iids = NULL;
+ oids = NULL;
+
+ ret = ctf_function_iter(cds->cds_ifp, ctf_diff_func_count, &ifcount);
+ if (ret != 0)
+ return (ret);
+ ret = ctf_function_iter(cds->cds_ofp, ctf_diff_func_count, &ofcount);
+ if (ret != 0)
+ return (ret);
+
+ cds->cds_ifuncs = ctf_alloc(sizeof (ctf_diff_func_t) * ifcount);
+ if (cds->cds_ifuncs == NULL)
+ return (ctf_set_errno(cds->cds_ifp, ENOMEM));
+
+ cds->cds_nifuncs = ifcount;
+ cds->cds_nextifunc = 0;
+
+ cds->cds_ofuncs = ctf_alloc(sizeof (ctf_diff_func_t) * ofcount);
+ if (cds->cds_ofuncs == NULL)
+ return (ctf_set_errno(cds->cds_ifp, ENOMEM));
+
+ cds->cds_nofuncs = ofcount;
+ cds->cds_nextofunc = 0;
+
+ cds->cds_ffillip = B_TRUE;
+ if ((ret = ctf_function_iter(cds->cds_ifp, ctf_diff_func_fill_cb,
+ cds)) != 0)
+ return (ret);
+
+ cds->cds_ffillip = B_FALSE;
+ if ((ret = ctf_function_iter(cds->cds_ofp, ctf_diff_func_fill_cb,
+ cds)) != 0)
+ return (ret);
+
+ /*
+ * Everything is initialized to not match. This could probably be faster
+ * with something that used a hash. But this part of the diff isn't used
+ * by merge.
+ */
+ for (i = 0; i < cds->cds_nifuncs; i++) {
+ for (j = 0; j < cds->cds_nofuncs; j++) {
+ ctf_diff_func_t *ifd, *ofd;
+ ctf_funcinfo_t ifip, ofip;
+ boolean_t match;
+
+ ifd = &cds->cds_ifuncs[i];
+ ofd = &cds->cds_ofuncs[j];
+ if (strcmp(ifd->cdf_name, ofd->cdf_name) != 0)
+ continue;
+
+ ret = ctf_func_info(cds->cds_ifp, ifd->cdf_symidx,
+ &ifip);
+ if (ret != 0)
+ goto out;
+ ret = ctf_func_info(cds->cds_ofp, ofd->cdf_symidx,
+ &ofip);
+ if (ret != 0) {
+ ret = ctf_set_errno(cds->cds_ifp,
+ ctf_errno(cds->cds_ofp));
+ goto out;
+ }
+
+ if (ifip.ctc_argc != ofip.ctc_argc &&
+ ifip.ctc_flags != ofip.ctc_flags)
+ continue;
+
+ /* Validate return type and arguments are the same */
+ if (ctf_diff_symid(cds, ifip.ctc_return,
+ ofip.ctc_return))
+ continue;
+
+ if (ifip.ctc_argc > idcnt) {
+ if (iids != NULL)
+ ctf_free(iids,
+ sizeof (ctf_id_t) * idcnt);
+ if (oids != NULL)
+ ctf_free(oids,
+ sizeof (ctf_id_t) * idcnt);
+ iids = oids = NULL;
+ idcnt = ifip.ctc_argc;
+ iids = ctf_alloc(sizeof (ctf_id_t) * idcnt);
+ if (iids == NULL) {
+ ret = ctf_set_errno(cds->cds_ifp,
+ ENOMEM);
+ goto out;
+ }
+ oids = ctf_alloc(sizeof (ctf_id_t) * idcnt);
+ if (iids == NULL) {
+ ret = ctf_set_errno(cds->cds_ifp,
+ ENOMEM);
+ goto out;
+ }
+ }
+
+ if ((ret = ctf_func_args(cds->cds_ifp, ifd->cdf_symidx,
+ ifip.ctc_argc, iids)) != 0)
+ goto out;
+ if ((ret = ctf_func_args(cds->cds_ofp, ofd->cdf_symidx,
+ ofip.ctc_argc, oids)) != 0)
+ goto out;
+
+ match = B_TRUE;
+ for (cti = 0; cti < ifip.ctc_argc; cti++) {
+ if (ctf_diff_symid(cds, iids[cti], oids[cti])) {
+ match = B_FALSE;
+ break;
+ }
+ }
+
+ if (match == B_FALSE)
+ continue;
+
+ ifd->cdf_matchidx = j;
+ ofd->cdf_matchidx = i;
+ break;
+ }
+ }
+
+ ret = 0;
+
+out:
+ if (iids != NULL)
+ ctf_free(iids, sizeof (ctf_id_t) * idcnt);
+ if (oids != NULL)
+ ctf_free(oids, sizeof (ctf_id_t) * idcnt);
+
+ return (ret);
+}
+
+/*
+ * In general, two functions are the same, if they have the same name and their
+ * arguments have the same types, including the return type. Like types, we
+ * basically have to do this in two passes. In the first phase we walk every
+ * type in the first container and try to find a match in the second.
+ */
+int
+ctf_diff_functions(ctf_diff_t *cds, ctf_diff_func_f cb, void *arg)
+{
+ int ret;
+ ulong_t i;
+
+ if (cds->cds_tvalid == B_FALSE) {
+ if ((ret = ctf_diff_types(cds, ctf_diff_void_cb, NULL)) != 0)
+ return (ret);
+ }
+
+ if (cds->cds_fvalid == B_FALSE) {
+ if ((ret = ctf_diff_func_fill(cds)) != 0)
+ return (ret);
+ cds->cds_fvalid = B_TRUE;
+ }
+
+ for (i = 0; i < cds->cds_nifuncs; i++) {
+ if (cds->cds_ifuncs[i].cdf_matchidx == ULONG_MAX) {
+ cb(cds->cds_ifp, cds->cds_ifuncs[i].cdf_symidx,
+ B_FALSE, NULL, ULONG_MAX, arg);
+ } else {
+ ulong_t idx = cds->cds_ifuncs[i].cdf_matchidx;
+ cb(cds->cds_ifp, cds->cds_ifuncs[i].cdf_symidx, B_TRUE,
+ cds->cds_ofp, cds->cds_ofuncs[idx].cdf_symidx, arg);
+ }
+ }
+
+ for (i = 0; i < cds->cds_nofuncs; i++) {
+ if (cds->cds_ofuncs[i].cdf_matchidx != ULONG_MAX)
+ continue;
+ cb(cds->cds_ofp, cds->cds_ofuncs[i].cdf_symidx, B_FALSE,
+ NULL, ULONG_MAX, arg);
+ }
+
+ return (0);
+}
+
+static int
+ctf_diff_obj_fill_cb(const char *name, ctf_id_t id, ulong_t symidx, void *arg)
+{
+ uint_t *next, max;
+ ctf_diff_obj_t *objptr;
+ ctf_diff_t *cds = arg;
+
+ if (cds->cds_ofillip == B_TRUE) {
+ max = cds->cds_niobj;
+ next = &cds->cds_nextiobj;
+ objptr = cds->cds_iobj + *next;
+ } else {
+ max = cds->cds_noobj;
+ next = &cds->cds_nextoobj;
+ objptr = cds->cds_oobj+ *next;
+
+ }
+
+ VERIFY(*next < max);
+ objptr->cdo_name = name;
+ objptr->cdo_symidx = symidx;
+ objptr->cdo_id = id;
+ objptr->cdo_matchidx = ULONG_MAX;
+ *next = *next + 1;
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+ctf_diff_obj_count(const char *name, ctf_id_t id, ulong_t symidx, void *arg)
+{
+ uint32_t *count = arg;
+
+ *count = *count + 1;
+
+ return (0);
+}
+
+
+static int
+ctf_diff_obj_fill(ctf_diff_t *cds)
+{
+ int ret;
+ uint32_t iocount, oocount;
+ ulong_t i, j;
+
+ iocount = 0;
+ oocount = 0;
+
+ ret = ctf_object_iter(cds->cds_ifp, ctf_diff_obj_count, &iocount);
+ if (ret != 0)
+ return (ret);
+
+ ret = ctf_object_iter(cds->cds_ofp, ctf_diff_obj_count, &oocount);
+ if (ret != 0)
+ return (ret);
+
+ cds->cds_iobj = ctf_alloc(sizeof (ctf_diff_obj_t) * iocount);
+ if (cds->cds_iobj == NULL)
+ return (ctf_set_errno(cds->cds_ifp, ENOMEM));
+ cds->cds_niobj = iocount;
+ cds->cds_nextiobj = 0;
+
+ cds->cds_oobj = ctf_alloc(sizeof (ctf_diff_obj_t) * oocount);
+ if (cds->cds_oobj == NULL)
+ return (ctf_set_errno(cds->cds_ifp, ENOMEM));
+ cds->cds_noobj = oocount;
+ cds->cds_nextoobj = 0;
+
+ cds->cds_ofillip = B_TRUE;
+ if ((ret = ctf_object_iter(cds->cds_ifp, ctf_diff_obj_fill_cb,
+ cds)) != 0)
+ return (ret);
+
+ cds->cds_ofillip = B_FALSE;
+ if ((ret = ctf_object_iter(cds->cds_ofp, ctf_diff_obj_fill_cb,
+ cds)) != 0)
+ return (ret);
+
+ for (i = 0; i < cds->cds_niobj; i++) {
+ for (j = 0; j < cds->cds_noobj; j++) {
+ ctf_diff_obj_t *id, *od;
+
+ id = &cds->cds_iobj[i];
+ od = &cds->cds_oobj[j];
+
+ if (id->cdo_name == NULL || od->cdo_name == NULL)
+ continue;
+ if (strcmp(id->cdo_name, od->cdo_name) != 0)
+ continue;
+
+ if (ctf_diff_symid(cds, id->cdo_id, od->cdo_id)) {
+ continue;
+ }
+
+ id->cdo_matchidx = j;
+ od->cdo_matchidx = i;
+ break;
+ }
+ }
+
+ return (0);
+}
+
+int
+ctf_diff_objects(ctf_diff_t *cds, ctf_diff_obj_f cb, void *arg)
+{
+ int ret;
+ ulong_t i;
+
+ if (cds->cds_tvalid == B_FALSE) {
+ if ((ret = ctf_diff_types(cds, ctf_diff_void_cb, NULL)) != 0)
+ return (ret);
+ }
+
+ if (cds->cds_ovalid == B_FALSE) {
+ if ((ret = ctf_diff_obj_fill(cds)) != 0)
+ return (ret);
+ cds->cds_ovalid = B_TRUE;
+ }
+
+ for (i = 0; i < cds->cds_niobj; i++) {
+ ctf_diff_obj_t *o = &cds->cds_iobj[i];
+
+ if (cds->cds_iobj[i].cdo_matchidx == ULONG_MAX) {
+ cb(cds->cds_ifp, o->cdo_symidx, o->cdo_id, B_FALSE,
+ NULL, ULONG_MAX, CTF_ERR, arg);
+ } else {
+ ctf_diff_obj_t *alt = &cds->cds_oobj[o->cdo_matchidx];
+ cb(cds->cds_ifp, o->cdo_symidx, o->cdo_id, B_TRUE,
+ cds->cds_ofp, alt->cdo_symidx, alt->cdo_id, arg);
+ }
+ }
+
+ for (i = 0; i < cds->cds_noobj; i++) {
+ ctf_diff_obj_t *o = &cds->cds_oobj[i];
+ if (o->cdo_matchidx != ULONG_MAX)
+ continue;
+ cb(cds->cds_ofp, o->cdo_symidx, o->cdo_id, B_FALSE, NULL,
+ ULONG_MAX, CTF_ERR, arg);
+ }
+
+ return (0);
+}
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..18be598ed9
--- /dev/null
+++ b/usr/src/lib/libctf/common/ctf_dwarf.c
@@ -0,0 +1,2995 @@
+/*
+ * 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 2018 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_cu_t's dwarf to CTF. This covers
+ * the work done in ctf_dwarf_convert_one().
+ *
+ * An individual ctf_cu_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 top-level 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_cu_t for each compilation unit
+ * 3) Add all ctf_cu_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 cu's, 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_cu_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_cu_t's to be processed in parallel by different
+ * threads.
+ *
+ * All of the ctf_cu_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 its 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_cu_t represents a single top-level DWARF die unit. While generally,
+ * the typical object file has 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 *cu_elf; /* shared libelf handle */
+ char *cu_name; /* basename of the DIE */
+ ctf_merge_t *cu_cmh; /* merge handle */
+ ctf_list_t cu_vars; /* List of variables */
+ ctf_list_t cu_funcs; /* List of functions */
+ ctf_list_t cu_bitfields; /* Bit field members */
+ Dwarf_Debug cu_dwarf; /* libdwarf handle */
+ Dwarf_Die cu_cu; /* libdwarf compilation unit */
+ Dwarf_Off cu_cuoff; /* cu's offset */
+ Dwarf_Off cu_maxoff; /* maximum offset */
+ ctf_file_t *cu_ctfp; /* output CTF file */
+ avl_tree_t cu_map; /* map die offsets to CTF types */
+ char *cu_errbuf; /* error message buffer */
+ size_t cu_errlen; /* error message buffer length */
+ size_t cu_ptrsz; /* object's pointer size */
+ boolean_t cu_bigend; /* is it big endian */
+ boolean_t cu_doweaks; /* should we convert weak symbols? */
+ uint_t cu_mach; /* machine type */
+ ctf_id_t cu_voidtid; /* void pointer */
+ ctf_id_t cu_longtid; /* id for a 'long' */
+} ctf_cu_t;
+
+static int ctf_dwarf_offset(ctf_cu_t *, Dwarf_Die, Dwarf_Off *);
+static int ctf_dwarf_convert_die(ctf_cu_t *, Dwarf_Die);
+static int ctf_dwarf_convert_type(ctf_cu_t *, Dwarf_Die, ctf_id_t *, int);
+
+static int ctf_dwarf_function_count(ctf_cu_t *, Dwarf_Die, ctf_funcinfo_t *,
+ boolean_t);
+static int ctf_dwarf_convert_fargs(ctf_cu_t *, Dwarf_Die, ctf_funcinfo_t *,
+ ctf_id_t *);
+
+typedef int (ctf_dwarf_symtab_f)(ctf_cu_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_cu_t *cup, ctf_file_t *cfp, int err, const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+ size_t off = 0;
+ ssize_t rem = cup->cu_errlen;
+ if (cfp != NULL)
+ err = ctf_errno(cfp);
+
+ if (err == ENOMEM)
+ return (err);
+
+ ret = snprintf(cup->cu_errbuf, rem, "die %s: ", cup->cu_name);
+ if (ret < 0)
+ goto err;
+ off += ret;
+ rem = MAX(rem - ret, 0);
+
+ va_start(ap, fmt);
+ ret = vsnprintf(cup->cu_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(cup->cu_errbuf + off, rem,
+ ": %s\n", ctf_errmsg(err));
+ }
+ va_end(ap);
+ return (ECTF_CONVBKERR);
+
+err:
+ cup->cu_errbuf[0] = '\0';
+ return (ECTF_CONVBKERR);
+}
+
+/*
+ * DWARF often opts 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_cu_t *cup)
+{
+ if (cup->cu_voidtid == CTF_ERR) {
+ ctf_encoding_t enc = { CTF_INT_SIGNED, 0, 0 };
+ cup->cu_voidtid = ctf_add_integer(cup->cu_ctfp, CTF_ADD_ROOT,
+ "void", &enc);
+ if (cup->cu_voidtid == CTF_ERR) {
+ (void) snprintf(cup->cu_errbuf, cup->cu_errlen,
+ "failed to create void type: %s\n",
+ ctf_errmsg(ctf_errno(cup->cu_ctfp)));
+ }
+ }
+
+ return (cup->cu_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_cu_t *cup)
+{
+ if (cup->cu_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 = cup->cu_ptrsz * 8;
+ cup->cu_longtid = ctf_add_integer(cup->cu_ctfp, CTF_ADD_NONROOT,
+ "long", &enc);
+ if (cup->cu_longtid == CTF_ERR) {
+ (void) snprintf(cup->cu_errbuf, cup->cu_errlen,
+ "failed to create long type: %s\n",
+ ctf_errmsg(ctf_errno(cup->cu_ctfp)));
+ }
+
+ }
+
+ return (cup->cu_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_cu_t *cup, 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(cup, 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 %" DW_PR_DUx "->%d\n", dwmap, off, id);
+ VERIFY(avl_find(&cup->cu_map, dwmap, &index) == NULL);
+ avl_insert(&cup->cu_map, dwmap, index);
+ return (0);
+}
+
+static int
+ctf_dwarf_attribute(ctf_cu_t *cup, 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(cup->cu_errbuf, cup->cu_errlen,
+ "failed to get attribute for type: %s\n",
+ dwarf_errmsg(derr));
+ return (ECTF_CONVBKERR);
+}
+
+static int
+ctf_dwarf_ref(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Half name, Dwarf_Off *refp)
+{
+ int ret;
+ Dwarf_Attribute attr;
+ Dwarf_Error derr;
+
+ if ((ret = ctf_dwarf_attribute(cup, die, name, &attr)) != 0)
+ return (ret);
+
+ if (dwarf_formref(attr, refp, &derr) == DW_DLV_OK) {
+ dwarf_dealloc(cup->cu_dwarf, attr, DW_DLA_ATTR);
+ return (0);
+ }
+
+ (void) snprintf(cup->cu_errbuf, cup->cu_errlen,
+ "failed to get unsigned attribute for type: %s\n",
+ dwarf_errmsg(derr));
+ return (ECTF_CONVBKERR);
+}
+
+static int
+ctf_dwarf_refdie(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Half name,
+ Dwarf_Die *diep)
+{
+ int ret;
+ Dwarf_Off off;
+ Dwarf_Error derr;
+
+ if ((ret = ctf_dwarf_ref(cup, die, name, &off)) != 0)
+ return (ret);
+
+ off += cup->cu_cuoff;
+ if ((ret = dwarf_offdie(cup->cu_dwarf, off, diep, &derr)) !=
+ DW_DLV_OK) {
+ (void) snprintf(cup->cu_errbuf, cup->cu_errlen,
+ "failed to get die from offset %" DW_PR_DUu ": %s\n",
+ off, dwarf_errmsg(derr));
+ return (ECTF_CONVBKERR);
+ }
+
+ return (0);
+}
+
+static int
+ctf_dwarf_signed(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Half name,
+ Dwarf_Signed *valp)
+{
+ int ret;
+ Dwarf_Attribute attr;
+ Dwarf_Error derr;
+
+ if ((ret = ctf_dwarf_attribute(cup, die, name, &attr)) != 0)
+ return (ret);
+
+ if (dwarf_formsdata(attr, valp, &derr) == DW_DLV_OK) {
+ dwarf_dealloc(cup->cu_dwarf, attr, DW_DLA_ATTR);
+ return (0);
+ }
+
+ (void) snprintf(cup->cu_errbuf, cup->cu_errlen,
+ "failed to get unsigned attribute for type: %s\n",
+ dwarf_errmsg(derr));
+ return (ECTF_CONVBKERR);
+}
+
+static int
+ctf_dwarf_unsigned(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Half name,
+ Dwarf_Unsigned *valp)
+{
+ int ret;
+ Dwarf_Attribute attr;
+ Dwarf_Error derr;
+
+ if ((ret = ctf_dwarf_attribute(cup, die, name, &attr)) != 0)
+ return (ret);
+
+ if (dwarf_formudata(attr, valp, &derr) == DW_DLV_OK) {
+ dwarf_dealloc(cup->cu_dwarf, attr, DW_DLA_ATTR);
+ return (0);
+ }
+
+ (void) snprintf(cup->cu_errbuf, cup->cu_errlen,
+ "failed to get unsigned attribute for type: %s\n",
+ dwarf_errmsg(derr));
+ return (ECTF_CONVBKERR);
+}
+
+static int
+ctf_dwarf_boolean(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Half name,
+ Dwarf_Bool *val)
+{
+ int ret;
+ Dwarf_Attribute attr;
+ Dwarf_Error derr;
+
+ if ((ret = ctf_dwarf_attribute(cup, die, name, &attr)) != 0)
+ return (ret);
+
+ if (dwarf_formflag(attr, val, &derr) == DW_DLV_OK) {
+ dwarf_dealloc(cup->cu_dwarf, attr, DW_DLA_ATTR);
+ return (0);
+ }
+
+ (void) snprintf(cup->cu_errbuf, cup->cu_errlen,
+ "failed to get boolean attribute for type: %s\n",
+ dwarf_errmsg(derr));
+
+ return (ECTF_CONVBKERR);
+}
+
+static int
+ctf_dwarf_string(ctf_cu_t *cup, 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(cup, 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(cup->cu_dwarf, attr, DW_DLA_ATTR);
+ return (ret);
+ }
+
+ (void) snprintf(cup->cu_errbuf, cup->cu_errlen,
+ "failed to get string attribute for type: %s\n",
+ dwarf_errmsg(derr));
+ return (ECTF_CONVBKERR);
+}
+
+static int
+ctf_dwarf_member_location(ctf_cu_t *cup, 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(cup, die, DW_AT_data_member_location,
+ &attr)) != 0)
+ return (ret);
+
+ if (dwarf_loclist(attr, &loc, &locnum, &derr) != DW_DLV_OK) {
+ (void) snprintf(cup->cu_errbuf, cup->cu_errlen,
+ "failed to obtain location list for member offset: %s",
+ dwarf_errmsg(derr));
+ dwarf_dealloc(cup->cu_dwarf, attr, DW_DLA_ATTR);
+ return (ECTF_CONVBKERR);
+ }
+ dwarf_dealloc(cup->cu_dwarf, attr, DW_DLA_ATTR);
+
+ if (locnum != 1 || loc->ld_s->lr_atom != DW_OP_plus_uconst) {
+ (void) snprintf(cup->cu_errbuf, cup->cu_errlen,
+ "failed to parse location structure for member");
+ dwarf_dealloc(cup->cu_dwarf, loc->ld_s, DW_DLA_LOC_BLOCK);
+ dwarf_dealloc(cup->cu_dwarf, loc, DW_DLA_LOCDESC);
+ return (ECTF_CONVBKERR);
+ }
+
+ *valp = loc->ld_s->lr_number;
+
+ dwarf_dealloc(cup->cu_dwarf, loc->ld_s, DW_DLA_LOC_BLOCK);
+ dwarf_dealloc(cup->cu_dwarf, loc, DW_DLA_LOCDESC);
+ return (0);
+}
+
+
+static int
+ctf_dwarf_offset(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Off *offsetp)
+{
+ Dwarf_Error derr;
+
+ if (dwarf_dieoffset(die, offsetp, &derr) == DW_DLV_OK)
+ return (0);
+
+ (void) snprintf(cup->cu_errbuf, cup->cu_errlen,
+ "failed to get die offset: %s\n",
+ dwarf_errmsg(derr));
+ return (ECTF_CONVBKERR);
+}
+
+/* simpler variant for debugging output */
+static Dwarf_Off
+ctf_die_offset(Dwarf_Die die)
+{
+ Dwarf_Off off = -1;
+ Dwarf_Error derr;
+
+ (void) dwarf_dieoffset(die, &off, &derr);
+ return (off);
+}
+
+static int
+ctf_dwarf_tag(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Half *tagp)
+{
+ Dwarf_Error derr;
+
+ if (dwarf_tag(die, tagp, &derr) == DW_DLV_OK)
+ return (0);
+
+ (void) snprintf(cup->cu_errbuf, cup->cu_errlen,
+ "failed to get tag type: %s\n",
+ dwarf_errmsg(derr));
+ return (ECTF_CONVBKERR);
+}
+
+static int
+ctf_dwarf_sib(ctf_cu_t *cup, Dwarf_Die base, Dwarf_Die *sibp)
+{
+ Dwarf_Error derr;
+ int ret;
+
+ *sibp = NULL;
+ ret = dwarf_siblingof(cup->cu_dwarf, base, sibp, &derr);
+ if (ret == DW_DLV_OK || ret == DW_DLV_NO_ENTRY)
+ return (0);
+
+ (void) snprintf(cup->cu_errbuf, cup->cu_errlen,
+ "failed to sibling from die: %s\n",
+ dwarf_errmsg(derr));
+ return (ECTF_CONVBKERR);
+}
+
+static int
+ctf_dwarf_child(ctf_cu_t *cup, 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(cup->cu_errbuf, cup->cu_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_cu_t *cup, Dwarf_Die die, boolean_t *igp)
+{
+ int ret;
+ Dwarf_Signed vis;
+ Dwarf_Bool ext;
+
+ if ((ret = ctf_dwarf_signed(cup, die, DW_AT_visibility, &vis)) == 0) {
+ *igp = vis == DW_VIS_exported;
+ return (0);
+ } else if (ret != ENOENT) {
+ return (ret);
+ }
+
+ if ((ret = ctf_dwarf_boolean(cup, 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_cu_t *cup, 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);
+ }
+
+ cup->cu_mach = ehdr.e_machine;
+
+ if (ehdr.e_ident[EI_CLASS] == ELFCLASS32) {
+ cup->cu_ptrsz = 4;
+ VERIFY(ctf_setmodel(cup->cu_ctfp, CTF_MODEL_ILP32) == 0);
+ } else if (ehdr.e_ident[EI_CLASS] == ELFCLASS64) {
+ cup->cu_ptrsz = 8;
+ VERIFY(ctf_setmodel(cup->cu_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) {
+ cup->cu_bigend = B_FALSE;
+ } else if (ehdr.e_ident[EI_DATA] == ELFDATA2MSB) {
+ cup->cu_bigend = B_TRUE;
+ } else {
+ (void) snprintf(errbuf, errlen,
+ "unknown ELF data encoding: %hhu", 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_cu_t *cup, 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 == cup->cu_mach)
+ break;
+ }
+
+ if (map->cdf_mach == EM_NONE) {
+ (void) snprintf(cup->cu_errbuf, cup->cu_errlen,
+ "Unsupported machine type: %d\n", cup->cu_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(cup->cu_errbuf, cup->cu_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_cu_t *cup, Dwarf_Die die, int *kindp,
+ ctf_encoding_t *enc)
+{
+ int ret;
+ Dwarf_Signed type;
+
+ if ((ret = ctf_dwarf_signed(cup, 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(cup, type, enc)) != 0)
+ return (ret);
+ break;
+ default:
+ (void) snprintf(cup->cu_errbuf, cup->cu_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, *last;
+ 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_r(buf, " ", &last); c != NULL;
+ c = strtok_r(NULL, " ", &last)) {
+ 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_cu_t *cup, 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(cup, die, DW_AT_name, &name)) != 0)
+ return (ret);
+ if ((ret = ctf_dwarf_unsigned(cup, 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(cup, die, &kind, &enc)) != 0)
+ return (ret);
+ }
+
+ id = ctf_add_encoded(cup->cu_ctfp, isroot, name, &enc, kind);
+ if (id == CTF_ERR) {
+ ret = ctf_errno(cup->cu_ctfp);
+ } else {
+ *idp = id;
+ ret = ctf_dwmap_add(cup, 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 have 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_cu_t *cup, Dwarf_Die die, ctf_id_t mid,
+ ulong_t *offp)
+{
+ int ret;
+ Dwarf_Unsigned loc, bitsz, bytesz;
+ Dwarf_Signed bitoff;
+ size_t off;
+ ssize_t tsz;
+
+ if ((ret = ctf_dwarf_unsigned(cup, die, DW_AT_data_bit_offset,
+ &loc)) == 0) {
+ *offp = loc;
+ return (0);
+ } else if (ret != ENOENT) {
+ return (ret);
+ }
+
+ if ((ret = ctf_dwarf_member_location(cup, die, &loc)) != 0)
+ return (ret);
+ off = loc * 8;
+
+ if ((ret = ctf_dwarf_signed(cup, 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(cup, die, DW_AT_bit_size, &bitsz)) != 0)
+ return (ret);
+
+ if ((ret = ctf_dwarf_unsigned(cup, die, DW_AT_byte_size,
+ &bytesz)) != 0) {
+ if (ret != ENOENT)
+ return (ret);
+ if ((tsz = ctf_type_size(cup->cu_ctfp, mid)) == CTF_ERR) {
+ int e = ctf_errno(cup->cu_ctfp);
+ (void) snprintf(cup->cu_errbuf, cup->cu_errlen,
+ "failed to get type size: %s", ctf_errmsg(e));
+ return (ECTF_CONVBKERR);
+ }
+ } else {
+ tsz = bytesz;
+ }
+ tsz *= 8;
+ if (cup->cu_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 need 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_cu_t *cup, 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(cup, 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(cup->cu_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(cup->cu_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 encoding 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(&cup->cu_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(cup->cu_ctfp, CTF_ADD_NONROOT,
+ name, &e);
+ if (cdb->cdb_id == CTF_ERR) {
+ (void) snprintf(cup->cu_errbuf, cup->cu_errlen,
+ "failed to get add bitfield type %s: %s", name,
+ ctf_errmsg(ctf_errno(cup->cu_ctfp)));
+ ctf_free(name, namesz + 1);
+ ctf_free(cdb, sizeof (ctf_dwbitf_t));
+ return (ECTF_CONVBKERR);
+ }
+ ctf_free(name, namesz + 1);
+ ctf_list_append(&cup->cu_bitfields, cdb);
+ }
+
+ *idp = cdb->cdb_id;
+
+ return (0);
+}
+
+static int
+ctf_dwarf_fixup_sou(ctf_cu_t *cup, 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(cup->cu_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(cup, 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(cup, memb, &tag)) != 0)
+ return (ret);
+
+ if (tag != DW_TAG_member)
+ continue;
+
+ if ((ret = ctf_dwarf_refdie(cup, memb, DW_AT_type, &tdie)) != 0)
+ return (ret);
+
+ if ((ret = ctf_dwarf_convert_type(cup, 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(cup, memb,
+ &mid)) != 0)
+ return (ret);
+ goto next;
+ }
+
+ if ((ret = ctf_dwarf_string(cup, 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(cup, memb, mid,
+ &memboff)) != 0) {
+ if (mname != NULL)
+ ctf_free(mname, strlen(mname) + 1);
+ return (ret);
+ }
+
+ if ((ret = ctf_dwarf_member_bitfield(cup, memb, &mid)) != 0)
+ return (ret);
+
+ ret = ctf_add_member(cup->cu_ctfp, base, mname, mid, memboff);
+ if (ret == CTF_ERR) {
+ (void) snprintf(cup->cu_errbuf, cup->cu_errlen,
+ "failed to add member %s: %s",
+ mname, ctf_errmsg(ctf_errno(cup->cu_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(cup, 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(cup, die, DW_AT_byte_size, &size)) != 0)
+ return (ret);
+ nsz = size;
+ if ((ctf_set_size(cup->cu_ctfp, base, nsz)) == CTF_ERR) {
+ int e = ctf_errno(cup->cu_ctfp);
+ (void) snprintf(cup->cu_errbuf, cup->cu_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_cu_t *cup, 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(cup, 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(cup, die, DW_AT_declaration,
+ &decl)) != 0) {
+ if (ret != ENOENT)
+ return (ret);
+ decl = 0;
+ }
+
+ if (decl != 0) {
+ base = ctf_add_forward(cup->cu_ctfp, isroot, name, kind);
+ } else if (kind == CTF_K_STRUCT) {
+ base = ctf_add_struct(cup->cu_ctfp, isroot, name);
+ } else {
+ base = ctf_add_union(cup->cu_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(cup->cu_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(cup, base, die, B_FALSE));
+ if ((ret = ctf_dwmap_add(cup, base, die, B_TRUE)) != 0)
+ return (ret);
+
+ /*
+ * Members are in children. However, gcc also allows empty ones.
+ */
+ if ((ret = ctf_dwarf_child(cup, die, &child)) != 0)
+ return (ret);
+ if (child == NULL)
+ return (0);
+
+ return (0);
+}
+
+static int
+ctf_dwarf_create_array_range(ctf_cu_t *cup, 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(cup, range, &sib)) != 0)
+ return (ret);
+ if (sib != NULL) {
+ ctf_id_t id;
+ if ((ret = ctf_dwarf_create_array_range(cup, 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(cup)) == CTF_ERR)
+ return (ctf_errno(cup->cu_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(cup, range, DW_AT_upper_bound,
+ &val)) == 0) {
+ ar.ctr_nelems = val + 1;
+ } else if (ret != ENOENT) {
+ return (ret);
+ } else if ((ret = ctf_dwarf_signed(cup, 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(cup->cu_ctfp, isroot, &ar)) == CTF_ERR)
+ return (ctf_errno(cup->cu_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_cu_t *cup, Dwarf_Die die, ctf_id_t *idp, int isroot)
+{
+ int ret;
+ Dwarf_Die tdie, rdie;
+ ctf_id_t tid;
+ Dwarf_Half rtag;
+
+ if ((ret = ctf_dwarf_refdie(cup, die, DW_AT_type, &tdie)) != 0)
+ return (ret);
+ if ((ret = ctf_dwarf_convert_type(cup, tdie, &tid,
+ CTF_ADD_NONROOT)) != 0)
+ return (ret);
+
+ if ((ret = ctf_dwarf_child(cup, die, &rdie)) != 0)
+ return (ret);
+ if ((ret = ctf_dwarf_tag(cup, rdie, &rtag)) != 0)
+ return (ret);
+ if (rtag != DW_TAG_subrange_type) {
+ (void) snprintf(cup->cu_errbuf, cup->cu_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(cup, rdie, idp, tid,
+ isroot)) != 0)
+ return (ret);
+ ctf_dprintf("Got back id %d\n", *idp);
+ return (ctf_dwmap_add(cup, *idp, die, B_FALSE));
+}
+
+static int
+ctf_dwarf_create_reference(ctf_cu_t *cup, 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(cup, 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(cup, die, DW_AT_type, &tdie)) != 0) {
+ if (ret != ENOENT) {
+ ctf_free(name, namelen);
+ return (ret);
+ }
+ if ((id = ctf_dwarf_void(cup)) == CTF_ERR) {
+ ctf_free(name, namelen);
+ return (ctf_errno(cup->cu_ctfp));
+ }
+ } else {
+ if ((ret = ctf_dwarf_convert_type(cup, tdie, &id,
+ CTF_ADD_NONROOT)) != 0) {
+ ctf_free(name, namelen);
+ return (ret);
+ }
+ }
+
+ if ((*idp = ctf_add_reftype(cup->cu_ctfp, isroot, name, id, kind)) ==
+ CTF_ERR) {
+ ctf_free(name, namelen);
+ return (ctf_errno(cup->cu_ctfp));
+ }
+
+ ctf_free(name, namelen);
+ return (ctf_dwmap_add(cup, *idp, die, B_FALSE));
+}
+
+static int
+ctf_dwarf_create_enum(ctf_cu_t *cup, 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(cup, die, DW_AT_name, &name)) != 0 &&
+ ret != ENOENT)
+ return (ret);
+ if (ret == ENOENT)
+ name = NULL;
+ id = ctf_add_enum(cup->cu_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(cup->cu_ctfp));
+ *idp = id;
+ if ((ret = ctf_dwmap_add(cup, id, die, B_FALSE)) != 0)
+ return (ret);
+
+ if ((ret = ctf_dwarf_child(cup, 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(cup, arg, &child)) != 0)
+ return (ret);
+
+ if ((ret = ctf_dwarf_tag(cup, arg, &tag)) != 0)
+ return (ret);
+
+ if (tag != DW_TAG_enumerator) {
+ if ((ret = ctf_dwarf_convert_type(cup, arg, NULL,
+ CTF_ADD_NONROOT)) != 0)
+ return (ret);
+ continue;
+ }
+
+ /*
+ * DWARF v4 section 5.7 tells us we'll always have names.
+ */
+ if ((ret = ctf_dwarf_string(cup, arg, DW_AT_name, &name)) != 0)
+ return (ret);
+
+ /*
+ * We have to be careful here: newer GCCs generate DWARF where
+ * an unsigned value will happily pass ctf_dwarf_signed().
+ * Since negative values will fail ctf_dwarf_unsigned(), we try
+ * that first to make sure we get the right value.
+ */
+ if ((ret = ctf_dwarf_unsigned(cup, arg, DW_AT_const_value,
+ &uval)) == 0) {
+ eval = (int)uval;
+ } else if ((ret = ctf_dwarf_signed(cup, arg, DW_AT_const_value,
+ &sval)) == 0) {
+ eval = sval;
+ }
+
+ if (ret != 0) {
+ if (ret != ENOENT)
+ return (ret);
+
+ (void) snprintf(cup->cu_errbuf, cup->cu_errlen,
+ "encountered enumeration without constant value\n");
+ return (ECTF_CONVBKERR);
+ }
+
+ ret = ctf_add_enumerator(cup->cu_ctfp, id, name, eval);
+ if (ret == CTF_ERR) {
+ (void) snprintf(cup->cu_errbuf, cup->cu_errlen,
+ "failed to add enumarator %s (%d) to %d\n",
+ name, eval, id);
+ ctf_free(name, strlen(name) + 1);
+ return (ctf_errno(cup->cu_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_cu_t *cup, 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(cup, 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(cup, die, DW_AT_type, &retdie)) != 0) {
+ if (ret != ENOENT)
+ return (ret);
+ if ((fi.ctc_return = ctf_dwarf_void(cup)) == CTF_ERR)
+ return (ctf_errno(cup->cu_ctfp));
+ } else {
+ if ((ret = ctf_dwarf_convert_type(cup, retdie, &fi.ctc_return,
+ CTF_ADD_NONROOT)) != 0)
+ return (ret);
+ }
+
+ if ((ret = ctf_dwarf_function_count(cup, 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(cup, die, &fi, argv)) != 0) {
+ ctf_free(argv, sizeof (ctf_id_t) * fi.ctc_argc);
+ return (ret);
+ }
+ }
+
+ if ((*idp = ctf_add_funcptr(cup->cu_ctfp, isroot, &fi, argv)) ==
+ CTF_ERR) {
+ ctf_free(argv, sizeof (ctf_id_t) * fi.ctc_argc);
+ return (ctf_errno(cup->cu_ctfp));
+ }
+
+ ctf_free(argv, sizeof (ctf_id_t) * fi.ctc_argc);
+ return (ctf_dwmap_add(cup, *idp, die, B_FALSE));
+}
+
+static int
+ctf_dwarf_convert_type(ctf_cu_t *cup, 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(cup, die, &offset)) != 0)
+ return (ret);
+
+ if (offset > cup->cu_maxoff) {
+ (void) snprintf(cup->cu_errbuf, cup->cu_errlen,
+ "die offset %llu beyond maximum for header %llu\n",
+ offset, cup->cu_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(&cup->cu_map, &lookup, NULL)) != NULL) {
+ *idp = map->cdm_id;
+ return (0);
+ }
+
+ if ((ret = ctf_dwarf_tag(cup, die, &tag)) != 0)
+ return (ret);
+
+ ret = ENOTSUP;
+ switch (tag) {
+ case DW_TAG_base_type:
+ ctf_dprintf("base\n");
+ ret = ctf_dwarf_create_base(cup, die, idp, isroot, offset);
+ break;
+ case DW_TAG_array_type:
+ ctf_dprintf("array\n");
+ ret = ctf_dwarf_create_array(cup, die, idp, isroot);
+ break;
+ case DW_TAG_enumeration_type:
+ ctf_dprintf("enum\n");
+ ret = ctf_dwarf_create_enum(cup, die, idp, isroot);
+ break;
+ case DW_TAG_pointer_type:
+ ctf_dprintf("pointer\n");
+ ret = ctf_dwarf_create_reference(cup, die, idp, CTF_K_POINTER,
+ isroot);
+ break;
+ case DW_TAG_structure_type:
+ ctf_dprintf("struct\n");
+ ret = ctf_dwarf_create_sou(cup, die, idp, CTF_K_STRUCT,
+ isroot);
+ break;
+ case DW_TAG_subroutine_type:
+ ctf_dprintf("fptr\n");
+ ret = ctf_dwarf_create_fptr(cup, die, idp, isroot);
+ break;
+ case DW_TAG_typedef:
+ ctf_dprintf("typedef\n");
+ ret = ctf_dwarf_create_reference(cup, die, idp, CTF_K_TYPEDEF,
+ isroot);
+ break;
+ case DW_TAG_union_type:
+ ctf_dprintf("union\n");
+ ret = ctf_dwarf_create_sou(cup, die, idp, CTF_K_UNION,
+ isroot);
+ break;
+ case DW_TAG_const_type:
+ ctf_dprintf("const\n");
+ ret = ctf_dwarf_create_reference(cup, die, idp, CTF_K_CONST,
+ isroot);
+ break;
+ case DW_TAG_volatile_type:
+ ctf_dprintf("volatile\n");
+ ret = ctf_dwarf_create_reference(cup, die, idp, CTF_K_VOLATILE,
+ isroot);
+ break;
+ case DW_TAG_restrict_type:
+ ctf_dprintf("restrict\n");
+ ret = ctf_dwarf_create_reference(cup, 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_cu_t *cup, Dwarf_Die die)
+{
+ int ret;
+ Dwarf_Die child;
+
+ if ((ret = ctf_dwarf_child(cup, die, &child)) != 0)
+ return (ret);
+
+ if (child == NULL)
+ return (0);
+
+ return (ctf_dwarf_convert_die(cup, die));
+}
+
+static int
+ctf_dwarf_function_count(ctf_cu_t *cup, Dwarf_Die die, ctf_funcinfo_t *fip,
+ boolean_t fptr)
+{
+ int ret;
+ Dwarf_Die child, sib, arg;
+
+ if ((ret = ctf_dwarf_child(cup, die, &child)) != 0)
+ return (ret);
+
+ arg = child;
+ while (arg != NULL) {
+ Dwarf_Half tag;
+
+ if ((ret = ctf_dwarf_tag(cup, 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(cup, 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(cup, arg, &sib)) != 0)
+ return (ret);
+ arg = sib;
+ }
+
+ return (0);
+}
+
+static int
+ctf_dwarf_convert_fargs(ctf_cu_t *cup, 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(cup, die, &child)) != 0)
+ return (ret);
+
+ arg = child;
+ while (arg != NULL) {
+ Dwarf_Half tag;
+
+ if ((ret = ctf_dwarf_tag(cup, arg, &tag)) != 0)
+ return (ret);
+ if (tag == DW_TAG_formal_parameter) {
+ Dwarf_Die tdie;
+
+ if ((ret = ctf_dwarf_refdie(cup, arg, DW_AT_type,
+ &tdie)) != 0)
+ return (ret);
+
+ if ((ret = ctf_dwarf_convert_type(cup, 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
+ * last entry.
+ */
+ if (i == fip->ctc_argc)
+ break;
+ }
+
+ if ((ret = ctf_dwarf_sib(cup, arg, &sib)) != 0)
+ return (ret);
+ arg = sib;
+ }
+
+ return (0);
+}
+
+static int
+ctf_dwarf_convert_function(ctf_cu_t *cup, 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(cup, 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(cup, die, DW_AT_type, &tdie)) == 0) {
+ if ((ret = ctf_dwarf_convert_type(cup, 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(cup)) ==
+ CTF_ERR) {
+ ctf_free(name, strlen(name) + 1);
+ ctf_free(cdf, sizeof (ctf_dwfunc_t));
+ return (ctf_errno(cup->cu_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(cup, 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(cup, 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(cup, 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(&cup->cu_funcs, cdf);
+ return (ret);
+}
+
+/*
+ * Convert variables, but only if they're not prototypes and have names.
+ */
+static int
+ctf_dwarf_convert_variable(ctf_cu_t *cup, Dwarf_Die die)
+{
+ int ret;
+ char *name;
+ Dwarf_Bool b;
+ Dwarf_Die tdie;
+ ctf_id_t id;
+ ctf_dwvar_t *cdv;
+
+ /* Skip "Non-Defining Declarations" */
+ if ((ret = ctf_dwarf_boolean(cup, die, DW_AT_declaration, &b)) == 0) {
+ if (b != 0)
+ return (0);
+ } else if (ret != ENOENT) {
+ return (ret);
+ }
+
+ /*
+ * If we find a DIE of "Declarations Completing Non-Defining
+ * Declarations", we will use the referenced type's DIE. This isn't
+ * quite correct, e.g. DW_AT_decl_line will be the forward declaration
+ * not this site. It's sufficient for what we need, however: in
+ * particular, we should find DW_AT_external as needed there.
+ */
+ if ((ret = ctf_dwarf_refdie(cup, die, DW_AT_specification,
+ &tdie)) == 0) {
+ Dwarf_Off offset;
+ if ((ret = ctf_dwarf_offset(cup, tdie, &offset)) != 0)
+ return (ret);
+ ctf_dprintf("die 0x%llx DW_AT_specification -> die 0x%llx\n",
+ ctf_die_offset(die), ctf_die_offset(tdie));
+ die = tdie;
+ } else if (ret != ENOENT) {
+ return (ret);
+ }
+
+ if ((ret = ctf_dwarf_string(cup, die, DW_AT_name, &name)) != 0 &&
+ ret != ENOENT)
+ return (ret);
+ if (ret == ENOENT)
+ return (0);
+
+ if ((ret = ctf_dwarf_refdie(cup, die, DW_AT_type, &tdie)) != 0) {
+ ctf_free(name, strlen(name) + 1);
+ return (ret);
+ }
+
+ if ((ret = ctf_dwarf_convert_type(cup, 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(cup, die, &cdv->cdv_global)) != 0) {
+ ctf_free(cdv, sizeof (ctf_dwvar_t));
+ ctf_free(name, strlen(name) + 1);
+ return (ret);
+ }
+
+ ctf_list_append(&cup->cu_vars, cdv);
+ return (0);
+}
+
+/*
+ * Walk through our set of top-level types and process them.
+ */
+static int
+ctf_dwarf_walk_toplevel(ctf_cu_t *cup, Dwarf_Die die)
+{
+ int ret;
+ Dwarf_Off offset;
+ Dwarf_Half tag;
+
+ if ((ret = ctf_dwarf_offset(cup, die, &offset)) != 0)
+ return (ret);
+
+ if (offset > cup->cu_maxoff) {
+ (void) snprintf(cup->cu_errbuf, cup->cu_errlen,
+ "die offset %llu beyond maximum for header %llu\n",
+ offset, cup->cu_maxoff);
+ return (ECTF_CONVBKERR);
+ }
+
+ if ((ret = ctf_dwarf_tag(cup, die, &tag)) != 0)
+ return (ret);
+
+ ret = 0;
+ switch (tag) {
+ case DW_TAG_subprogram:
+ ctf_dprintf("top level func\n");
+ ret = ctf_dwarf_convert_function(cup, die);
+ break;
+ case DW_TAG_variable:
+ ctf_dprintf("top level var\n");
+ ret = ctf_dwarf_convert_variable(cup, die);
+ break;
+ case DW_TAG_lexical_block:
+ ctf_dprintf("top level block\n");
+ ret = ctf_dwarf_walk_lexical(cup, 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(cup, 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_cu_t *cup, Dwarf_Die die)
+{
+ while (die != NULL) {
+ int ret;
+ Dwarf_Die sib;
+
+ if ((ret = ctf_dwarf_walk_toplevel(cup, die)) != 0)
+ return (ret);
+
+ if ((ret = ctf_dwarf_sib(cup, die, &sib)) != 0)
+ return (ret);
+ die = sib;
+ }
+ return (0);
+}
+
+static int
+ctf_dwarf_fixup_die(ctf_cu_t *cup, boolean_t addpass)
+{
+ ctf_dwmap_t *map;
+
+ for (map = avl_first(&cup->cu_map); map != NULL;
+ map = AVL_NEXT(&cup->cu_map, map)) {
+ int ret;
+ if (map->cdm_fix == B_FALSE)
+ continue;
+ if ((ret = ctf_dwarf_fixup_sou(cup, map->cdm_die, map->cdm_id,
+ addpass)) != 0)
+ return (ret);
+ }
+
+ return (0);
+}
+
+static ctf_dwfunc_t *
+ctf_dwarf_match_func(ctf_cu_t *cup, 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 || cup->cu_name == NULL))
+ return (NULL);
+
+ for (cdf = ctf_list_next(&cup->cu_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, cup->cu_name) != 0)
+ continue;
+ return (cdf);
+ }
+
+ return (NULL);
+}
+static ctf_dwvar_t *
+ctf_dwarf_match_var(ctf_cu_t *cup, 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 || cup->cu_name == NULL))
+ return (NULL);
+ ctf_dprintf("Still considering %s\n", name);
+
+ for (cdv = ctf_list_next(&cup->cu_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, cup->cu_name) != 0)
+ continue;
+ return (cdv);
+ }
+
+ return (NULL);
+}
+
+static int
+ctf_dwarf_symtab_iter(ctf_cu_t *cup, ctf_dwarf_symtab_f *func, void *arg)
+{
+ int ret;
+ ulong_t i;
+ ctf_file_t *fp = cup->cu_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(cup, gsymp, i, file, name, arg);
+ if (ret != 0)
+ return (ret);
+ }
+
+ return (0);
+}
+
+static int
+ctf_dwarf_conv_funcvars_cb(ctf_cu_t *cup, 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(cup, file, name,
+ bind);
+ ctf_dprintf("match for %s (%d): %p\n", name, idx, cdv);
+ if (cdv == NULL)
+ return (0);
+ ret = ctf_add_object(cup->cu_ctfp, idx, cdv->cdv_type);
+ ctf_dprintf("added object %s\n", name);
+ } else {
+ ctf_dwfunc_t *cdf = ctf_dwarf_match_func(cup, file, name,
+ bind);
+ if (cdf == NULL)
+ return (0);
+ ret = ctf_add_function(cup->cu_ctfp, idx, &cdf->cdf_fip,
+ cdf->cdf_argv);
+ }
+
+ if (ret == CTF_ERR) {
+ return (ctf_errno(cup->cu_ctfp));
+ }
+
+ return (0);
+}
+
+static int
+ctf_dwarf_conv_funcvars(ctf_cu_t *cup)
+{
+ return (ctf_dwarf_symtab_iter(cup, ctf_dwarf_conv_funcvars_cb, NULL));
+}
+
+/*
+ * 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)
+ *
+ * To perform this check, we first iterate over the symbol table. For each weak
+ * symbol that we encounter, we then do a second walk over the symbol table,
+ * calling ctf_dwarf_conv_check_weak(). If a symbol matches the above, then it's
+ * either a local or global symbol. If we find a global symbol then we go with
+ * it and stop searching for additional matches.
+ *
+ * If instead, we find a local symbol, things are more complicated. The first
+ * thing we do is to try and see if we have file information about both symbols
+ * (STT_FILE). If they both have file information and it matches, then we treat
+ * that as a good match and stop searching for additional matches.
+ *
+ * Otherwise, this means we have a non-matching file and a local symbol. We
+ * treat this as a candidate and if we find a better match (one of the two cases
+ * above), use that instead. There are two different ways this can happen.
+ * Either this is a completely different symbol, or it's a once-global symbol
+ * that was scoped to local via a mapfile. In the former 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_cu_t *cup, 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_cu_t *cup, ulong_t idx, ulong_t matchidx)
+{
+ ctf_id_t id = ctf_lookup_by_symbol(cup->cu_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(cup->cu_ctfp) == ECTF_NOTYPEDAT)
+ return (0);
+ else
+ return (ctf_errno(cup->cu_ctfp));
+ }
+
+ if (ctf_add_object(cup->cu_ctfp, idx, id) == CTF_ERR)
+ return (ctf_errno(cup->cu_ctfp));
+
+ return (0);
+}
+
+static int
+ctf_dwarf_duplicate_func(ctf_cu_t *cup, ulong_t idx, ulong_t matchidx)
+{
+ int ret;
+ ctf_funcinfo_t fip;
+ ctf_id_t *args = NULL;
+
+ if (ctf_func_info(cup->cu_ctfp, matchidx, &fip) == CTF_ERR) {
+ if (ctf_errno(cup->cu_ctfp) == ECTF_NOFUNCDAT)
+ return (0);
+ else
+ return (ctf_errno(cup->cu_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(cup->cu_ctfp, matchidx, fip.ctc_argc, args) ==
+ CTF_ERR) {
+ ctf_free(args, sizeof (ctf_id_t) * fip.ctc_argc);
+ return (ctf_errno(cup->cu_ctfp));
+ }
+ }
+
+ ret = ctf_add_function(cup->cu_ctfp, idx, &fip, args);
+ if (args != NULL)
+ ctf_free(args, sizeof (ctf_id_t) * fip.ctc_argc);
+ if (ret == CTF_ERR)
+ return (ctf_errno(cup->cu_ctfp));
+
+ return (0);
+}
+
+static int
+ctf_dwarf_conv_weaks_cb(ctf_cu_t *cup, 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(cup, 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(cup, idx, cweak.cweak_idx);
+ } else {
+ ret = ctf_dwarf_duplicate_func(cup, idx, cweak.cweak_idx);
+ }
+
+ return (ret);
+}
+
+static int
+ctf_dwarf_conv_weaks(ctf_cu_t *cup)
+{
+ return (ctf_dwarf_symtab_iter(cup, ctf_dwarf_conv_weaks_cb, NULL));
+}
+
+/* ARGSUSED */
+static int
+ctf_dwarf_convert_one(void *arg, void *unused)
+{
+ int ret;
+ ctf_file_t *dedup;
+ ctf_cu_t *cup = arg;
+
+ ctf_dprintf("converting die: %s\n", cup->cu_name);
+ ctf_dprintf("max offset: %x\n", cup->cu_maxoff);
+ VERIFY(cup != NULL);
+
+ ret = ctf_dwarf_convert_die(cup, cup->cu_cu);
+ ctf_dprintf("ctf_dwarf_convert_die (%s) returned %d\n", cup->cu_name,
+ ret);
+ if (ret != 0) {
+ return (ret);
+ }
+ if (ctf_update(cup->cu_ctfp) != 0) {
+ return (ctf_dwarf_error(cup, cup->cu_ctfp, 0,
+ "failed to update output ctf container"));
+ }
+
+ ret = ctf_dwarf_fixup_die(cup, B_FALSE);
+ ctf_dprintf("ctf_dwarf_fixup_die (%s) returned %d\n", cup->cu_name,
+ ret);
+ if (ret != 0) {
+ return (ret);
+ }
+ if (ctf_update(cup->cu_ctfp) != 0) {
+ return (ctf_dwarf_error(cup, cup->cu_ctfp, 0,
+ "failed to update output ctf container"));
+ }
+
+ ret = ctf_dwarf_fixup_die(cup, B_TRUE);
+ ctf_dprintf("ctf_dwarf_fixup_die (%s) returned %d\n", cup->cu_name,
+ ret);
+ if (ret != 0) {
+ return (ret);
+ }
+ if (ctf_update(cup->cu_ctfp) != 0) {
+ return (ctf_dwarf_error(cup, cup->cu_ctfp, 0,
+ "failed to update output ctf container"));
+ }
+
+
+ if ((ret = ctf_dwarf_conv_funcvars(cup)) != 0) {
+ return (ctf_dwarf_error(cup, NULL, ret,
+ "failed to convert strong functions and variables"));
+ }
+
+ if (ctf_update(cup->cu_ctfp) != 0) {
+ return (ctf_dwarf_error(cup, cup->cu_ctfp, 0,
+ "failed to update output ctf container"));
+ }
+
+ if (cup->cu_doweaks == B_TRUE) {
+ if ((ret = ctf_dwarf_conv_weaks(cup)) != 0) {
+ return (ctf_dwarf_error(cup, NULL, ret,
+ "failed to convert weak functions and variables"));
+ }
+
+ if (ctf_update(cup->cu_ctfp) != 0) {
+ return (ctf_dwarf_error(cup, cup->cu_ctfp, 0,
+ "failed to update output ctf container"));
+ }
+ }
+
+ ctf_phase_dump(cup->cu_ctfp, "pre-dedup");
+ ctf_dprintf("adding inputs for dedup\n");
+ if ((ret = ctf_merge_add(cup->cu_cmh, cup->cu_ctfp)) != 0) {
+ return (ctf_dwarf_error(cup, NULL, ret,
+ "failed to add inputs for merge"));
+ }
+
+ ctf_dprintf("starting merge\n");
+ if ((ret = ctf_merge_dedup(cup->cu_cmh, &dedup)) != 0) {
+ return (ctf_dwarf_error(cup, NULL, ret,
+ "failed to deduplicate die"));
+ }
+ ctf_close(cup->cu_ctfp);
+ cup->cu_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_cu_t *cup)
+{
+ 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", cup);
+ cup->cu_elf = NULL;
+ ctf_dprintf("Trying to free name: %p\n", cup->cu_name);
+ if (cup->cu_name != NULL)
+ ctf_free(cup->cu_name, strlen(cup->cu_name) + 1);
+ ctf_dprintf("Trying to free merge handle: %p\n", cup->cu_cmh);
+ if (cup->cu_cmh != NULL) {
+ ctf_merge_fini(cup->cu_cmh);
+ cup->cu_cmh = NULL;
+ }
+
+ ctf_dprintf("Trying to free functions\n");
+ for (cdf = ctf_list_next(&cup->cu_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(&cup->cu_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(&cup->cu_bitfields); cdb != NULL; cdb = ndb) {
+ ndb = ctf_list_next(cdb);
+ ctf_free(cdb, sizeof (ctf_dwbitf_t));
+ }
+
+ ctf_dprintf("Trying to clean up dwarf_t: %p\n", cup->cu_dwarf);
+ (void) dwarf_finish(cup->cu_dwarf, &derr);
+ cup->cu_dwarf = NULL;
+ ctf_close(cup->cu_ctfp);
+
+ cookie = NULL;
+ while ((map = avl_destroy_nodes(&cup->cu_map, &cookie)) != NULL) {
+ ctf_free(map, sizeof (ctf_dwmap_t));
+ }
+ avl_destroy(&cup->cu_map);
+ cup->cu_errbuf = NULL;
+}
+
+static void
+ctf_dwarf_free_dies(ctf_cu_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_cu_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);
+}
+
+static int
+ctf_dwarf_init_die(int fd, Elf *elf, ctf_cu_t *cup, 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(cup->cu_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.
+ */
+ cup->cu_voidtid = CTF_ERR;
+ cup->cu_longtid = CTF_ERR;
+ cup->cu_elf = elf;
+ cup->cu_maxoff = nexthdr - 1;
+ cup->cu_ctfp = ctf_fdcreate(fd, &ret);
+ if (cup->cu_ctfp == NULL) {
+ ctf_free(cup, sizeof (ctf_cu_t));
+ return (ret);
+ }
+ avl_create(&cup->cu_map, ctf_dwmap_comp, sizeof (ctf_dwmap_t),
+ offsetof(ctf_dwmap_t, cdm_avl));
+ cup->cu_errbuf = errbuf;
+ cup->cu_errlen = errlen;
+ bzero(&cup->cu_vars, sizeof (ctf_list_t));
+ bzero(&cup->cu_funcs, sizeof (ctf_list_t));
+ bzero(&cup->cu_bitfields, sizeof (ctf_list_t));
+
+ if ((ret = ctf_dwarf_die_elfenc(elf, cup, errbuf,
+ errlen)) != 0) {
+ avl_destroy(&cup->cu_map);
+ ctf_free(cup, sizeof (ctf_cu_t));
+ return (ret);
+ }
+
+ if ((ret = ctf_dwarf_sib(cup, NULL, &cu)) != 0) {
+ avl_destroy(&cup->cu_map);
+ ctf_free(cup, sizeof (ctf_cu_t));
+ return (ret);
+ }
+ if (cu == NULL) {
+ (void) snprintf(errbuf, errlen,
+ "file does not contain DWARF data\n");
+ avl_destroy(&cup->cu_map);
+ ctf_free(cup, sizeof (ctf_cu_t));
+ return (ECTF_CONVBKERR);
+ }
+
+ if ((ret = ctf_dwarf_child(cup, cu, &child)) != 0) {
+ avl_destroy(&cup->cu_map);
+ ctf_free(cup, sizeof (ctf_cu_t));
+ return (ret);
+ }
+ if (child == NULL) {
+ (void) snprintf(errbuf, errlen,
+ "file does not contain DWARF data\n");
+ avl_destroy(&cup->cu_map);
+ ctf_free(cup, sizeof (ctf_cu_t));
+ return (ECTF_CONVBKERR);
+ }
+
+ cup->cu_cuoff = offset;
+ cup->cu_cu = child;
+
+ if ((cup->cu_cmh = ctf_merge_init(fd, &ret)) == NULL) {
+ avl_destroy(&cup->cu_map);
+ ctf_free(cup, sizeof (ctf_cu_t));
+ return (ret);
+ }
+
+ if (ctf_dwarf_string(cup, cu, DW_AT_name, &name) == 0) {
+ size_t len = strlen(name) + 1;
+ char *b = basename(name);
+ cup->cu_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_cu_t *cdies = NULL, *cup;
+ 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) {
+ /*
+ * We may want to expect DWARF data here and fail conversion if
+ * it's missing. 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);
+ }
+
+ /*
+ * Iterate over all of the compilation units and create a ctf_cu_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.
+ */
+ 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_cu_t) * ndies);
+ if (cdies == NULL) {
+ *errp = ENOMEM;
+ return (CTF_CONV_ERROR);
+ }
+
+ for (i = 0; i < ndies; i++) {
+ cup = &cdies[i];
+ ret = dwarf_elf_init(elf, DW_DLC_READ, NULL, NULL,
+ &cup->cu_dwarf, &derr);
+ if (ret != 0) {
+ ctf_free(cdies, sizeof (ctf_cu_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;
+ }
+ cup->cu_doweaks = ndies > 1 ? B_FALSE : B_TRUE;
+ }
+
+ ctf_dprintf("found %d DWARF die(s)\n", ndies);
+
+ /*
+ * If we only have one compilation unit, 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++) {
+ cup = &cdies[i];
+ ctf_dprintf("adding die %s: %p, %x %x\n", cup->cu_name,
+ cup->cu_cu, cup->cu_cuoff, cup->cu_maxoff);
+ if (workq_add(wqp, cup) == -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++) {
+ cup = &cdies[i];
+ if ((ret = ctf_merge_add(cmp, cup->cu_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->cu_ctfp;
+ cdies->cu_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
new file mode 100644
index 0000000000..7f668fe528
--- /dev/null
+++ b/usr/src/lib/libctf/common/ctf_elfwrite.c
@@ -0,0 +1,422 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * Copyright (c) 2015, Joyent, Inc.
+ */
+
+/*
+ * Routines for writing ctf data to elf files.
+ */
+
+#include <libctf_impl.h>
+#include <libctf.h>
+#include <gelf.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <libelf.h>
+
+static int
+ctf_write_elf(ctf_file_t *fp, Elf *src, Elf *dst, int flags)
+{
+ GElf_Ehdr sehdr, dehdr;
+ Elf_Scn *sscn, *dscn;
+ Elf_Data *sdata, *ddata;
+ GElf_Shdr shdr;
+ int symtab_idx = -1;
+ off_t new_offset = 0;
+ off_t ctfnameoff = 0;
+ int compress = (flags & CTF_ELFWRITE_F_COMPRESS);
+ 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) {
+ ret = ctf_set_errno(fp, EINVAL);
+ goto out;
+ }
+
+ 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) {
+ 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) {
+ 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
+ * existing) are SHF_ALLOC'd, so they won't be in areas referenced by
+ * program headers. As such, we can just blindly copy the program
+ * headers from the existing file to the new file.
+ */
+ if (nphdr != 0) {
+ (void) elf_flagelf(dst, ELF_C_SET, ELF_F_LAYOUT);
+ 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) {
+ 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;
+ }
+ }
+ }
+
+ secxlate = ctf_alloc(sizeof (int) * nshdr);
+ for (srcidx = dstidx = 0; srcidx < nshdr; srcidx++) {
+ Elf_Scn *scn = elf_getscn(src, srcidx);
+ GElf_Shdr shdr;
+ char *sname;
+
+ if (gelf_getshdr(scn, &shdr) == NULL) {
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
+ }
+ sname = elf_strptr(src, strndx, shdr.sh_name);
+ if (sname == NULL) {
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
+ }
+
+ if (strcmp(sname, CTF_ELF_SCN_NAME) == 0) {
+ secxlate[srcidx] = -1;
+ } else {
+ secxlate[srcidx] = dstidx++;
+ curnmoff += strlen(sname) + 1;
+ }
+
+ new_offset = (off_t)dehdr.e_phoff;
+ }
+
+ for (srcidx = 1; srcidx < nshdr; srcidx++) {
+ char *sname;
+
+ sscn = elf_getscn(src, srcidx);
+ if (gelf_getshdr(sscn, &shdr) == NULL) {
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
+ }
+
+ if (secxlate[srcidx] == -1) {
+ changing = 1;
+ continue;
+ }
+
+ dscn = elf_newscn(dst);
+ if (dscn == NULL) {
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
+ }
+
+ /*
+ * If this file has program headers, we need to explicitly lay
+ * out sections. If none of the sections prior to this one have
+ * been removed, then we can just use the existing location. If
+ * one or more sections have been changed, then we need to
+ * adjust this one to avoid holes.
+ */
+ if (changing && nphdr != 0) {
+ pad = new_offset % shdr.sh_addralign;
+
+ if (pad != 0)
+ new_offset += shdr.sh_addralign - pad;
+ shdr.sh_offset = new_offset;
+ }
+
+ shdr.sh_link = secxlate[shdr.sh_link];
+
+ if (shdr.sh_type == SHT_REL || shdr.sh_type == SHT_RELA)
+ shdr.sh_info = secxlate[shdr.sh_info];
+
+ sname = elf_strptr(src, strndx, shdr.sh_name);
+ if (sname == NULL) {
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
+ }
+ if ((sdata = elf_getdata(sscn, NULL)) == NULL) {
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
+ }
+ if ((ddata = elf_newdata(dscn)) == NULL) {
+ 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);
+
+ strdatasz = ddata->d_size + shdr.sh_size +
+ seclen + 1;
+ ddata->d_buf = strdatabuf = ctf_alloc(strdatasz);
+ if (ddata->d_buf == NULL) {
+ 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,
+ CTF_ELF_SCN_NAME);
+ ctfnameoff = (off_t)shdr.sh_size;
+ shdr.sh_size += seclen + 1;
+ ddata->d_size += seclen + 1;
+
+ if (nphdr != 0)
+ changing = 1;
+ }
+
+ if (shdr.sh_type == SHT_SYMTAB && shdr.sh_entsize != 0) {
+ int nsym = shdr.sh_size / shdr.sh_entsize;
+
+ symtab_idx = secxlate[srcidx];
+
+ symdatasz = shdr.sh_size;
+ ddata->d_buf = symdatabuf = ctf_alloc(symdatasz);
+ if (ddata->d_buf == NULL) {
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
+ }
+ (void) bcopy(sdata->d_buf, ddata->d_buf, shdr.sh_size);
+
+ for (i = 0; i < nsym; i++) {
+ GElf_Sym sym;
+ short newscn;
+
+ (void) gelf_getsym(ddata, i, &sym);
+
+ if (sym.st_shndx >= SHN_LORESERVE)
+ continue;
+
+ if ((newscn = secxlate[sym.st_shndx]) !=
+ sym.st_shndx) {
+ sym.st_shndx =
+ (newscn == -1 ? 1 : newscn);
+
+ if (gelf_update_sym(ddata, i, &sym) ==
+ 0) {
+ ret = ctf_set_errno(fp,
+ ECTF_ELF);
+ goto out;
+ }
+ }
+ }
+ }
+
+ if (gelf_update_shdr(dscn, &shdr) == NULL) {
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
+ }
+
+ new_offset = (off_t)shdr.sh_offset;
+ if (shdr.sh_type != SHT_NOBITS)
+ new_offset += shdr.sh_size;
+ }
+
+ if (symtab_idx == -1) {
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
+ }
+
+ /* Add the ctf section */
+ if ((dscn = elf_newscn(dst)) == NULL) {
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
+ }
+ if (gelf_getshdr(dscn, &shdr) == NULL) {
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
+ }
+ shdr.sh_name = ctfnameoff;
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_size = fp->ctf_size;
+ shdr.sh_link = symtab_idx;
+ shdr.sh_addralign = 4;
+ if (changing && nphdr != 0) {
+ pad = new_offset % shdr.sh_addralign;
+
+ if (pad)
+ new_offset += shdr.sh_addralign - pad;
+
+ shdr.sh_offset = new_offset;
+ new_offset += shdr.sh_size;
+ }
+
+ if ((ddata = elf_newdata(dscn)) == NULL) {
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
+ }
+
+ if (compress != 0) {
+ int err;
+
+ if (ctf_zopen(&err) == NULL) {
+ ret = ctf_set_errno(fp, err);
+ goto out;
+ }
+
+ if ((err = ctf_compress(fp, &cdata, &asize, &elfsize)) != 0) {
+ ret = ctf_set_errno(fp, err);
+ goto out;
+ }
+ ddata->d_buf = cdata;
+ ddata->d_size = elfsize;
+ } else {
+ ddata->d_buf = (void *)fp->ctf_base;
+ ddata->d_size = fp->ctf_size;
+ }
+ ddata->d_align = shdr.sh_addralign;
+
+ if (gelf_update_shdr(dscn, &shdr) == 0) {
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
+ }
+
+ /* update the section header location */
+ if (nphdr != 0) {
+ size_t align = gelf_fsize(dst, ELF_T_ADDR, 1, EV_CURRENT);
+ size_t r = new_offset % align;
+
+ if (r)
+ new_offset += align - r;
+
+ dehdr.e_shoff = new_offset;
+ }
+
+ /* commit to disk */
+ if (sehdr.e_shstrndx == SHN_XINDEX)
+ dehdr.e_shstrndx = SHN_XINDEX;
+ else
+ dehdr.e_shstrndx = secxlate[sehdr.e_shstrndx];
+ if (gelf_update_ehdr(dst, &dehdr) == NULL) {
+ ret = ctf_set_errno(fp, ECTF_ELF);
+ goto out;
+ }
+ if (elf_update(dst, ELF_C_WRITE) < 0) {
+ 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);
+ if (secxlate != NULL)
+ ctf_free(secxlate, sizeof (int) * nshdr);
+
+ return (ret);
+}
+
+int
+ctf_elffdwrite(ctf_file_t *fp, int ifd, int ofd, int flags)
+{
+ int ret;
+ Elf *ielf, *oelf;
+
+ (void) elf_version(EV_CURRENT);
+ if ((ielf = elf_begin(ifd, ELF_C_READ, NULL)) == NULL)
+ return (ctf_set_errno(fp, ECTF_ELF));
+
+ if ((oelf = elf_begin(ofd, ELF_C_WRITE, NULL)) == NULL)
+ return (ctf_set_errno(fp, ECTF_ELF));
+
+ ret = ctf_write_elf(fp, ielf, oelf, flags);
+
+ (void) elf_end(ielf);
+ (void) elf_end(oelf);
+
+ return (ret);
+}
+
+int
+ctf_elfwrite(ctf_file_t *fp, const char *input, const char *output, int flags)
+{
+ struct stat st;
+ int ifd, ofd, ret;
+
+ if ((ifd = open(input, O_RDONLY)) < 0)
+ return (ctf_set_errno(fp, errno));
+
+ if (fstat(ifd, &st) < 0)
+ return (ctf_set_errno(fp, errno));
+
+ if ((ofd = open(output, O_RDWR | O_CREAT | O_TRUNC, st.st_mode)) < 0)
+ return (ctf_set_errno(fp, errno));
+
+ ret = ctf_elffdwrite(fp, ifd, ofd, flags);
+
+ if (close(ifd) != 0 && ret == 0)
+ ret = ctf_set_errno(fp, errno);
+ if (close(ofd) != 0 && ret == 0)
+ ret = ctf_set_errno(fp, errno);
+
+ return (ret);
+}
diff --git a/usr/src/lib/libctf/common/ctf_lib.c b/usr/src/lib/libctf/common/ctf_lib.c
index e71ebc6d9d..e183b15f52 100644
--- a/usr/src/lib/libctf/common/ctf_lib.c
+++ b/usr/src/lib/libctf/common/ctf_lib.c
@@ -23,6 +23,9 @@
* Copyright 2003 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
+/*
+ * Copyright (c) 2015, Joyent, Inc.
+ */
#include <sys/types.h>
#include <sys/stat.h>
@@ -33,6 +36,8 @@
#include <errno.h>
#include <dlfcn.h>
#include <gelf.h>
+#include <zlib.h>
+#include <sys/debug.h>
#ifdef _LP64
static const char *_libctf_zlib = "/usr/lib/64/libz.so.1";
@@ -42,6 +47,9 @@ static const char *_libctf_zlib = "/usr/lib/libz.so.1";
static struct {
int (*z_uncompress)(uchar_t *, ulong_t *, const uchar_t *, ulong_t);
+ int (*z_initcomp)(z_stream *, int, const char *, int);
+ int (*z_compress)(z_stream *, int);
+ int (*z_finicomp)(z_stream *);
const char *(*z_error)(int);
void *z_dlp;
} zlib;
@@ -49,6 +57,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)
@@ -84,9 +104,14 @@ ctf_zopen(int *errp)
return (ctf_set_open_errno(errp, ECTF_ZINIT));
zlib.z_uncompress = (int (*)()) dlsym(zlib.z_dlp, "uncompress");
+ zlib.z_initcomp = (int (*)()) dlsym(zlib.z_dlp, "deflateInit_");
+ zlib.z_compress = (int (*)()) dlsym(zlib.z_dlp, "deflate");
+ zlib.z_finicomp = (int (*)()) dlsym(zlib.z_dlp, "deflateEnd");
zlib.z_error = (const char *(*)()) dlsym(zlib.z_dlp, "zError");
- if (zlib.z_uncompress == NULL || zlib.z_error == NULL) {
+ if (zlib.z_uncompress == NULL || zlib.z_error == NULL ||
+ zlib.z_initcomp == NULL|| zlib.z_compress == NULL ||
+ zlib.z_finicomp == NULL) {
(void) dlclose(zlib.z_dlp);
bzero(&zlib, sizeof (zlib));
return (ctf_set_open_errno(errp, ECTF_ZINIT));
@@ -111,6 +136,206 @@ z_strerror(int err)
return (zlib.z_error(err));
}
+static int
+ctf_zdata_init(ctf_zdata_t *czd, ctf_file_t *fp)
+{
+ 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 (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 -
+ ((uintptr_t)czd->czd_next - (uintptr_t)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 between our two phases such that we
+ * maintain a different dictionary between 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 - (uintptr_t)czd.czd_buf;
+
+ return (0);
+}
+
+int
+z_compress(void *dst, size_t *dstlen, const void *src, size_t srclen)
+{
+ z_stream zs;
+ int err;
+
+ bzero(&zs, sizeof (z_stream));
+ zs.next_in = (uchar_t *)src;
+ zs.avail_in = srclen;
+ zs.next_out = dst;
+ zs.avail_out = *dstlen;
+
+ if ((err = zlib.z_initcomp(&zs, Z_BEST_COMPRESSION, ZLIB_VERSION,
+ sizeof (z_stream))) != Z_OK)
+ return (err);
+
+ if ((err = zlib.z_compress(&zs, Z_FINISH)) != Z_STREAM_END) {
+ (void) zlib.z_finicomp(&zs);
+ return (err == Z_OK ? Z_BUF_ERROR : err);
+ }
+
+ *dstlen = zs.total_out;
+ return (zlib.z_finicomp(&zs));
+}
+
/*
* Convert a 32-bit ELF file header into GElf.
*/
@@ -189,7 +414,7 @@ ctf_sect_munmap(const ctf_sect_t *sp)
* responsible for closing the file descriptor when it is no longer needed.
*/
ctf_file_t *
-ctf_fdopen(int fd, int *errp)
+ctf_fdcreate_int(int fd, int *errp, ctf_sect_t *ctfp)
{
ctf_sect_t ctfsect, symsect, strsect;
ctf_file_t *fp = NULL;
@@ -221,6 +446,9 @@ ctf_fdopen(int fd, int *errp)
*/
if (nbytes >= sizeof (ctf_preamble_t) &&
hdr.ctf.ctp_magic == CTF_MAGIC) {
+ if (ctfp != NULL)
+ return (ctf_set_open_errno(errp, EINVAL));
+
if (hdr.ctf.ctp_version > CTF_VERSION)
return (ctf_set_open_errno(errp, ECTF_CTFVERS));
@@ -370,7 +598,8 @@ ctf_fdopen(int fd, int *errp)
continue; /* corrupt sh_name field */
if (shp->sh_type == SHT_PROGBITS &&
- strcmp(strs + shp->sh_name, _CTF_SECTION) == 0) {
+ strcmp(strs + shp->sh_name, _CTF_SECTION) == 0 &&
+ ctfp == NULL) {
ctfsect.cts_name = strs + shp->sh_name;
ctfsect.cts_type = shp->sh_type;
ctfsect.cts_flags = shp->sh_flags;
@@ -397,18 +626,22 @@ ctf_fdopen(int fd, int *errp)
free(sp); /* free section header array */
- if (ctfsect.cts_type == SHT_NULL) {
- (void) munmap(strs_map, strs_mapsz);
- return (ctf_set_open_errno(errp, ECTF_NOCTFDATA));
- }
+ if (ctfp == NULL) {
+ if (ctfsect.cts_type == SHT_NULL && ctfp == NULL) {
+ (void) munmap(strs_map, strs_mapsz);
+ return (ctf_set_open_errno(errp,
+ ECTF_NOCTFDATA));
+ }
- /*
- * Now mmap the CTF data, symtab, and strtab sections and
- * call ctf_bufopen() to do the rest of the work.
- */
- if (ctf_sect_mmap(&ctfsect, fd) == MAP_FAILED) {
- (void) munmap(strs_map, strs_mapsz);
- return (ctf_set_open_errno(errp, ECTF_MMAP));
+ /*
+ * Now mmap the CTF data, symtab, and strtab sections
+ * and call ctf_bufopen() to do the rest of the work.
+ */
+ if (ctf_sect_mmap(&ctfsect, fd) == MAP_FAILED) {
+ (void) munmap(strs_map, strs_mapsz);
+ return (ctf_set_open_errno(errp, ECTF_MMAP));
+ }
+ ctfp = &ctfsect;
}
if (symsect.cts_type != SHT_NULL &&
@@ -418,12 +651,13 @@ ctf_fdopen(int fd, int *errp)
(void) ctf_set_open_errno(errp, ECTF_MMAP);
goto bad; /* unmap all and abort */
}
- fp = ctf_bufopen(&ctfsect, &symsect, &strsect, errp);
+ fp = ctf_bufopen(ctfp, &symsect, &strsect, errp);
} else
- fp = ctf_bufopen(&ctfsect, NULL, NULL, errp);
+ fp = ctf_bufopen(ctfp, NULL, NULL, errp);
bad:
if (fp == NULL) {
- ctf_sect_munmap(&ctfsect);
+ if (ctfp == NULL)
+ ctf_sect_munmap(&ctfsect);
ctf_sect_munmap(&symsect);
ctf_sect_munmap(&strsect);
} else
@@ -436,6 +670,12 @@ bad:
return (ctf_set_open_errno(errp, ECTF_FMT));
}
+ctf_file_t *
+ctf_fdopen(int fd, int *errp)
+{
+ return (ctf_fdcreate_int(fd, errp, NULL));
+}
+
/*
* Open the specified file and return a pointer to a CTF container. The file
* can be either an ELF file or raw CTF file. This is just a convenient
@@ -502,3 +742,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
new file mode 100644
index 0000000000..c93a2a4f7c
--- /dev/null
+++ b/usr/src/lib/libctf/common/ctf_merge.c
@@ -0,0 +1,1551 @@
+/*
+ * 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.
+ */
+
+/*
+ * To perform a merge of two CTF containers, we first diff the two containers
+ * types. For every type that's in the src container, but not in the dst
+ * container, we note it and add it to dst container. If there are any objects
+ * or functions associated with src, we go through and update the types that
+ * they refer to such that they all refer to types in the dst container.
+ *
+ * The bulk of the logic for the merge, after we've run the diff, occurs in
+ * ctf_merge_common().
+ *
+ * In terms of exported APIs, we don't really export a simple merge two
+ * containers, as the general way this is used, in something like ctfmerge(1),
+ * is to add all the containers and then let us figure out the best way to merge
+ * it.
+ */
+
+#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 <mergeq.h>
+#include <errno.h>
+
+typedef struct ctf_merge_tinfo {
+ uint16_t cmt_map; /* Map to the type in out */
+ boolean_t cmt_fixup;
+ boolean_t cmt_forward;
+ boolean_t cmt_missing;
+} ctf_merge_tinfo_t;
+
+/*
+ * State required for doing an individual merge of two containers.
+ */
+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? */
+ boolean_t cm_unique; /* are we doing a uniquify? */
+} ctf_merge_types_t;
+
+typedef struct ctf_merge_objmap {
+ list_node_t cmo_node;
+ const char *cmo_name; /* Symbol name */
+ ulong_t cmo_idx; /* Symbol ID */
+ ctf_id_t cmo_tid; /* Type ID */
+} ctf_merge_objmap_t;
+
+typedef struct ctf_merge_funcmap {
+ list_node_t cmf_node;
+ const char *cmf_name; /* Symbol name */
+ ulong_t cmf_idx; /* Symbol ID */
+ ctf_id_t cmf_rtid; /* Type ID */
+ uint_t cmf_flags; /* ctf_funcinfo_t ctc_flags */
+ uint_t cmf_argc; /* Number of arguments */
+ ctf_id_t cmf_args[]; /* Types of arguments */
+} ctf_merge_funcmap_t;
+
+typedef struct ctf_merge_input {
+ list_node_t cmi_node;
+ 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 {
+ 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 */
+ int cmh_flags; /* Flags that control merge behavior */
+ char *cmh_label; /* Optional label */
+ char *cmh_pname; /* Parent name */
+};
+
+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)
+{
+ ctf_merge_types_t *cmp = arg;
+ ctf_merge_tinfo_t *cmt = cmp->cm_tmap;
+
+ if (same == B_TRUE) {
+ if (ctf_type_kind(ifp, iid) == CTF_K_FORWARD &&
+ ctf_type_kind(ofp, oid) != CTF_K_FORWARD) {
+ VERIFY(cmt[oid].cmt_map == 0);
+
+ /*
+ * If we're uniquifying types, it's possible for the
+ * container that we're uniquifying against to have a
+ * forward which exists in the container being reduced.
+ * For example, genunix has the machcpu structure as a
+ * forward which is actually in unix and we uniquify
+ * unix against genunix. In such cases, we explicitly do
+ * not do any mapping of the forward information, lest
+ * we risk losing the real definition. Instead, mark
+ * that it's missing.
+ */
+ if (cmp->cm_unique == B_TRUE) {
+ cmt[oid].cmt_missing = B_TRUE;
+ return;
+ }
+
+ cmt[oid].cmt_map = iid;
+ cmt[oid].cmt_forward = B_TRUE;
+ ctf_dprintf("merge diff forward mapped %d->%d\n", oid,
+ iid);
+ return;
+ }
+
+ /*
+ * We could have multiple things that a given type ends up
+ * matching in the world of forwards and pointers to forwards.
+ * For now just take the first one...
+ */
+ 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);
+ }
+}
+
+static int
+ctf_merge_add_number(ctf_merge_types_t *cmp, ctf_id_t id)
+{
+ int ret, flags;
+ const ctf_type_t *tp;
+ const char *name;
+ ctf_encoding_t en;
+
+ if (ctf_type_encoding(cmp->cm_src, id, &en) != 0)
+ return (CTF_ERR);
+
+ tp = LCTF_INDEX_TO_TYPEPTR(cmp->cm_src, id);
+ name = ctf_strraw(cmp->cm_src, tp->ctt_name);
+ if (CTF_INFO_ISROOT(tp->ctt_info) != 0)
+ flags = CTF_ADD_ROOT;
+ else
+ flags = CTF_ADD_NONROOT;
+
+ ret = ctf_add_encoded(cmp->cm_out, flags, name, &en,
+ ctf_type_kind(cmp->cm_src, id));
+
+ if (ret == CTF_ERR)
+ return (ret);
+
+ VERIFY(cmp->cm_tmap[id].cmt_map == 0);
+ cmp->cm_tmap[id].cmt_map = ret;
+ return (0);
+}
+
+static int
+ctf_merge_add_array(ctf_merge_types_t *cmp, ctf_id_t id)
+{
+ int ret, flags;
+ const ctf_type_t *tp;
+ ctf_arinfo_t ar;
+
+ if (ctf_array_info(cmp->cm_src, id, &ar) == CTF_ERR)
+ return (CTF_ERR);
+
+ tp = LCTF_INDEX_TO_TYPEPTR(cmp->cm_src, id);
+ if (CTF_INFO_ISROOT(tp->ctt_info) != 0)
+ flags = CTF_ADD_ROOT;
+ else
+ flags = CTF_ADD_NONROOT;
+
+ if (cmp->cm_tmap[ar.ctr_contents].cmt_map == 0) {
+ ret = ctf_merge_add_type(cmp, ar.ctr_contents);
+ if (ret != 0)
+ return (ret);
+ ASSERT(cmp->cm_tmap[ar.ctr_contents].cmt_map != 0);
+ }
+ 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);
+ if (ret != 0)
+ return (ret);
+ ASSERT(cmp->cm_tmap[ar.ctr_index].cmt_map != 0);
+ }
+ ar.ctr_index = ctf_merge_gettype(cmp, ar.ctr_index);
+
+ ret = ctf_add_array(cmp->cm_out, flags, &ar);
+ if (ret == CTF_ERR)
+ return (ret);
+
+ VERIFY(cmp->cm_tmap[id].cmt_map == 0);
+ cmp->cm_tmap[id].cmt_map = ret;
+
+ return (0);
+}
+
+static int
+ctf_merge_add_reftype(ctf_merge_types_t *cmp, ctf_id_t id)
+{
+ int ret, flags;
+ const ctf_type_t *tp;
+ ctf_id_t reftype;
+ const char *name;
+
+ tp = LCTF_INDEX_TO_TYPEPTR(cmp->cm_src, id);
+ name = ctf_strraw(cmp->cm_src, tp->ctt_name);
+ if (CTF_INFO_ISROOT(tp->ctt_info) != 0)
+ flags = CTF_ADD_ROOT;
+ else
+ flags = CTF_ADD_NONROOT;
+
+ reftype = ctf_type_reference(cmp->cm_src, id);
+ if (reftype == CTF_ERR)
+ return (ctf_set_errno(cmp->cm_out, ctf_errno(cmp->cm_src)));
+
+ if (cmp->cm_tmap[reftype].cmt_map == 0) {
+ ret = ctf_merge_add_type(cmp, reftype);
+ if (ret != 0)
+ return (ret);
+ ASSERT(cmp->cm_tmap[reftype].cmt_map != 0);
+ }
+ reftype = ctf_merge_gettype(cmp, reftype);
+
+ ret = ctf_add_reftype(cmp->cm_out, flags, name, reftype,
+ ctf_type_kind(cmp->cm_src, id));
+ if (ret == CTF_ERR)
+ return (ret);
+
+ VERIFY(cmp->cm_tmap[id].cmt_map == 0);
+ cmp->cm_tmap[id].cmt_map = ret;
+ return (0);
+}
+
+static int
+ctf_merge_add_typedef(ctf_merge_types_t *cmp, ctf_id_t id)
+{
+ int ret, flags;
+ const ctf_type_t *tp;
+ const char *name;
+ ctf_id_t reftype;
+
+ tp = LCTF_INDEX_TO_TYPEPTR(cmp->cm_src, id);
+ name = ctf_strraw(cmp->cm_src, tp->ctt_name);
+ if (CTF_INFO_ISROOT(tp->ctt_info) != 0)
+ flags = CTF_ADD_ROOT;
+ else
+ flags = CTF_ADD_NONROOT;
+
+ reftype = ctf_type_reference(cmp->cm_src, id);
+ if (reftype == CTF_ERR)
+ return (ctf_set_errno(cmp->cm_out, ctf_errno(cmp->cm_src)));
+
+ if (cmp->cm_tmap[reftype].cmt_map == 0) {
+ ret = ctf_merge_add_type(cmp, reftype);
+ if (ret != 0)
+ return (ret);
+ ASSERT(cmp->cm_tmap[reftype].cmt_map != 0);
+ }
+ reftype = ctf_merge_gettype(cmp, reftype);
+
+ ret = ctf_add_typedef(cmp->cm_out, flags, name, reftype);
+ if (ret == CTF_ERR)
+ return (ret);
+
+ VERIFY(cmp->cm_tmap[id].cmt_map == 0);
+ cmp->cm_tmap[id].cmt_map = ret;
+ return (0);
+}
+
+typedef struct ctf_merge_enum {
+ ctf_file_t *cme_fp;
+ ctf_id_t cme_id;
+} ctf_merge_enum_t;
+
+static int
+ctf_merge_add_enumerator(const char *name, int value, void *arg)
+{
+ ctf_merge_enum_t *cmep = arg;
+
+ return (ctf_add_enumerator(cmep->cme_fp, cmep->cme_id, name, value) ==
+ CTF_ERR);
+}
+
+static int
+ctf_merge_add_enum(ctf_merge_types_t *cmp, ctf_id_t id)
+{
+ int flags;
+ const ctf_type_t *tp;
+ const char *name;
+ ctf_id_t enumid;
+ ctf_merge_enum_t cme;
+
+ tp = LCTF_INDEX_TO_TYPEPTR(cmp->cm_src, id);
+ name = ctf_strraw(cmp->cm_src, tp->ctt_name);
+ if (CTF_INFO_ISROOT(tp->ctt_info) != 0)
+ flags = CTF_ADD_ROOT;
+ else
+ flags = CTF_ADD_NONROOT;
+
+ enumid = ctf_add_enum(cmp->cm_out, flags, name);
+ if (enumid == CTF_ERR)
+ return (enumid);
+
+ cme.cme_fp = cmp->cm_out;
+ cme.cme_id = enumid;
+ if (ctf_enum_iter(cmp->cm_src, id, ctf_merge_add_enumerator,
+ &cme) != 0)
+ return (CTF_ERR);
+
+ VERIFY(cmp->cm_tmap[id].cmt_map == 0);
+ cmp->cm_tmap[id].cmt_map = enumid;
+ return (0);
+}
+
+static int
+ctf_merge_add_func(ctf_merge_types_t *cmp, ctf_id_t id)
+{
+ int ret, flags, i;
+ const ctf_type_t *tp;
+ ctf_funcinfo_t ctc;
+ ctf_id_t *argv;
+
+ tp = LCTF_INDEX_TO_TYPEPTR(cmp->cm_src, id);
+ if (CTF_INFO_ISROOT(tp->ctt_info) != 0)
+ flags = CTF_ADD_ROOT;
+ else
+ flags = CTF_ADD_NONROOT;
+
+ if (ctf_func_info_by_id(cmp->cm_src, id, &ctc) == CTF_ERR)
+ return (ctf_set_errno(cmp->cm_out, ctf_errno(cmp->cm_src)));
+
+ argv = ctf_alloc(sizeof (ctf_id_t) * ctc.ctc_argc);
+ if (argv == NULL)
+ return (ctf_set_errno(cmp->cm_out, ENOMEM));
+ if (ctf_func_args_by_id(cmp->cm_src, id, ctc.ctc_argc, argv) ==
+ CTF_ERR) {
+ ctf_free(argv, sizeof (ctf_id_t) * ctc.ctc_argc);
+ return (ctf_set_errno(cmp->cm_out, ctf_errno(cmp->cm_src)));
+ }
+
+ if (cmp->cm_tmap[ctc.ctc_return].cmt_map == 0) {
+ ret = ctf_merge_add_type(cmp, ctc.ctc_return);
+ if (ret != 0)
+ return (ret);
+ ASSERT(cmp->cm_tmap[ctc.ctc_return].cmt_map != 0);
+ }
+ 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) {
+ ret = ctf_merge_add_type(cmp, argv[i]);
+ if (ret != 0)
+ return (ret);
+ ASSERT(cmp->cm_tmap[argv[i]].cmt_map != 0);
+ }
+ argv[i] = ctf_merge_gettype(cmp, argv[i]);
+ }
+
+ ret = ctf_add_funcptr(cmp->cm_out, flags, &ctc, argv);
+ ctf_free(argv, sizeof (ctf_id_t) * ctc.ctc_argc);
+ if (ret == CTF_ERR)
+ return (ret);
+
+ VERIFY(cmp->cm_tmap[id].cmt_map == 0);
+ cmp->cm_tmap[id].cmt_map = ret;
+ return (0);
+}
+
+static int
+ctf_merge_add_forward(ctf_merge_types_t *cmp, ctf_id_t id)
+{
+ int ret, flags;
+ const ctf_type_t *tp;
+ const char *name;
+
+ tp = LCTF_INDEX_TO_TYPEPTR(cmp->cm_src, id);
+ name = ctf_strraw(cmp->cm_src, tp->ctt_name);
+ if (CTF_INFO_ISROOT(tp->ctt_info) != 0)
+ flags = CTF_ADD_ROOT;
+ else
+ flags = CTF_ADD_NONROOT;
+
+ /*
+ * ctf_add_forward tries to check to see if a given forward already
+ * exists in one of its hash tables. If we're here then we know that we
+ * have a forward in a container that isn't present in another.
+ * Therefore, we choose a token hash table to satisfy the API choice
+ * here.
+ */
+ ret = ctf_add_forward(cmp->cm_out, flags, name, CTF_K_STRUCT);
+ if (ret == CTF_ERR)
+ return (CTF_ERR);
+
+ VERIFY(cmp->cm_tmap[id].cmt_map == 0);
+ cmp->cm_tmap[id].cmt_map = ret;
+ return (0);
+}
+
+typedef struct ctf_merge_su {
+ ctf_merge_types_t *cms_cm;
+ ctf_id_t cms_id;
+} ctf_merge_su_t;
+
+static int
+ctf_merge_add_member(const char *name, ctf_id_t type, ulong_t offset, void *arg)
+{
+ ctf_merge_su_t *cms = 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);
+}
+
+/*
+ * During the first pass, we always add the generic structure and union but none
+ * of its members as they might not all have been mapped yet. Instead we just
+ * mark all structures and unions as needing to be fixed up.
+ */
+static int
+ctf_merge_add_sou(ctf_merge_types_t *cmp, ctf_id_t id, boolean_t forward)
+{
+ int flags, kind;
+ const ctf_type_t *tp;
+ const char *name;
+ ctf_id_t suid;
+
+ tp = LCTF_INDEX_TO_TYPEPTR(cmp->cm_src, id);
+ name = ctf_strraw(cmp->cm_src, tp->ctt_name);
+ if (CTF_INFO_ISROOT(tp->ctt_info) != 0)
+ flags = CTF_ADD_ROOT;
+ else
+ flags = CTF_ADD_NONROOT;
+ kind = ctf_type_kind(cmp->cm_src, id);
+
+ if (kind == CTF_K_STRUCT)
+ suid = ctf_add_struct(cmp->cm_out, flags, name);
+ else
+ suid = ctf_add_union(cmp->cm_out, flags, name);
+
+ if (suid == CTF_ERR)
+ return (suid);
+
+ /*
+ * If this is a forward reference then its mapping should already
+ * exist.
+ */
+ 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);
+ }
+ cmp->cm_tmap[id].cmt_fixup = B_TRUE;
+
+ return (0);
+}
+
+static int
+ctf_merge_add_type(ctf_merge_types_t *cmp, ctf_id_t id)
+{
+ int kind, ret;
+
+ /*
+ * We may end up evaluating a type more than once as we may deal with it
+ * as we recursively evaluate some kind of reference and then we may see
+ * it normally.
+ */
+ if (cmp->cm_tmap[id].cmt_map != 0)
+ return (0);
+
+ kind = ctf_type_kind(cmp->cm_src, id);
+ switch (kind) {
+ case CTF_K_INTEGER:
+ case CTF_K_FLOAT:
+ ret = ctf_merge_add_number(cmp, id);
+ break;
+ case CTF_K_ARRAY:
+ ret = ctf_merge_add_array(cmp, id);
+ break;
+ case CTF_K_POINTER:
+ case CTF_K_VOLATILE:
+ case CTF_K_CONST:
+ case CTF_K_RESTRICT:
+ ret = ctf_merge_add_reftype(cmp, id);
+ break;
+ case CTF_K_TYPEDEF:
+ ret = ctf_merge_add_typedef(cmp, id);
+ break;
+ case CTF_K_ENUM:
+ ret = ctf_merge_add_enum(cmp, id);
+ break;
+ case CTF_K_FUNCTION:
+ ret = ctf_merge_add_func(cmp, id);
+ break;
+ case CTF_K_FORWARD:
+ ret = ctf_merge_add_forward(cmp, id);
+ break;
+ case CTF_K_STRUCT:
+ case CTF_K_UNION:
+ ret = ctf_merge_add_sou(cmp, id, B_FALSE);
+ break;
+ case CTF_K_UNKNOWN:
+ /*
+ * We don't add unknown types, and we later assert that nothing
+ * should reference them.
+ */
+ return (0);
+ default:
+ abort();
+ }
+
+ return (ret);
+}
+
+static int
+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);
+}
+
+static int
+ctf_merge_fixup_type(ctf_merge_types_t *cmp, ctf_id_t id)
+{
+ int kind, ret;
+
+ kind = ctf_type_kind(cmp->cm_src, id);
+ switch (kind) {
+ case CTF_K_STRUCT:
+ case CTF_K_UNION:
+ ret = ctf_merge_fixup_sou(cmp, id);
+ break;
+ default:
+ VERIFY(0);
+ ret = CTF_ERR;
+ }
+
+ 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.
+ *
+ * Pass 1 checks for forward references in the output container that we know
+ * exist in the source container.
+ *
+ * Pass 2 adds all the missing types from the source container. As part of this
+ * we may be adding a type as a forward reference that doesn't exist yet.
+ * Any types that we encounter in this form, we need to add to a third pass.
+ *
+ * Pass 3 is the fixup pass. Here we go through and find all the types that were
+ * missing in the first.
+ *
+ * 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. 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_sou(cmp, i, B_TRUE);
+ if (ret != 0) {
+ return (ret);
+ }
+ }
+ }
+
+ /* Pass 2 */
+ 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) {
+ ctf_dprintf("Failed to merge type %d\n", i);
+ return (ret);
+ }
+ }
+ }
+
+ ret = ctf_update(cmp->cm_out);
+ 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) {
+ ret = ctf_merge_fixup_type(cmp, i);
+ if (ret != 0)
+ return (ret);
+ }
+ }
+
+ if (cmp->cm_dedup == B_TRUE) {
+ ctf_merge_fixup_dedup_map(cmp);
+ }
+
+ return (0);
+}
+
+/*
+ * Uniquification is slightly different from a stock merge. For starters, we
+ * don't need to replace any forward references in the output. In this case
+ * though, the types that already exist are in a parent container to the empty
+ * output container.
+ */
+static int
+ctf_merge_uniquify_types(ctf_merge_types_t *cmp)
+{
+ int i, ret;
+
+ for (i = 1; i <= cmp->cm_src->ctf_typemax; i++) {
+ if (cmp->cm_tmap[i].cmt_missing == B_FALSE)
+ continue;
+ ret = ctf_merge_add_type(cmp, i);
+ if (ret != 0)
+ return (ret);
+ }
+
+ ret = ctf_update(cmp->cm_out);
+ if (ret != 0)
+ return (ret);
+
+ for (i = 1; i <= cmp->cm_src->ctf_typemax; i++) {
+ if (cmp->cm_tmap[i].cmt_fixup == B_FALSE)
+ continue;
+ ret = ctf_merge_fixup_type(cmp, i);
+ if (ret != 0)
+ return (ret);
+ }
+
+ return (0);
+}
+
+static int
+ctf_merge_types_init(ctf_merge_types_t *cmp)
+{
+ cmp->cm_tmap = ctf_alloc(sizeof (ctf_merge_tinfo_t) *
+ (cmp->cm_src->ctf_typemax + 1));
+ if (cmp->cm_tmap == NULL)
+ return (ctf_set_errno(cmp->cm_out, ENOMEM));
+ bzero(cmp->cm_tmap, sizeof (ctf_merge_tinfo_t) *
+ (cmp->cm_src->ctf_typemax + 1));
+ return (0);
+}
+
+static void
+ctf_merge_types_fini(ctf_merge_types_t *cmp)
+{
+ ctf_free(cmp->cm_tmap, sizeof (ctf_merge_tinfo_t) *
+ (cmp->cm_src->ctf_typemax + 1));
+}
+
+/*
+ * 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_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_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));
+
+ if (ctf_getmodel(out) != ctf_getmodel(source))
+ return (ctf_set_errno(out, ECTF_DMODEL));
+
+ if ((ret = ctf_diff_init(out, source, &cdp)) != 0)
+ return (ret);
+
+ cm.cm_out = out;
+ cm.cm_src = source;
+ cm.cm_dedup = B_FALSE;
+ cm.cm_unique = B_FALSE;
+ ret = ctf_merge_types_init(&cm);
+ if (ret != 0) {
+ ctf_diff_fini(cdp);
+ return (ctf_set_errno(out, ret));
+ }
+
+ ret = ctf_diff_types(cdp, ctf_merge_diffcb, &cm);
+ if (ret != 0)
+ goto cleanup;
+ ret = ctf_merge_common(&cm);
+ ctf_dprintf("merge common returned with %d\n", ret);
+ if (ret == 0) {
+ ret = ctf_update(out);
+ 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(&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(&scmi->cmi_fmap); cmf != NULL;
+ cmf = list_next(&scmi->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;
+ }
+ }
+
+ /*
+ * 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);
+ if (ret != 0)
+ return (ctf_errno(out));
+ return (0);
+}
+
+/*
+ * After performing a pass, we need to go through the object and function type
+ * maps and potentially fix them up based on the new maps that we haev.
+ */
+static void
+ctf_merge_fixup_nontypes(ctf_merge_types_t *cmp, ctf_merge_input_t *cmi)
+{
+ ctf_merge_objmap_t *cmo;
+ ctf_merge_funcmap_t *cmf;
+
+ for (cmo = list_head(&cmi->cmi_omap); cmo != NULL;
+ cmo = list_next(&cmi->cmi_omap, cmo)) {
+ if (cmo->cmo_tid == 0)
+ continue;
+ VERIFY(cmp->cm_tmap[cmo->cmo_tid].cmt_map != 0);
+ cmo->cmo_tid = cmp->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(cmp->cm_tmap[cmf->cmf_rtid].cmt_map != 0);
+ cmf->cmf_rtid = cmp->cm_tmap[cmf->cmf_rtid].cmt_map;
+ for (i = 0; i < cmf->cmf_argc; i++) {
+ VERIFY(cmp->cm_tmap[cmf->cmf_args[i]].cmt_map !=
+ 0);
+ cmf->cmf_args[i] =
+ cmp->cm_tmap[cmf->cmf_args[i]].cmt_map;
+ }
+ }
+}
+
+static int
+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 *parent = cmh->cmh_unique;
+
+ *outp = NULL;
+ out = ctf_fdcreate(cmh->cmh_ofd, &err);
+ if (out == NULL)
+ return (ctf_set_errno(src, err));
+
+ out->ctf_parname = cmh->cmh_pname;
+ if (ctf_setmodel(out, ctf_getmodel(parent)) != 0) {
+ (void) ctf_set_errno(src, ctf_errno(out));
+ ctf_close(out);
+ return (CTF_ERR);
+ }
+
+ if (ctf_import(out, parent) != 0) {
+ (void) ctf_set_errno(src, ctf_errno(out));
+ ctf_close(out);
+ return (CTF_ERR);
+ }
+
+ if ((ret = ctf_diff_init(parent, src, &cdp)) != 0) {
+ ctf_close(out);
+ return (ctf_set_errno(src, ctf_errno(parent)));
+ }
+
+ cm.cm_out = parent;
+ cm.cm_src = src;
+ cm.cm_dedup = B_FALSE;
+ cm.cm_unique = B_TRUE;
+ ret = ctf_merge_types_init(&cm);
+ if (ret != 0) {
+ ctf_close(out);
+ ctf_diff_fini(cdp);
+ return (ctf_set_errno(src, ret));
+ }
+
+ ret = ctf_diff_types(cdp, ctf_merge_diffcb, &cm);
+ if (ret == 0) {
+ cm.cm_out = out;
+ ret = ctf_merge_uniquify_types(&cm);
+ if (ret == 0)
+ ret = ctf_update(out);
+ }
+
+ if (ret != 0) {
+ ctf_merge_types_fini(&cm);
+ ctf_diff_fini(cdp);
+ return (ctf_set_errno(src, ctf_errno(cm.cm_out)));
+ }
+
+ for (cmi = list_head(&cmh->cmh_inputs); cmi != NULL;
+ cmi = list_next(&cmh->cmh_inputs, cmi)) {
+ ctf_merge_fixup_nontypes(&cm, cmi);
+ }
+
+ ctf_merge_types_fini(&cm);
+ ctf_diff_fini(cdp);
+ *outp = out;
+ return (0);
+}
+
+static void
+ctf_merge_fini_input(ctf_merge_input_t *cmi)
+{
+ ctf_merge_objmap_t *cmo;
+ ctf_merge_funcmap_t *cmf;
+
+ while ((cmo = list_remove_head(&cmi->cmi_omap)) != NULL)
+ ctf_free(cmo, sizeof (ctf_merge_objmap_t));
+
+ while ((cmf = list_remove_head(&cmi->cmi_fmap)) != NULL)
+ 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));
+}
+
+void
+ctf_merge_fini(ctf_merge_t *cmh)
+{
+ size_t len;
+ ctf_merge_input_t *cmi;
+
+ if (cmh->cmh_label != NULL) {
+ len = strlen(cmh->cmh_label) + 1;
+ ctf_free(cmh->cmh_label, len);
+ }
+
+ if (cmh->cmh_pname != NULL) {
+ len = strlen(cmh->cmh_pname) + 1;
+ ctf_free(cmh->cmh_pname, len);
+ }
+
+ while ((cmi = list_remove_head(&cmh->cmh_inputs)) != NULL)
+ ctf_merge_fini_input(cmi);
+
+ ctf_free(cmh, sizeof (ctf_merge_t));
+}
+
+ctf_merge_t *
+ctf_merge_init(int fd, int *errp)
+{
+ int err;
+ ctf_merge_t *out;
+ struct stat st;
+
+ if (errp == NULL)
+ errp = &err;
+
+ if (fd != -1 && fstat(fd, &st) != 0) {
+ *errp = EINVAL;
+ return (NULL);
+ }
+
+ out = ctf_alloc(sizeof (ctf_merge_t));
+ if (out == NULL) {
+ *errp = ENOMEM;
+ return (NULL);
+ }
+
+ if (fd == -1) {
+ out->cmh_msyms = B_FALSE;
+ } else {
+ out->cmh_msyms = B_TRUE;
+ }
+
+ 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;
+
+ return (out);
+}
+
+int
+ctf_merge_label(ctf_merge_t *cmh, const char *label)
+{
+ char *dup;
+
+ if (label == NULL)
+ return (EINVAL);
+
+ dup = ctf_strdup(label);
+ if (dup == NULL)
+ return (EAGAIN);
+
+ if (cmh->cmh_label != NULL) {
+ size_t len = strlen(cmh->cmh_label) + 1;
+ ctf_free(cmh->cmh_label, len);
+ }
+
+ cmh->cmh_label = dup;
+ return (0);
+}
+
+static int
+ctf_merge_add_funcs_cb(const char *name, ulong_t idx, ctf_funcinfo_t *fip,
+ void *arg)
+{
+ ctf_merge_input_t *cmi = arg;
+ ctf_merge_funcmap_t *fmap;
+
+ fmap = ctf_alloc(sizeof (ctf_merge_funcmap_t) +
+ sizeof (ctf_id_t) * fip->ctc_argc);
+ if (fmap == NULL)
+ return (ENOMEM);
+
+ fmap->cmf_idx = idx;
+ fmap->cmf_rtid = fip->ctc_return;
+ fmap->cmf_flags = fip->ctc_flags;
+ fmap->cmf_argc = fip->ctc_argc;
+ fmap->cmf_name = name;
+
+ if (ctf_func_args(cmi->cmi_input, idx, fmap->cmf_argc,
+ fmap->cmf_args) != 0) {
+ ctf_free(fmap, sizeof (ctf_merge_funcmap_t) +
+ sizeof (ctf_id_t) * fip->ctc_argc);
+ return (ctf_errno(cmi->cmi_input));
+ }
+
+ list_insert_tail(&cmi->cmi_fmap, fmap);
+ return (0);
+}
+
+static int
+ctf_merge_add_objs_cb(const char *name, ctf_id_t id, ulong_t idx, void *arg)
+{
+ ctf_merge_input_t *cmi = arg;
+ ctf_merge_objmap_t *cmo;
+
+ cmo = ctf_alloc(sizeof (ctf_merge_objmap_t));
+ if (cmo == NULL)
+ return (ENOMEM);
+
+ cmo->cmo_name = name;
+ cmo->cmo_idx = idx;
+ cmo->cmo_tid = id;
+ list_insert_tail(&cmi->cmi_omap, cmo);
+ 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;
+ ctf_file_t *empty;
+
+ if (input->ctf_flags & LCTF_CHILD)
+ return (ECTF_MCHILD);
+
+ cmi = ctf_alloc(sizeof (ctf_merge_input_t));
+ 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));
+ list_create(&cmi->cmi_omap, sizeof (ctf_merge_funcmap_t),
+ offsetof(ctf_merge_objmap_t, cmo_node));
+
+ if (cmh->cmh_msyms == B_TRUE) {
+ if ((ret = ctf_function_iter(input, ctf_merge_add_funcs_cb,
+ cmi)) != 0) {
+ ctf_merge_fini_input(cmi);
+ return (ret);
+ }
+
+ if ((ret = ctf_object_iter(input, ctf_merge_add_objs_cb,
+ cmi)) != 0) {
+ ctf_merge_fini_input(cmi);
+ return (ret);
+ }
+ }
+
+ 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);
+}
+
+int
+ctf_merge_uniquify(ctf_merge_t *cmh, ctf_file_t *u, const char *pname)
+{
+ char *dup;
+
+ if (u->ctf_flags & LCTF_CHILD)
+ return (ECTF_MCHILD);
+ if (pname == NULL)
+ return (EINVAL);
+ dup = ctf_strdup(pname);
+ if (dup == NULL)
+ return (EINVAL);
+ if (cmh->cmh_pname != NULL) {
+ size_t len = strlen(cmh->cmh_pname) + 1;
+ ctf_free(cmh->cmh_pname, len);
+ }
+ cmh->cmh_pname = dup;
+ cmh->cmh_unique = u;
+ return (0);
+}
+
+static int
+ctf_merge_symbols(ctf_merge_t *cmh, ctf_file_t *fp)
+{
+ int err;
+ ulong_t i;
+
+ 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;
+ ctf_merge_input_t *cmi;
+ ctf_merge_objmap_t *cmo;
+
+ if (fp->ctf_symtab.cts_entsize == sizeof (Elf32_Sym)) {
+ const Elf32_Sym *symp = (Elf32_Sym *)symbase + i;
+ int type = ELF32_ST_TYPE(symp->st_info);
+ if (type != STT_OBJECT)
+ 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);
+ } else {
+ const Elf64_Sym *symp = (Elf64_Sym *)symbase + i;
+ int type = ELF64_ST_TYPE(symp->st_info);
+ if (type != STT_OBJECT)
+ 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);
+ }
+
+ cmo = NULL;
+ for (cmi = list_head(&cmh->cmh_inputs); cmi != NULL;
+ cmi = list_next(&cmh->cmh_inputs, cmi)) {
+ for (cmo = list_head(&cmi->cmi_omap); cmo != NULL;
+ cmo = list_next(&cmi->cmi_omap, cmo)) {
+ if (strcmp(cmo->cmo_name, name) == 0)
+ goto found;
+ }
+ }
+found:
+ if (cmo != NULL) {
+ if (cmo->cmo_tid == 0)
+ continue;
+ 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);
+ }
+ }
+ }
+
+ return (0);
+}
+
+static int
+ctf_merge_functions(ctf_merge_t *cmh, ctf_file_t *fp)
+{
+ int err;
+ ulong_t i;
+ ctf_funcinfo_t fi;
+
+ 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;
+ ctf_merge_input_t *cmi;
+ ctf_merge_funcmap_t *cmf;
+
+ if (fp->ctf_symtab.cts_entsize == sizeof (Elf32_Sym)) {
+ const Elf32_Sym *symp = (Elf32_Sym *)symbase + i;
+ int type = ELF32_ST_TYPE(symp->st_info);
+ if (ELF32_ST_TYPE(symp->st_info) != 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);
+ } else {
+ const Elf64_Sym *symp = (Elf64_Sym *)symbase + i;
+ int type = ELF64_ST_TYPE(symp->st_info);
+ if (ELF64_ST_TYPE(symp->st_info) != 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);
+ }
+
+ cmf = NULL;
+ for (cmi = list_head(&cmh->cmh_inputs); cmi != NULL;
+ cmi = list_next(&cmh->cmh_inputs, cmi)) {
+ for (cmf = list_head(&cmi->cmi_fmap); cmf != NULL;
+ cmf = list_next(&cmi->cmi_fmap, cmf)) {
+ if (strcmp(cmf->cmf_name, name) == 0)
+ goto found;
+ }
+ }
+found:
+ if (cmf != NULL) {
+ fi.ctc_return = cmf->cmf_rtid;
+ fi.ctc_argc = cmf->cmf_argc;
+ fi.ctc_flags = cmf->cmf_flags;
+ if ((err = ctf_add_function(fp, i, &fi,
+ cmf->cmf_args)) != 0)
+ return (err);
+ }
+ }
+
+ return (0);
+
+}
+
+int
+ctf_merge_merge(ctf_merge_t *cmh, ctf_file_t **outp)
+{
+ int err, merr;
+ ctf_merge_input_t *cmi;
+ 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);
+ if (label == NULL)
+ return (ECTF_NOLABEL);
+ if (strcmp(label, cmh->cmh_label) != 0)
+ return (ECTF_LCONFLICT);
+ }
+
+ if (mergeq_init(&mqp, cmh->cmh_nthreads) == -1) {
+ return (errno);
+ }
+
+ VERIFY(cmh->cmh_ninputs % 2 == 0);
+ for (cmi = list_head(&cmh->cmh_inputs); cmi != NULL;
+ cmi = list_next(&cmh->cmh_inputs, cmi)) {
+ if (mergeq_add(mqp, cmi) == -1) {
+ err = errno;
+ mergeq_fini(mqp);
+ }
+ }
+
+ 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) {
+ 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 += CTF_CHILD_START;
+ 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) {
+ 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;
+
+ 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);
+ }
+}
+
+/*
+ * 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_types_t cm;
+
+ 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;
+ cm.cm_unique = B_FALSE;
+
+ 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.
+ */
+ ctf_merge_fixup_nontypes(&cm, cmi);
+
+ 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 3fd69318de..0951ae0606 100644
--- a/usr/src/lib/libctf/common/libctf.h
+++ b/usr/src/lib/libctf/common/libctf.h
@@ -23,6 +23,9 @@
* Copyright 2001-2003 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
+/*
+ * Copyright (c) 2015, Joyent, Inc.
+ */
/*
* This header file defines the interfaces available from the CTF debugger
@@ -32,7 +35,7 @@
* the fullness of time after we gain more experience with the interfaces.
*
* In the meantime, be aware that any program linked with libctf in this
- * release of Solaris is almost guaranteed to break in the next release.
+ * release of illumos is almost guaranteed to break in the next release.
*
* In short, do not user this header file or libctf for any purpose.
*/
@@ -40,8 +43,6 @@
#ifndef _LIBCTF_H
#define _LIBCTF_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/ctf_api.h>
#ifdef __cplusplus
@@ -53,6 +54,44 @@ extern "C" {
*/
extern int _libctf_debug;
+typedef enum ctf_diff_flag {
+ CTF_DIFF_F_IGNORE_INTNAMES = 0x01
+} ctf_diff_flag_t;
+
+typedef struct ctf_diff ctf_diff_t;
+typedef void (*ctf_diff_type_f)(ctf_file_t *, ctf_id_t, boolean_t, ctf_file_t *,
+ ctf_id_t, void *);
+typedef void (*ctf_diff_func_f)(ctf_file_t *, ulong_t, boolean_t, ctf_file_t *,
+ ulong_t, void *);
+typedef void (*ctf_diff_obj_f)(ctf_file_t *, ulong_t, ctf_id_t, boolean_t,
+ ctf_file_t *, ulong_t, ctf_id_t, void *);
+
+extern int ctf_diff_init(ctf_file_t *, ctf_file_t *, ctf_diff_t **);
+extern uint_t ctf_diff_getflags(ctf_diff_t *);
+extern int ctf_diff_setflags(ctf_diff_t *, uint_t);
+extern int ctf_diff_types(ctf_diff_t *, ctf_diff_type_f, void *);
+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
+extern int ctf_elffdwrite(ctf_file_t *, int, int, int);
+extern int ctf_elfwrite(ctf_file_t *, const char *, const char *, int);
+
#ifdef __cplusplus
}
#endif
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 5573e8db25..b26ee8ceb4 100644
--- a/usr/src/lib/libctf/common/mapfile-vers
+++ b/usr/src/lib/libctf/common/mapfile-vers
@@ -23,7 +23,7 @@
#
#
-# Copyright (c) 2013, Joyent, Inc. All rights reserved.
+# Copyright 2018 Joyent, Inc.
#
#
@@ -53,9 +53,12 @@ SYMBOL_VERSION SUNWprivate_1.2 {
ctf_add_enumerator;
ctf_add_float;
ctf_add_forward;
+ ctf_add_funcptr;
ctf_add_function;
ctf_add_integer;
+ ctf_add_label;
ctf_add_member;
+ ctf_add_object;
ctf_add_pointer;
ctf_add_restrict;
ctf_add_struct;
@@ -64,19 +67,52 @@ SYMBOL_VERSION SUNWprivate_1.2 {
ctf_add_union;
ctf_add_volatile;
ctf_create;
+ ctf_dataptr;
ctf_delete_type;
+ ctf_diff_fini;
+ ctf_diff_functions;
+ ctf_diff_getflags;
+ ctf_diff_init;
+ ctf_diff_objects;
+ ctf_diff_setflags;
+ ctf_diff_types;
ctf_discard;
ctf_dup;
+ 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_max_id;
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_nr_syms;
+ 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;
ctf_type_cmp;
+ ctf_type_cname;
ctf_type_compat;
ctf_type_pointer;
ctf_update;
diff --git a/usr/src/lib/libdtrace/common/dt_decl.c b/usr/src/lib/libdtrace/common/dt_decl.c
index bbb561d027..c9bd469ddb 100644
--- a/usr/src/lib/libdtrace/common/dt_decl.c
+++ b/usr/src/lib/libdtrace/common/dt_decl.c
@@ -22,7 +22,7 @@
/*
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2014 by Delphix. All rights reserved.
- * Copyright (c) 2013 Joyent, Inc. All rights reserved.
+ * Copyright (c) 2015 Joyent, Inc. All rights reserved.
*/
#include <strings.h>
@@ -652,7 +652,7 @@ dt_decl_member(dt_node_t *dnp)
}
if (ctf_add_member(dsp->ds_ctfp, dsp->ds_type,
- ident, dtt.dtt_type) == CTF_ERR) {
+ ident, dtt.dtt_type, ULONG_MAX) == CTF_ERR) {
xyerror(D_UNKNOWN, "failed to define member '%s': %s\n",
idname, ctf_errmsg(ctf_errno(dsp->ds_ctfp)));
}
diff --git a/usr/src/lib/libdtrace/common/dt_open.c b/usr/src/lib/libdtrace/common/dt_open.c
index eb88f7e993..3e3bf9768a 100644
--- a/usr/src/lib/libdtrace/common/dt_open.c
+++ b/usr/src/lib/libdtrace/common/dt_open.c
@@ -1140,13 +1140,13 @@ alloc:
* Add intrinsic pointer types that are needed to initialize printf
* format dictionary types (see table in dt_printf.c).
*/
- (void) ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT,
+ (void) ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT, NULL,
ctf_lookup_by_name(dmp->dm_ctfp, "void"));
- (void) ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT,
+ (void) ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT, NULL,
ctf_lookup_by_name(dmp->dm_ctfp, "char"));
- (void) ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT,
+ (void) ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT, NULL,
ctf_lookup_by_name(dmp->dm_ctfp, "int"));
if (ctf_update(dmp->dm_ctfp) != 0) {
@@ -1206,11 +1206,11 @@ alloc:
ctc.ctc_argc = 0;
ctc.ctc_flags = 0;
- dtp->dt_type_func = ctf_add_function(dmp->dm_ctfp,
+ dtp->dt_type_func = ctf_add_funcptr(dmp->dm_ctfp,
CTF_ADD_ROOT, &ctc, NULL);
- dtp->dt_type_fptr = ctf_add_pointer(dmp->dm_ctfp,
- CTF_ADD_ROOT, dtp->dt_type_func);
+ dtp->dt_type_fptr = ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT, NULL,
+ dtp->dt_type_func);
/*
* We also insert CTF definitions for the special D intrinsic types
diff --git a/usr/src/lib/libdtrace/common/dt_parser.c b/usr/src/lib/libdtrace/common/dt_parser.c
index 7f771a8079..e652f337d9 100644
--- a/usr/src/lib/libdtrace/common/dt_parser.c
+++ b/usr/src/lib/libdtrace/common/dt_parser.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2013, Joyent Inc. All rights reserved.
+ * Copyright (c) 2015, Joyent Inc. All rights reserved.
* Copyright (c) 2012, 2016 by Delphix. All rights reserved.
*/
@@ -288,7 +288,7 @@ dt_type_pointer(dtrace_typeinfo_t *tip)
return (dt_set_errno(dtp, EDT_CTF));
}
- ptr = ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT, type);
+ ptr = ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT, NULL, type);
if (ptr == CTF_ERR || ctf_update(dmp->dm_ctfp) == CTF_ERR) {
dtp->dt_ctferr = ctf_errno(dmp->dm_ctfp);
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..10e841cfac
--- /dev/null
+++ b/usr/src/lib/libdwarf/Makefile.com
@@ -0,0 +1,94 @@
+#
+# 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
+
+SMATCH = off
+
+.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 ad40eb762a..ad40eb762a 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..15a899f96f
--- /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..25c9eb2892
--- /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 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++;
+ }
+ (void) 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;
+ (void) 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/man/man1/Makefile b/usr/src/man/man1/Makefile
index 8629eff5d0..5fce6a9c4f 100644
--- a/usr/src/man/man1/Makefile
+++ b/usr/src/man/man1/Makefile
@@ -84,6 +84,8 @@ MANFILES= acctcom.1 \
csh.1 \
csplit.1 \
ctags.1 \
+ ctfdiff.1 \
+ ctfdump.1 \
ctrun.1 \
ctstat.1 \
ctwatch.1 \
diff --git a/usr/src/man/man1/ctfdiff.1 b/usr/src/man/man1/ctfdiff.1
new file mode 100644
index 0000000000..145d8ff194
--- /dev/null
+++ b/usr/src/man/man1/ctfdiff.1
@@ -0,0 +1,348 @@
+.\"
+.\" 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.
+.\"
+.Dd Oct 4, 2014
+.Dt CTFDIFF 1
+.Os
+.Sh NAME
+.Nm ctfdiff
+.Nd compare two CTF containers
+.Sh SYNOPSIS
+.Nm ctfdiff
+.Op Fl afIloqt
+.Op Fl F Ar function
+.Op Fl O Ar object
+.Op Fl p Ar parent1
+.Op Fl p Ar parent2
+.Op Fl T Ar type
+.Ar file1 file2
+.Sh DESCRIPTION
+The
+.Nm
+utility identifies differences between the contents of the
+.Sy CTF
+containers found in
+.Em file1
+and
+.Em file2 .
+.Lp
+.Nm
+can find differences between two
+.Sy CTF
+container's
+.Sy labels ,
+.Sy functions ,
+.Sy objects ,
+and
+.Sy types .
+When no options are specified,
+.Nm
+will only consider
+.Sy functions ,
+.Sy objects,
+and
+.Sy types .
+.Lp
+Two
+.Sy labels
+are the same if they have the same name.
+Two
+.Sy objects
+are the same if they have the same name and the type of the
+object is the same.
+Two
+.Sy functions
+are considered the same if they have the same name, the same return
+type, the same number of arguments, and the types of their arguments are
+the same.
+.Lp
+Two
+.Sy types
+are considered the same if they have the same name, they represent the same
+kind of thing, and the contents of the type are the same.
+This varies for each specific kind, for example, two structs are the
+same if they have the same members whose types, offsets, and names are
+all the same.
+For more information on the specifics for what we look at
+for each kind of type, and the kinds themselves, see the information we
+use to encode them in
+.Xr ctf 4 .
+If the option
+.Fl I
+is specified, then the names of basic integer types are ignored.
+For an example of where this makes sense, see
+.Sy Example 4 .
+.Lp
+If the
+.Sy CTF
+container found inside of either
+.Em file1
+or
+.Em file2
+has been uniquified (see
+.Xr ctf 4
+for more on uniquification), then the parent
+.Sy CTF
+container is also required for the diff to complete.
+.Sh OPTIONS
+The following options are supported:
+.Bl -hang -width Ds
+.It Fl a
+.Bd -filled -compact
+Diff
+.Sy labels ,
+.Sy types ,
+.Sy objects ,
+and
+.Sy functions .
+.Ed
+.It Fl f
+.Bd -filled -compact
+Diff
+.Sy function
+type argument information.
+.Ed
+.It Fl F Ar function
+.Bd -filled -compact
+When diffing
+.Sy functions ,
+only consider the function
+.Em function .
+This option requires that the option
+.Fl f
+be specified and can be repeated multiple times.
+.Ed
+.It Fl I
+.Bd -filled -compact
+Ignore the names of integral types.
+This option is useful when comparing types between two
+.Sy CTF
+containers that have different programming models.
+In this case, when comparing integers, the name of the type is not
+considered.
+This means that the ILP32 type long which is a 32-bit wide signed
+integer is the same as the LP64 type int which is a 32-bit wide signed
+integer, even though they have different names.
+.Ed
+.It Fl l
+.Bd -filled -compact
+Diff the
+.Sy labels
+contained inside the
+.Sy CTF
+containers.
+.Ed
+.It Fl o
+.Bd -filled -compact
+Diff type information for
+.Sy objects .
+.Ed
+.It Fl O Ar object
+.Bd -filled -compact
+When diffing type information for
+.Sy objects ,
+only compare if the object is name
+.Em object .
+This option requires
+.Fl o
+to be specified and can be repeated multiple times.
+.Ed
+.It Fl p Ar parent1
+.Bd -filled -compact
+Specifies the path of file that is the parent of the
+.Sy CTF
+container inside of
+.Em file1
+is
+.Em parent1 .
+This option is required if
+.Em file1
+has been uniquified.
+For more information on uniquification, see
+.Xr ctf 4 .
+.Ed
+.It Fl P Ar parent2
+.Bd -filled -compact
+Specifies the path of file that is the parent of the
+.Sy CTF
+container inside of
+.Em file2 is
+.Em parent2 .
+This option is required if
+.Em file1
+has been uniquified.
+For more information on uniquification, see
+.Xr ctf 4 .
+.Ed
+.It Fl q
+.Bd -filled -compact
+Enables quiet mode.
+Standard output from the diff will not be emitted.
+However, diagnostics messages will still be emitted to standard error.
+.Ed
+.It Fl t
+.Bd -filled -compact
+Diff the
+.Sy type
+information sections in the
+.Sy CTF
+containers.
+.Ed
+.It Fl T Ar type
+.Bd -filled -compact
+When diffing the
+.Sy types
+section, only consider it if the type is name
+.Em type .
+Types specified here do not impact the diffing of
+.Sy objects
+or
+.Sy functions .
+Even with
+.Fl T
+specified, other types will be diffed as necessary for the evaluation of
+the named types; however, the results of those intermediate differences
+will not impact the results of
+.Nm ,
+only named types are considered when evaluating the exit status of
+.Nm .
+.Ed
+.El
+.Sh EXIT STATUS
+.Bl -inset
+.It Sy 0
+.Bd -filled -offset indent -compact
+Execution completed successfully, no differences were detected
+between
+.Em file1
+and
+.Em file2 .
+.Ed
+.It Sy 1
+.Bd -filled -offset indent -compact
+Execution completed successfully, but differences were detected
+between
+.Em file1
+and
+.Em file2 .
+.Ed
+.It Sy 2
+.D1 Invalid command line options were specified.
+.It Sy 3
+.D1 A fatal error occurred.
+.El
+.Sh EXAMPLES
+.Sy Example 1
+Diffing Two
+.Sy CTF
+Containers
+.Lp
+The following example compares two
+.Sy CTF
+containers using the default set
+of comparisons:
+.Sy objects ,
+.Sy functions ,
+and
+.Sy types .
+.Bd -literal -offset 6n
+$ ctfdiff /usr/lib/libc.so.1 /usr/lib/libdtrace.so.1
+ctf container /usr/lib/libc.so.1 type 37 is different
+ctf container /usr/lib/libc.so.1 type 38 is different
+ctf container /usr/lib/libc.so.1 type 39 is different
+ctf container /usr/lib/libc.so.1 type 40 is different
+ctf container /usr/lib/libc.so.1 type 41 is different
+ctf container /usr/lib/libc.so.1 type 42 is different
+ctf container /usr/lib/libc.so.1 type 43 is different
+ctf container /usr/lib/libc.so.1 type 47 is different
+ctf container /usr/lib/libc.so.1 type 48 is different
+ctf container /usr/lib/libc.so.1 type 49 is different
+\&...
+.Ed
+.Sy Example 2
+Diffing Types Between Two
+.Sy CTF
+Containers with Parents
+.Lp
+The following example compares two
+.Sy CTF
+containers
+.Sy /ws/rm/zlan/proto/kernel/drv/amd64/vnd
+and
+.Sy /ws/rm/zlan/proto/kernel/drv/amd64/overlay
+that have been uniquified against the same container
+.Sy /ws/rm/zlan/proto/kernel/amd64/genunix .
+.Bd -literal -offset 6n
+$ ctfdiff -t -p /ws/rm/zlan/proto/kernel/amd64/genunix \\
+ -P /ws/rm/zlan/proto/kernel/amd64/genunix \\
+ /ws/rm/zlan/proto/kernel/drv/amd64/vnd \\
+ /ws/rm/zlan/proto/kernel/drv/amd64/overlay
+ctf container /ws/rm/zlan/proto/kernel/drv/amd64/vnd type 32769 is different
+ctf container /ws/rm/zlan/proto/kernel/drv/amd64/vnd type 32770 is different
+ctf container /ws/rm/zlan/proto/kernel/drv/amd64/vnd type 32771 is different
+ctf container /ws/rm/zlan/proto/kernel/drv/amd64/vnd type 32772 is different
+ctf container /ws/rm/zlan/proto/kernel/drv/amd64/vnd type 32774 is different
+ctf container /ws/rm/zlan/proto/kernel/drv/amd64/vnd type 32775 is different
+ctf container /ws/rm/zlan/proto/kernel/drv/amd64/vnd type 32776 is different
+ctf container /ws/rm/zlan/proto/kernel/drv/amd64/vnd type 32777 is different
+ctf container /ws/rm/zlan/proto/kernel/drv/amd64/vnd type 32778 is different
+ctf container /ws/rm/zlan/proto/kernel/drv/amd64/vnd type 32779 is different
+\&...
+.Ed
+.Lp
+.Sy Example 3
+Diffing a Specific Function in Two
+.Sy CTF
+Containers
+.Lp
+This example shows us looking for differences in the function
+.Sy libzfs_core_init
+in two different version of the library
+.Sy libzfs_core.so.1 .
+.Bd -literal -offset 6n
+$ ctfdiff -f -F libzfs_core_init /usr/lib/libzfs_core.so.1 \\
+ /ws/rm/ctf/proto/usr/lib/libzfs_core.so.1
+$ echo $?
+.Ed
+.Lp
+.Sy Example 4
+Diffing Types to Find Differences Between Different Data Models.
+.Lp
+This example looks for differences between structures used in an ioctl
+that the kernel wants to be bitness neutral by comparing a 32-bit and
+64-bit library that consumes it.
+In this example, we'll use the library
+.Sy libvnd.so.1
+and the types
+.Sy vnd_ioc_attach_t ,
+.Sy vnd_ioc_link_t ,
+.Sy vnd_ioc_unlink_t ,
+.Sy vnd_ioc_buf_t ,
+and
+.Sy vnd_ioc_info_t .
+.Bd -literal -offset 6n
+$ ctfdiff -t -I -T vnd_ioc_attach_t -T vnd_ioc_link_t \\
+ -T vnd_ioc_unlink_t -T vnd_ioc_buf_t -T vnd_ioc_info_t \\
+ i386/libvnd.so.1 amd64/libvnd.so.1
+$ echo $?
+0
+.Ed
+.Sh INTERFACE STABILITY
+The command syntax is
+.Sy Committed .
+The output format is
+.Sy Uncommitted .
+.Sh SEE ALSO
+.Xr ctfdump 1 ,
+.Xr diff 1 ,
+.Xr ctf 4
diff --git a/usr/src/man/man1/ctfdump.1 b/usr/src/man/man1/ctfdump.1
new file mode 100644
index 0000000000..b80c856eaf
--- /dev/null
+++ b/usr/src/man/man1/ctfdump.1
@@ -0,0 +1,446 @@
+.\"
+.\" 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 2018, Joyent, Inc.
+.\"
+.Dd Oct 2, 2018
+.Dt CTFDUMP 1
+.Os
+.Sh NAME
+.Nm ctfdump
+.Nd dump parts of ctf data from files
+.Sh SYNOPSIS
+.Nm ctfdump
+.Op Fl cdfhlsSt
+.Op Fl p Ar parent
+.Op Fl u Ar outfile
+.Ar file
+.Sh DESCRIPTION
+The
+.Nm
+utility dumps and decodes the
+.Sy CTF
+data contained inside of
+.Sy ELF
+objects and raw
+.Sy CTF
+files.
+.Lp
+.Nm
+can dump information about the
+.Sy CTF header ,
+the
+.Sy labels
+encoded in the
+.Sy CTF
+container,
+the types of
+.Sy data objects ,
+the internal
+.Sy string
+table,
+the types of the return function and the arguments for
+.Sy functions ,
+and of course, it displays information about the
+.Sy types
+defined in the
+.Sy CTF
+container.
+.Lp
+.Nm
+can also be used to dump out the raw
+.Sy CTF
+data and send it to another file.
+When writing out data, it always ensures that the
+.Sy CTF
+data is decompressed.
+In this form, the
+.Sy CTF
+data can be inspected using
+.Nm
+and other tools such as
+.Xr mdb 1 .
+.Lp
+.Nm
+in
+.Fl c
+mode will generate C-style output, which can be used for comparison.
+Note that this output is not directly compilable.
+.Lp
+When no options are specified,
+.Nm
+displays all information, except the C-style output.
+However, when the
+.Fl u
+option is used, then no information is displayed by default, unless
+requested through the appropriate option.
+.Sh OPTIONS
+The following options are supported:
+.Bl -hang -width Ds
+.It Fl c
+.Bd -filled -compact
+Generate C-style output.
+.Ed
+.It Fl d
+.Bd -filled -compact
+Dump the types of symbols that correspond to objects.
+.Ed
+.It Fl f
+.Bd -filled -compact
+Dump the types of the return values and arguments of the functions.
+.Ed
+.It Fl h
+.Bd -filled -compact
+Dump the
+.Sy CTF
+header
+.Ed
+.It Fl l
+.Bd -filled -compact
+Dump all
+.Sy CTF
+labels associated with the file.
+.Ed
+.It Fl p Ar parent
+.Bd -filled -compact
+Use the type information in
+.Em parent
+to supplement output.
+This is useful when a
+.Nm CTF
+container has been
+.Sy uniquified
+against
+.Em parent .
+This allows
+.Nm
+to use the names of types when used with
+.Fl t .
+.Ed
+.It Fl s
+.Bd -filled -compact
+Dump the internal
+.Sy CTF
+string table
+.Ed
+.It Fl S
+.Bd -filled -compact
+Displays statistics about the
+.Sy CTF
+container.
+.Ed
+.It Fl t
+.Bd -filled -compact
+Dump the type information contained in the
+.Sy CTF
+conatiner.
+.Ed
+.It Fl u Ar outfile
+.Bd -filled -compact
+Copies the uncompressed
+.Sy CTF
+data to the file specified by
+.Em outfile .
+This can be used to make it easier to inspect the raw
+.Sy CTF
+data.
+.Ed
+.El
+.Sh OUTPUT
+When the
+.Nm
+utility is executed with its default options, it prints out a textual
+representation of the
+.Sy CTF
+information.
+Note, the output format of
+.Nm
+is subject to change at any time and should not be relied upon as a
+stable format to be used for parsing.
+.Ss CTF Header
+This section describes the values in the
+.Sy CTF
+header.
+Each line in the section describes the value of one of the
+members of the header.
+For more information on the meaning and interpretation of these members,
+see
+.Xr ctf 4 .
+.Ss Label Table
+This section describes information about the labels present in the
+.Sy CTF
+information.
+Each entry in this section, if present, starts with a
+number and is followed by a string.
+An example entry in the label section might look like:
+.Bd -literal
+\&...
+ 2270 joyent_20151001T070028Z
+\&...
+.Ed
+.Pp
+The number,
+.Em 2270 ,
+represents the last type that the label applies to.
+The string,
+.Em joyent_20151001T070028Z ,
+is the name of the label.
+In this case, if there were no other labels,
+it would indicate that the label applied to all types up to, and
+including, the type number 2270.
+For more information on how labels are used with
+.Sy CTF
+information, see the section
+.Em The Label Section
+in
+.Xr ctf 4 .
+.Ss Data Objects
+This section describes the type information relating to data objects
+from the symbol table.
+An entry for a data object consists of four columns.
+The first column is just a monotonic ID.
+The second number is the type id of the object.
+The third column is the name of the symbol and the fourth column is the
+corresponding index from the symbol table.
+.Pp
+Take for example, the following couple of entries:
+.Bd -literal
+\&...
+ [0] 13 hz (48)
+ [1] 78 _nd (49)
+ [2] 1656 __pfmt_label (56)
+ [3] 926 _aio_hash (68)
+ [4] 13 _lio_free (70)
+ [5] 1321 u8_number_of_bytes (73)
+\&...
+.Ed
+.Pp
+Let's take the first entry in the list above.
+The symbol is named
+.Sy hz .
+It is the first data object, as indicated by the number zero in
+brackets.
+It has a type id of 13 and in this case, it has a symbol table index of
+48.
+.Ss Functions
+This section describes the type information for functions.
+For each function present in the symbol table with type information, the
+function's entry into the function section, the function's name, the
+function's symbol table index, the function's return type, and the types
+of the function's arguments are printed.
+If a function is a variadic function, then the variadic argument is
+printed as the string
+.Sy '...' .
+.Pp
+Take for example, the following couple of entries:
+.Bd -literal
+\&...
+ [687] pfprint_stack (3110) returns: 11 args: (385, 115, 29, 1704, 223, 116, 2)
+ [688] pfprint_stddev (3111) returns: 11 args: (385, 115, 29, 1704, 223, 116, 2)
+ [689] pfprint_quantize (3112) returns: 11 args: (385, 115, 29, 1704, 223, 116, 2)
+ [690] pfprint_lquantize (3113) returns: 11 args: (385, 115, 29, 1704, 223, 116, 2)
+ [691] pfprint_llquantize (3114) returns: 11 args: (385, 115, 29, 1704, 223, 116, 2)
+\&...
+.Ed
+.Pp
+The first column is the function's entry number in the function type
+information section.
+It is enclosed in brackets.
+The next column is the function's name and it is followed in parenthesis
+by the its index in the
+symbol table.
+The following portions of this entry describe the return
+type and then all of the arguments, in positional order.
+.Ss Types
+The types section gives information about each type in the
+.Sy CTF
+container.
+Each entry begins with its type identifier.
+The type identifier may either be in square brackets or in angle
+brackets.
+If the type identifier is enclosed in angle brackets, then that
+represents that it is a root type or top-level type.
+If it is square brackets, then it is not.
+For more information on root types, see
+.Xr ctf 4 .
+.Pp
+Next, the type will have its name and kind.
+If it is an array, it will be followed with a subscript that describes
+the number of entries in the array.
+If it is a pointer, it will followed by the
+.Sy *
+symbol to indicate that it is a pointer.
+If the type has the
+.Sy const ,
+.Sy volatile ,
+.Sy typedef ,
+or
+.Sy restrict
+keyword applied to it, that will precede the name.
+All of these reference types, including pointer, will then be followed
+with an example of the type that they refer to.
+.Pp
+Types which are an integral or floating point value will be followed by
+information about their encoding and the number of bits represented in
+the type.
+.Pp
+Arrays will be followed by two different entries, the contents and
+index.
+The contents member contains the type id of the array's contents
+and the index member describes a type which can represent the array's
+index.
+.Pp
+Structures and unions will be preceded with the corresponding C keyword,
+.Sy struct
+or
+.Sy union .
+After that, the size in bytes of the structure will be indicated.
+ON each subsequent line, a single member will be listed.
+That line will contain the member's name, it's type identifier, and the
+offset into the structure that it can be found in, in bits.
+.Pp
+The following show examples of type information for all of these
+different types:
+.Bd -literal
+\&...
+ [5] char [12] contents: 1, index: 2
+ [6] short encoding=SIGNED offset=0 bits=16
+ <7> struct exit_status (4 bytes)
+ e_termination type=6 off=0
+ e_exit type=6 off=16
+
+ <8> typedef time_t refers to 2
+ <9> struct utmp (36 bytes)
+ ut_user type=3 off=0
+ ut_id type=4 off=64
+ ut_line type=5 off=96
+ ut_pid type=6 off=192
+ ut_type type=6 off=208
+ ut_exit type=7 off=224
+ ut_time type=8 off=256
+
+ <10> struct utmp * refers to 9
+ [11] const struct utmp refers to 9
+ [12] const struct utmp * refers to 11
+ <13> int encoding=SIGNED offset=0 bits=32
+ <14> typedef int32_t refers to 13
+\&...
+.Ed
+.Ss String Table
+This section describes all of the strings that are present in the
+.Sy CTF
+container.
+Each line represents an entry in the string table.
+First the byte offset into the string table is shown in brackets and
+then the
+string's value is displayed.
+Note the following examples:
+.Bd -literal
+ [0] \0
+ [1] joyent_20151001T070028Z
+ [25] char
+ [30] long
+ [35] short
+.Ed
+.Ss Statistics
+This section contains miscellaneous statistics about the
+.Sy CTF
+data present.
+Each line contains a single statistic.
+A brief explanation of the statistic is placed first, followed by an
+equals sign, and then finally the value.
+.Sh EXIT STATUS
+.Bl -inset
+.It Sy 0
+.Dl Execution completed successfully.
+.It Sy 1
+.Dl A fatal error occurred.
+.It Sy 2
+.Dl Invalid command line options were specified.
+.El
+.Sh EXAMPLES
+.Sy Example 1
+Displaying the Type Section of a Single File
+.Lp
+The following example dumps the type section of the file
+.Sy /usr/lib/libc.so.1 .
+.Bd -literal -offset 6n
+$ ctfdump -t /usr/lib/libc.so.1
+- Types ----------------------------------------------------
+
+ <1> int encoding=SIGNED offset=0 bits=32
+ <2> long encoding=SIGNED offset=0 bits=32
+ <3> typedef pid_t refers to 2
+ <4> unsigned int encoding=0x0 offset=0 bits=32
+ <5> typedef uid_t refers to 4
+ <6> typedef gid_t refers to 5
+ <7> typedef uintptr_t refers to 4
+\&...
+.Ed
+.Lp
+.Sy Example 2
+Dumping the CTF data to Another File
+.Lp
+The following example dumps the entire CTF data from the file
+.Sy /usr/lib/libc.so.1
+and places it into the file
+.Sy ctf.out .
+This then shows how you can use the
+.Xr mdb 1
+to inspect its contents.
+.Bd -literal -offset 6n
+$ ctfdump -u ctf.out /usr/lib/libc.so.1
+$ mdb ./ctf.out
+> ::typedef -r /usr/lib/libctf.so.1
+> 0::print ctf_header_t
+{
+ cth_preamble = {
+ ctp_magic = 0xcff1
+ ctp_version = 0x2
+ ctp_flags = 0
+ }
+ cth_parlabel = 0
+ cth_parname = 0
+ cth_lbloff = 0
+ cth_objtoff = 0x8
+ cth_funcoff = 0x5e0
+ cth_typeoff = 0x7178
+ cth_stroff = 0x12964
+ cth_strlen = 0x7c9c
+}
+.Ed
+.Lp
+.Sy Example 3
+Dumping C-style output
+.Bd -literal -offset 6n
+$ ctfdump -c ./genunix | more
+/* Types */
+
+typedef Elf64_Addr Addr;
+
+typedef unsigned char Bool;
+
+typedef struct CK_AES_CCM_PARAMS CK_AES_CCM_PARAMS;
+
+typedef struct CK_AES_GCM_PARAMS CK_AES_GCM_PARAMS;
+\&...
+.Ed
+.Sh INTERFACE STABILITY
+The command syntax is
+.Sy Committed .
+The output format is
+.Sy Uncommitted .
+.Sh SEE ALSO
+.Xr ctfdiff 1 ,
+.Xr dump 1 ,
+.Xr elfdump 1 ,
+.Xr mdb 1 ,
+.Xr ctf 4
diff --git a/usr/src/pkg/manifests/developer-build-onbld.mf b/usr/src/pkg/manifests/developer-build-onbld.mf
index 1e45753b0d..8339396dc7 100644
--- a/usr/src/pkg/manifests/developer-build-onbld.mf
+++ b/usr/src/pkg/manifests/developer-build-onbld.mf
@@ -78,6 +78,7 @@ file path=opt/onbld/bin/$(ARCH)/codereview mode=0555
$(i386_ONLY)file path=opt/onbld/bin/$(ARCH)/cpcgen mode=0555
file path=opt/onbld/bin/$(ARCH)/cscope-fast mode=0555
file path=opt/onbld/bin/$(ARCH)/ctfconvert mode=0555
+file path=opt/onbld/bin/$(ARCH)/ctfdiff mode=0555
file path=opt/onbld/bin/$(ARCH)/ctfdump mode=0555
file path=opt/onbld/bin/$(ARCH)/ctfmerge mode=0555
file path=opt/onbld/bin/$(ARCH)/ctfstabs mode=0555
@@ -145,6 +146,7 @@ file path=opt/onbld/etc/exception_lists/interface_cmp
file path=opt/onbld/etc/its.conf
file path=opt/onbld/etc/its.reg
file path=opt/onbld/lib/$(ARCH)/64/libmakestate.so.1
+file path=opt/onbld/lib/$(ARCH)/libctf.so.1 mode=0555
file path=opt/onbld/lib/$(ARCH)/libdwarf.so.1
file path=opt/onbld/lib/$(ARCH)/libmakestate.so.1
file path=opt/onbld/lib/perl/onbld_elfmod.pm
@@ -395,10 +397,11 @@ legacy pkg=SUNWonbld desc="tools used to build the OS-Net consolidation" \
name="OS-Net Build Tools" version=11.11,REV=2009.10.22
license cr_Sun license=cr_Sun
license lic_CDDL license=lic_CDDL
-license usr/src/tools/ctf/dwarf/THIRDPARTYLICENSE \
- license=usr/src/tools/ctf/dwarf/THIRDPARTYLICENSE
+license usr/src/lib/libdwarf/THIRDPARTYLICENSE \
+ license=usr/src/lib/libdwarf/THIRDPARTYLICENSE
link path=opt/onbld/bin/$(ARCH)/dmake target=make
link path=opt/onbld/bin/git-nits target=git-pbchk
+link path=opt/onbld/lib/$(ARCH)/libctf.so target=libctf.so.1
$(python2tools_ONLY)link path=opt/onbld/lib/python \
target=python$(PYTHON_VERSION)
link path=opt/onbld/man/man1onbld/git-nits.1onbld target=git-pbchk.1onbld
diff --git a/usr/src/pkg/manifests/developer-debug-ctf.mf b/usr/src/pkg/manifests/developer-debug-ctf.mf
new file mode 100644
index 0000000000..30485f3284
--- /dev/null
+++ b/usr/src/pkg/manifests/developer-debug-ctf.mf
@@ -0,0 +1,32 @@
+#
+# 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 2016 Joyent, Inc.
+#
+
+set name=pkg.fmri value=pkg:/developer/debug/ctf@$(PKGVERS)
+set name=pkg.description value="Compact C Type Format (CTF) Tools"
+set name=pkg.summary value="CTF Tools"
+set name=info.classification \
+ value=org.opensolaris.category.2008:Development/System
+set name=variant.arch value=$(ARCH)
+dir path=usr group=sys
+dir path=usr/bin
+dir path=usr/share
+dir path=usr/share/man
+dir path=usr/share/man/man1
+file path=usr/bin/ctfdiff mode=0555
+file path=usr/bin/ctfdump mode=0555
+file path=usr/share/man/man1/ctfdiff.1
+file path=usr/share/man/man1/ctfdump.1
+license cr_Sun license=cr_Sun
+license lic_CDDL license=lic_CDDL
diff --git a/usr/src/pkg/manifests/system-library.mf b/usr/src/pkg/manifests/system-library.mf
index 941e64f74c..8e06bfc121 100644
--- a/usr/src/pkg/manifests/system-library.mf
+++ b/usr/src/pkg/manifests/system-library.mf
@@ -200,6 +200,7 @@ file path=lib/$(ARCH64)/libdl.so.1
file path=lib/$(ARCH64)/libdladm.so.1
file path=lib/$(ARCH64)/libdlpi.so.1
file path=lib/$(ARCH64)/libdoor.so.1
+file path=lib/$(ARCH64)/libdwarf.so.1
file path=lib/$(ARCH64)/libefi.so.1
file path=lib/$(ARCH64)/libelf.so.1
file path=lib/$(ARCH64)/libfakekernel.so.1
@@ -279,6 +280,7 @@ file path=lib/libdl.so.1
file path=lib/libdladm.so.1
file path=lib/libdlpi.so.1
file path=lib/libdoor.so.1
+file path=lib/libdwarf.so.1
file path=lib/libefi.so.1
file path=lib/libelf.so.1
file path=lib/libelfsign.so.1
@@ -587,6 +589,8 @@ license usr/src/lib/libcmd/THIRDPARTYLICENSE \
license=usr/src/lib/libcmd/THIRDPARTYLICENSE
license usr/src/lib/libdll/THIRDPARTYLICENSE \
license=usr/src/lib/libdll/THIRDPARTYLICENSE
+license usr/src/lib/libdwarf/THIRDPARTYLICENSE \
+ license=usr/src/lib/libdwarf/THIRDPARTYLICENSE
license usr/src/lib/libinetutil/common/THIRDPARTYLICENSE \
license=usr/src/lib/libinetutil/common/THIRDPARTYLICENSE
license usr/src/lib/libkmf/THIRDPARTYLICENSE \
@@ -846,6 +850,8 @@ link path=usr/lib/$(ARCH64)/libdoor.so \
target=../../../lib/$(ARCH64)/libdoor.so.1
link path=usr/lib/$(ARCH64)/libdoor.so.1 \
target=../../../lib/$(ARCH64)/libdoor.so.1
+link path=usr/lib/$(ARCH64)/libdwarf.so.1 \
+ target=../../../lib/$(ARCH64)/libdwarf.so.1
link path=usr/lib/$(ARCH64)/libefi.so \
target=../../../lib/$(ARCH64)/libefi.so.1
link path=usr/lib/$(ARCH64)/libefi.so.1 \
@@ -1105,6 +1111,7 @@ link path=usr/lib/libdlpi.so target=../../lib/libdlpi.so.1
link path=usr/lib/libdlpi.so.1 target=../../lib/libdlpi.so.1
link path=usr/lib/libdoor.so target=../../lib/libdoor.so.1
link path=usr/lib/libdoor.so.1 target=../../lib/libdoor.so.1
+link path=usr/lib/libdwarf.so.1 target=../../lib/libdwarf.so.1
link path=usr/lib/libefi.so target=../../lib/libefi.so.1
link path=usr/lib/libefi.so.1 target=../../lib/libefi.so.1
link path=usr/lib/libelf.so target=../../lib/libelf.so.1
diff --git a/usr/src/pkg/manifests/system-test-utiltest.mf b/usr/src/pkg/manifests/system-test-utiltest.mf
index 3b4003b812..5937d96c61 100644
--- a/usr/src/pkg/manifests/system-test-utiltest.mf
+++ b/usr/src/pkg/manifests/system-test-utiltest.mf
@@ -36,6 +36,7 @@ dir path=opt/util-tests/tests/dis/sparc
dir path=opt/util-tests/tests/files
dir path=opt/util-tests/tests/libnvpair_json
dir path=opt/util-tests/tests/libsff
+dir path=opt/util-tests/tests/mergeq
file path=opt/util-tests/README mode=0444
file path=opt/util-tests/bin/print_json mode=0555
file path=opt/util-tests/bin/utiltest mode=0555
@@ -331,6 +332,8 @@ file path=opt/util-tests/tests/libsff/libsff_opts.out mode=0444
file path=opt/util-tests/tests/libsff/libsff_strings mode=0555
file path=opt/util-tests/tests/libsff/libsff_wave mode=0555
file path=opt/util-tests/tests/libsff/libsff_wave.out mode=0444
+file path=opt/util-tests/tests/mergeq/mqt mode=0555
+file path=opt/util-tests/tests/mergeq/wqt mode=0555
file path=opt/util-tests/tests/printf_test mode=0555
file path=opt/util-tests/tests/set-linkprop mode=0555
file path=opt/util-tests/tests/xargs_test mode=0555
diff --git a/usr/src/test/util-tests/runfiles/default.run b/usr/src/test/util-tests/runfiles/default.run
index 046888427f..f22bdd5094 100644
--- a/usr/src/test/util-tests/runfiles/default.run
+++ b/usr/src/test/util-tests/runfiles/default.run
@@ -31,6 +31,9 @@ outputdir = /var/tmp/test_results
[/opt/util-tests/tests/xargs_test]
+[/opt/util-tests/tests/mergeq/mqt]
+[/opt/util-tests/tests/mergeq/wqt]
+
[/opt/util-tests/tests/libnvpair_json]
tests = ['json_00_blank', 'json_01_boolean', 'json_02_numbers',
'json_03_empty_arrays', 'json_04_number_arrays', 'json_05_strings',
diff --git a/usr/src/test/util-tests/tests/Makefile b/usr/src/test/util-tests/tests/Makefile
index a32af36d11..35751b8e30 100644
--- a/usr/src/test/util-tests/tests/Makefile
+++ b/usr/src/test/util-tests/tests/Makefile
@@ -17,6 +17,6 @@
#
SUBDIRS = date dis dladm iconv libnvpair_json libsff printf xargs grep_xpg4
-SUBDIRS += demangle
+SUBDIRS += demangle mergeq workq
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..ef6c6abb99
--- /dev/null
+++ b/usr/src/test/util-tests/tests/mergeq/Makefile
@@ -0,0 +1,64 @@
+#
+# 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
+
+PROG = mqt
+OBJS = mqt.o mergeq.o
+
+include $(SRC)/cmd/Makefile.cmd
+include $(SRC)/cmd/Makefile.ctf
+include $(SRC)/test/Makefile.com
+
+CMDS = $(PROG:%=$(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)
+
+$(TESTDIR):
+ $(INS.dir)
+
+$(TESTDIR)/%: %
+ $(INS.file)
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..e61e9173d2
--- /dev/null
+++ b/usr/src/test/util-tests/tests/mergeq/mqt.c
@@ -0,0 +1,217 @@
+/*
+ * 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)
+{
+ 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..ab5455fd86
--- /dev/null
+++ b/usr/src/test/util-tests/tests/workq/Makefile
@@ -0,0 +1,64 @@
+#
+# 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
+
+PROG = wqt
+OBJS = wqt.o workq.o
+
+include $(SRC)/cmd/Makefile.cmd
+include $(SRC)/cmd/Makefile.ctf
+include $(SRC)/test/Makefile.com
+
+CMDS = $(PROG:%=$(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)
+
+$(TESTDIR):
+ $(INS.dir)
+
+$(TESTDIR)/%: %
+ $(INS.file)
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 dee410e27c..3ea3da7ca1 100644
--- a/usr/src/tools/ctf/Makefile
+++ b/usr/src/tools/ctf/Makefile
@@ -26,7 +26,9 @@
include ../Makefile.tools
-SUBDIRS = cvt dump stabs ctfstrip
+SUBDIRS = stabs ctfstrip libctf ctfdiff ctfdump
+$(BUILD_OLD_CTF_TOOLS)SUBDIRS += cvt
+$(BUILD_NEW_CTF_TOOLS)SUBDIRS += ctfmerge ctfconvert
.PARALLEL: $(SUBDIRS)
@@ -38,6 +40,11 @@ lint := TARGET= lint
.KEEP_STATE:
+ctfmerge: libctf
+ctfdiff: libctf
+ctfdump: libctf
+ctfconvert: libctf
+
all clean clobber install lint: dwarf .WAIT $(SUBDIRS)
dwarf $(SUBDIRS): FRC
diff --git a/usr/src/tools/ctf/Makefile.ctf b/usr/src/tools/ctf/Makefile.ctf
index 56e569a674..c07310eddf 100644
--- a/usr/src/tools/ctf/Makefile.ctf
+++ b/usr/src/tools/ctf/Makefile.ctf
@@ -29,12 +29,12 @@ include ../../../Makefile.tools
#
# A `make install' from the tools directory needs to work, even if the rest of
# the tree hasn't been built. As such, we need to tell the ctf builds how
-# to find the ctf specific headers located outside the tools subtree. We also
+# to find the ctf specific headers located outside the tools subtree. We also
# want to tell them how to get to the common tools source files.
-#
+#
# For additonal details on the ordering of includes via -I, see the comments
# in $(SRC)/tools/ctf/common/ctf_headers.h.
-#
+#
HDRDIRS= \
-_gcc=-nostdinc \
@@ -42,6 +42,7 @@ HDRDIRS= \
-I$(SRC) \
-I/usr/include \
-I$(SRC)/uts/common \
+ -I$(SRC)/common/ctf \
-I$(NATIVE_ADJUNCT)/include
CPPFLAGS += $(HDRDIRS)
diff --git a/usr/src/tools/ctf/common/ctf_headers.h b/usr/src/tools/ctf/common/ctf_headers.h
index b00b8fd9a6..572fb03d4d 100644
--- a/usr/src/tools/ctf/common/ctf_headers.h
+++ b/usr/src/tools/ctf/common/ctf_headers.h
@@ -22,21 +22,21 @@
/*
* Copyright 2003 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2018 Joyent, Inc.
*/
#ifndef _CTF_HEADERS_H
#define _CTF_HEADERS_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* Because the ON tools are executed on the system where they are built,
* the tools need to include the headers installed on the build system,
* rather than those in the ON source tree. However, some of the headers
* required by the tools are part of the ON source tree, but not delivered
- * as part of Solaris. These include the following:
+ * as part of illumos. 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
*
@@ -63,10 +63,22 @@
* This last -I include is needed in order to prevent a build failure
* when <sys/ctf_api.h> is included via a nested #include rather than
* an explicit path #include.
+ *
+ * We'll also include the local ccompile.h - older build systems often lack
+ * useful definitions like __unused. Finally, we'll also define ARRAY_SIZE:
+ * unfortunately, older systems sysmacros.h only have this defined for the
+ * kernel, and we can't easily pick it up otherwise.
*/
+#include <uts/common/sys/ccompile.h>
#include <uts/common/sys/ctf.h>
#include <uts/common/sys/ctf_api.h>
+#include <common/ctf/ctf_impl.h>
#include <lib/libctf/common/libctf.h>
+#include <lib/libctf/common/libctf_impl.h>
+
+#if !defined(ARRAY_SIZE)
+#define ARRAY_SIZE(x) (sizeof (x) / sizeof (x[0]))
+#endif
#endif /* _CTF_HEADERS_H */
diff --git a/usr/src/tools/ctf/dump/Makefile b/usr/src/tools/ctf/ctfconvert/Makefile
index 16d8280cdd..07fadc5f8f 100644
--- a/usr/src/tools/ctf/dump/Makefile
+++ b/usr/src/tools/ctf/ctfconvert/Makefile
@@ -23,7 +23,6 @@
# Copyright (c) 2001 by Sun Microsystems, Inc.
# All rights reserved.
#
-#ident "%Z%%M% %I% %E% SMI"
include ../../Makefile.tools
diff --git a/usr/src/tools/ctf/ctfconvert/Makefile.com b/usr/src/tools/ctf/ctfconvert/Makefile.com
new file mode 100644
index 0000000000..33268bd979
--- /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
+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/dump/sparc/Makefile b/usr/src/tools/ctf/ctfconvert/i386/Makefile
index 94e5883ae2..cd5462bee3 100644
--- a/usr/src/tools/ctf/dump/sparc/Makefile
+++ b/usr/src/tools/ctf/ctfconvert/i386/Makefile
@@ -23,6 +23,5 @@
# Copyright (c) 2001 by Sun Microsystems, Inc.
# All rights reserved.
#
-#ident "%Z%%M% %I% %E% SMI"
include ../Makefile.com
diff --git a/usr/src/tools/ctf/dump/i386/Makefile b/usr/src/tools/ctf/ctfconvert/sparc/Makefile
index 94e5883ae2..cd5462bee3 100644
--- a/usr/src/tools/ctf/dump/i386/Makefile
+++ b/usr/src/tools/ctf/ctfconvert/sparc/Makefile
@@ -23,6 +23,5 @@
# Copyright (c) 2001 by Sun Microsystems, Inc.
# All rights reserved.
#
-#ident "%Z%%M% %I% %E% SMI"
include ../Makefile.com
diff --git a/usr/src/tools/ctf/ctfdiff/Makefile b/usr/src/tools/ctf/ctfdiff/Makefile
new file mode 100644
index 0000000000..07fadc5f8f
--- /dev/null
+++ b/usr/src/tools/ctf/ctfdiff/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/ctfdiff/Makefile.com b/usr/src/tools/ctf/ctfdiff/Makefile.com
new file mode 100644
index 0000000000..3c5e19fb6e
--- /dev/null
+++ b/usr/src/tools/ctf/ctfdiff/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 = ctfdiff
+SRCS = ctfdiff.c
+
+include ../../Makefile.ctf
+
+CFLAGS += $(CCVERBOSE)
+LDLIBS += -lctf
+
+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/ctfdiff/%.c
+ $(COMPILE.c) $<
+
+$(ROOTONBLDMACHPROG): $(PROG)
+
+install: $(ROOTONBLDMACHPROG)
+
+clean:
+ $(RM) $(OBJS) $(LINTFILES)
+
+include $(SRC)/tools/Makefile.targ
diff --git a/usr/src/tools/ctf/ctfdiff/i386/Makefile b/usr/src/tools/ctf/ctfdiff/i386/Makefile
new file mode 100644
index 0000000000..89525f10f4
--- /dev/null
+++ b/usr/src/tools/ctf/ctfdiff/i386/Makefile
@@ -0,0 +1,16 @@
+#
+# 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.
+#
+
+include ../Makefile.com
diff --git a/usr/src/tools/ctf/ctfdiff/sparc/Makefile b/usr/src/tools/ctf/ctfdiff/sparc/Makefile
new file mode 100644
index 0000000000..89525f10f4
--- /dev/null
+++ b/usr/src/tools/ctf/ctfdiff/sparc/Makefile
@@ -0,0 +1,16 @@
+#
+# 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.
+#
+
+include ../Makefile.com
diff --git a/usr/src/tools/ctf/ctfdump/Makefile b/usr/src/tools/ctf/ctfdump/Makefile
new file mode 100644
index 0000000000..20d304216a
--- /dev/null
+++ b/usr/src/tools/ctf/ctfdump/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.
+#
+
+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/ctfdump/Makefile.com b/usr/src/tools/ctf/ctfdump/Makefile.com
new file mode 100644
index 0000000000..66e7a170c4
--- /dev/null
+++ b/usr/src/tools/ctf/ctfdump/Makefile.com
@@ -0,0 +1,47 @@
+#
+# 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 2018 Joyent, Inc.
+
+PROG = ctfdump
+SRCS = ctfdump.c
+
+include ../../Makefile.ctf
+
+CSTD = $(CSTD_GNU99)
+C99LMODE = -Xc99=%all
+CFLAGS += $(CCVERBOSE)
+LDLIBS += -lctf
+
+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/ctfdump/%.c
+ $(COMPILE.c) $<
+
+$(ROOTONBLDMACHPROG): $(PROG)
+
+install: $(ROOTONBLDMACHPROG)
+
+clean:
+ $(RM) $(OBJS) $(LINTFILES)
+
+include $(SRC)/tools/Makefile.targ
diff --git a/usr/src/tools/ctf/ctfdump/i386/Makefile b/usr/src/tools/ctf/ctfdump/i386/Makefile
new file mode 100644
index 0000000000..89525f10f4
--- /dev/null
+++ b/usr/src/tools/ctf/ctfdump/i386/Makefile
@@ -0,0 +1,16 @@
+#
+# 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.
+#
+
+include ../Makefile.com
diff --git a/usr/src/tools/ctf/ctfdump/sparc/Makefile b/usr/src/tools/ctf/ctfdump/sparc/Makefile
new file mode 100644
index 0000000000..89525f10f4
--- /dev/null
+++ b/usr/src/tools/ctf/ctfdump/sparc/Makefile
@@ -0,0 +1,16 @@
+#
+# 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.
+#
+
+include ../Makefile.com
diff --git a/usr/src/tools/ctf/ctfmerge/Makefile b/usr/src/tools/ctf/ctfmerge/Makefile
new file mode 100644
index 0000000000..07fadc5f8f
--- /dev/null
+++ b/usr/src/tools/ctf/ctfmerge/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/ctfmerge/Makefile.com b/usr/src/tools/ctf/ctfmerge/Makefile.com
new file mode 100644
index 0000000000..2702fa9d13
--- /dev/null
+++ b/usr/src/tools/ctf/ctfmerge/Makefile.com
@@ -0,0 +1,46 @@
+#
+# 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 = ctfmerge
+SRCS = ctfmerge.c
+
+include ../../Makefile.ctf
+
+CFLAGS += $(CCVERBOSE)
+LDLIBS += -lctf -lelf
+
+LDFLAGS += \
+ -L$(ROOTONBLDLIBMACH) \
+ '-R$$ORIGIN/../../lib/$(MACH)' \
+
+CPPFLAGS += -include ../../common/ctf_headers.h
+CERRWARN += -_gcc=-Wno-unused-variable
+CERRWARN += -_gcc=-Wno-uninitialized
+
+OBJS = $(SRCS:%.c=%.o)
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+%.o: $(SRC)/cmd/ctfmerge/%.c
+ $(COMPILE.c) $<
+
+$(ROOTONBLDMACHPROG): $(PROG)
+
+install: $(ROOTONBLDMACHPROG)
+
+clean:
+ $(RM) $(OBJS) $(LINTFILES)
+
+include $(SRC)/tools/Makefile.targ
diff --git a/usr/src/tools/ctf/ctfmerge/i386/Makefile b/usr/src/tools/ctf/ctfmerge/i386/Makefile
new file mode 100644
index 0000000000..cd5462bee3
--- /dev/null
+++ b/usr/src/tools/ctf/ctfmerge/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/ctfmerge/sparc/Makefile b/usr/src/tools/ctf/ctfmerge/sparc/Makefile
new file mode 100644
index 0000000000..cd5462bee3
--- /dev/null
+++ b/usr/src/tools/ctf/ctfmerge/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/cvt/Makefile.com b/usr/src/tools/ctf/cvt/Makefile.com
index 5cdccf6dfc..052cf02cc4 100644
--- a/usr/src/tools/ctf/cvt/Makefile.com
+++ b/usr/src/tools/ctf/cvt/Makefile.com
@@ -32,6 +32,7 @@ PROG=ctfconvert ctfmerge
GENSRCS= \
alist.c \
+ altexec.c \
barrier.c \
ctf.c \
fifo.c \
@@ -71,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/cvt/altexec.c b/usr/src/tools/ctf/cvt/altexec.c
new file mode 100644
index 0000000000..c986c0731a
--- /dev/null
+++ b/usr/src/tools/ctf/cvt/altexec.c
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+/*
+ * Alternate execution engine for CTF tools
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ctftools.h"
+
+void
+ctf_altexec(const char *env, int argc, char **argv)
+{
+ const char *alt;
+ char *altexec;
+
+ alt = getenv(env);
+ if (alt == NULL || *alt == '\0')
+ return;
+
+ altexec = strdup(alt);
+ if (altexec == NULL)
+ terminate("failed to allocate memory for altexec\n");
+
+ if (unsetenv(env) != 0)
+ aborterr("failed to remove %s from environment", env);
+
+ (void) execv(altexec, argv);
+ terminate("failed to altexec %s", altexec);
+}
diff --git a/usr/src/tools/ctf/cvt/ctfconvert.c b/usr/src/tools/ctf/cvt/ctfconvert.c
index 36496c5e9d..af005e1934 100644
--- a/usr/src/tools/ctf/cvt/ctfconvert.c
+++ b/usr/src/tools/ctf/cvt/ctfconvert.c
@@ -146,6 +146,7 @@ main(int argc, char **argv)
{
tdata_t *filetd, *mstrtd;
char *label = NULL;
+ char *altexec;
int verbose = 0;
int ignore_non_c = 0;
int c;
@@ -156,12 +157,21 @@ main(int argc, char **argv)
progname = basename(argv[0]);
+ ctf_altexec("CTFCONVERT_ALTEXEC", argc, argv);
+
if (getenv("CTFCONVERT_DEBUG_LEVEL"))
debug_level = atoi(getenv("CTFCONVERT_DEBUG_LEVEL"));
if (getenv("CTFCONVERT_DEBUG_PARSE"))
debug_parse = atoi(getenv("CTFCONVERT_DEBUG_PARSE"));
- while ((c = getopt(argc, argv, ":l:L:o:ivs")) != EOF) {
+ if ((altexec = getenv("CTFCONVERT_ALTEXEC")) != NULL) {
+ (void) unsetenv("CTFCONVERT_ALTEXEC");
+ (void) execv(altexec, argv);
+ (void) fprintf(stderr, "ctfconvert altexec failed to "
+ "run %s: %s\n", altexec, strerror(errno));
+ }
+
+ while ((c = getopt(argc, argv, ":l:L:o:givs")) != EOF) {
switch (c) {
case 'l':
label = optarg;
diff --git a/usr/src/tools/ctf/cvt/ctfmerge.c b/usr/src/tools/ctf/cvt/ctfmerge.c
index 1d6d751d99..d2db789e5f 100644
--- a/usr/src/tools/ctf/cvt/ctfmerge.c
+++ b/usr/src/tools/ctf/cvt/ctfmerge.c
@@ -748,6 +748,8 @@ main(int argc, char **argv)
progname = basename(argv[0]);
+ ctf_altexec("CTFMERGE_ALTEXEC", argc, argv);
+
if (getenv("CTFMERGE_DEBUG_LEVEL"))
debug_level = atoi(getenv("CTFMERGE_DEBUG_LEVEL"));
diff --git a/usr/src/tools/ctf/cvt/ctftools.h b/usr/src/tools/ctf/cvt/ctftools.h
index 79746cba52..7547ce389f 100644
--- a/usr/src/tools/ctf/cvt/ctftools.h
+++ b/usr/src/tools/ctf/cvt/ctftools.h
@@ -442,6 +442,9 @@ void warning(char *, ...);
void vadebug(int, char *, va_list);
void debug(int, char *, ...);
+/* altexec.c */
+void ctf_altexec(const char *, int argc, char **);
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/tools/ctf/dump/dump.c b/usr/src/tools/ctf/dump/dump.c
deleted file mode 100644
index 5579bae596..0000000000
--- a/usr/src/tools/ctf/dump/dump.c
+++ /dev/null
@@ -1,1028 +0,0 @@
-/*
- * 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 2004 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-#pragma ident "%Z%%M% %I% %E% SMI"
-
-#include <sys/types.h>
-#include <sys/sysmacros.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-
-#include <strings.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <gelf.h>
-#include <zlib.h>
-
-#include "ctf_headers.h"
-#include "utils.h"
-#include "symbol.h"
-
-#define WARN(x) { warn(x); return (E_ERROR); }
-
-/*
- * Flags that indicate what data is to be displayed. An explicit `all' value is
- * provided to allow the code to distinguish between a request for everything
- * (currently requested by invoking ctfdump without flags) and individual
- * requests for all of the types of data (an invocation with all flags). In the
- * former case, we want to be able to implicitly adjust the definition of `all'
- * based on the CTF version of the file being dumped. For example, if a v2 file
- * is being dumped, `all' includes F_LABEL - a request to dump the label
- * section. If a v1 file is being dumped, `all' does not include F_LABEL,
- * because v1 CTF doesn't support labels. We need to be able to distinguish
- * between `ctfdump foo', which has an implicit request for labels if `foo'
- * supports them, and `ctfdump -l foo', which has an explicity request. In the
- * latter case, we exit with an error if `foo' is a v1 CTF file.
- */
-static enum {
- F_DATA = 0x01, /* show data object section */
- F_FUNC = 0x02, /* show function section */
- F_HDR = 0x04, /* show header */
- F_STR = 0x08, /* show string table */
- F_TYPES = 0x10, /* show type section */
- F_STATS = 0x20, /* show statistics */
- F_LABEL = 0x40, /* show label section */
- F_ALL = 0x80, /* explicit request for `all' */
- F_ALLMSK = 0xff /* show all sections and statistics */
-} flags = 0;
-
-static struct {
- ulong_t s_ndata; /* total number of data objects */
- ulong_t s_nfunc; /* total number of functions */
- ulong_t s_nargs; /* total number of function arguments */
- ulong_t s_argmax; /* longest argument list */
- ulong_t s_ntypes; /* total number of types */
- ulong_t s_types[16]; /* number of types by kind */
- ulong_t s_nsmem; /* total number of struct members */
- ulong_t s_nsbytes; /* total size of all structs */
- ulong_t s_smmax; /* largest struct in terms of members */
- ulong_t s_sbmax; /* largest struct in terms of bytes */
- ulong_t s_numem; /* total number of union members */
- ulong_t s_nubytes; /* total size of all unions */
- ulong_t s_ummax; /* largest union in terms of members */
- ulong_t s_ubmax; /* largest union in terms of bytes */
- ulong_t s_nemem; /* total number of enum members */
- ulong_t s_emmax; /* largest enum in terms of members */
- ulong_t s_nstr; /* total number of strings */
- size_t s_strlen; /* total length of all strings */
- size_t s_strmax; /* longest string length */
-} stats;
-
-typedef struct ctf_data {
- caddr_t cd_ctfdata; /* Pointer to the CTF data */
- size_t cd_ctflen; /* Length of CTF data */
-
- /*
- * cd_symdata will be non-NULL if the CTF data is being retrieved from
- * an ELF file with a symbol table. cd_strdata and cd_nsyms should be
- * used only if cd_symdata is non-NULL.
- */
- Elf_Data *cd_symdata; /* Symbol table */
- Elf_Data *cd_strdata; /* Symbol table strings */
- int cd_nsyms; /* Number of symbol table entries */
-} ctf_data_t;
-
-static const char *
-ref_to_str(uint_t name, const ctf_header_t *hp, const ctf_data_t *cd)
-{
- size_t offset = CTF_NAME_OFFSET(name);
- const char *s = cd->cd_ctfdata + hp->cth_stroff + offset;
-
- if (CTF_NAME_STID(name) != CTF_STRTAB_0)
- return ("<< ??? - name in external strtab >>");
-
- if (offset >= hp->cth_strlen)
- return ("<< ??? - name exceeds strlab len >>");
-
- if (hp->cth_stroff + offset >= cd->cd_ctflen)
- return ("<< ??? - file truncated >>");
-
- if (s[0] == '\0')
- return ("(anon)");
-
- return (s);
-}
-
-static const char *
-int_encoding_to_str(uint_t encoding)
-{
- static char buf[32];
-
- if (encoding == 0 || (encoding & ~(CTF_INT_SIGNED | CTF_INT_CHAR |
- CTF_INT_BOOL | CTF_INT_VARARGS)) != 0)
- (void) snprintf(buf, sizeof (buf), " 0x%x", encoding);
- else {
- buf[0] = '\0';
- if (encoding & CTF_INT_SIGNED)
- (void) strcat(buf, " SIGNED");
- if (encoding & CTF_INT_CHAR)
- (void) strcat(buf, " CHAR");
- if (encoding & CTF_INT_BOOL)
- (void) strcat(buf, " BOOL");
- if (encoding & CTF_INT_VARARGS)
- (void) strcat(buf, " VARARGS");
- }
-
- return (buf + 1);
-}
-
-static const char *
-fp_encoding_to_str(uint_t encoding)
-{
- static const char *const encs[] = {
- NULL, "SINGLE", "DOUBLE", "COMPLEX", "DCOMPLEX", "LDCOMPLEX",
- "LDOUBLE", "INTERVAL", "DINTERVAL", "LDINTERVAL", "IMAGINARY",
- "DIMAGINARY", "LDIMAGINARY"
- };
-
- static char buf[16];
-
- if (encoding < 1 || encoding >= (sizeof (encs) / sizeof (char *))) {
- (void) snprintf(buf, sizeof (buf), "%u", encoding);
- return (buf);
- }
-
- return (encs[encoding]);
-}
-
-static void
-print_line(const char *s)
-{
- static const char line[] = "----------------------------------------"
- "----------------------------------------";
- (void) printf("\n%s%.*s\n\n", s, (int)(78 - strlen(s)), line);
-}
-
-static int
-print_header(const ctf_header_t *hp, const ctf_data_t *cd)
-{
- print_line("- CTF Header ");
-
- (void) printf(" cth_magic = 0x%04x\n", hp->cth_magic);
- (void) printf(" cth_version = %u\n", hp->cth_version);
- (void) printf(" cth_flags = 0x%02x\n", hp->cth_flags);
- (void) printf(" cth_parlabel = %s\n",
- ref_to_str(hp->cth_parlabel, hp, cd));
- (void) printf(" cth_parname = %s\n",
- ref_to_str(hp->cth_parname, hp, cd));
- (void) printf(" cth_lbloff = %u\n", hp->cth_lbloff);
- (void) printf(" cth_objtoff = %u\n", hp->cth_objtoff);
- (void) printf(" cth_funcoff = %u\n", hp->cth_funcoff);
- (void) printf(" cth_typeoff = %u\n", hp->cth_typeoff);
- (void) printf(" cth_stroff = %u\n", hp->cth_stroff);
- (void) printf(" cth_strlen = %u\n", hp->cth_strlen);
-
- return (E_SUCCESS);
-}
-
-static int
-print_labeltable(const ctf_header_t *hp, const ctf_data_t *cd)
-{
- /* LINTED - pointer alignment */
- const ctf_lblent_t *ctl = (ctf_lblent_t *)(cd->cd_ctfdata +
- hp->cth_lbloff);
- ulong_t i, n = (hp->cth_objtoff - hp->cth_lbloff) / sizeof (*ctl);
-
- print_line("- Label Table ");
-
- if (hp->cth_lbloff & 3)
- WARN("cth_lbloff is not aligned properly\n");
- if (hp->cth_lbloff >= cd->cd_ctflen)
- WARN("file is truncated or cth_lbloff is corrupt\n");
- if (hp->cth_objtoff >= cd->cd_ctflen)
- WARN("file is truncated or cth_objtoff is corrupt\n");
- if (hp->cth_lbloff > hp->cth_objtoff)
- WARN("file is corrupt -- cth_lbloff > cth_objtoff\n");
-
- for (i = 0; i < n; i++, ctl++) {
- (void) printf(" %5u %s\n", ctl->ctl_typeidx,
- ref_to_str(ctl->ctl_label, hp, cd));
- }
-
- return (E_SUCCESS);
-}
-
-/*
- * Given the current symbol index (-1 to start at the beginning of the symbol
- * table) and the type of symbol to match, this function returns the index of
- * the next matching symbol (if any), and places the name of that symbol in
- * *namep. If no symbol is found, -1 is returned.
- */
-static int
-next_sym(const ctf_data_t *cd, const int symidx, const uchar_t matchtype,
- char **namep)
-{
- int i;
-
- for (i = symidx + 1; i < cd->cd_nsyms; i++) {
- GElf_Sym sym;
- char *name;
- int type;
-
- if (gelf_getsym(cd->cd_symdata, i, &sym) == 0)
- return (-1);
-
- name = (char *)cd->cd_strdata->d_buf + sym.st_name;
- type = GELF_ST_TYPE(sym.st_info);
-
- /*
- * Skip various types of symbol table entries.
- */
- if (type != matchtype || ignore_symbol(&sym, name))
- continue;
-
- /* Found one */
- *namep = name;
- return (i);
- }
-
- return (-1);
-}
-
-static int
-read_data(const ctf_header_t *hp, const ctf_data_t *cd)
-{
- /* LINTED - pointer alignment */
- const ushort_t *idp = (ushort_t *)(cd->cd_ctfdata + hp->cth_objtoff);
- ulong_t n = (hp->cth_funcoff - hp->cth_objtoff) / sizeof (ushort_t);
-
- if (flags != F_STATS)
- print_line("- Data Objects ");
-
- if (hp->cth_objtoff & 1)
- WARN("cth_objtoff is not aligned properly\n");
- if (hp->cth_objtoff >= cd->cd_ctflen)
- WARN("file is truncated or cth_objtoff is corrupt\n");
- if (hp->cth_funcoff >= cd->cd_ctflen)
- WARN("file is truncated or cth_funcoff is corrupt\n");
- if (hp->cth_objtoff > hp->cth_funcoff)
- WARN("file is corrupt -- cth_objtoff > cth_funcoff\n");
-
- if (flags != F_STATS) {
- int symidx, len, i;
- char *name = NULL;
-
- for (symidx = -1, i = 0; i < n; i++) {
- int nextsym;
-
- if (cd->cd_symdata == NULL || (nextsym = next_sym(cd,
- symidx, STT_OBJECT, &name)) < 0)
- name = NULL;
- else
- symidx = nextsym;
-
- len = printf(" [%u] %u", i, *idp++);
- if (name != NULL)
- (void) printf("%*s%s (%u)", (15 - len), "",
- name, symidx);
- (void) putchar('\n');
- }
- }
-
- stats.s_ndata = n;
- return (E_SUCCESS);
-}
-
-static int
-read_funcs(const ctf_header_t *hp, const ctf_data_t *cd)
-{
- /* LINTED - pointer alignment */
- const ushort_t *fp = (ushort_t *)(cd->cd_ctfdata + hp->cth_funcoff);
-
- /* LINTED - pointer alignment */
- const ushort_t *end = (ushort_t *)(cd->cd_ctfdata + hp->cth_typeoff);
-
- ulong_t id;
- int symidx;
-
- if (flags != F_STATS)
- print_line("- Functions ");
-
- if (hp->cth_funcoff & 1)
- WARN("cth_funcoff is not aligned properly\n");
- if (hp->cth_funcoff >= cd->cd_ctflen)
- WARN("file is truncated or cth_funcoff is corrupt\n");
- if (hp->cth_typeoff >= cd->cd_ctflen)
- WARN("file is truncated or cth_typeoff is corrupt\n");
- if (hp->cth_funcoff > hp->cth_typeoff)
- WARN("file is corrupt -- cth_funcoff > cth_typeoff\n");
-
- for (symidx = -1, id = 0; fp < end; id++) {
- ushort_t info = *fp++;
- ushort_t kind = CTF_INFO_KIND(info);
- ushort_t n = CTF_INFO_VLEN(info);
- ushort_t i;
- int nextsym;
- char *name;
-
- if (cd->cd_symdata == NULL || (nextsym = next_sym(cd, symidx,
- STT_FUNC, &name)) < 0)
- name = NULL;
- else
- symidx = nextsym;
-
- if (kind == CTF_K_UNKNOWN && n == 0)
- continue; /* skip padding */
-
- if (kind != CTF_K_FUNCTION) {
- (void) printf(" [%lu] unexpected kind -- %u\n",
- id, kind);
- return (E_ERROR);
- }
-
- if (fp + n > end) {
- (void) printf(" [%lu] vlen %u extends past section "
- "boundary\n", id, n);
- return (E_ERROR);
- }
-
- if (flags != F_STATS) {
- (void) printf(" [%lu] FUNC ", id);
- if (name != NULL)
- (void) printf("(%s) ", name);
- (void) printf("returns: %u args: (", *fp++);
-
- if (n != 0) {
- (void) printf("%u", *fp++);
- for (i = 1; i < n; i++)
- (void) printf(", %u", *fp++);
- }
-
- (void) printf(")\n");
- } else
- fp += n + 1; /* skip to next function definition */
-
- stats.s_nfunc++;
- stats.s_nargs += n;
- stats.s_argmax = MAX(stats.s_argmax, n);
- }
-
- return (E_SUCCESS);
-}
-
-static int
-read_types(const ctf_header_t *hp, const ctf_data_t *cd)
-{
- /* LINTED - pointer alignment */
- const ctf_type_t *tp = (ctf_type_t *)(cd->cd_ctfdata + hp->cth_typeoff);
-
- /* LINTED - pointer alignment */
- const ctf_type_t *end = (ctf_type_t *)(cd->cd_ctfdata + hp->cth_stroff);
-
- ulong_t id;
-
- if (flags != F_STATS)
- print_line("- Types ");
-
- if (hp->cth_typeoff & 3)
- WARN("cth_typeoff is not aligned properly\n");
- if (hp->cth_typeoff >= cd->cd_ctflen)
- WARN("file is truncated or cth_typeoff is corrupt\n");
- if (hp->cth_stroff >= cd->cd_ctflen)
- WARN("file is truncated or cth_stroff is corrupt\n");
- if (hp->cth_typeoff > hp->cth_stroff)
- WARN("file is corrupt -- cth_typeoff > cth_stroff\n");
-
- id = 1;
- if (hp->cth_parlabel || hp->cth_parname)
- id += 1 << CTF_PARENT_SHIFT;
-
- for (/* */; tp < end; id++) {
- ulong_t i, n = CTF_INFO_VLEN(tp->ctt_info);
- size_t size, increment, vlen = 0;
- int kind = CTF_INFO_KIND(tp->ctt_info);
-
- union {
- const void *ptr;
- const ctf_array_t *ap;
- const ctf_member_t *mp;
- const ctf_lmember_t *lmp;
- const ctf_enum_t *ep;
- const ushort_t *argp;
- } u;
-
- if (flags != F_STATS) {
- (void) printf(" %c%lu%c ",
- "[<"[CTF_INFO_ISROOT(tp->ctt_info)], id,
- "]>"[CTF_INFO_ISROOT(tp->ctt_info)]);
- }
-
- if (tp->ctt_size == CTF_LSIZE_SENT) {
- increment = sizeof (ctf_type_t);
- size = (size_t)CTF_TYPE_LSIZE(tp);
- } else {
- increment = sizeof (ctf_stype_t);
- size = tp->ctt_size;
- }
- u.ptr = (caddr_t)tp + increment;
-
- switch (kind) {
- case CTF_K_INTEGER:
- if (flags != F_STATS) {
- uint_t encoding = *((const uint_t *)u.ptr);
-
- (void) printf("INTEGER %s encoding=%s offset=%u"
- " bits=%u", ref_to_str(tp->ctt_name, hp,
- cd), int_encoding_to_str(
- CTF_INT_ENCODING(encoding)),
- CTF_INT_OFFSET(encoding),
- CTF_INT_BITS(encoding));
- }
- vlen = sizeof (uint_t);
- break;
-
- case CTF_K_FLOAT:
- if (flags != F_STATS) {
- uint_t encoding = *((const uint_t *)u.ptr);
-
- (void) printf("FLOAT %s encoding=%s offset=%u "
- "bits=%u", ref_to_str(tp->ctt_name, hp,
- cd), fp_encoding_to_str(
- CTF_FP_ENCODING(encoding)),
- CTF_FP_OFFSET(encoding),
- CTF_FP_BITS(encoding));
- }
- vlen = sizeof (uint_t);
- break;
-
- case CTF_K_POINTER:
- if (flags != F_STATS) {
- (void) printf("POINTER %s refers to %u",
- ref_to_str(tp->ctt_name, hp, cd),
- tp->ctt_type);
- }
- break;
-
- case CTF_K_ARRAY:
- if (flags != F_STATS) {
- (void) printf("ARRAY %s content: %u index: %u "
- "nelems: %u\n", ref_to_str(tp->ctt_name,
- hp, cd), u.ap->cta_contents,
- u.ap->cta_index, u.ap->cta_nelems);
- }
- vlen = sizeof (ctf_array_t);
- break;
-
- case CTF_K_FUNCTION:
- if (flags != F_STATS) {
- (void) printf("FUNCTION %s returns: %u args: (",
- ref_to_str(tp->ctt_name, hp, cd),
- tp->ctt_type);
-
- if (n != 0) {
- (void) printf("%u", *u.argp++);
- for (i = 1; i < n; i++, u.argp++)
- (void) printf(", %u", *u.argp);
- }
-
- (void) printf(")");
- }
-
- vlen = sizeof (ushort_t) * (n + (n & 1));
- break;
-
- case CTF_K_STRUCT:
- case CTF_K_UNION:
- if (kind == CTF_K_STRUCT) {
- stats.s_nsmem += n;
- stats.s_smmax = MAX(stats.s_smmax, n);
- stats.s_nsbytes += size;
- stats.s_sbmax = MAX(stats.s_sbmax, size);
-
- if (flags != F_STATS)
- (void) printf("STRUCT");
- } else {
- stats.s_numem += n;
- stats.s_ummax = MAX(stats.s_ummax, n);
- stats.s_nubytes += size;
- stats.s_ubmax = MAX(stats.s_ubmax, size);
-
- if (flags != F_STATS)
- (void) printf("UNION");
- }
-
- if (flags != F_STATS) {
- (void) printf(" %s (%d bytes)\n",
- ref_to_str(tp->ctt_name, hp, cd), size);
-
- if (size >= CTF_LSTRUCT_THRESH) {
- for (i = 0; i < n; i++, u.lmp++) {
- (void) printf(
- "\t%s type=%u off=%llu\n",
- ref_to_str(u.lmp->ctlm_name,
- hp, cd), u.lmp->ctlm_type,
- CTF_LMEM_OFFSET(u.lmp));
- }
- } else {
- for (i = 0; i < n; i++, u.mp++) {
- (void) printf(
- "\t%s type=%u off=%u\n",
- ref_to_str(u.mp->ctm_name,
- hp, cd), u.mp->ctm_type,
- u.mp->ctm_offset);
- }
- }
- }
-
- vlen = n * (size >= CTF_LSTRUCT_THRESH ?
- sizeof (ctf_lmember_t) : sizeof (ctf_member_t));
- break;
-
- case CTF_K_ENUM:
- if (flags != F_STATS) {
- (void) printf("ENUM %s\n",
- ref_to_str(tp->ctt_name, hp, cd));
-
- for (i = 0; i < n; i++, u.ep++) {
- (void) printf("\t%s = %d\n",
- ref_to_str(u.ep->cte_name, hp, cd),
- u.ep->cte_value);
- }
- }
-
- stats.s_nemem += n;
- stats.s_emmax = MAX(stats.s_emmax, n);
-
- vlen = sizeof (ctf_enum_t) * n;
- break;
-
- case CTF_K_FORWARD:
- if (flags != F_STATS) {
- (void) printf("FORWARD %s",
- ref_to_str(tp->ctt_name, hp, cd));
- }
- break;
-
- case CTF_K_TYPEDEF:
- if (flags != F_STATS) {
- (void) printf("TYPEDEF %s refers to %u",
- ref_to_str(tp->ctt_name, hp, cd),
- tp->ctt_type);
- }
- break;
-
- case CTF_K_VOLATILE:
- if (flags != F_STATS) {
- (void) printf("VOLATILE %s refers to %u",
- ref_to_str(tp->ctt_name, hp, cd),
- tp->ctt_type);
- }
- break;
-
- case CTF_K_CONST:
- if (flags != F_STATS) {
- (void) printf("CONST %s refers to %u",
- ref_to_str(tp->ctt_name, hp, cd),
- tp->ctt_type);
- }
- break;
-
- case CTF_K_RESTRICT:
- if (flags != F_STATS) {
- (void) printf("RESTRICT %s refers to %u",
- ref_to_str(tp->ctt_name, hp, cd),
- tp->ctt_type);
- }
- break;
-
- case CTF_K_UNKNOWN:
- break; /* hole in type id space */
-
- default:
- (void) printf("unexpected kind %u\n", kind);
- return (E_ERROR);
- }
-
- if (flags != F_STATS)
- (void) printf("\n");
-
- stats.s_ntypes++;
- stats.s_types[kind]++;
-
- tp = (ctf_type_t *)((uintptr_t)tp + increment + vlen);
- }
-
- return (E_SUCCESS);
-}
-
-static int
-read_strtab(const ctf_header_t *hp, const ctf_data_t *cd)
-{
- size_t n, off, len = hp->cth_strlen;
- const char *s = cd->cd_ctfdata + hp->cth_stroff;
-
- if (flags != F_STATS)
- print_line("- String Table ");
-
- if (hp->cth_stroff >= cd->cd_ctflen)
- WARN("file is truncated or cth_stroff is corrupt\n");
- if (hp->cth_stroff + hp->cth_strlen > cd->cd_ctflen)
- WARN("file is truncated or cth_strlen is corrupt\n");
-
- for (off = 0; len != 0; off += n) {
- if (flags != F_STATS) {
- (void) printf(" [%lu] %s\n", (ulong_t)off,
- s[0] == '\0' ? "\\0" : s);
- }
- n = strlen(s) + 1;
- len -= n;
- s += n;
-
- stats.s_nstr++;
- stats.s_strlen += n;
- stats.s_strmax = MAX(stats.s_strmax, n);
- }
-
- return (E_SUCCESS);
-}
-
-static void
-long_stat(const char *name, ulong_t value)
-{
- (void) printf(" %-36s= %lu\n", name, value);
-}
-
-static void
-fp_stat(const char *name, float value)
-{
- (void) printf(" %-36s= %.2f\n", name, value);
-}
-
-static int
-print_stats(void)
-{
- print_line("- CTF Statistics ");
-
- long_stat("total number of data objects", stats.s_ndata);
- (void) printf("\n");
-
- long_stat("total number of functions", stats.s_nfunc);
- long_stat("total number of function arguments", stats.s_nargs);
- long_stat("maximum argument list length", stats.s_argmax);
-
- if (stats.s_nfunc != 0) {
- fp_stat("average argument list length",
- (float)stats.s_nargs / (float)stats.s_nfunc);
- }
-
- (void) printf("\n");
-
- long_stat("total number of types", stats.s_ntypes);
- long_stat("total number of integers", stats.s_types[CTF_K_INTEGER]);
- long_stat("total number of floats", stats.s_types[CTF_K_FLOAT]);
- long_stat("total number of pointers", stats.s_types[CTF_K_POINTER]);
- long_stat("total number of arrays", stats.s_types[CTF_K_ARRAY]);
- long_stat("total number of func types", stats.s_types[CTF_K_FUNCTION]);
- long_stat("total number of structs", stats.s_types[CTF_K_STRUCT]);
- long_stat("total number of unions", stats.s_types[CTF_K_UNION]);
- long_stat("total number of enums", stats.s_types[CTF_K_ENUM]);
- long_stat("total number of forward tags", stats.s_types[CTF_K_FORWARD]);
- long_stat("total number of typedefs", stats.s_types[CTF_K_TYPEDEF]);
- long_stat("total number of volatile types",
- stats.s_types[CTF_K_VOLATILE]);
- long_stat("total number of const types", stats.s_types[CTF_K_CONST]);
- long_stat("total number of restrict types",
- stats.s_types[CTF_K_RESTRICT]);
- long_stat("total number of unknowns (holes)",
- stats.s_types[CTF_K_UNKNOWN]);
-
- (void) printf("\n");
-
- long_stat("total number of struct members", stats.s_nsmem);
- long_stat("maximum number of struct members", stats.s_smmax);
- long_stat("total size of all structs", stats.s_nsbytes);
- long_stat("maximum size of a struct", stats.s_sbmax);
-
- if (stats.s_types[CTF_K_STRUCT] != 0) {
- fp_stat("average number of struct members",
- (float)stats.s_nsmem / (float)stats.s_types[CTF_K_STRUCT]);
- fp_stat("average size of a struct", (float)stats.s_nsbytes /
- (float)stats.s_types[CTF_K_STRUCT]);
- }
-
- (void) printf("\n");
-
- long_stat("total number of union members", stats.s_numem);
- long_stat("maximum number of union members", stats.s_ummax);
- long_stat("total size of all unions", stats.s_nubytes);
- long_stat("maximum size of a union", stats.s_ubmax);
-
- if (stats.s_types[CTF_K_UNION] != 0) {
- fp_stat("average number of union members",
- (float)stats.s_numem / (float)stats.s_types[CTF_K_UNION]);
- fp_stat("average size of a union", (float)stats.s_nubytes /
- (float)stats.s_types[CTF_K_UNION]);
- }
-
- (void) printf("\n");
-
- long_stat("total number of enum members", stats.s_nemem);
- long_stat("maximum number of enum members", stats.s_emmax);
-
- if (stats.s_types[CTF_K_ENUM] != 0) {
- fp_stat("average number of enum members",
- (float)stats.s_nemem / (float)stats.s_types[CTF_K_ENUM]);
- }
-
- (void) printf("\n");
-
- long_stat("total number of unique strings", stats.s_nstr);
- long_stat("bytes of string data", stats.s_strlen);
- long_stat("maximum string length", stats.s_strmax);
-
- if (stats.s_nstr != 0) {
- fp_stat("average string length",
- (float)stats.s_strlen / (float)stats.s_nstr);
- }
-
- (void) printf("\n");
- return (E_SUCCESS);
-}
-
-static int
-print_usage(FILE *fp, int verbose)
-{
- (void) fprintf(fp, "Usage: %s [-dfhlsSt] [-u file] file\n", getpname());
-
- if (verbose) {
- (void) fprintf(fp,
- "\t-d dump data object section\n"
- "\t-f dump function section\n"
- "\t-h dump file header\n"
- "\t-l dump label table\n"
- "\t-s dump string table\n"
- "\t-S dump statistics\n"
- "\t-t dump type section\n"
- "\t-u save uncompressed CTF to a file\n");
- }
-
- return (E_USAGE);
-}
-
-static Elf_Scn *
-findelfscn(Elf *elf, GElf_Ehdr *ehdr, char *secname)
-{
- GElf_Shdr shdr;
- Elf_Scn *scn;
- char *name;
-
- for (scn = NULL; (scn = elf_nextscn(elf, scn)) != NULL; ) {
- if (gelf_getshdr(scn, &shdr) != NULL && (name =
- elf_strptr(elf, ehdr->e_shstrndx, shdr.sh_name)) != NULL &&
- strcmp(name, secname) == 0)
- return (scn);
- }
-
- return (NULL);
-}
-
-int
-main(int argc, char *argv[])
-{
- const char *filename = NULL;
- const char *ufile = NULL;
- int error = 0;
- int c, fd, ufd;
-
- ctf_data_t cd;
- const ctf_preamble_t *pp;
- ctf_header_t *hp;
- Elf *elf;
- GElf_Ehdr ehdr;
-
- (void) elf_version(EV_CURRENT);
-
- for (opterr = 0; optind < argc; optind++) {
- while ((c = getopt(argc, argv, "dfhlsStu:")) != (int)EOF) {
- switch (c) {
- case 'd':
- flags |= F_DATA;
- break;
- case 'f':
- flags |= F_FUNC;
- break;
- case 'h':
- flags |= F_HDR;
- break;
- case 'l':
- flags |= F_LABEL;
- break;
- case 's':
- flags |= F_STR;
- break;
- case 'S':
- flags |= F_STATS;
- break;
- case 't':
- flags |= F_TYPES;
- break;
- case 'u':
- ufile = optarg;
- break;
- default:
- if (optopt == '?')
- return (print_usage(stdout, 1));
- warn("illegal option -- %c\n", optopt);
- return (print_usage(stderr, 0));
- }
- }
-
- if (optind < argc) {
- if (filename != NULL)
- return (print_usage(stderr, 0));
- filename = argv[optind];
- }
- }
-
- if (filename == NULL)
- return (print_usage(stderr, 0));
-
- if (flags == 0 && ufile == NULL)
- flags = F_ALLMSK;
-
- if ((fd = open(filename, O_RDONLY)) == -1)
- die("failed to open %s", filename);
-
- if ((elf = elf_begin(fd, ELF_C_READ, NULL)) != NULL &&
- gelf_getehdr(elf, &ehdr) != NULL) {
-
- Elf_Data *dp;
- Elf_Scn *ctfscn = findelfscn(elf, &ehdr, ".SUNW_ctf");
- Elf_Scn *symscn;
- GElf_Shdr ctfshdr;
-
- if (ctfscn == NULL || (dp = elf_getdata(ctfscn, NULL)) == NULL)
- die("%s does not contain .SUNW_ctf data\n", filename);
-
- cd.cd_ctfdata = dp->d_buf;
- cd.cd_ctflen = dp->d_size;
-
- /*
- * If the sh_link field of the CTF section header is non-zero
- * it indicates which section contains the symbol table that
- * should be used. We default to the .symtab section if sh_link
- * is zero or if there's an error reading the section header.
- */
- if (gelf_getshdr(ctfscn, &ctfshdr) != NULL &&
- ctfshdr.sh_link != 0) {
- symscn = elf_getscn(elf, ctfshdr.sh_link);
- } else {
- symscn = findelfscn(elf, &ehdr, ".symtab");
- }
-
- /* If we found a symbol table, find the corresponding strings */
- if (symscn != NULL) {
- GElf_Shdr shdr;
- Elf_Scn *symstrscn;
-
- if (gelf_getshdr(symscn, &shdr) != NULL) {
- symstrscn = elf_getscn(elf, shdr.sh_link);
-
- cd.cd_nsyms = shdr.sh_size / shdr.sh_entsize;
- cd.cd_symdata = elf_getdata(symscn, NULL);
- cd.cd_strdata = elf_getdata(symstrscn, NULL);
- }
- }
- } else {
- struct stat st;
-
- if (fstat(fd, &st) == -1)
- die("failed to fstat %s", filename);
-
- cd.cd_ctflen = st.st_size;
- cd.cd_ctfdata = mmap(NULL, cd.cd_ctflen, PROT_READ,
- MAP_PRIVATE, fd, 0);
- if (cd.cd_ctfdata == MAP_FAILED)
- die("failed to mmap %s", filename);
- }
-
- /*
- * Get a pointer to the CTF data buffer and interpret the first portion
- * as a ctf_header_t. Validate the magic number and size.
- */
-
- if (cd.cd_ctflen < sizeof (ctf_preamble_t))
- die("%s does not contain a CTF preamble\n", filename);
-
- /* LINTED - pointer alignment */
- pp = (const ctf_preamble_t *)cd.cd_ctfdata;
-
- if (pp->ctp_magic != CTF_MAGIC)
- die("%s does not appear to contain CTF data\n", filename);
-
- if (pp->ctp_version == CTF_VERSION) {
- /* LINTED - pointer alignment */
- hp = (ctf_header_t *)cd.cd_ctfdata;
- cd.cd_ctfdata = (caddr_t)cd.cd_ctfdata + sizeof (ctf_header_t);
-
- if (cd.cd_ctflen < sizeof (ctf_header_t)) {
- die("%s does not contain a v%d CTF header\n", filename,
- CTF_VERSION);
- }
-
- } else {
- die("%s contains unsupported CTF version %d\n", filename,
- pp->ctp_version);
- }
-
- /*
- * If the data buffer is compressed, then malloc a buffer large enough
- * to hold the decompressed data, and use zlib to decompress it.
- */
- if (hp->cth_flags & CTF_F_COMPRESS) {
- z_stream zstr;
- void *buf;
- int rc;
-
- if ((buf = malloc(hp->cth_stroff + hp->cth_strlen)) == NULL)
- die("failed to allocate decompression buffer");
-
- bzero(&zstr, sizeof (z_stream));
- zstr.next_in = (void *)cd.cd_ctfdata;
- zstr.avail_in = cd.cd_ctflen;
- zstr.next_out = buf;
- zstr.avail_out = hp->cth_stroff + hp->cth_strlen;
-
- if ((rc = inflateInit(&zstr)) != Z_OK)
- die("failed to initialize zlib: %s\n", zError(rc));
-
- if ((rc = inflate(&zstr, Z_FINISH)) != Z_STREAM_END)
- die("failed to decompress CTF data: %s\n", zError(rc));
-
- if ((rc = inflateEnd(&zstr)) != Z_OK)
- die("failed to finish decompression: %s\n", zError(rc));
-
- if (zstr.total_out != hp->cth_stroff + hp->cth_strlen)
- die("CTF data is corrupt -- short decompression\n");
-
- cd.cd_ctfdata = buf;
- cd.cd_ctflen = hp->cth_stroff + hp->cth_strlen;
- }
-
- if (flags & F_HDR)
- error |= print_header(hp, &cd);
- if (flags & (F_LABEL))
- error |= print_labeltable(hp, &cd);
- if (flags & (F_DATA | F_STATS))
- error |= read_data(hp, &cd);
- if (flags & (F_FUNC | F_STATS))
- error |= read_funcs(hp, &cd);
- if (flags & (F_TYPES | F_STATS))
- error |= read_types(hp, &cd);
- if (flags & (F_STR | F_STATS))
- error |= read_strtab(hp, &cd);
- if (flags & F_STATS)
- error |= print_stats();
-
- /*
- * If the -u option is specified, write the uncompressed CTF data to a
- * raw CTF file. CTF data can already be extracted compressed by
- * applying elfdump -w -N .SUNW_ctf to an ELF file, so we don't bother.
- */
- if (ufile != NULL) {
- ctf_header_t h;
-
- bcopy(hp, &h, sizeof (h));
- h.cth_flags &= ~CTF_F_COMPRESS;
-
- if ((ufd = open(ufile, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0 ||
- write(ufd, &h, sizeof (h)) != sizeof (h) ||
- write(ufd, cd.cd_ctfdata, cd.cd_ctflen) != cd.cd_ctflen) {
- warn("failed to write CTF data to '%s'", ufile);
- error |= E_ERROR;
- }
-
- (void) close(ufd);
- }
-
- if (elf != NULL)
- (void) elf_end(elf);
-
- (void) close(fd);
- return (error);
-}
diff --git a/usr/src/tools/ctf/dwarf/Makefile.com b/usr/src/tools/ctf/dwarf/Makefile.com
index 2e18b40fe4..67ffd64a62 100644
--- a/usr/src/tools/ctf/dwarf/Makefile.com
+++ b/usr/src/tools/ctf/dwarf/Makefile.com
@@ -69,11 +69,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/dump/Makefile.com b/usr/src/tools/ctf/libctf/Makefile
index 9877fa06a3..8ea6ae181f 100644
--- a/usr/src/tools/ctf/dump/Makefile.com
+++ b/usr/src/tools/ctf/libctf/Makefile
@@ -19,49 +19,28 @@
# CDDL HEADER END
#
#
-# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-.KEEP_STATE:
-.SUFFIXES:
-
-PROG = ctfdump
-SRCS = dump.c utils.c symbol.c
-
-include ../../Makefile.ctf
-
-LDFLAGS += -L$(NATIVE_ADJUNCT)/lib
-LDLIBS += -lelf -lz
-
-OBJS = $(SRCS:%.c=%.o)
-LINTFILES = $(SRCS:%.c=%.ln)
+include ../../Makefile.tools
-CERRWARN += -_gcc=-Wno-uninitialized
+HDRS = libctf.h
+HDRDIR = common
-.NO_PARALLEL:
-.PARALLEL: $(OBJS) $(LINTFILES)
+SUBDIRS = $(MACH)
-all: $(PROG)
+all := TARGET = all
+clean := TARGET = clean
+clobber := TARGET = clobber
+install := TARGET = install
+lint := TARGET = lint
-$(PROG): $(OBJS)
- $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
- $(POST_PROCESS)
-
-%.o: ../%.c
- $(COMPILE.c) $<
-
-$(ROOTONBLDMACHPROG): $(PROG)
-
-install: $(ROOTONBLDMACHPROG)
-
-clean:
- $(RM) $(OBJS) $(LINTFILES)
+.KEEP_STATE:
-%.ln: ../%.c
- $(LINT.c) -c $<
+all clean clobber install lint: $(SUBDIRS)
-lint: $(LINTFILES)
- $(LINT) $(LINTFLAGS) $(LINTFILES) $(LDLIBS)
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
-include ../../Makefile.ctf.targ
+FRC:
diff --git a/usr/src/tools/ctf/libctf/Makefile.com b/usr/src/tools/ctf/libctf/Makefile.com
new file mode 100644
index 0000000000..89769c5521
--- /dev/null
+++ b/usr/src/tools/ctf/libctf/Makefile.com
@@ -0,0 +1,51 @@
+#
+# 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 2018 Joyent, Inc.
+#
+
+include $(SRC)/lib/libctf/Makefile.shared.com
+include ../../Makefile.ctf
+
+CSTD = $(CSTD_GNU99)
+C99LMODE = -Xc99=%all
+
+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 -L$(ROOTONBLDLIBMACH) -ldwarf -lavl
+DYNFLAGS += '-R$$ORIGIN/../../lib/$(MACH)'
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+install: all $(ROOTONBLDLIBMACH)/libctf.so.1 $(ROOTONBLDLIBMACH)/libctf.so
+
+$(ROOTONBLDLIBMACH)/%: %
+ $(INS.file)
+
+$(ROOTONBLDLIBMACH)/$(LIBLINKS): $(ROOTONBLDLIBMACH)/$(LIBLINKS)$(VERS)
+ $(INS.liblink)
+
+#
+# Just like with libdwarf, we can't actually add ctf to ourselves,
+# because we're part of the tools for creating CTF.
+#
+$(DYNLIB) := CTFMERGE_POST= :
+CTFCONVERT_O= :
+
+include $(SRC)/lib/Makefile.targ
+include $(SRC)/lib/libctf/Makefile.shared.targ
diff --git a/usr/src/tools/ctf/libctf/i386/Makefile b/usr/src/tools/ctf/libctf/i386/Makefile
new file mode 100644
index 0000000000..a926f22084
--- /dev/null
+++ b/usr/src/tools/ctf/libctf/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 (c) 2015, Joyent, Inc.
+#
+
+include ../Makefile.com
+
+all: $(LIBS)
diff --git a/usr/src/tools/ctf/libctf/sparc/Makefile b/usr/src/tools/ctf/libctf/sparc/Makefile
new file mode 100644
index 0000000000..a926f22084
--- /dev/null
+++ b/usr/src/tools/ctf/libctf/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 (c) 2015, Joyent, Inc.
+#
+
+include ../Makefile.com
+
+all: $(LIBS)
diff --git a/usr/src/tools/make/lib/bsd/Makefile b/usr/src/tools/make/lib/bsd/Makefile
index 3dff341075..d9f7398611 100644
--- a/usr/src/tools/make/lib/bsd/Makefile
+++ b/usr/src/tools/make/lib/bsd/Makefile
@@ -28,4 +28,8 @@ install: all
lint:
+# We can't create CTF in the tools build because of a bootstrap bug with the new CTF
+$(DYNLIB) := CTFMERGE_POST= :
+CTFCONVERT_O= :
+
include $(SRC)/lib/Makefile.targ
diff --git a/usr/src/tools/make/lib/makestate/Makefile.com b/usr/src/tools/make/lib/makestate/Makefile.com
index 7f2324264c..1bf0d1d89a 100644
--- a/usr/src/tools/make/lib/makestate/Makefile.com
+++ b/usr/src/tools/make/lib/makestate/Makefile.com
@@ -37,4 +37,8 @@ $(ROOTONBLDLIBMACH)/%: %
$(ROOTONBLDLIBMACH64)/%: %
$(INS.file)
+# We can't create CTF in the tools build because of a bootstrap bug with the new CTF
+$(DYNLIB) := CTFMERGE_POST= :
+CTFCONVERT_O= :
+
include $(SRC)/lib/Makefile.targ
diff --git a/usr/src/tools/make/lib/mksh/Makefile b/usr/src/tools/make/lib/mksh/Makefile
index 418cb43cb5..b351b488bb 100644
--- a/usr/src/tools/make/lib/mksh/Makefile
+++ b/usr/src/tools/make/lib/mksh/Makefile
@@ -29,4 +29,8 @@ install: all
lint:
+# We can't create CTF in the tools build because of a bootstrap bug with the new CTF
+$(DYNLIB) := CTFMERGE_POST= :
+CTFCONVERT_O= :
+
include $(SRC)/lib/Makefile.targ
diff --git a/usr/src/tools/make/lib/vroot/Makefile b/usr/src/tools/make/lib/vroot/Makefile
index e119349178..7965de5fde 100644
--- a/usr/src/tools/make/lib/vroot/Makefile
+++ b/usr/src/tools/make/lib/vroot/Makefile
@@ -50,4 +50,8 @@ install: all
lint:
+# We can't create CTF in the tools build because of a bootstrap bug with the new CTF
+$(DYNLIB) := CTFMERGE_POST= :
+CTFCONVERT_O= :
+
include $(SRC)/lib/Makefile.targ
diff --git a/usr/src/uts/Makefile.uts b/usr/src/uts/Makefile.uts
index d1121d25ff..8f341807e1 100644
--- a/usr/src/uts/Makefile.uts
+++ b/usr/src/uts/Makefile.uts
@@ -643,3 +643,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.
+#
+$(BUILD_NEW_CTF_TOOLS)CTFCVTFLAGS += -X
diff --git a/usr/src/uts/common/ctf/ctf_mod.c b/usr/src/uts/common/ctf/ctf_mod.c
index b34cf400cd..421b922c96 100644
--- a/usr/src/uts/common/ctf/ctf_mod.c
+++ b/usr/src/uts/common/ctf/ctf_mod.c
@@ -24,8 +24,6 @@
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/sysmacros.h>
#include <sys/modctl.h>
#include <sys/debug.h>
@@ -117,6 +115,15 @@ ctf_version(int version)
/*ARGSUSED*/
ctf_file_t *
+ctf_fdcreate_int(int fd, int *errp, ctf_sect_t *ctfp)
+{
+ if (errp != NULL)
+ *errp = ENOTSUP;
+ return (NULL);
+}
+
+/*ARGSUSED*/
+ctf_file_t *
ctf_modopen(struct module *mp, int *error)
{
ctf_sect_t ctfsect, symsect, strsect;
diff --git a/usr/src/uts/common/sys/ctf.h b/usr/src/uts/common/sys/ctf.h
index 065e985b82..2e41cf9a0e 100644
--- a/usr/src/uts/common/sys/ctf.h
+++ b/usr/src/uts/common/sys/ctf.h
@@ -22,13 +22,13 @@
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ *
+ * Copyright 2018 Joyent, Inc.
*/
#ifndef _CTF_H
#define _CTF_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/types.h>
#ifdef __cplusplus
@@ -220,11 +220,13 @@ typedef struct ctf_type {
#define CTF_TYPE_NAME(stid, offset) \
(((stid) << 31) | ((offset) & 0x7fffffff))
-#define CTF_TYPE_ISPARENT(id) ((id) < 0x8000)
-#define CTF_TYPE_ISCHILD(id) ((id) > 0x7fff)
+#define CTF_CHILD_START (0x8000)
+#define CTF_TYPE_ISPARENT(id) ((id) < CTF_CHILD_START)
+#define CTF_TYPE_ISCHILD(id) ((id) >= CTF_CHILD_START)
-#define CTF_TYPE_TO_INDEX(id) ((id) & 0x7fff)
-#define CTF_INDEX_TO_TYPE(id, child) ((child) ? ((id) | 0x8000) : (id))
+#define CTF_TYPE_TO_INDEX(id) ((id) & (CTF_CHILD_START - 1))
+#define CTF_INDEX_TO_TYPE(id, child) \
+ ((child) ? ((id) | CTF_CHILD_START) : (id))
#define CTF_PARENT_SHIFT 15
#define CTF_STRTAB_0 0 /* symbolic define for string table id 0 */
diff --git a/usr/src/uts/common/sys/ctf_api.h b/usr/src/uts/common/sys/ctf_api.h
index 04d73c3181..073cc4f0d6 100644
--- a/usr/src/uts/common/sys/ctf_api.h
+++ b/usr/src/uts/common/sys/ctf_api.h
@@ -24,7 +24,7 @@
* Use is subject to license terms.
*/
/*
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2018 Joyent, Inc.
*/
/*
@@ -60,6 +60,65 @@ extern "C" {
typedef struct ctf_file ctf_file_t;
typedef long ctf_id_t;
+#define ECTF_BASE 1000 /* base value for libctf errnos */
+
+enum {
+ ECTF_FMT = ECTF_BASE, /* file is not in CTF or ELF format */
+ ECTF_ELFVERS, /* ELF version is more recent than libctf */
+ ECTF_CTFVERS, /* CTF version is more recent than libctf */
+ ECTF_ENDIAN, /* data is different endian-ness than lib */
+ ECTF_SYMTAB, /* symbol table uses invalid entry size */
+ ECTF_SYMBAD, /* symbol table data buffer invalid */
+ ECTF_STRBAD, /* string table data buffer invalid */
+ ECTF_CORRUPT, /* file data corruption detected */
+ ECTF_NOCTFDATA, /* ELF file does not contain CTF data */
+ ECTF_NOCTFBUF, /* buffer does not contain CTF data */
+ ECTF_NOSYMTAB, /* symbol table data is not available */
+ ECTF_NOPARENT, /* parent CTF container is not available */
+ ECTF_DMODEL, /* data model mismatch */
+ ECTF_MMAP, /* failed to mmap a data section */
+ ECTF_ZMISSING, /* decompression library not installed */
+ ECTF_ZINIT, /* failed to initialize decompression library */
+ ECTF_ZALLOC, /* failed to allocate decompression buffer */
+ ECTF_DECOMPRESS, /* failed to decompress CTF data */
+ ECTF_STRTAB, /* string table for this string is missing */
+ ECTF_BADNAME, /* string offset is corrupt w.r.t. strtab */
+ ECTF_BADID, /* invalid type ID number */
+ ECTF_NOTSOU, /* type is not a struct or union */
+ ECTF_NOTENUM, /* type is not an enum */
+ ECTF_NOTSUE, /* type is not a struct, union, or enum */
+ ECTF_NOTINTFP, /* type is not an integer or float */
+ ECTF_NOTARRAY, /* type is not an array */
+ ECTF_NOTREF, /* type does not reference another type */
+ ECTF_NAMELEN, /* buffer is too small to hold type name */
+ ECTF_NOTYPE, /* no type found corresponding to name */
+ ECTF_SYNTAX, /* syntax error in type name */
+ ECTF_NOTFUNC, /* symtab entry does not refer to a function */
+ ECTF_NOFUNCDAT, /* no func info available for function */
+ ECTF_NOTDATA, /* symtab entry does not refer to a data obj */
+ ECTF_NOTYPEDAT, /* no type info available for object */
+ ECTF_NOLABEL, /* no label found corresponding to name */
+ ECTF_NOLABELDATA, /* file does not contain any labels */
+ ECTF_NOTSUP, /* feature not supported */
+ ECTF_NOENUMNAM, /* enum element name not found */
+ ECTF_NOMEMBNAM, /* member name not found */
+ ECTF_RDONLY, /* CTF container is read-only */
+ ECTF_DTFULL, /* CTF type is full (no more members allowed) */
+ ECTF_FULL, /* CTF container is full */
+ ECTF_DUPMEMBER, /* duplicate member name definition */
+ ECTF_CONFLICT, /* conflicting type definition present */
+ ECTF_REFERENCED, /* type has outstanding references */
+ ECTF_NOTDYN, /* type is not a dynamic type */
+ ECTF_ELF, /* elf library failure */
+ ECTF_MCHILD, /* cannot merge child container */
+ ECTF_LABELEXISTS, /* label already exists */
+ ECTF_LCONFLICT, /* merged labels conflict */
+ 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 */
+};
+
/*
* If the debugger needs to provide the CTF library with a set of raw buffers
* for use as the CTF data, symbol table, and string table, it can do so by
@@ -143,19 +202,24 @@ typedef struct ctf_lblinfo {
typedef int ctf_visit_f(const char *, ctf_id_t, ulong_t, int, void *);
typedef int ctf_member_f(const char *, ctf_id_t, ulong_t, void *);
typedef int ctf_enum_f(const char *, int, void *);
-typedef int ctf_type_f(ctf_id_t, void *);
+typedef int ctf_type_f(ctf_id_t, boolean_t, void *);
typedef int ctf_label_f(const char *, const ctf_lblinfo_t *, void *);
+typedef int ctf_function_f(const char *, ulong_t, ctf_funcinfo_t *, void *);
+typedef int ctf_object_f(const char *, ctf_id_t, ulong_t, void *);
+typedef int ctf_string_f(const char *, void *);
extern ctf_file_t *ctf_bufopen(const ctf_sect_t *, const ctf_sect_t *,
const ctf_sect_t *, int *);
extern ctf_file_t *ctf_fdopen(int, int *);
extern ctf_file_t *ctf_open(const char *, int *);
extern ctf_file_t *ctf_create(int *);
+extern ctf_file_t *ctf_fdcreate(int, int *);
extern ctf_file_t *ctf_dup(ctf_file_t *);
extern void ctf_close(ctf_file_t *);
extern ctf_file_t *ctf_parent_file(ctf_file_t *);
extern const char *ctf_parent_name(ctf_file_t *);
+extern const char *ctf_parent_label(ctf_file_t *);
extern int ctf_import(ctf_file_t *, ctf_file_t *);
extern int ctf_setmodel(ctf_file_t *, int);
@@ -165,23 +229,34 @@ extern void ctf_setspecific(ctf_file_t *, void *);
extern void *ctf_getspecific(ctf_file_t *);
extern int ctf_errno(ctf_file_t *);
+extern uint_t ctf_flags(ctf_file_t *);
extern const char *ctf_errmsg(int);
extern int ctf_version(int);
+extern ctf_id_t ctf_max_id(ctf_file_t *);
+extern ulong_t ctf_nr_syms(ctf_file_t *);
+
extern int ctf_func_info(ctf_file_t *, ulong_t, ctf_funcinfo_t *);
+extern int ctf_func_info_by_id(ctf_file_t *, ctf_id_t, ctf_funcinfo_t *);
extern int ctf_func_args(ctf_file_t *, ulong_t, uint_t, ctf_id_t *);
+extern int ctf_func_args_by_id(ctf_file_t *, ctf_id_t, uint_t, ctf_id_t *);
extern ctf_id_t ctf_lookup_by_name(ctf_file_t *, const char *);
extern ctf_id_t ctf_lookup_by_symbol(ctf_file_t *, ulong_t);
+extern char *ctf_symbol_name(ctf_file_t *, ulong_t, char *, size_t);
+
extern ctf_id_t ctf_type_resolve(ctf_file_t *, ctf_id_t);
extern ssize_t ctf_type_lname(ctf_file_t *, ctf_id_t, char *, size_t);
extern char *ctf_type_name(ctf_file_t *, ctf_id_t, char *, size_t);
extern char *ctf_type_qname(ctf_file_t *, ctf_id_t, char *, size_t,
const char *);
+extern char *ctf_type_cname(ctf_file_t *, ctf_id_t, char *, size_t,
+ const char *);
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 *);
@@ -201,37 +276,50 @@ extern int ctf_label_info(ctf_file_t *, const char *, ctf_lblinfo_t *);
extern int ctf_member_iter(ctf_file_t *, ctf_id_t, ctf_member_f *, void *);
extern int ctf_enum_iter(ctf_file_t *, ctf_id_t, ctf_enum_f *, void *);
-extern int ctf_type_iter(ctf_file_t *, ctf_type_f *, void *);
+extern int ctf_type_iter(ctf_file_t *, boolean_t, ctf_type_f *, void *);
extern int ctf_label_iter(ctf_file_t *, ctf_label_f *, void *);
+extern int ctf_function_iter(ctf_file_t *, ctf_function_f *, void *);
+extern int ctf_object_iter(ctf_file_t *, ctf_object_f *, void *);
+extern int ctf_string_iter(ctf_file_t *, ctf_string_f *, void *);
extern ctf_id_t ctf_add_array(ctf_file_t *, uint_t, const ctf_arinfo_t *);
-extern ctf_id_t ctf_add_const(ctf_file_t *, uint_t, ctf_id_t);
+extern ctf_id_t ctf_add_const(ctf_file_t *, uint_t, const char *, ctf_id_t);
extern ctf_id_t ctf_add_enum(ctf_file_t *, uint_t, const char *);
extern ctf_id_t ctf_add_float(ctf_file_t *, uint_t,
const char *, const ctf_encoding_t *);
extern ctf_id_t ctf_add_forward(ctf_file_t *, uint_t, const char *, uint_t);
-extern ctf_id_t ctf_add_function(ctf_file_t *, uint_t,
- const ctf_funcinfo_t *, const ctf_id_t *);
+extern ctf_id_t ctf_add_funcptr(ctf_file_t *, uint_t, const ctf_funcinfo_t *,
+ const ctf_id_t *);
extern ctf_id_t ctf_add_integer(ctf_file_t *, uint_t,
const char *, const ctf_encoding_t *);
-extern ctf_id_t ctf_add_pointer(ctf_file_t *, uint_t, ctf_id_t);
+extern ctf_id_t ctf_add_pointer(ctf_file_t *, uint_t, const char *, ctf_id_t);
extern ctf_id_t ctf_add_type(ctf_file_t *, ctf_file_t *, ctf_id_t);
extern ctf_id_t ctf_add_typedef(ctf_file_t *, uint_t, const char *, ctf_id_t);
-extern ctf_id_t ctf_add_restrict(ctf_file_t *, uint_t, ctf_id_t);
+extern ctf_id_t ctf_add_restrict(ctf_file_t *, uint_t, const char *, ctf_id_t);
extern ctf_id_t ctf_add_struct(ctf_file_t *, uint_t, const char *);
extern ctf_id_t ctf_add_union(ctf_file_t *, uint_t, const char *);
-extern ctf_id_t ctf_add_volatile(ctf_file_t *, uint_t, ctf_id_t);
+extern ctf_id_t ctf_add_volatile(ctf_file_t *, uint_t, const char *, ctf_id_t);
extern int ctf_add_enumerator(ctf_file_t *, ctf_id_t, const char *, int);
-extern int ctf_add_member(ctf_file_t *, ctf_id_t, const char *, ctf_id_t);
+extern int ctf_add_member(ctf_file_t *, ctf_id_t, const char *, ctf_id_t,
+ ulong_t);
+
+
+extern int ctf_add_function(ctf_file_t *, ulong_t, const ctf_funcinfo_t *,
+ const ctf_id_t *);
+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);
extern int ctf_update(ctf_file_t *);
extern int ctf_discard(ctf_file_t *);
extern int ctf_write(ctf_file_t *, int);
+extern void ctf_dataptr(ctf_file_t *, const void **, size_t *);
#ifdef _KERNEL
diff --git a/usr/src/uts/intel/ctf/Makefile b/usr/src/uts/intel/ctf/Makefile
index 207a5ff8ea..97457d0c0c 100644
--- a/usr/src/uts/intel/ctf/Makefile
+++ b/usr/src/uts/intel/ctf/Makefile
@@ -22,6 +22,8 @@
# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright 2018 Joyent, Inc.
+#
UTSBASE = ../..
@@ -36,6 +38,8 @@ ALL_TARGET = $(BINARY)
LINT_TARGET = $(MODULE).lint
INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+C99LMODE= -Xc99=%all
+
CPPFLAGS += -I$(SRC)/common/ctf -DCTF_OLD_VERSIONS
LDFLAGS += $(BREDUCE) -M$(UTSBASE)/common/ctf/mapfile -dy
diff --git a/usr/src/uts/sparc/ctf/Makefile b/usr/src/uts/sparc/ctf/Makefile
index bb40cd9d28..0f463684dd 100644
--- a/usr/src/uts/sparc/ctf/Makefile
+++ b/usr/src/uts/sparc/ctf/Makefile
@@ -22,6 +22,8 @@
# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright 2018 Joyent, Inc.
+#
UTSBASE = ../..
@@ -36,6 +38,8 @@ ALL_TARGET = $(BINARY)
LINT_TARGET = $(MODULE).lint
INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+C99LMODE= -Xc99=%all
+
CFLAGS += $(CCVERBOSE)
CPPFLAGS += -I$(SRC)/common/ctf -DCTF_OLD_VERSIONS
LDFLAGS += $(BREDUCE) -M$(UTSBASE)/common/ctf/mapfile -dy