diff options
Diffstat (limited to 'usr/src')
-rw-r--r-- | usr/src/Makefile.lint | 1 | ||||
-rw-r--r-- | usr/src/Makefile.master | 10 | ||||
-rw-r--r-- | usr/src/Targetdirs | 10 | ||||
-rw-r--r-- | usr/src/cmd/Makefile | 4 | ||||
-rw-r--r-- | usr/src/cmd/ctfconvert/Makefile | 33 | ||||
-rw-r--r-- | usr/src/cmd/ctfconvert/ctfconvert.c | 388 | ||||
-rw-r--r-- | usr/src/cmd/ctfdiff/Makefile | 33 | ||||
-rw-r--r-- | usr/src/cmd/ctfdiff/ctfdiff.c | 518 | ||||
-rw-r--r-- | usr/src/cmd/ctfdump/Makefile | 36 | ||||
-rw-r--r-- | usr/src/cmd/ctfdump/ctfdump.c | 1235 | ||||
-rw-r--r-- | usr/src/cmd/ctfmerge/Makefile | 33 | ||||
-rw-r--r-- | usr/src/cmd/ctfmerge/ctfmerge.c | 536 | ||||
-rw-r--r-- | usr/src/cmd/mdb/Makefile.libstandctf | 5 | ||||
-rw-r--r-- | usr/src/cmd/mdb/common/kmdb/kmdb_promif.c | 7 | ||||
-rw-r--r-- | usr/src/cmd/mdb/common/libstandctf/ctf_subr.c | 9 | ||||
-rw-r--r-- | usr/src/cmd/mdb/common/mdb/mdb_ctf.c | 15 | ||||
-rw-r--r-- | usr/src/cmd/mdb/common/mdb/mdb_debug.c | 4 | ||||
-rw-r--r-- | usr/src/cmd/mdb/common/mdb/mdb_debug.h | 4 | ||||
-rw-r--r-- | usr/src/common/ctf/ctf_create.c | 842 | ||||
-rw-r--r-- | usr/src/common/ctf/ctf_error.c | 12 | ||||
-rw-r--r-- | usr/src/common/ctf/ctf_hash.c | 3 | ||||
-rw-r--r-- | usr/src/common/ctf/ctf_impl.h | 90 | ||||
-rw-r--r-- | usr/src/common/ctf/ctf_open.c | 40 | ||||
-rw-r--r-- | usr/src/common/ctf/ctf_types.c | 470 | ||||
-rw-r--r-- | usr/src/common/ctf/ctf_util.c | 43 | ||||
-rw-r--r-- | usr/src/lib/Makefile | 5 | ||||
-rw-r--r-- | usr/src/lib/Makefile.lib | 13 | ||||
-rw-r--r-- | usr/src/lib/Makefile.targ | 5 | ||||
-rw-r--r-- | usr/src/lib/libctf/Makefile.com | 41 | ||||
-rw-r--r-- | usr/src/lib/libctf/Makefile.shared.com | 90 | ||||
-rw-r--r-- | usr/src/lib/libctf/Makefile.shared.targ | 30 | ||||
-rw-r--r-- | usr/src/lib/libctf/common/ctf_convert.c | 210 | ||||
-rw-r--r-- | usr/src/lib/libctf/common/ctf_diff.c | 1360 | ||||
-rw-r--r-- | usr/src/lib/libctf/common/ctf_dwarf.c | 2995 | ||||
-rw-r--r-- | usr/src/lib/libctf/common/ctf_elfwrite.c | 422 | ||||
-rw-r--r-- | usr/src/lib/libctf/common/ctf_lib.c | 296 | ||||
-rw-r--r-- | usr/src/lib/libctf/common/ctf_merge.c | 1551 | ||||
-rw-r--r-- | usr/src/lib/libctf/common/ctf_subr.c | 28 | ||||
-rw-r--r-- | usr/src/lib/libctf/common/libctf.h | 45 | ||||
-rw-r--r-- | usr/src/lib/libctf/common/libctf_impl.h | 59 | ||||
-rw-r--r-- | usr/src/lib/libctf/common/mapfile-vers | 38 | ||||
-rw-r--r-- | usr/src/lib/libdtrace/common/dt_decl.c | 4 | ||||
-rw-r--r-- | usr/src/lib/libdtrace/common/dt_open.c | 12 | ||||
-rw-r--r-- | usr/src/lib/libdtrace/common/dt_parser.c | 4 | ||||
-rw-r--r-- | usr/src/lib/libdwarf/Makefile | 40 | ||||
-rw-r--r-- | usr/src/lib/libdwarf/Makefile.com | 94 | ||||
-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/Makefile | 19 | ||||
-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/Makefile | 18 | ||||
-rw-r--r-- | usr/src/lib/libdwarf/sparc/Makefile | 18 | ||||
-rw-r--r-- | usr/src/lib/libdwarf/sparcv9/Makefile | 19 | ||||
-rw-r--r-- | usr/src/lib/mergeq/mergeq.c | 606 | ||||
-rw-r--r-- | usr/src/lib/mergeq/mergeq.h | 52 | ||||
-rw-r--r-- | usr/src/lib/mergeq/workq.c | 311 | ||||
-rw-r--r-- | usr/src/lib/mergeq/workq.h | 52 | ||||
-rw-r--r-- | usr/src/man/man1/Makefile | 2 | ||||
-rw-r--r-- | usr/src/man/man1/ctfdiff.1 | 348 | ||||
-rw-r--r-- | usr/src/man/man1/ctfdump.1 | 446 | ||||
-rw-r--r-- | usr/src/pkg/manifests/developer-build-onbld.mf | 7 | ||||
-rw-r--r-- | usr/src/pkg/manifests/developer-debug-ctf.mf | 32 | ||||
-rw-r--r-- | usr/src/pkg/manifests/system-library.mf | 7 | ||||
-rw-r--r-- | usr/src/pkg/manifests/system-test-utiltest.mf | 3 | ||||
-rw-r--r-- | usr/src/test/util-tests/runfiles/default.run | 3 | ||||
-rw-r--r-- | usr/src/test/util-tests/tests/Makefile | 2 | ||||
-rw-r--r-- | usr/src/test/util-tests/tests/mergeq/Makefile | 64 | ||||
-rw-r--r-- | usr/src/test/util-tests/tests/mergeq/mqt.c | 217 | ||||
-rw-r--r-- | usr/src/test/util-tests/tests/workq/Makefile | 64 | ||||
-rw-r--r-- | usr/src/test/util-tests/tests/workq/wqt.c | 196 | ||||
-rw-r--r-- | usr/src/tools/ctf/Makefile | 9 | ||||
-rw-r--r-- | usr/src/tools/ctf/Makefile.ctf | 7 | ||||
-rw-r--r-- | usr/src/tools/ctf/common/ctf_headers.h | 18 | ||||
-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.com | 44 | ||||
-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/Makefile | 44 | ||||
-rw-r--r-- | usr/src/tools/ctf/ctfdiff/Makefile.com | 44 | ||||
-rw-r--r-- | usr/src/tools/ctf/ctfdiff/i386/Makefile | 16 | ||||
-rw-r--r-- | usr/src/tools/ctf/ctfdiff/sparc/Makefile | 16 | ||||
-rw-r--r-- | usr/src/tools/ctf/ctfdump/Makefile | 33 | ||||
-rw-r--r-- | usr/src/tools/ctf/ctfdump/Makefile.com | 47 | ||||
-rw-r--r-- | usr/src/tools/ctf/ctfdump/i386/Makefile | 16 | ||||
-rw-r--r-- | usr/src/tools/ctf/ctfdump/sparc/Makefile | 16 | ||||
-rw-r--r-- | usr/src/tools/ctf/ctfmerge/Makefile | 44 | ||||
-rw-r--r-- | usr/src/tools/ctf/ctfmerge/Makefile.com | 46 | ||||
-rw-r--r-- | usr/src/tools/ctf/ctfmerge/i386/Makefile | 27 | ||||
-rw-r--r-- | usr/src/tools/ctf/ctfmerge/sparc/Makefile | 27 | ||||
-rw-r--r-- | usr/src/tools/ctf/cvt/Makefile.com | 3 | ||||
-rw-r--r-- | usr/src/tools/ctf/cvt/altexec.c | 45 | ||||
-rw-r--r-- | usr/src/tools/ctf/cvt/ctfconvert.c | 12 | ||||
-rw-r--r-- | usr/src/tools/ctf/cvt/ctfmerge.c | 2 | ||||
-rw-r--r-- | usr/src/tools/ctf/cvt/ctftools.h | 3 | ||||
-rw-r--r-- | usr/src/tools/ctf/dump/dump.c | 1028 | ||||
-rw-r--r-- | usr/src/tools/ctf/dwarf/Makefile.com | 7 | ||||
-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.com | 51 | ||||
-rw-r--r-- | usr/src/tools/ctf/libctf/i386/Makefile | 18 | ||||
-rw-r--r-- | usr/src/tools/ctf/libctf/sparc/Makefile | 18 | ||||
-rw-r--r-- | usr/src/tools/make/lib/bsd/Makefile | 4 | ||||
-rw-r--r-- | usr/src/tools/make/lib/makestate/Makefile.com | 4 | ||||
-rw-r--r-- | usr/src/tools/make/lib/mksh/Makefile | 4 | ||||
-rw-r--r-- | usr/src/tools/make/lib/vroot/Makefile | 4 | ||||
-rw-r--r-- | usr/src/uts/Makefile.uts | 8 | ||||
-rw-r--r-- | usr/src/uts/common/ctf/ctf_mod.c | 11 | ||||
-rw-r--r-- | usr/src/uts/common/sys/ctf.h | 14 | ||||
-rw-r--r-- | usr/src/uts/common/sys/ctf_api.h | 108 | ||||
-rw-r--r-- | usr/src/uts/intel/ctf/Makefile | 4 | ||||
-rw-r--r-- | usr/src/uts/sparc/ctf/Makefile | 4 |
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 |