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