diff options
author | Caroline Tice <ctice@apple.com> | 2013-03-15 23:31:18 +0000 |
---|---|---|
committer | Caroline Tice <ctice@apple.com> | 2013-03-15 23:31:18 +0000 |
commit | d25e34f7584ea8bf1186f727b32d50709cdefdc6 (patch) | |
tree | 155947ea5d4445c93ae76e56d9b73c1400253455 | |
parent | 94f0469389163b7e3b00d3876931cb4cef48174f (diff) |
Add vtable verification feature but do not turn it on.google/gcc-4_7-mobile
git-svn-id: https://gcc.gnu.org/svn/gcc/branches/google/gcc-4_7-mobile@196691 138bc75d-0d04-0410-961f-82ee72b054a4
55 files changed, 6209 insertions, 113 deletions
diff --git a/configure b/configure index a51db6ea867..120b23b56c2 100755 --- a/configure +++ b/configure @@ -13687,7 +13687,7 @@ else esac if test $ok = yes; then # An in-tree tool is available and we can use it - CXX_FOR_TARGET='$$r/$(HOST_SUBDIR)/gcc/g++ -B$$r/$(HOST_SUBDIR)/gcc/ -nostdinc++ `if test -f $$r/$(TARGET_SUBDIR)/libstdc++-v3/scripts/testsuite_flags; then $(SHELL) $$r/$(TARGET_SUBDIR)/libstdc++-v3/scripts/testsuite_flags --build-includes; else echo -funconfigured-libstdc++-v3 ; fi` -L$$r/$(TARGET_SUBDIR)/libstdc++-v3/src -L$$r/$(TARGET_SUBDIR)/libstdc++-v3/src/.libs' + CXX_FOR_TARGET='$$r/$(HOST_SUBDIR)/gcc/g++ -B$$r/$(HOST_SUBDIR)/gcc/ -nostdinc++ `if test -f $$r/$(TARGET_SUBDIR)/libstdc++-v3/scripts/testsuite_flags; then $(SHELL) $$r/$(TARGET_SUBDIR)/libstdc++-v3/scripts/testsuite_flags --build-includes; else echo -funconfigured-libstdc++-v3 ; fi` -L$$r/$(TARGET_SUBDIR)/libstdc++-v3/src -L$$r/$(TARGET_SUBDIR)/libstdc++-v3/src/.libs -L$$r/$(TARGET_SUBDIR)/libstdc++-v3/libsupc++/.libs' { $as_echo "$as_me:${as_lineno-$LINENO}: result: just compiled" >&5 $as_echo "just compiled" >&6; } elif expr "x$CXX_FOR_TARGET" : "x/" > /dev/null; then @@ -13732,7 +13732,7 @@ else esac if test $ok = yes; then # An in-tree tool is available and we can use it - RAW_CXX_FOR_TARGET='$$r/$(HOST_SUBDIR)/gcc/xgcc -shared-libgcc -B$$r/$(HOST_SUBDIR)/gcc -nostdinc++ -L$$r/$(TARGET_SUBDIR)/libstdc++-v3/src -L$$r/$(TARGET_SUBDIR)/libstdc++-v3/src/.libs' + RAW_CXX_FOR_TARGET='$$r/$(HOST_SUBDIR)/gcc/xgcc -shared-libgcc -B$$r/$(HOST_SUBDIR)/gcc -nostdinc++ -L$$r/$(TARGET_SUBDIR)/libstdc++-v3/src -L$$r/$(TARGET_SUBDIR)/libstdc++-v3/src/.libs -L$$r/$(TARGET_SUBDIR)/libstdc++-v3/libsupc++/.libs' { $as_echo "$as_me:${as_lineno-$LINENO}: result: just compiled" >&5 $as_echo "just compiled" >&6; } elif expr "x$RAW_CXX_FOR_TARGET" : "x/" > /dev/null; then diff --git a/configure.ac b/configure.ac index ad6903ec472..97a68bfff23 100644 --- a/configure.ac +++ b/configure.ac @@ -3126,10 +3126,10 @@ GCC_TARGET_TOOL(as, AS_FOR_TARGET, AS, [gas/as-new]) GCC_TARGET_TOOL(cc, CC_FOR_TARGET, CC, [gcc/xgcc -B$$r/$(HOST_SUBDIR)/gcc/]) dnl see comments for CXX_FOR_TARGET_FLAG_TO_PASS GCC_TARGET_TOOL(c++, CXX_FOR_TARGET, CXX, - [gcc/g++ -B$$r/$(HOST_SUBDIR)/gcc/ -nostdinc++ `if test -f $$r/$(TARGET_SUBDIR)/libstdc++-v3/scripts/testsuite_flags; then $(SHELL) $$r/$(TARGET_SUBDIR)/libstdc++-v3/scripts/testsuite_flags --build-includes; else echo -funconfigured-libstdc++-v3 ; fi` -L$$r/$(TARGET_SUBDIR)/libstdc++-v3/src -L$$r/$(TARGET_SUBDIR)/libstdc++-v3/src/.libs], + [gcc/g++ -B$$r/$(HOST_SUBDIR)/gcc/ -nostdinc++ `if test -f $$r/$(TARGET_SUBDIR)/libstdc++-v3/scripts/testsuite_flags; then $(SHELL) $$r/$(TARGET_SUBDIR)/libstdc++-v3/scripts/testsuite_flags --build-includes; else echo -funconfigured-libstdc++-v3 ; fi` -L$$r/$(TARGET_SUBDIR)/libstdc++-v3/src -L$$r/$(TARGET_SUBDIR)/libstdc++-v3/src/.libs -L$$r/$(TARGET_SUBDIR)/libstdc++-v3/libsupc++/.libs], c++) GCC_TARGET_TOOL(c++ for libstdc++, RAW_CXX_FOR_TARGET, CXX, - [gcc/xgcc -shared-libgcc -B$$r/$(HOST_SUBDIR)/gcc -nostdinc++ -L$$r/$(TARGET_SUBDIR)/libstdc++-v3/src -L$$r/$(TARGET_SUBDIR)/libstdc++-v3/src/.libs], + [gcc/xgcc -shared-libgcc -B$$r/$(HOST_SUBDIR)/gcc -nostdinc++ -L$$r/$(TARGET_SUBDIR)/libstdc++-v3/src -L$$r/$(TARGET_SUBDIR)/libstdc++-v3/src/.libs -L$$r/$(TARGET_SUBDIR)/libstdc++-v3/libsupc++/.libs], c++) GCC_TARGET_TOOL(dlltool, DLLTOOL_FOR_TARGET, DLLTOOL, [binutils/dlltool]) GCC_TARGET_TOOL(gcc, GCC_FOR_TARGET, , [gcc/xgcc -B$$r/$(HOST_SUBDIR)/gcc/]) diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 222282b58a1..4e25c942eb6 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -866,6 +866,7 @@ TREE_H = tree.h all-tree.def tree.def c-family/c-common.def \ $(INPUT_H) statistics.h $(VEC_H) treestruct.def $(HASHTAB_H) \ double-int.h alias.h $(SYMTAB_H) $(FLAGS_H) vecir.h \ $(REAL_H) $(FIXED_VALUE_H) +CP_TREE_H = cp/cp-tree.h REGSET_H = regset.h $(BITMAP_H) hard-reg-set.h BASIC_BLOCK_H = basic-block.h $(PREDICT_H) $(VEC_H) $(FUNCTION_H) cfghooks.h GIMPLE_H = gimple.h gimple.def gsstruct.def pointer-set.h $(VEC_H) \ @@ -1448,6 +1449,7 @@ OBJS = \ tree-vect-loop-manip.o \ tree-vect-slp.o \ tree-vectorizer.o \ + tree-vtable-verify.o \ tree-vrp.o \ tree.o \ value-prof.o \ @@ -2694,6 +2696,13 @@ tree-vectorizer.o: tree-vectorizer.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ $(TM_H) $(GGC_H) $(TREE_H) $(DIAGNOSTIC_H) $(TREE_FLOW_H) $(TREE_DUMP_H) \ $(CFGLOOP_H) $(TREE_PASS_H) $(TREE_VECTORIZER_H) $(TIMEVAR_H) \ tree-pretty-print.h +tree-vtable-verify.o: tree-vtable-verify.c tree-vtable-verify.h \ + gt-tree-vtable-verify.h +# $(CONFIG_H) \ +# $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) $(CP_TREE_H) $(TM_P_H) \ +# $(BASIC_BLOCK_H) output.h $(TREE_FLOW_H) $(TREE_DUMP_H) $(TREE_PASS_H) \ +# $(TIMEVAR_H) $(CFGLOOP_H) $(FLAGS_H) $(TREE_INLINE_H) $(SCEV_H) \ +# $(DIAGNOSTIC_CORE_H) gimple-pretty-print.h toplev.h langhooks.h tree-loop-distribution.o: tree-loop-distribution.c $(CONFIG_H) $(SYSTEM_H) \ coretypes.h $(TREE_FLOW_H) $(CFGLOOP_H) $(TREE_DATA_REF_H) $(TREE_PASS_H) tree-parloops.o: tree-parloops.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ @@ -3796,6 +3805,7 @@ GTFILES = $(CPP_ID_DATA_H) $(srcdir)/input.h $(srcdir)/coretypes.h \ $(srcdir)/lto-streamer.h \ $(srcdir)/target-globals.h \ $(srcdir)/ipa-inline.h \ + $(srcdir)/tree-vtable-verify.c \ @all_gtfiles@ # Compute the list of GT header files from the corresponding C sources, diff --git a/gcc/cgraph.c b/gcc/cgraph.c index 8d2958151b5..e3a59ee77d2 100644 --- a/gcc/cgraph.c +++ b/gcc/cgraph.c @@ -2512,7 +2512,10 @@ cgraph_add_new_function (tree fndecl, bool lowered) case CGRAPH_STATE_FINISHED: /* At the very end of compilation we have to do all the work up to expansion. */ - node = cgraph_create_node (fndecl); + if (flag_vtable_verify) + node = cgraph_get_create_node (fndecl); + else + node = cgraph_create_node (fndecl); if (lowered) node->lowered = true; cgraph_analyze_function (node); diff --git a/gcc/common.opt b/gcc/common.opt index e4e53a94917..6250fe6b2a1 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -2407,6 +2407,22 @@ Enum(symbol_visibility) String(hidden) Value(VISIBILITY_HIDDEN) EnumValue Enum(symbol_visibility) String(protected) Value(VISIBILITY_PROTECTED) +fvtable-verify= +Common Joined RejectNegative Enum(vtv_priority) Var(flag_vtable_verify) Init(VTV_NO_PRIORITY) +Validate vtable pointers before using them. + +Enum +Name(vtv_priority) Type(enum vtv_priority) UnknownError(unknown vtable verify initialization priority %qs) + +EnumValue +Enum(vtv_priority) String(none) Value(VTV_NO_PRIORITY) + +EnumValue +Enum(vtv_priority) String(std) Value(VTV_STANDARD_PRIORITY) + +EnumValue +Enum(vtv_priority) String(preinit) Value(VTV_PREINIT_PRIORITY) + fvpt Common Report Var(flag_value_profile_transformations) Optimization Use expression value profiles in optimizations diff --git a/gcc/config/gnu-user.h b/gcc/config/gnu-user.h index cb457490e7a..ee17b4bf3ad 100644 --- a/gcc/config/gnu-user.h +++ b/gcc/config/gnu-user.h @@ -44,11 +44,13 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see #if defined HAVE_LD_PIE #define GNU_USER_TARGET_STARTFILE_SPEC \ "%{!shared: %{pg|p|profile:gcrt1.o%s;pie:Scrt1.o%s;:crt1.o%s}} \ - crti.o%s %{static:crtbeginT.o%s;shared|pie:crtbeginS.o%s;:crtbegin.o%s}" + crti.o%s %{static:crtbeginT.o%s;shared|pie:crtbeginS.o%s;:crtbegin.o%s} \ + %{fvtable-verify*:vtv_start.o%s}" #else #define GNU_USER_TARGET_STARTFILE_SPEC \ "%{!shared: %{pg|p|profile:gcrt1.o%s;:crt1.o%s}} \ - crti.o%s %{static:crtbeginT.o%s;shared|pie:crtbeginS.o%s;:crtbegin.o%s}" + crti.o%s %{static:crtbeginT.o%s;shared|pie:crtbeginS.o%s;:crtbegin.o%s} \ + %{fvtable-verify*:vtv_start.o%s}" #endif #undef STARTFILE_SPEC #define STARTFILE_SPEC GNU_USER_TARGET_STARTFILE_SPEC @@ -60,7 +62,8 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see GNU userspace "finalizer" file, `crtn.o'. */ #define GNU_USER_TARGET_ENDFILE_SPEC \ - "%{shared|pie:crtendS.o%s;:crtend.o%s} crtn.o%s" + "%{fvtable-verify*:vtv_end.o%s} \ + %{shared|pie:crtendS.o%s;:crtend.o%s} crtn.o%s" #undef ENDFILE_SPEC #define ENDFILE_SPEC GNU_USER_TARGET_ENDFILE_SPEC diff --git a/gcc/config/i386/gnu-user.h b/gcc/config/i386/gnu-user.h index e645fad3cf7..692509ef098 100644 --- a/gcc/config/i386/gnu-user.h +++ b/gcc/config/i386/gnu-user.h @@ -111,7 +111,8 @@ along with GCC; see the file COPYING3. If not see /* Similar to standard GNU userspace, but adding -ffast-math support. */ #undef ENDFILE_SPEC #define ENDFILE_SPEC \ - "%{Ofast|ffast-math|funsafe-math-optimizations:crtfastmath.o%s} \ + "%{fvtable-verify*:vtv_end.o%s} \ + %{Ofast|ffast-math|funsafe-math-optimizations:crtfastmath.o%s} \ %{mpc32:crtprec32.o%s} \ %{mpc64:crtprec64.o%s} \ %{mpc80:crtprec80.o%s} \ diff --git a/gcc/config/i386/gnu-user64.h b/gcc/config/i386/gnu-user64.h index 954f3b2ff24..03b8c12d87b 100644 --- a/gcc/config/i386/gnu-user64.h +++ b/gcc/config/i386/gnu-user64.h @@ -88,7 +88,8 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see /* Similar to standard GNU userspace, but adding -ffast-math support. */ #undef ENDFILE_SPEC #define ENDFILE_SPEC \ - "%{Ofast|ffast-math|funsafe-math-optimizations:crtfastmath.o%s} \ + "%{fvtable-verify*:vtv_end.o%s} \ + %{Ofast|ffast-math|funsafe-math-optimizations:crtfastmath.o%s} \ %{mpc32:crtprec32.o%s} \ %{mpc64:crtprec64.o%s} \ %{mpc80:crtprec80.o%s} \ diff --git a/gcc/cp/Make-lang.in b/gcc/cp/Make-lang.in index 26055b632d9..6e8c1354f1f 100644 --- a/gcc/cp/Make-lang.in +++ b/gcc/cp/Make-lang.in @@ -82,7 +82,7 @@ CXX_AND_OBJCXX_OBJS = cp/call.o cp/decl.o cp/expr.o cp/pt.o cp/typeck2.o \ cp/typeck.o cp/cvt.o cp/except.o cp/friend.o cp/init.o cp/method.o \ cp/search.o cp/semantics.o cp/tree.o cp/repo.o cp/dump.o cp/optimize.o \ cp/mangle.o cp/cp-objcp-common.o cp/name-lookup.o cp/cxx-pretty-print.o \ - cp/cp-gimplify.o tree-mudflap.o $(CXX_C_OBJS) + cp/cp-gimplify.o tree-mudflap.o cp/vtable-class-hierarchy.o $(CXX_C_OBJS) # Language-specific object files for C++. CXX_OBJS = cp/cp-lang.o c-family/stub-objc.o $(CXX_AND_OBJCXX_OBJS) @@ -340,7 +340,12 @@ cp/parser.o: cp/parser.c $(CXX_TREE_H) $(TM_H) $(DIAGNOSTIC_CORE_H) \ tree-threadsafe-analyze.h cp/cp-gimplify.o: cp/cp-gimplify.c $(CXX_TREE_H) $(C_COMMON_H) \ $(TM_H) coretypes.h pointer-set.h tree-iterator.h $(SPLAY_TREE_H) - +cp/vtable-class-hierarchy.o: cp/vtable-class-hierarchy.c \ + $(TM_H) $(TIMEVAR_H) $(CXX_TREE_H) intl.h $(CXX_PARSER_H) cp/decl.h \ + $(FLAGS_H) $(DIAGNOSTIC_CORE_H) output.h $(CGRAPH_H) c-family/c-common.h \ + c-family/c-objc.h $(PLUGIN_H) \ + tree-iterator.h tree-vtable-verify.h $(GIMPLE_H) \ + gt-cp-vtable-class-hierarchy.h cp/name-lookup.o: cp/name-lookup.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ $(TM_H) $(CXX_TREE_H) $(TIMEVAR_H) gt-cp-name-lookup.h \ $(DIAGNOSTIC_CORE_H) $(FLAGS_H) debug.h pointer-set.h diff --git a/gcc/cp/class.c b/gcc/cp/class.c index 3f9e2e02639..5ee2e6c4bd9 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -6136,6 +6136,9 @@ finish_struct_1 (tree t) maybe_suppress_debug_info (t); + if (flag_vtable_verify) + vtv_save_class_info (t); + dump_class_hierarchy (t); /* Finish debugging output for this type. */ diff --git a/gcc/cp/config-lang.in b/gcc/cp/config-lang.in index 3ed3d8e880f..3f68601636a 100644 --- a/gcc/cp/config-lang.in +++ b/gcc/cp/config-lang.in @@ -30,4 +30,4 @@ compilers="cc1plus\$(exeext)" target_libs="target-libstdc++-v3" -gtfiles="\$(srcdir)/cp/rtti.c \$(srcdir)/cp/mangle.c \$(srcdir)/cp/name-lookup.h \$(srcdir)/cp/name-lookup.c \$(srcdir)/cp/cp-tree.h \$(srcdir)/cp/decl.h \$(srcdir)/cp/call.c \$(srcdir)/cp/decl.c \$(srcdir)/cp/decl2.c \$(srcdir)/cp/pt.c \$(srcdir)/cp/repo.c \$(srcdir)/cp/semantics.c \$(srcdir)/cp/tree.c \$(srcdir)/cp/parser.h \$(srcdir)/cp/parser.c \$(srcdir)/cp/method.c \$(srcdir)/cp/typeck2.c \$(srcdir)/c-family/c-common.c \$(srcdir)/c-family/c-common.h \$(srcdir)/c-family/c-objc.h \$(srcdir)/c-family/c-lex.c \$(srcdir)/c-family/c-pragma.h \$(srcdir)/c-family/c-pragma.c \$(srcdir)/cp/class.c \$(srcdir)/cp/cp-objcp-common.c \$(srcdir)/cp/cp-lang.c \$(srcdir)/cp/except.c" +gtfiles="\$(srcdir)/cp/rtti.c \$(srcdir)/cp/mangle.c \$(srcdir)/cp/name-lookup.h \$(srcdir)/cp/name-lookup.c \$(srcdir)/cp/cp-tree.h \$(srcdir)/cp/decl.h \$(srcdir)/cp/call.c \$(srcdir)/cp/decl.c \$(srcdir)/cp/decl2.c \$(srcdir)/cp/pt.c \$(srcdir)/cp/repo.c \$(srcdir)/cp/semantics.c \$(srcdir)/cp/tree.c \$(srcdir)/cp/parser.h \$(srcdir)/cp/parser.c \$(srcdir)/cp/method.c \$(srcdir)/cp/typeck2.c \$(srcdir)/c-family/c-common.c \$(srcdir)/c-family/c-common.h \$(srcdir)/c-family/c-objc.h \$(srcdir)/c-family/c-lex.c \$(srcdir)/c-family/c-pragma.h \$(srcdir)/c-family/c-pragma.c \$(srcdir)/cp/class.c \$(srcdir)/cp/cp-objcp-common.c \$(srcdir)/cp/cp-lang.c \$(srcdir)/cp/except.c \$(srcdir)/cp/vtable-class-hierarchy.c" diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 5d5a5c86532..91882d089d4 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -5149,6 +5149,8 @@ extern void note_vague_linkage_fn (tree); extern tree build_artificial_parm (tree, tree); extern bool possibly_inlined_p (tree); extern int parm_index (tree); +extern tree vtv_start_verification_constructor_init_function (void); +extern tree vtv_finish_verification_constructor_init_function (tree); /* in error.c */ extern void init_error (void); @@ -5236,6 +5238,7 @@ extern tree build_java_class_ref (tree); extern tree integral_constant_value (tree); extern tree decl_constant_value_safe (tree); extern int diagnose_uninitialized_cst_or_ref_member (tree, bool, bool); +extern tree build_vtbl_address (tree); /* in lex.c */ extern void cxx_dup_lang_specific_decl (tree); @@ -5917,6 +5920,7 @@ extern tree mangle_thunk (tree, int, tree, tree); extern tree mangle_conv_op_name_for_type (tree); extern tree mangle_guard_variable (tree); extern tree mangle_ref_init_variable (tree); +extern char * get_mangled_vtable_map_var_name (tree); /* in dump.c */ extern bool cp_dump_tree (void *, tree); @@ -5964,6 +5968,12 @@ extern bool cxx_omp_privatize_by_reference (const_tree); extern void suggest_alternatives_for (location_t, tree); extern tree strip_using_decl (tree); +/* in vtable-class-hierarchy.c */ +extern void vtv_compute_class_hierarchy_transitive_closure (void); +extern void vtv_generate_init_routine (void); +extern void vtv_save_class_info (tree); +extern void vtv_recover_class_info (void); + /* -- end of C++ */ #endif /* ! GCC_CP_TREE_H */ diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index cd02b4521c9..3e156c98bd9 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -2778,7 +2778,8 @@ set_guard (tree guard) } /* Start the process of running a particular set of global constructors - or destructors. Subroutine of do_[cd]tors. */ + or destructors. Subroutine of do_[cd]tors. Also called from + vtv_start_verification_constructor_init_function. */ static tree start_objects (int method_type, int initp) @@ -2830,8 +2831,12 @@ start_objects (int method_type, int initp) return body; } -/* Finish the process of running a particular set of global constructors - or destructors. Subroutine of do_[cd]tors. */ +/* Finish the process of running a particular set of global + constructors or destructors. Subroutine of do_[cd]tors. Also + called from vtv_finish_verification_constructor_init_function. + + This function returns a tree containing the functino decl for the + functin it finished creating. */ static void finish_objects (int method_type, int initp, tree body) @@ -4121,8 +4126,22 @@ cp_write_global_declarations (void) timevar_start (TV_PHASE_CGRAPH); + if (flag_vtable_verify) + { + vtv_recover_class_info (); + vtv_compute_class_hierarchy_transitive_closure (); + } + cgraph_finalize_compilation_unit (); + if (flag_vtable_verify) + { + /* Generate the special constructor initialization function that + calls __VLTRegisterPairs, and give it a very high initialization + priority. */ + vtv_generate_init_routine (); + } + timevar_stop (TV_PHASE_CGRAPH); timevar_start (TV_PHASE_CHECK_DBGINFO); @@ -4471,4 +4490,23 @@ mark_used (tree decl) return true; } +tree +vtv_start_verification_constructor_init_function (void) +{ + return start_objects ('I', MAX_RESERVED_INIT_PRIORITY - 1); +} + +tree +vtv_finish_verification_constructor_init_function (tree function_body) +{ + tree fn; + + finish_compound_stmt (function_body); + fn = finish_function (0); + DECL_STATIC_CONSTRUCTOR (fn) = 1; + decl_init_priority_insert (fn, MAX_RESERVED_INIT_PRIORITY - 1); + + return fn; +} + #include "gt-cp-decl2.h" diff --git a/gcc/cp/g++spec.c b/gcc/cp/g++spec.c index 3a9faa9c1e9..9a107b04ae1 100644 --- a/gcc/cp/g++spec.c +++ b/gcc/cp/g++spec.c @@ -51,6 +51,8 @@ along with GCC; see the file COPYING3. If not see #define LIBSTDCXX_STATIC NULL #endif +#define VTABLE_LOAD_MODULE_INIT "--whole-archive,-lvtv_init,--no-whole-archive" + void lang_specific_driver (struct cl_decoded_option **in_decoded_options, unsigned int *in_decoded_options_count, @@ -112,6 +114,11 @@ lang_specific_driver (struct cl_decoded_option **in_decoded_options, /* The total number of arguments with the new stuff. */ unsigned int num_args = 1; + /* The command line contains a -fvtable_verify. We need to add the + init library if we are linking and if we are adding the stdc++ + library. */ + int saw_vtable_verify = 0; + argc = *in_decoded_options_count; decoded_options = *in_decoded_options; added_libraries = *in_added_libraries; @@ -237,6 +244,13 @@ lang_specific_driver (struct cl_decoded_option **in_decoded_options, } } break; + + case OPT_fvtable_verify_: + if (strcmp (arg, "std") == 0) + saw_vtable_verify = 1; + else if (strcmp (arg, "preinit") == 0) + saw_vtable_verify = 2; + break; } } @@ -248,6 +262,12 @@ lang_specific_driver (struct cl_decoded_option **in_decoded_options, /* Add one for shared_libgcc or extra static library. */ num_args = argc + added + need_math + (library > 0) * 4 + 1; + + /* Add two more linker args, '-Wl,-u_vtable_map_vars_start and + '-Wl,-u_vtable_map_vars_end. */ + if (saw_vtable_verify && library > 0) + num_args += 2; + new_decoded_options = XNEWVEC (struct cl_decoded_option, num_args); i = 0; @@ -310,6 +330,33 @@ lang_specific_driver (struct cl_decoded_option **in_decoded_options, j++; } + /* Add option to make sure that if we are doing 'std' vtable + verification then we link with the libvtv_init library. */ + + if (saw_vtable_verify == 1 && library > 0) + { + generate_option(OPT_Wl_, VTABLE_LOAD_MODULE_INIT, 1, + CL_DRIVER, &new_decoded_options[j]); + added_libraries++; + j++; + } + + /* If we are doing vtable verification, make sure the linker does + not garbage-collect the special symbols that mark the start and + end of the ".vtable_map_vars" section in the binary. (See + comments in vtv_start.c and vtv_end.c for more details). */ + + if (saw_vtable_verify > 0 && library > 0) + { + generate_option (OPT_Wl_,"-u_vtable_map_vars_start", 1, + CL_DRIVER, &new_decoded_options[j]); + j++; + + generate_option (OPT_Wl_,"-u_vtable_map_vars_end", 1, + CL_DRIVER, &new_decoded_options[j]); + j++; + } + /* Add `-lstdc++' if we haven't already done so. */ if (library > 0) { diff --git a/gcc/cp/init.c b/gcc/cp/init.c index 4e6d3fb5554..0c998b4ef10 100644 --- a/gcc/cp/init.c +++ b/gcc/cp/init.c @@ -46,7 +46,6 @@ static tree initializing_context (tree); static void expand_cleanup_for_base (tree, tree); static tree dfs_initialize_vtbl_ptrs (tree, void *); static tree build_field_list (tree, tree, int *); -static tree build_vtbl_address (tree); static int diagnose_uninitialized_cst_or_ref_member_1 (tree, tree, bool, bool); /* We are about to generate some complex initialization code. @@ -1105,7 +1104,7 @@ emit_mem_initializers (tree mem_inits) /* Returns the address of the vtable (i.e., the value that should be assigned to the vptr) for BINFO. */ -static tree +tree build_vtbl_address (tree binfo) { tree binfo_for = binfo; diff --git a/gcc/cp/mangle.c b/gcc/cp/mangle.c index 98806325d31..a65773311c7 100644 --- a/gcc/cp/mangle.c +++ b/gcc/cp/mangle.c @@ -3413,7 +3413,8 @@ mangle_decl (const tree decl) DECL_WEAK (alias) = 1; if (TREE_CODE (decl) == FUNCTION_DECL) cgraph_same_body_alias (cgraph_get_create_node (decl), alias, decl); - else + else if (!flag_vtable_verify + || TREE_CODE (decl) == VAR_DECL) varpool_extra_name_alias (alias, decl); #endif } @@ -3739,4 +3740,33 @@ write_java_integer_type_codes (const tree type) gcc_unreachable (); } +/* Given a CLASS_TYPE, such as a record for std::bad_exception this + function generates a mangled name for the vtable map variable of + the class type. For example, if the class type is + "std::bad_exception", the mangled name for the class is + "St13bad_exception". This function would generate the name + "_ZN4_VTVISt13bad_exceptionE12__vtable_mapE", which unmangles as: + "_VTV<std::bad_exception>::__vtable_map". */ + + +char * +get_mangled_vtable_map_var_name (tree class_type) +{ + char *var_name = NULL; + + gcc_assert (TREE_CODE (class_type) == RECORD_TYPE); + + tree class_id = DECL_ASSEMBLER_NAME (TYPE_NAME (class_type)); + unsigned int len = strlen (IDENTIFIER_POINTER (class_id)) + + strlen ("_ZN4_VTVI") + + strlen ("E12__vtable_mapE") + 1; + + var_name = (char *) xmalloc (len); + + sprintf (var_name, "_ZN4_VTVI%sE12__vtable_mapE", + IDENTIFIER_POINTER (class_id)); + + return var_name; +} + #include "gt-cp-mangle.h" diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 6643047b60f..87c6eec1e08 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -17799,6 +17799,7 @@ mark_class_instantiated (tree t, int extern_p) if (! extern_p) { CLASSTYPE_DEBUG_REQUESTED (t) = 1; + rest_of_type_compilation (t, 1); } } diff --git a/gcc/cp/vtable-class-hierarchy.c b/gcc/cp/vtable-class-hierarchy.c new file mode 100644 index 00000000000..a2d999cf44b --- /dev/null +++ b/gcc/cp/vtable-class-hierarchy.c @@ -0,0 +1,1370 @@ +/* Copyright (C) 2012 Free Software Foundation, Inc. + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GCC is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +/* Virtual Table Pointer Security Pass - Detect corruption of vtable pointers + before using them for virtual method dispatches. */ + +/* This file is part of the vtable security feature implementation. + The vtable security feature is designed to detect when a virtual + call is about to be made through an invalid vtable pointer + (possibly due to data corruption or malicious attacks). The + compiler finds every virtual call, and inserts a verification call + before the virtual call. The verification call takes the actual + vtable pointer value in the object through which the virtual call + is being made, and compares the vtable pointer against a set of all + valid vtable pointers that the object could contain (this set is + based on the declared type of the object). If the pointer is in + the valid set, execution is allowed to continue; otherwise the + program is halted. + + There are several pieces needed in order to make this work: 1. For + every virtual class in the program (i.e. a class that contains + virtual methods), we need to build the set of all possible valid + vtables that an object of that class could point to. This includes + vtables for any class(es) that inherit from the class under + consideration. 2. For every such data set we build up, we need a + way to find and reference the data set. This is complicated by the + fact that the real vtable addresses are not known until runtime, + when the program is loaded into memory, but we need to reference the + sets at compile time when we are inserting verification calls into + the program. 3. We need to find every virtual call in the program, + and insert the verification call (with the appropriate arguments) + before the virtual call. 4. We need some runtime library pieces: + the code to build up the data sets at runtime; the code to actually + perform the verification using the data sets; and some code to set + protections on the data sets, so they themselves do not become + hacker targets. + + To find and reference the set of valid vtable pointers for any given + virtual class, we create a special global varible for each virtual + class. We refer to this as the "vtable map variable" for that + class. The vtable map variable has the type "void *", and is + initialized by the compiler to NULL. At runtime when the set of + valid vtable pointers for a virtual class, e.g. class Foo, is built, + the vtable map variable for class Foo is made to point to the set. + During compile time, when the compiler is inserting verification + calls into the program, it passes the vtable map variable for the + appropriate class to the verification call, so that at runtime the + verification call can find the appropriate data set. + + The actual set of valid vtable pointers for a virtual class, + e.g. class Foo, cannot be built until runtime, when the vtables get + loaded into memory and their addresses are known. But the knowledge + about which vtables belong in which class' hierarchy is only known + at compile time. Therefore at compile time we collect class + hierarchy and vtable information about every virtual class, and we + generate calls to build up the data sets at runtime. To build the + data sets, we call one of the functions we add to the runtime + library, __VLTRegisterPair. __VLTRegisterPair takes two arguments, + a vtable map variable and the address of a vtable. If the vtable + map variable is currently NULL, it creates a new data set (hash + table), makes the vtable map variable point to the new data set, and + inserts the vtable address into the data set. If the vtable map + variable is not NULL, it just inserts the vtable address into the + data set. In order to make sure that our data sets are built before + any verification calls happen, we create a special constructor + initialization function for each compilation unit, give it a very + high initialization priority, and insert all of our calls to + __VLTRegisterPair into our special constructor initialization + function. + + The vtable verification feature is controlled by the flag + '-fvtable-verify='. There are three flavors of this: + '-fvtable-verify=std', '-fvtable-verify=preinit', and + '-fvtable-verify=none'. If the option '-fvtable-verfy=preinit' is + used, then our constructor initialization function gets put into the + preinit array. This is necessary if there are data sets that need + to be built very early in execution. If the constructor + initialization function gets put into the preinit array, the we also + add calls to __VLTChangePermission at the beginning and end of the + function. The call at the beginning sets the permissions on the + data sets and vtable map variables to read/write, and the one at the + end makes them read-only. If the '-fvtable-verify=std' option is + used, the constructor initialization functions are executed at their + normal time, and the __VLTChangePermission calls are handled + differently (see the comments in libstdc++-v3/libsupc++/vtv_rts.cc). + The option '-fvtable-verify=none' turns off vtable verification. + + This file contains code to find and record the class hierarchies for + the virtual classes in a program, and all the vtables associated + with each such class; to generate the vtable map variables; and to + generate the constructor initialization function (with the calls to + __VLTRegisterPair, and __VLTChangePermission). The main data + structures used for collecting the class hierarchy data and + building/maintaining the vtable map variable data are defined in + gcc/tree-vtable-verify.h, because they are used both here and in + gcc/tree-vtable-verify.c. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "timevar.h" +#include "cpplib.h" +#include "tree.h" +#include "cp-tree.h" +#include "intl.h" +#include "c-family/c-pragma.h" +#include "decl.h" +#include "flags.h" +#include "diagnostic-core.h" +#include "output.h" +#include "target.h" +#include "cgraph.h" +#include "c-family/c-common.h" +#include "c-family/c-objc.h" +#include "plugin.h" +#include "tree-iterator.h" +#include "tree-vtable-verify.h" +#include "gimple.h" + +/* Mark these specially since they need to be stored in precompiled + header IR. */ +static GTY (()) tree vlt_saved_class_info = NULL_TREE; +static GTY (()) tree vlt_register_pairs_fndecl = NULL_TREE; +static GTY (()) tree vlt_init_set_symbol_fndecl = NULL_TREE; +static GTY (()) tree vlt_change_permission_fndecl = NULL_TREE; + +struct work_node { + struct vtv_graph_node *node; + struct work_node *next; +}; + +struct vtbl_map_node *vtable_find_or_create_map_decl (tree); + +/* As part of vtable verification the compiler generates and inserts + calls to __VLTVerifyVtablePointer, which is in libstdc++. This + function builds and initializes the function decl that is used + in generating those function calls. + + In addition to __VLTVerifyVtablePointer there is also + __VLTVerifyVtablePointerDebug which can be used in place of + __VLTVerifyVtablePointer, and which takes extra parameters and + outputs extra information, to help debug problems. The debug + version of this function is generated and used if vtv_debug is + true. + + The signatures for these functions are: + + void * __VLTVerifyVtablePointer (void **, void*); + void * __VLTVerifyVtablePointerDebug (void**, void *, char *, int, char *, + int); +*/ + +static void +build_vtable_verify_fndecl (void) +{ + tree void_ptr_type = build_pointer_type (void_type_node); + tree arg_types = NULL_TREE; + tree func_type = NULL_TREE; + struct lang_decl *ld; +#ifdef VTV_DEBUG + tree const_char_ptr_type = build_pointer_type + (build_qualified_type (char_type_node, + TYPE_QUAL_CONST)); +#endif + + if (verify_vtbl_ptr_fndecl != NULL_TREE) + return; + + ld = ggc_alloc_cleared_lang_decl (sizeof (struct lang_decl_fn)); + ld->u.base.selector = 1; + + arg_types = build_tree_list (NULL_TREE, build_pointer_type (void_ptr_type)); + arg_types = chainon (arg_types, build_tree_list (NULL_TREE, + const_ptr_type_node)); + +#ifdef VTV_DEBUG + /* Arg types for the debugging version of function. */ + arg_types = chainon (arg_types, build_tree_list (NULL_TREE, + const_char_ptr_type)); + arg_types = chainon (arg_types, build_tree_list (NULL_TREE, + const_char_ptr_type)); +#endif + + arg_types = chainon (arg_types, build_tree_list (NULL_TREE, void_type_node)); + func_type = build_function_type (const_ptr_type_node, arg_types); + +#ifdef VTV_DEBUG + /* const void * + __VLTVerifyVtablePointerDebug (void ** set_handle_ptr, + const void * vtable_ptr, + const char * set_symbol_name, + const char * vtable_name) */ + + verify_vtbl_ptr_fndecl = build_fn_decl ("__VLTVerifyVtablePointerDebug", + func_type); +#else + /* const void * + __VLTVerifyVtablePointerDebug (void ** set_handle_ptr, + const void * vtable_ptr) */ + + verify_vtbl_ptr_fndecl = build_fn_decl ("__VLTVerifyVtablePointer", + func_type); +#endif + + TREE_NOTHROW (verify_vtbl_ptr_fndecl) = 1; + DECL_ATTRIBUTES (verify_vtbl_ptr_fndecl) + = tree_cons (get_identifier ("leaf"), NULL, + DECL_ATTRIBUTES (verify_vtbl_ptr_fndecl)); + DECL_PURE_P (verify_vtbl_ptr_fndecl) = 1; + TREE_PUBLIC (verify_vtbl_ptr_fndecl) = 1; +#ifdef VTV_STATIC_VERIFY + DECL_VISIBILITY (verify_vtbl_ptr_fndecl) = 1; +#endif + DECL_PRESERVE_P (verify_vtbl_ptr_fndecl) = 1; + DECL_LANG_SPECIFIC (verify_vtbl_ptr_fndecl) = ld; + SET_DECL_LANGUAGE (verify_vtbl_ptr_fndecl, lang_cplusplus); +} + +/* As part of vtable verification the compiler generates and inserts calls + to __VLTRegisterPair and __VLTChangePermission, which are in libsupc++. + This function builds and initializes the function decls that are used + in generating those function calls. + + In addition to __VLTRegisterPair there is also __VLTRegisterPairDebug + which can be used in place of __VLTRegisterPair, and which takes extra + parameters and outputs extra information, to help debug problems. The + debug version of this function is generated and used if VTV_DEBUG is + defined. + + The signatures for these functions are: + + void __VLTChangePermission (int); + void __VLTRegisterPair (void **, void*, int); + void __VLTRegisterPairDebug (void**, void *, int, char *, int, char *, int); +*/ + +static void +init_functions (void) +{ + tree void_ptr_type = build_pointer_type (void_type_node); + tree arg_types = NULL_TREE; + tree change_permission_type = void_type_node; + tree register_pairs_type = void_type_node; + tree init_set_symbol_type = void_type_node; +#ifdef VTV_DEBUG + tree const_char_ptr_type = build_pointer_type (build_qualified_type + (char_type_node, + TYPE_QUAL_CONST)); +#endif + + if (vlt_change_permission_fndecl != NULL_TREE) + return; + + gcc_assert (vlt_register_pairs_fndecl == NULL_TREE); + + arg_types = build_tree_list (NULL_TREE, integer_type_node); + arg_types = chainon (arg_types, build_tree_list (NULL_TREE, void_type_node)); + + change_permission_type = build_function_type (change_permission_type, + arg_types); + vlt_change_permission_fndecl = build_fn_decl ("__VLTChangePermission", + change_permission_type); + TREE_NOTHROW (vlt_change_permission_fndecl) = 1; + DECL_ATTRIBUTES (vlt_change_permission_fndecl) = + tree_cons (get_identifier ("leaf"), NULL, + DECL_ATTRIBUTES (vlt_change_permission_fndecl)); + TREE_PUBLIC (vlt_change_permission_fndecl) = 1; + DECL_PRESERVE_P (vlt_change_permission_fndecl) = 1; + retrofit_lang_decl (vlt_change_permission_fndecl); + SET_DECL_LANGUAGE (vlt_change_permission_fndecl, lang_cplusplus); + + arg_types = build_tree_list (NULL_TREE, build_pointer_type (void_ptr_type)); + arg_types = chainon (arg_types, build_tree_list (NULL_TREE, + const_ptr_type_node)); + +#ifdef VTV_DEBUG + /* These arguments are only used by the debug version of RegisterPair */ + arg_types = chainon (arg_types, build_tree_list (NULL_TREE, + const_char_ptr_type)); + arg_types = chainon (arg_types, build_tree_list (NULL_TREE, + const_char_ptr_type)); +#endif + + arg_types = chainon (arg_types, build_tree_list (NULL_TREE, void_type_node)); + + register_pairs_type = build_function_type (register_pairs_type, arg_types); + +#ifdef VTV_DEBUG + /* void + __VLTRegisterPairDebug (void ** set_handle_ptr, const void * vtable_ptr, + const char * set_symbol_name, const char * vtable_name) + */ + + vlt_register_pairs_fndecl = build_fn_decl ("__VLTRegisterPairDebug", + register_pairs_type); +#else + /* void __VLTRegisterPair (void **set_handle_ptr, const void *vtable_ptr) */ + vlt_register_pairs_fndecl = build_fn_decl ("__VLTRegisterPair", + register_pairs_type); +#endif + + TREE_NOTHROW (vlt_register_pairs_fndecl) = 1; + DECL_ATTRIBUTES (vlt_register_pairs_fndecl) = + tree_cons (get_identifier ("leaf"), NULL, + DECL_ATTRIBUTES (vlt_register_pairs_fndecl)); + TREE_PUBLIC (vlt_register_pairs_fndecl) = 1; + DECL_PRESERVE_P (vlt_register_pairs_fndecl) = 1; + retrofit_lang_decl (vlt_register_pairs_fndecl); + SET_DECL_LANGUAGE (vlt_register_pairs_fndecl, lang_cplusplus); + + arg_types = build_tree_list (NULL_TREE, build_pointer_type (void_ptr_type)); + arg_types = chainon (arg_types, build_tree_list (NULL_TREE, + const_ptr_type_node)); + arg_types = chainon (arg_types, build_tree_list (NULL_TREE, + size_type_node)); + arg_types = chainon (arg_types, build_tree_list (NULL_TREE, void_type_node)); + + init_set_symbol_type = build_function_type (init_set_symbol_type, arg_types); + +#ifdef VTV_DEBUG + /* void __VLTInitSetSymbolDebug(void ** set_handle_ptr, + const void * set_symbol_key, + size_t size_hint) + */ + vlt_init_set_symbol_fndecl = build_fn_decl ("__VLTInitSetSymbolDebug", + init_set_symbol_type); +#else + /* void __VLTInitSetSymbol(void ** set_handle_ptr, + const void * set_symbol_key, + size_t size_hint) + */ + vlt_init_set_symbol_fndecl = build_fn_decl ("__VLTInitSetSymbol", + init_set_symbol_type); +#endif + + TREE_NOTHROW (vlt_init_set_symbol_fndecl) = 1; + DECL_ATTRIBUTES (vlt_init_set_symbol_fndecl) = + tree_cons (get_identifier ("leaf"), NULL, + DECL_ATTRIBUTES (vlt_init_set_symbol_fndecl)); + TREE_PUBLIC (vlt_init_set_symbol_fndecl) = 1; + DECL_PRESERVE_P (vlt_init_set_symbol_fndecl) = 1; + retrofit_lang_decl (vlt_init_set_symbol_fndecl); + SET_DECL_LANGUAGE (vlt_init_set_symbol_fndecl, lang_cplusplus); +} + +/* This is a helper function for + vtv_compute_class_hierarchy_transitive_closure. It adds a + vtv_graph_node to the WORKLIST, which is a linked list of + seen-but-not-yet-processed nodes. INSERTED is a bitmap, one bit + per node, to help make sure that we don't insert a node into the + worklist more than once. Each node represents a class somewhere in + our class hierarchy information. Every node in the graph gets added + to the worklist exactly once and removed from the worklist exactly + once (when all of its children have been processed). */ + +static void +add_to_worklist (struct work_node **worklist, struct vtv_graph_node *node, + sbitmap inserted) +{ + struct work_node *new_work_node; + + if (TEST_BIT (inserted, node->class_uid)) + return; + + new_work_node = XNEW (struct work_node); + new_work_node->next = *worklist; + new_work_node->node = node; + *worklist = new_work_node; + + SET_BIT (inserted, node->class_uid); +} + +/* This is a helper function for + vtv_compute_class_hierarchy_transitive_closure. It goes through + the WORKLIST of class hierarchy nodes looking for a "leaf" node, + i.e. a node whose children in the hierarchy have all been + processed. When it finds the next leaf node, it removes it from + the linked list (WORKLIST) and returns the node. */ + +static struct vtv_graph_node * +find_and_remove_next_leaf_node (struct work_node **worklist) +{ + struct work_node *prev, *cur; + + for (prev = NULL, cur = *worklist; cur; prev = cur, cur = cur->next) + { + if (cur->node->num_children == cur->node->num_processed_children) + { + if (prev == NULL) + (*worklist) = cur->next; + else + prev->next = cur->next; + + cur->next = NULL; + return cur->node; + } + } + + return NULL; +} + +/* In our class hierarchy graph, each class node contains a bitmap, + with one bit for each class in the hierarchy. The bits are set for + classes that are descendants in the graph of the current node. + Initially the descendants bitmap is only set for immediate + descendants. This function traverses the class hierarchy graph, + bottom up, filling in the transitive closures for the descendants + as we rise up the graph. */ + +void +vtv_compute_class_hierarchy_transitive_closure (void) +{ + struct work_node *worklist = NULL; + struct vtbl_map_node *cur; + sbitmap inserted = sbitmap_alloc (num_vtable_map_nodes); + unsigned i; + + /* Note: Every node in the graph gets added to the worklist exactly + once and removed from the worklist exactly once (when all of its + children have been processed). Each node's children edges are + followed exactly once, and each node's parent edges are followed + exactly once. So this algorithm is roughly O(V + 2E), i.e. + O(E + V). */ + + /* Set-up: */ + /* Find all the "leaf" nodes in the graph, and add them to the worklist. */ + sbitmap_zero (inserted); + for (cur = vtbl_map_nodes; cur; cur = cur->next) + { + if (cur->class_info + && (cur->class_info->num_children == 0) + && ! (TEST_BIT (inserted, cur->class_info->class_uid))) + add_to_worklist (&worklist, cur->class_info, inserted); + } + + /* Main work: pull next leaf node off work list, process it, add its + parents to the worklist, where a 'leaf' node is one that has no + children, or all of its children have been processed. */ + while (worklist) + { + struct vtv_graph_node *temp_node = + find_and_remove_next_leaf_node (&worklist); + + gcc_assert (temp_node != NULL); + temp_node->descendants = sbitmap_alloc (num_vtable_map_nodes); + sbitmap_zero (temp_node->descendants); + SET_BIT (temp_node->descendants, temp_node->class_uid); + for (i = 0; i < temp_node->num_children; ++i) + sbitmap_a_or_b (temp_node->descendants, temp_node->descendants, + temp_node->children[i]->descendants); + for (i = 0; i < temp_node->num_parents; ++i) + { + temp_node->parents[i]->num_processed_children = + temp_node->parents[i]->num_processed_children + 1; + if (!TEST_BIT (inserted, temp_node->parents[i]->class_uid)) + add_to_worklist (&worklist, temp_node->parents[i], inserted); + } + } + + build_vtable_verify_fndecl (); +} + +/* Keep track of which pairs we have already created __VLTRegisterPair + calls for, to prevent creating duplicate calls within the same + compilation unit. VTABLE_DECL is the var decl for the vtable of + the (descendant) class that we are adding to our class hierarchy + data. VPTR_ADDRESS is and expression for calculating the correct + offset into the vtable (VTABLE_DECL). It is the actual vtable + pointer address that will be stored in our list of valid vtable + pointers for BASE_CLASS. BASE_CLASS is the record_type node for + the base class to whose hiearchy we want to add + VPTR_ADDRESS. (VTABLE_DECL should be the vtable for BASE_CLASS or + one of BASE_CLASS' descendents. */ + +static bool +check_and_record_registered_pairs (tree vtable_decl, tree vptr_address, + tree base_class) +{ + unsigned offset; + struct vtbl_map_node *base_vtable_map_node; + bool inserted_something = false; + + if (TREE_OPERAND_LENGTH (vptr_address) == 1) + { + tree tmp_address = TREE_OPERAND (vptr_address, 0); + offset = TREE_INT_CST_LOW (TREE_OPERAND (tmp_address, 1)); + } + else + offset = TREE_INT_CST_LOW (TREE_OPERAND (vptr_address, 1)); + + base_vtable_map_node = vtbl_map_get_node (base_class); + + inserted_something = vtbl_map_node_registration_insert (base_vtable_map_node, + vtable_decl, + offset); + return !inserted_something; +} + +/* A class may contain secondary vtables in it, for various reasons. + This function goes through the decl chain of a class record looking + for any fields that point to secondary vtables, and adding calls to + __VLTRegisterPair for the secondary vtable pointers. + + BASE_CLASS_DECL_ARG is an expression for the address of the vtable + map variable for the BASE_CLASS (whose hierarchy we are currently + updating). BASE_CLASS is the record_type node for the base class. + RECORD_TYPE is the record_type node for the descendant class that + we are possibly adding to BASE_CLASS's hierarchy. BODY is the + function body for the constructor init function to which we are + adding our calls to __VLTRegisterPair. */ + +static void +register_construction_vtables (tree base_class_decl_arg, tree base_class, + tree record_type, tree body) +{ + tree vtbl_var_decl; + tree binfo; + + if (TREE_CODE (record_type) != RECORD_TYPE) + return; + + binfo = TYPE_BINFO (record_type); + vtbl_var_decl = CLASSTYPE_VTABLES (record_type); + + if (CLASSTYPE_VBASECLASSES (record_type)) + { + tree vtt_decl; + tree sub_vtt_addr = NULL_TREE; + bool already_registered = false; + tree val_vtbl_decl = NULL_TREE; + tree arg1 = NULL_TREE; + + vtt_decl = DECL_CHAIN (vtbl_var_decl); + if (BINFO_SUBVTT_INDEX (binfo)) + sub_vtt_addr = fold_build_pointer_plus (vtt_decl, + BINFO_SUBVTT_INDEX (binfo)); + + /* Check to see if we have found a constructor vtable. Add its + data if appropriate. */ + if (vtt_decl) + { + tree values = DECL_INITIAL (vtt_decl); + struct varpool_node *vp_node = varpool_node (vtt_decl); + if (vp_node->finalized + && TREE_ASM_WRITTEN (vtt_decl) + && values != NULL_TREE + && TREE_CODE (values) == CONSTRUCTOR + && TREE_CODE (TREE_TYPE (values)) == ARRAY_TYPE) + { + tree call_expr = NULL_TREE; + unsigned HOST_WIDE_INT cnt; + constructor_elt *ce; +#ifdef VTV_DEBUG + int len1 = strlen (IDENTIFIER_POINTER + (DECL_NAME (TREE_OPERAND (base_class_decl_arg, + 0)))); + arg1 = build_string_literal (len1 + 1, + IDENTIFIER_POINTER + (DECL_NAME + (TREE_OPERAND + (base_class_decl_arg, + 0)))); +#endif + /* Loop through the initialization values for this vtable to + get all the correct vtable pointer addresses that we need + to add to our set of valid vtable pointers for the current + base class. */ + + for (cnt = 0; + VEC_iterate (constructor_elt, CONSTRUCTOR_ELTS (values), + cnt, ce); + cnt++) + { + tree value = ce->value; + + /* We need to check value and find the bit where we + have something with 2 arguments, the first + argument of which is an ADDR_EXPR and the second + argument of which is an INTEGER_CST. */ + + while (value && TREE_OPERAND_LENGTH (value) == 1 + && TREE_CODE (TREE_OPERAND (value, 0)) == ADDR_EXPR) + value = TREE_OPERAND (value, 0); + + /* The VAR_DECL for the vtable should be the first + argument of the ADDR_EXPR, which is the first + argument of value.*/ + + if (TREE_OPERAND (value, 0)) + val_vtbl_decl = TREE_OPERAND (value, 0); + + while (TREE_CODE (val_vtbl_decl) != VAR_DECL + && TREE_OPERAND (val_vtbl_decl, 0)) + val_vtbl_decl = TREE_OPERAND (val_vtbl_decl, 0); + + gcc_assert (TREE_CODE (val_vtbl_decl) == VAR_DECL); + + /* Check to see if we already have this vtable pointer in + our valid set for this base class. */ + already_registered = check_and_record_registered_pairs + (val_vtbl_decl, + value, + base_class); + + if (already_registered) + continue; +#ifdef VTV_DEBUG + { + int len2 = strlen (IDENTIFIER_POINTER + (DECL_NAME (val_vtbl_decl))); + tree arg2 = build_string_literal (len2 + 1, + IDENTIFIER_POINTER + (DECL_NAME + (val_vtbl_decl))); + + /* Generate the call to __VLTRegisterPairDebug to + add this vtable pointer to our set of valid + pointers for the base class. */ + + call_expr = build_call_expr (vlt_register_pairs_fndecl, 4, + base_class_decl_arg, value, + arg1, arg2); + } +#else + /* Generate the call to __VLTRegisterPair to add + this vtable pointer to our set of valid pointers + for the base class. */ + + call_expr = build_call_expr (vlt_register_pairs_fndecl, 2, + base_class_decl_arg, value); +#endif + append_to_statement_list (call_expr, &body); + } + } + + if (sub_vtt_addr) + { + already_registered = check_and_record_registered_pairs + (vtt_decl, + sub_vtt_addr, + record_type); + + if (!already_registered) + { + tree call_expr; +#ifdef VTV_DEBUG + { + int len2 = IDENTIFIER_LENGTH (DECL_NAME (val_vtbl_decl)); + tree arg2 = build_string_literal (len2 + 1, + IDENTIFIER_POINTER + (DECL_NAME + (val_vtbl_decl))); + + call_expr = build_call_expr (vlt_register_pairs_fndecl, + 4, base_class_decl_arg, + sub_vtt_addr, arg1, arg2); + } +#else + { + call_expr = build_call_expr (vlt_register_pairs_fndecl, + 2, base_class_decl_arg, + sub_vtt_addr); + } +#endif + append_to_statement_list (call_expr, &body); + } + } + } + } +} + +/* This function iterates through all the vtables it can find from the + BINFO of a class, to make sure we have found ALL of the vtables + that an object of that class could point to. Generate calls to + __VLTRegisterPair for those vtable pointers that we find. + + BINFO is the tree_binfo node for the BASE_CLASS. BODY is the + function body for the constructor init function to which we are + adding calls to __VLTRegisterPair. ARG1 is an expression for the + address of the vtable map variable (for the BASE_CLASS), that will + point to the updated data set. BASE_CLASS is the record_type node + for the base class whose set of valid vtable pointers we are + updating. STR1 and STR2 are all debugging information, to be passed + as parameters to __VLTRegisterPairDebug. STR1 represents the name + of the vtable map variable to be updated by the call. Similarly, + STR2 represents the name of the class whose vtable pointer is being + added to the hierarchy. */ + +static void +register_other_binfo_vtables (tree binfo, tree body, tree arg1, tree str1, + tree str2, tree base_class) +{ + unsigned ix; + tree base_binfo; + tree vtable_decl; + bool already_registered; + + if (binfo == NULL_TREE) + return; + + for (ix = 0; BINFO_BASE_ITERATE (binfo, ix, base_binfo); ix++) + { + if ((!BINFO_PRIMARY_P (base_binfo) + || BINFO_VIRTUAL_P (base_binfo)) + && (vtable_decl = get_vtbl_decl_for_binfo (base_binfo)) + && !(DECL_VTABLE_OR_VTT_P (vtable_decl) + && DECL_CONSTRUCTION_VTABLE_P (vtable_decl))) + { + tree vtable_address = build_vtbl_address (base_binfo); + tree call_expr; + + already_registered = check_and_record_registered_pairs + (vtable_decl, + vtable_address, + base_class); + if (!already_registered) + { +#ifdef VTV_DEBUG + call_expr = build_call_expr (vlt_register_pairs_fndecl, 4, + arg1, vtable_address, + str1, str2); +#else + call_expr = build_call_expr (vlt_register_pairs_fndecl, 2, + arg1, vtable_address); +#endif + append_to_statement_list (call_expr, &body); + } + } + + register_other_binfo_vtables (base_binfo, body, arg1, str1, str2, + base_class); + } +} + +/* The set of valid vtable pointers for any given class are stored in + a hash table. For reasons of efficiency, that hash table size is + always a power of two. In order to try to prevent re-sizing the + hash tables very often, we pass __VLTRegisterPair an initial guess + as to the number of entries the hashtable will eventually need + (rounded up to the nearest power of two). This function takes the + class information we have collected for a particular class, + CLASS_NODE, and calculates the hash table size guess. */ + +static int +guess_num_vtable_pointers (struct vtv_graph_node *class_node) +{ + tree vtbl; + int total_num_vtbls = 0; + int num_vtbls_power_of_two = 1; + unsigned i; + + for (i = 0; i < num_vtable_map_nodes; ++i) + if (TEST_BIT (class_node->descendants, i)) + { + tree class_type = vtbl_map_nodes_array[i]->class_info->class_type; + for (vtbl = CLASSTYPE_VTABLES (class_type); vtbl; + vtbl = DECL_CHAIN (vtbl)) + { + total_num_vtbls++; + if (total_num_vtbls > num_vtbls_power_of_two) + num_vtbls_power_of_two <<= 1; + } + } + return num_vtbls_power_of_two; +} + +/* This function goes through our internal class hierarchy & vtable + pointer data structure and outputs calls to __VLTRegisterPair for + every class-vptr pair (for those classes whose vtable would be + output in the current compilation unit). These calls get put into + our constructor initialization function. BODY is the function + body, so far, of our constructor initialization function, to which we + add the calls. */ + +static bool +register_all_pairs (tree body) +{ + struct vtbl_map_node *current; + bool registered_at_least_one = false; + + for (current = vtbl_map_nodes; current; current = current->next) + { + unsigned i; + tree base_class = current->class_info->class_type; + tree base_ptr_var_decl = current->vtbl_map_decl; + tree str1 = NULL_TREE; + + gcc_assert (current->class_info != NULL); + + +#ifdef VTV_DEBUG + str1 = build_string_literal + (IDENTIFIER_LENGTH (DECL_NAME (base_ptr_var_decl)) + 1, + IDENTIFIER_POINTER (DECL_NAME (base_ptr_var_decl))); +#endif + + for (i = 0; i < num_vtable_map_nodes; ++i) + if (TEST_BIT (current->class_info->descendants, i)) + { + struct vtbl_map_node *vtbl_class_node = vtbl_map_nodes_array[i]; + tree class_type = vtbl_class_node->class_info->class_type; + + if (class_type + && (TREE_CODE (class_type) == RECORD_TYPE)) + { + tree new_type; + tree arg1; + tree call_expr; + bool already_registered; + + tree binfo = TYPE_BINFO (class_type); + tree vtable_decl; + bool vtable_should_be_output = false; + + vtable_decl = CLASSTYPE_VTABLES (class_type); + + /* Handle main vtable for this class. */ + + if (vtable_decl) + vtable_should_be_output = TREE_ASM_WRITTEN (vtable_decl); + + if (vtable_decl && vtable_should_be_output + && BINFO_VTABLE (binfo)) + { + tree vtable_address = build_vtbl_address (binfo); + + already_registered = check_and_record_registered_pairs + (vtable_decl, + vtable_address, + base_class); + + if (!already_registered) + { + tree str2 = NULL_TREE; + new_type = build_pointer_type (TREE_TYPE + (base_ptr_var_decl)); + arg1 = build1 (ADDR_EXPR, new_type, base_ptr_var_decl); + +#ifdef VTV_DEBUG + str2 = build_string_literal (IDENTIFIER_LENGTH + (DECL_NAME (vtable_decl)) + + 1, + IDENTIFIER_POINTER + (DECL_NAME (vtable_decl))); + + /* This call expr has the 2 "real" arguments, + plus 2 debugging arguments. */ + call_expr = build_call_expr (vlt_register_pairs_fndecl, + 4, arg1, vtable_address, + str1, str2); +#else + call_expr = build_call_expr (vlt_register_pairs_fndecl, + 2, arg1, vtable_address); +#endif + + append_to_statement_list (call_expr, &body); + + registered_at_least_one = true; + + /* Find and handle any 'extra' vtables associated + with this class, via virtual inheritance. */ + register_construction_vtables (arg1, base_class, class_type, + body); + + /* Find and handle any 'extra' vtables associated + with this class, via multiple inheritance. */ + register_other_binfo_vtables (binfo, body, arg1, str1, + str2, base_class); + } + } + } + } + } + + return registered_at_least_one; +} + +/* Given a tree containing a class type (CLASS_TYPE), this function + finds and returns the class hierarchy node for that class in our + data structure. */ + +static struct vtv_graph_node * +find_graph_node (tree class_type) +{ + struct vtbl_map_node *vtbl_node; + + vtbl_node = vtbl_map_get_node (class_type); + if (vtbl_node) + return vtbl_node->class_info; + + return NULL; +} + +/* This function adds an edge to our class hierarchy graph. + EDGE_ARRAY will either be an array of parent nodes or an array of + children nodes for a particular class. NUM_ENTRIES is the current + number of entries in the array. MAX_WENTRIES is the maximum number + of entries the array can hold. NEW_ENTRY is a vtv_graph_node + representing the new child or parent node to be added to the + EDGE_ARRAY. */ + +static void +add_edge_to_graph (struct vtv_graph_node ***edge_array, unsigned *num_entries, + unsigned *max_entries, struct vtv_graph_node *new_entry) +{ + /* Check array size, and re-size it if necessary. */ + if (*num_entries >= ((*max_entries) - 1)) + { + unsigned new_size = 2 * (*max_entries); + unsigned i; + *edge_array = (struct vtv_graph_node **) + xrealloc (*edge_array, new_size * sizeof (struct vtv_graph_node *)); + + for (i = *max_entries; i < new_size; ++i) + (*edge_array)[i] = NULL; + *max_entries = new_size; + } + + (*edge_array)[*num_entries] = new_entry; + *num_entries = (*num_entries) + 1; +} + +/* Add base class/derived class pair to our internal class hierarchy + data structure. BASE_NODE is our vtv_graph_node that corresponds + to a base class. DERIVED_NODE is our vtv_graph_node that + corresponds to a class that is a descendant of the base class + (possibly the base class itself). */ + +static void +add_hierarchy_pair (struct vtv_graph_node *base_node, + struct vtv_graph_node *derived_node) +{ + add_edge_to_graph (&(base_node->children), &(base_node->num_children), + &(base_node->max_children), derived_node); + add_edge_to_graph (&(derived_node->parents), &(derived_node->num_parents), + &(derived_node->max_parents), base_node); +} + +/* This functions adds a new base class/derived class relationship to + our class hierarchy data structure. Both parameters are trees + representing the class types, i.e. RECORD_TYPE trees. + DERIVED_CLASS can be the same as BASE_CLASS. */ + +static void +update_class_hierarchy_information (tree base_class, + tree derived_class) +{ + struct vtv_graph_node *base_node = find_graph_node (base_class); + struct vtv_graph_node *derived_node = find_graph_node (derived_class); + + add_hierarchy_pair (base_node, derived_node); +} + +/* Generate an undefined variable (a reference) to a varible defined + in the vtv_init libraty. In that way, if the a module is not linked + with the vtv_init library, the linker will generate an undefined + symbol error. Which is much better that getting a segmentation + violation at runtime. The parameter, INIT_ROUTINE_BODY, is the + function body of our constructor initialization function, to which + we add the reference to this symbol (and all of our calls to + __VLTRegisterPair). + + For more information, see comments in + libstdc++-v3/libsupc++/vtv_init.cc. */ + +static void +create_undef_reference_to_vtv_init (tree init_routine_body) +{ + const char *vtv_init_undef_var = "__vtv_defined_in_vtv_init_lib"; + tree var_decl; + tree init_zero; + + var_decl = build_decl (UNKNOWN_LOCATION, VAR_DECL, + get_identifier (vtv_init_undef_var), + int32_type_node); + TREE_PUBLIC (var_decl) = 1; + DECL_EXTERNAL (var_decl) = 1; + TREE_STATIC (var_decl) = 1; + SET_DECL_ASSEMBLER_NAME (var_decl, get_identifier (vtv_init_undef_var)); + DECL_ARTIFICIAL (var_decl) = 1; + TREE_READONLY (var_decl) = 0; + DECL_IGNORED_P (var_decl) = 1; + DECL_PRESERVE_P (var_decl) = 1; + varpool_finalize_decl (var_decl); + + /* Store a value in the undefined variable to force the creation of a + a reference. */ + init_zero = build2 (MODIFY_EXPR, TREE_TYPE (var_decl), var_decl, + integer_zero_node); + append_to_statement_list (init_zero, &init_routine_body); + +} + +/* A simple hash function on strings */ +/* Be careful about changing this routine. The values generated will + be stored in the calls to InitSet. So, changing this routine may + cause a binary incompatibility. */ + +static uint32_t +vtv_string_hash(const char *in) +{ + const char *s = in; + uint32_t h = 0; + + gcc_assert (in != NULL); + for ( ; *s; ++s) + h = 5 * h + *s; + return h; +} + +/* This function goes through all of our vtable map nodes, and for + each one that is actually used, it generates a call to + __VLTInitSetSymbol, with the appropriate arguments, and inserts the + calls as the start of our constructor initialization function + (INIT_ROUTINE_BODY). */ + +static bool +init_all_sets (tree init_routine_body) +{ + struct vtbl_map_node *current; + bool inited_at_least_one = false; + tree_stmt_iterator i = tsi_start (init_routine_body); + + for (current = vtbl_map_nodes; current; current = current->next) + { + if (!(current->is_used || (htab_elements (current->registered) > 0))) + continue; + + size_t size_hint = guess_num_vtable_pointers (current->class_info); + tree set_handle_var_decl = current->vtbl_map_decl; + + tree void_ptr_type = build_pointer_type (TREE_TYPE (set_handle_var_decl)); + tree arg1 = build1 (ADDR_EXPR, void_ptr_type, set_handle_var_decl); + + uint32_t len1 = IDENTIFIER_LENGTH (DECL_NAME (set_handle_var_decl)); + uint32_t hash_value = vtv_string_hash (IDENTIFIER_POINTER + (DECL_NAME (set_handle_var_decl))); + tree arg2, arg3, init_set_call; + + /* Build a buffer with the memory representation of + insert_only_hash_map::key_value as defined in vtv_map.h. This + will be passed as the second argument to InitSet. */ + #define KEY_TYPE_FIXED_SIZE 8 + + void *key_buffer = xmalloc (len1 + KEY_TYPE_FIXED_SIZE); + uint32_t *value_ptr = (uint32_t *) key_buffer; + + /* Set the len and hash for the string. */ + *value_ptr = len1; + value_ptr++; + *value_ptr = hash_value; + + /* Now copy the string representation of the vtbl map name... */ + memcpy ((char *) key_buffer + KEY_TYPE_FIXED_SIZE, + IDENTIFIER_POINTER (DECL_NAME (set_handle_var_decl)), + len1); + + /* ... and build a string literal from it. This will make a copy + so the key_bufffer is not needed anymore after this. */ + arg2 = build_string_literal (len1 + KEY_TYPE_FIXED_SIZE, + (char *) key_buffer); + free (key_buffer); + + /* size_t maybe different at compile time vs at runtime but + there should not be a problem in here. We dont expect such + large number of elements in the set. */ + arg3 = build_int_cst (size_type_node, size_hint); + init_set_call = build_call_expr (vlt_init_set_symbol_fndecl, + 3, arg1, arg2, arg3); + gcc_assert (size_hint != 0); + tsi_link_before (&i, init_set_call, TSI_SAME_STMT); + + inited_at_least_one = true; + } + return inited_at_least_one; +} + + +/* This function calls register_all_pairs, which actually generates + all the calls to __VLTRegisterPair (in the verification constructor + init function). It also generates the calls to + __VLTChangePermission, if the verification constructor init + function is going into the preinit array. INIT_ROUTINE_BODY is + the body of our constructior initialization function, to which we + add our function calls.*/ + +static bool +vtv_register_class_hierarchy_information (tree init_routine_body) +{ + bool registered_something = false; + bool inited_some_sets = true; + + init_functions (); + + /* TODO: Temp fix. Needs to be tightened. */ + if (num_vtable_map_nodes == 0) + return false;; + + /* Add class hierarchy pairs to the vtable map data structure. */ + registered_something = register_all_pairs (init_routine_body); + + /* Initialialize all vtable map variables (pointers to our data + sets. */ + inited_some_sets = init_all_sets (init_routine_body); + + if (registered_something || inited_some_sets) + { + /* If this function is going into the preinit_array, then we + need to manually call __VLTChangePermission, rather than + depending on initialization prioritys in vtv_init. */ + if (flag_vtable_verify == VTV_PREINIT_PRIORITY) + { + /* Pass __VLTP_READ_WRITE value as defined in vtv_rts.h. */ + tree arg_read_write = build_int_cst (integer_type_node, 1); + tree arg_read_only = build_int_cst (integer_type_node, 0); + + tree call_rw_expr = build_call_expr (vlt_change_permission_fndecl, + 1, arg_read_write); + tree call_r_expr = build_call_expr (vlt_change_permission_fndecl, + 1, arg_read_only); + tree_stmt_iterator i = tsi_start (init_routine_body); + /* Insert the call to make permissions read-write at the + beginning of the init routine. */ + tsi_link_before (&i, call_rw_expr, TSI_SAME_STMT); + + /* Append the call to make permissions read-only at the + end of the init routine. */ + append_to_statement_list (call_r_expr, &init_routine_body); + } + + if (flag_vtable_verify == VTV_STANDARD_PRIORITY) + create_undef_reference_to_vtv_init (init_routine_body); + } + + return registered_something || inited_some_sets; +} + +/* This function writes records data about the number of virtual calls + we found and the number of verification calls we generated. It is + primarily for debugging purposes. */ + +static void +write_out_counters (void) +{ + if (total_num_virtual_calls == 0) + return; + + FILE *fp = fopen ("/tmp/vtable-verification-counters.log", "a"); + double pct_done = (total_num_verified_vcalls * 100) / total_num_virtual_calls; + + if (fp) + { + fprintf (fp, "%s %d %d (%.2f %%)\n", main_input_filename, + total_num_virtual_calls, total_num_verified_vcalls, + pct_done); + fclose (fp); + } +} + +/* Generate the special constructor function that calls + __VLTChangePermission and __VLTRegisterPairs, and give it a very + high initialization priority. */ + +void +vtv_generate_init_routine (void) +{ + tree init_routine_body; + bool vtable_classes_found = false; +#ifdef VTV_COUNT + bool debug_num_verified = true; +#else + bool debug_num_verified = false; +#endif + + if (debug_num_verified) + write_out_counters (); + + push_lang_context (lang_name_c); + + /* The priority for this init function (constructor) is carefully + chosen so that it will happen after the calls to unprotect the + memory used for vtable verification and before the memory is + protected again. */ + init_routine_body = vtv_start_verification_constructor_init_function (); + + vtable_classes_found = + vtv_register_class_hierarchy_information (init_routine_body); + + if (vtable_classes_found) + { + current_function_decl = + vtv_finish_verification_constructor_init_function (init_routine_body); + allocate_struct_function (current_function_decl, false); + TREE_STATIC (current_function_decl) = 1; + TREE_USED (current_function_decl) = 1; + DECL_PRESERVE_P (current_function_decl) = 1; + if (flag_vtable_verify == VTV_PREINIT_PRIORITY) + { + DECL_STATIC_CONSTRUCTOR (current_function_decl) = 0; + assemble_vtv_preinit_initializer (current_function_decl); + } + + gimplify_function_tree (current_function_decl); + cgraph_add_new_function (current_function_decl, false); + + cgraph_process_new_functions (); + } + pop_lang_context (); +} + +/* This funtion takes a tree containing a class type (BASE_TYPE), and + it either finds the existing vtbl_map_node for that class in our + data structure, or it creates a new node and adds it to the data + structure if there is not one for the class already. As part of + this process it also creates the global vtable map variable for the + class. */ + +struct vtbl_map_node * +vtable_find_or_create_map_decl (tree base_type) +{ + struct vtbl_map_node *vtable_map_node = NULL; + + /* Verify the type has an associated vtable. */ + if (!TYPE_BINFO (base_type) || !BINFO_VTABLE (TYPE_BINFO (base_type))) + return NULL; + + /* If we've already created the variable, just look for it. */ + vtable_map_node = vtbl_map_get_node (base_type); + + if (!vtable_map_node || (vtable_map_node->vtbl_map_decl == NULL_TREE)) + { + /* If we haven't already created the *__vtable_map global + variable for this class, do so now, and add it to the + varpool, to make sure it gets saved and written out. */ + char *var_name = NULL; + tree var_decl = NULL; + tree var_type = build_pointer_type (void_type_node); + tree initial_value = build_int_cst (make_node (INTEGER_TYPE), 0); + + /* Create map lookup symbol for base class */ + var_name = get_mangled_vtable_map_var_name (base_type); + var_decl = build_decl (UNKNOWN_LOCATION, VAR_DECL, + get_identifier (var_name), var_type); + TREE_PUBLIC (var_decl) = 1; + DECL_EXTERNAL (var_decl) = 0; + TREE_STATIC (var_decl) = 1; + DECL_VISIBILITY (var_decl) = VISIBILITY_HIDDEN; + SET_DECL_ASSEMBLER_NAME (var_decl, get_identifier (var_name)); + DECL_ARTIFICIAL (var_decl) = 1; + /* We cannot mark this variable as read-only otherwise the gold + linker will not put it in the relro section. It seems if it + is marked as read-only, gold will put it in the .text + segment. */ + TREE_READONLY (var_decl) = 0; + DECL_IGNORED_P (var_decl) = 1; + + /* Put these mmap variables in to .vtable_map_vars sections, so + we can find and protect them. */ + + DECL_SECTION_NAME (var_decl) = build_string (strlen (".vtable_map_vars"), + ".vtable_map_vars"); + DECL_HAS_IMPLICIT_SECTION_NAME_P (var_decl) = true; + DECL_COMDAT_GROUP (var_decl) = get_identifier (var_name); + DECL_INITIAL (var_decl) = initial_value; + + varpool_finalize_decl (var_decl); + if (!vtable_map_node) + vtable_map_node = find_or_create_vtbl_map_node (base_type); + if (vtable_map_node->vtbl_map_decl == NULL_TREE) + vtable_map_node->vtbl_map_decl = var_decl; + } + + gcc_assert (vtable_map_node); + return vtable_map_node; +} + +/* This function is used to build up our class hierarchy data for a + particular class. TYPE is the record_type tree node for the + class. */ + +static void +vtv_save_base_class_info (tree type) +{ + if (flag_vtable_verify) + { + tree binfo = TYPE_BINFO (type); + tree base_binfo; + struct vtbl_map_node *own_map; + int i; + + /* First make sure to create the map for this record type. */ + own_map = vtable_find_or_create_map_decl (type); + if (own_map == NULL) + return; + + /* Go through the list of all base classes for the current + (derived) type, make sure the *__vtable_map global variable + for the base class exists, and add the base class/derived + class pair to the class hierarchy information we are + accumulating (for vtable pointer verification). */ + for (i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); i++) + { + tree tree_val = BINFO_TYPE (base_binfo); + struct vtbl_map_node *vtable_map_node = NULL; + + vtable_map_node = vtable_find_or_create_map_decl (tree_val); + + if (vtable_map_node != NULL) + update_class_hierarchy_information (tree_val, type); + } + } +} + +/* This function adds classes we are interested in to a list of + classes that is saved during pre-compiled header generation. + RECORD is the record_type node for the class we are adding to the + list. */ + +void +vtv_save_class_info (tree record) +{ + if (!flag_vtable_verify || TREE_CODE (record) == UNION_TYPE) + return; + + gcc_assert (TREE_CODE (record) == RECORD_TYPE); + + vlt_saved_class_info = tree_cons (NULL_TREE, record, vlt_saved_class_info); +} + + +/* This function goes through the list of classes we saved before the + pre-compiled header generation and calls vtv_save_base_class_info + on each one, to build up our class hierarchy data structure. */ + +void +vtv_recover_class_info (void) +{ + tree current_class; + tree class_chain = vlt_saved_class_info; + while (class_chain != NULL_TREE) + { + current_class = TREE_VALUE (class_chain); + gcc_assert (TREE_CODE (current_class) == RECORD_TYPE); + + vtv_save_base_class_info (current_class); + class_chain = TREE_CHAIN (class_chain); + } + + /* Let the garbabe collector collect the memory associated with the + chain. */ + vlt_saved_class_info = NULL_TREE; +} + +#include "gt-cp-vtable-class-hierarchy.h" diff --git a/gcc/flag-types.h b/gcc/flag-types.h index 313e94057fe..281d0aa622d 100644 --- a/gcc/flag-types.h +++ b/gcc/flag-types.h @@ -223,4 +223,11 @@ enum opt_info_verbosity_levels { OPT_INFO_MED = 2, OPT_INFO_MAX = 3 }; + +/* flag_vtable_verify initialization levels. */ +enum vtv_priority { + VTV_NO_PRIORITY = 0, /* i.E. Do NOT do vtable verification. */ + VTV_STANDARD_PRIORITY = 1, + VTV_PREINIT_PRIORITY = 2 +}; #endif /* ! GCC_FLAG_TYPES_H */ diff --git a/gcc/output.h b/gcc/output.h index bd83199b6a9..2f1b20c9ff3 100644 --- a/gcc/output.h +++ b/gcc/output.h @@ -215,6 +215,10 @@ extern void assemble_end_function (tree, const char *); initial value (that will be done by the caller). */ extern void assemble_variable (tree, int, int, int); +/* Put the vtable verification constructor initialization function + into the preinit array. */ +extern void assemble_vtv_preinit_initializer (tree); + /* Compute the alignment of variable specified by DECL. DONT_OUTPUT_DATA is from assemble_variable. */ extern void align_variable (tree decl, bool dont_output_data); diff --git a/gcc/passes.c b/gcc/passes.c index c277d80f0fb..882ac99f511 100644 --- a/gcc/passes.c +++ b/gcc/passes.c @@ -1434,6 +1434,7 @@ init_optimization_passes (void) NEXT_PASS (pass_tm_memopt); NEXT_PASS (pass_tm_edges); } + NEXT_PASS (pass_vtable_verify); NEXT_PASS (pass_lower_complex_O0); NEXT_PASS (pass_cleanup_eh); NEXT_PASS (pass_lower_resx); diff --git a/gcc/timevar.def b/gcc/timevar.def index 9753f09bb22..4daa3233add 100644 --- a/gcc/timevar.def +++ b/gcc/timevar.def @@ -257,6 +257,7 @@ DEFTIMEVAR (TV_TREE_THREADSAFE , "thread safety analysis") DEFTIMEVAR (TV_PLUGIN_INIT , "plugin initialization") DEFTIMEVAR (TV_PLUGIN_RUN , "plugin execution") DEFTIMEVAR (TV_SIMPLIFY_GOT , "simplify got") +DEFTIMEVAR (TV_VTABLE_VERIFICATION , "vtable verification") /* Everything else in rest_of_compilation not included above. */ DEFTIMEVAR (TV_EARLY_LOCAL , "early local passes") diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h index 5d6771ffc5e..d5516053906 100644 --- a/gcc/tree-pass.h +++ b/gcc/tree-pass.h @@ -461,6 +461,7 @@ extern struct gimple_opt_pass pass_split_functions; extern struct gimple_opt_pass pass_feedback_split_functions; extern struct gimple_opt_pass pass_threadsafe_analyze; extern struct gimple_opt_pass pass_tree_convert_builtin_dispatch; +extern struct gimple_opt_pass pass_vtable_verify; /* IPA Passes */ extern struct simple_ipa_opt_pass pass_ipa_lower_emutls; diff --git a/gcc/tree-vtable-verify.c b/gcc/tree-vtable-verify.c new file mode 100644 index 00000000000..24c8fca9f27 --- /dev/null +++ b/gcc/tree-vtable-verify.c @@ -0,0 +1,754 @@ +/* Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 + Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +/* Virtual Table Pointer Security Pass - Detect corruption of vtable pointers + before using them for virtual method dispatches. */ + +/* This file is part of the vtable security feature implementation. + The vtable security feature is designed to detect when a virtual + call is about to be made through an invalid vtable pointer + (possibly due to data corruption or malicious attacks). The + compiler finds every virtual call, and inserts a verification call + before the virtual call. The verification call takes the actual + vtable pointer value in the object through which the virtual call + is being made, and compares the vtable pointer against a set of all + valid vtable pointers that the object could contain (this set is + based on the declared type of the object). If the pointer is in + the valid set, execution is allowed to continue; otherwise the + program is halted. + + There are several pieces needed in order to make this work: 1. For + every virtual class in the program (i.e. a class that contains + virtual methods), we need to build the set of all possible valid + vtables that an object of that class could point to. This includes + vtables for any class(es) that inherit from the class under + consideration. 2. For every such data set we build up, we need a + way to find and reference the data set. This is complicated by the + fact that the real vtable addresses are not known until runtime, + when the program is loaded into memory, but we need to reference the + sets at compile time when we are inserting verification calls into + the program. 3. We need to find every virtual call in the program, + and insert the verification call (with the appropriate arguments) + before the virtual call. 4. We need some runtime library pieces: + the code to build up the data sets at runtime; the code to actually + perform the verification using the data sets; and some code to set + protections on the data sets, so they themselves do not become + hacker targets. + + To find and reference the set of valid vtable pointers for any given + virtual class, we create a special global varible for each virtual + class. We refer to this as the "vtable map variable" for that + class. The vtable map variable has the type "void *", and is + initialized by the compiler to NULL. At runtime when the set of + valid vtable pointers for a virtual class, e.g. class Foo, is built, + the vtable map variable for class Foo is made to point to the set. + During compile time, when the compiler is inserting verification + calls into the program, it passes the vtable map variable for the + appropriate class to the verification call, so that at runtime the + verification call can find the appropriate data set. + + The actual set of valid vtable pointers for a virtual class, + e.g. class Foo, cannot be built until runtime, when the vtables get + loaded into memory and their addresses are known. But the knowledge + about which vtables belong in which class' hierarchy is only known + at compile time. Therefore at compile time we collect class + hierarchy and vtable information about every virtual class, and we + generate calls to build up the data sets at runtime. To build the + data sets, we call one of the functions we add to the runtime + library, __VLTRegisterPair. __VLTRegisterPair takes two arguments, + a vtable map variable and the address of a vtable. If the vtable + map variable is currently NULL, it creates a new data set (hash + table), makes the vtable map variable point to the new data set, and + inserts the vtable address into the data set. If the vtable map + variable is not NULL, it just inserts the vtable address into the + data set. In order to make sure that our data sets are built before + any verification calls happen, we create a special constructor + initialization function for each compilation unit, give it a very + high initialization priority, and insert all of our calls to + __VLTRegisterPair into our special constructor initialization + function. + + The vtable verification feature is controlled by the flag + '-fvtable-verify='. There are three flavors of this: + '-fvtable-verify=std', '-fvtable-verify=preinit', and + '-fvtable-verify=none'. If the option '-fvtable-verfy=preinit' is + used, then our constructor initialization function gets put into the + preinit array. This is necessary if there are data sets that need + to be built very early in execution. If the constructor + initialization function gets put into the preinit array, the we also + add calls to __VLTChangePermission at the beginning and end of the + function. The call at the beginning sets the permissions on the + data sets and vtable map variables to read/write, and the one at the + end makes them read-only. If the '-fvtable-verify=std' option is + used, the constructor initialization functions are executed at their + normal time, and the __VLTChangePermission calls are handled + differently (see the comments in libstdc++-v3/libsupc++/vtv_rts.cc). + The option '-fvtable-verify=none' turns off vtable verification. + + This file contains code for the tree pass that goes through all the + statements in each basic block, looking for virtual calls, and + inserting a call to __VLTVerifyVtablePointer (with appropriate + arguments) before each one. It also contains the hash table + functions for the data structures used for collecting the class + hierarchy data and building/maintaining the vtable map variable data + are defined in gcc/tree-vtable-verify.h. These data structures are + shared with the code in the C++ front end that collects the class + hierarchy & vtable information and generates the vtable map + variables (see cp/vtable-class-hierarchy.c). This tree pass should + run just before the gimple is converted to RTL. + + Some implementation details for this pass: + + To find the all of the virtual calls, we iterate through all the + gimple statements in each basic block, looking for any call + statement with the code "OBJ_TYPE_REF". Once we have found the + virtual call, we need to find the vtable pointer through which the + call is being made, and the type of the object containing the + pointer (to find the appropriate vtable map variable). We then use + these to build a call to __VLTVerifyVtablePointer, passing the + vtable map variable, and the vtable pointer. We insert the + verification call just after the gimple statement that gets the + vtable pointer out of the object, and we update the next + statement to depend on the result returned from + __VLTVerifyVtablePointer (the vtable pointer value), to ensure + subsequent compiler phases don't remove or reorder the call (it's no + good to have the verification occur after the virtual call, for + example). To find the vtable pointer being used (and the type of + the object) we search backwards through the def_stmts chain from the + virtual call (see verify_bb_vtables for more details). */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "tree.h" +/* #include "cp/cp-tree.h" */ +#include "tm_p.h" +#include "basic-block.h" +#include "output.h" +#include "tree-flow.h" +#include "tree-dump.h" +#include "tree-pass.h" +#include "timevar.h" +#include "cfgloop.h" +#include "flags.h" +#include "tree-inline.h" +#include "tree-scalar-evolution.h" +#include "diagnostic-core.h" +#include "gimple-pretty-print.h" +#include "toplev.h" +#include "langhooks.h" + +#include "tree-vtable-verify.h" + +int total_num_virtual_calls = 0; +int total_num_verified_vcalls = 0; + +unsigned num_vtable_map_nodes = 0; +bool any_verification_calls_generated = false; + +extern GTY(()) tree verify_vtbl_ptr_fndecl; +tree verify_vtbl_ptr_fndecl = NULL_TREE; + +unsigned int vtable_verify_main (void); + +/* The following few functions are for the vtbl pointer hash table + in the 'registered' field of the struct vtable_map_node. The hash + table keeps track of which vtable pointers have been used in + calls to __VLTRegisterPair with that particular vtable map variable. */ + +/* This function checks to see if a particular VTABLE_DECL and OFFSET are + already in the 'registered' hash table for NODE. */ + +bool +vtbl_map_node_registration_find (struct vtbl_map_node *node, + tree vtable_decl, + unsigned offset) +{ + struct vtable_registration key; + struct vtable_registration **slot; + + gcc_assert (node && node->registered); + + key.vtable_decl = vtable_decl; + slot = (struct vtable_registration **) htab_find_slot (node->registered, + &key, NO_INSERT); + + if (slot && (*slot) && (*slot)->offsets) + { + unsigned i; + for (i = 0; i < (*slot)->cur_offset; ++i) + if ((*slot)->offsets[i] == offset) + return true; + } + + return false; +} + +/* This function inserts VTABLE_DECL and OFFSET into the 'registered' + hash table for NODE. */ + +bool +vtbl_map_node_registration_insert (struct vtbl_map_node *node, + tree vtable_decl, + unsigned offset) +{ + struct vtable_registration key; + struct vtable_registration **slot; + bool inserted_something = false; + + if (!node || !node->registered) + return false; + + key.vtable_decl = vtable_decl; + slot = (struct vtable_registration **) htab_find_slot (node->registered, + &key, INSERT); + + if (! *slot) + { + unsigned i; + struct vtable_registration *node; + node = XNEW (struct vtable_registration); + node->vtable_decl = vtable_decl; + + /* We expect the number of different offsets in any given vtable + that will be valid vtable pointers to be small (but we know + it can be greater than 1). To avoid having to resize this + very often, we randomly chose 10 as a reasonable-seeming + size. */ + node->offsets = (unsigned *) xmalloc (10 * sizeof (unsigned)); + for (i= 0; i < 10; ++i) + node->offsets[i] = 0; + node->offsets[0] = offset; + node->cur_offset = 1; + node->max_offsets = 10; + *slot = node; + inserted_something = true; + } + else + { + /* We found the vtable_decl slot; we need to see if it already + contains the offset. If not, we need to add the offset. */ + unsigned i; + bool found = false; + for (i = 0; i < (*slot)->cur_offset && !found; ++i) + if ((*slot)->offsets[i] == offset) + found = true; + + if (!found) + { + /* Re-size the offset array if necessary. */ + if ((*slot)->cur_offset == (*slot)->max_offsets) + { + unsigned new_max = 2 * (*slot)->max_offsets; + (*slot)->offsets = (unsigned *) + xrealloc ((*slot)->offsets, new_max * sizeof (unsigned)); + + for (i = (*slot)->max_offsets; i < new_max; ++i) + (*slot)->offsets[i] = 0; + (*slot)->max_offsets = new_max; + } + /* Insert the new offset. */ + (*slot)->offsets[(*slot)->cur_offset] = offset; + (*slot)->cur_offset = (*slot)->cur_offset + 1; + inserted_something = true; + } + } + return inserted_something; +} + +/* Hashtable functions for vtable_registration hashtables. */ + +static hashval_t +hash_vtable_registration (const void *p) +{ + const struct vtable_registration *n = (const struct vtable_registration *) p; + return (hashval_t) (DECL_UID (n->vtable_decl)); +} + +static int +eq_vtable_registration (const void *p1, const void *p2) +{ + const struct vtable_registration *n1 = + (const struct vtable_registration *) p1; + const struct vtable_registration *n2 = + (const struct vtable_registration *) p2; + return (DECL_UID (n1->vtable_decl) == DECL_UID (n2->vtable_decl)); +} + +/* End of hashtable functions for "registered" hashtables. */ + + +/* Here are the three data structures into which we insert vtable map nodes. + We use three data structures because of the vastly different ways we need + to find the nodes for various tasks (see comments in tree-vtable-verify.h + for more details. */ + +/* Vtable map variable nodes stored in a hash table. */ +static htab_t vtbl_map_hash = NULL; + +/* Vtable map variable nodes stored in a linked list. */ +struct vtbl_map_node *vtbl_map_nodes = NULL; + +/* Vtable map variable nodes stored in an array. */ +struct vtbl_map_node **vtbl_map_nodes_array = NULL; + +/* This function take a vtbl_map_node, NODE, and inserts it in the + array of vtable map nodes, using it's uid as the position in the + array into which to insert it. (The uids are unique and + monotonically grow as the nodes are generated.) */ + +static void +vtable_map_array_insert (struct vtbl_map_node *node) +{ + static unsigned array_size = 0; + unsigned i; + + /* If the array is NULL, allocate it with an initial size of 16. */ + if (vtbl_map_nodes_array == NULL || array_size == 0) + { + array_size = 16; + vtbl_map_nodes_array = (struct vtbl_map_node **) + xmalloc (array_size * sizeof (struct vtbl_map_node *)); + memset (vtbl_map_nodes_array, 0, + array_size * sizeof (struct vtbl_map_node *)); + } + else if (node->uid >= array_size) + { + /* Check to see if the array is large enough to hold another + node; resize it if it is not. */ + unsigned new_size = 2 * array_size; + vtbl_map_nodes_array = (struct vtbl_map_node **) + xrealloc (vtbl_map_nodes_array, + new_size * sizeof (struct vtbl_map_node *)); + + for (i = array_size; i < new_size; ++i) + vtbl_map_nodes_array[i] = NULL; + + array_size = new_size; + } + + gcc_assert (node->uid < array_size + && vtbl_map_nodes_array[node->uid] == NULL); + + /* Insert the node into the array. */ + vtbl_map_nodes_array[node->uid] = node; +} + +/* Hashtable functions for vtbl_map_hash. */ + +/* Returns a hash code for P. */ + +static hashval_t +hash_vtbl_map_node (const void *p) +{ + const struct vtbl_map_node *n = (const struct vtbl_map_node *) p; + return (hashval_t) IDENTIFIER_HASH_VALUE (n->class_name); +} + +/* Returns nonzero if P1 and P2 are equal. */ + +static int +eq_vtbl_map_node (const void *p1, const void *p2) +{ + const struct vtbl_map_node *n1 = (const struct vtbl_map_node *) p1; + const struct vtbl_map_node *n2 = (const struct vtbl_map_node *) p2; + return (IDENTIFIER_HASH_VALUE (n1->class_name) == + IDENTIFIER_HASH_VALUE (n2->class_name)); +} + +/* Return vtbl_map node for CLASS_TYPE without creating a new one. */ + +struct vtbl_map_node * +vtbl_map_get_node (tree class_type) +{ + struct vtbl_map_node key; + struct vtbl_map_node **slot; + + tree class_type_decl; + tree class_name; + unsigned int type_quals; + + if (!vtbl_map_hash) + return NULL; + + gcc_assert (TREE_CODE (class_type) == RECORD_TYPE); + + /* Find the TYPE_DECL for the class. */ + class_type_decl = TYPE_NAME (class_type); + + /* Verify that there aren't any qualifiers on the type. */ + type_quals = TYPE_QUALS (TREE_TYPE (class_type_decl)); + gcc_assert (type_quals == TYPE_UNQUALIFIED); + + /* Get the mangled name for the unqualified type. */ + class_name = DECL_ASSEMBLER_NAME (class_type_decl); + + key.class_name = class_name; + slot = (struct vtbl_map_node **) htab_find_slot (vtbl_map_hash, &key, + NO_INSERT); + if (!slot) + return NULL; + return *slot; +} + +/* Return vtbl_map node assigned to BASE_CLASS_TYPE. Create new one + when needed. */ + +struct vtbl_map_node * +find_or_create_vtbl_map_node (tree base_class_type) +{ + struct vtbl_map_node key; + struct vtbl_map_node *node; + struct vtbl_map_node **slot; + unsigned i; + tree class_type_decl; + unsigned int type_quals; + /* Our data shows 90% of classes have no more than 4 parents or children, + so we will use 4 as our default hierarchy array size. */ + unsigned int default_array_size = 4; + + if (!vtbl_map_hash) + vtbl_map_hash = htab_create (10, hash_vtbl_map_node, + eq_vtbl_map_node, NULL); + + /* Find the TYPE_DECL for the class. */ + class_type_decl = TYPE_NAME (base_class_type); + + /* Verify that there aren't any type qualifiers on type. */ + type_quals = TYPE_QUALS (TREE_TYPE (class_type_decl)); + gcc_assert (type_quals == TYPE_UNQUALIFIED); + + key.class_name = DECL_ASSEMBLER_NAME (class_type_decl); + slot = (struct vtbl_map_node **) htab_find_slot (vtbl_map_hash, &key, + INSERT); + + if (*slot) + return *slot; + + node = XNEW (struct vtbl_map_node); + node->vtbl_map_decl = NULL_TREE; + node->class_name = key.class_name; + node->uid = num_vtable_map_nodes++; + + node->class_info = XNEW (struct vtv_graph_node); + node->class_info->class_type = base_class_type; + node->class_info->class_uid = node->uid; + node->class_info->max_parents = default_array_size; + node->class_info->max_children = default_array_size; + node->class_info->num_parents = 0; + node->class_info->num_children = 0; + node->class_info->num_processed_children = 0; + node->class_info->parents = + (struct vtv_graph_node **) + xmalloc (default_array_size * sizeof (struct vtv_graph_node *)); + node->class_info->children = + (struct vtv_graph_node **) + xmalloc (default_array_size * sizeof (struct vtv_graph_node *)); + + for (i = 0; i < default_array_size; ++i) + { + node->class_info->parents[i] = NULL; + node->class_info->children[i] = NULL; + } + + node->registered = htab_create (16, hash_vtable_registration, + eq_vtable_registration, NULL); + node->is_used = false; + node->next = vtbl_map_nodes; + if (vtbl_map_nodes) + vtbl_map_nodes->prev = node; + + vtable_map_array_insert (node); + + vtbl_map_nodes = node; + *slot = node; + return node; +} + +/* End of hashtable functions for vtable_map variables hash table. */ + +/* Given a gimple STMT, this function checks to see if the statement + is an assignment, the rhs of which is getting the vtable pointer + value out of an object. (i.e. it's the value we need to verify + because its the vtable pointer that will be used for a virtual + call). */ + +static bool +is_vtable_assignment_stmt (gimple stmt) +{ + + if (gimple_code (stmt) != GIMPLE_ASSIGN) + return false; + else + { + tree lhs = gimple_assign_lhs (stmt); + tree rhs = gimple_assign_rhs1 (stmt); + + if (TREE_CODE (lhs) != SSA_NAME) + return false; + + if (TREE_CODE (rhs) != COMPONENT_REF) + return false; + + if (! (TREE_OPERAND (rhs, 1)) + || (TREE_CODE (TREE_OPERAND (rhs, 1)) != FIELD_DECL)) + return false; + + if (! DECL_VIRTUAL_P (TREE_OPERAND (rhs, 1))) + return false; + } + + return true; +} + +/* This function attempts to recover the declared class of an object + that is used in making a virtual call. First we try looking + directly at the THIS_OBJECT itself. If that does not work, we try to + get the type from the gimple assignment statement that extracts the + vtable pointer from the object (DEF_STMT). The gimple + statment usually looks something like this: + + D.2201_4 = MEM[(struct Event *)this_1(D)]._vptr.Event */ + +static tree +extract_object_class_type (tree rhs) +{ + tree result = NULL_TREE; + + if (TREE_CODE (rhs) == COMPONENT_REF) + { + tree op0 = TREE_OPERAND (rhs, 0); + tree op1 = TREE_OPERAND (rhs, 1); + + if (TREE_CODE (op1) == FIELD_DECL + && DECL_VIRTUAL_P (op1)) + { + if (TREE_CODE (op0) == COMPONENT_REF + && TREE_CODE (TREE_OPERAND (op0, 0)) == MEM_REF + && TREE_CODE (TREE_TYPE (TREE_OPERAND (op0, 0)))== RECORD_TYPE) + result = TREE_TYPE (TREE_OPERAND (op0, 0)); + else + result = TREE_TYPE (op0); + } + else if (TREE_CODE (op0) == COMPONENT_REF) + { + result = extract_object_class_type (op0); + if (result == NULL_TREE + && TREE_CODE (op1) == COMPONENT_REF) + result = extract_object_class_type (op1); + } + } + + return result; +} + +/* Search through all the statements in a basic block (BB), searching + for virtual method calls. For each virtual method dispatch, find + the vptr value used, and the statically declared type of the + object; retrieve the vtable map variable for the type of the + object; generate a call to __VLTVerifyVtablePointer; and insert the + generated call into the basic block, after the point where the vptr + value is gotten out of the object and before the virtual method + dispatch. Make the virtual method dispatch depend on the return + value from the verification call, so that subsequent optimizations + cannot reorder the two calls. */ + +static void +verify_bb_vtables (basic_block bb) +{ + gimple_seq stmts; + gimple stmt = NULL; + gimple_stmt_iterator gsi_vtbl_assign; + gimple_stmt_iterator gsi_virtual_call; + tree this_object; + + stmts = bb_seq (bb); + gsi_virtual_call = gsi_start (stmts); + this_object = NULL_TREE; + for (; !gsi_end_p (gsi_virtual_call); gsi_next (&gsi_virtual_call)) + { + stmt = gsi_stmt (gsi_virtual_call); + if (is_vtable_assignment_stmt (stmt)) + { + tree lhs = gimple_assign_lhs (stmt); + tree vtbl_var_decl = NULL_TREE; + struct vtbl_map_node *vtable_map_node; + tree vtbl_decl = NULL_TREE; + gimple call_stmt; + struct gimplify_ctx gctx; + const char *vtable_name = "<unknown>"; + tree tmp0; + bool found; + + gsi_vtbl_assign = gsi_for_stmt (stmt); + + /* Now we have found the virtual method dispatch and + the preceding access of the _vptr.* field... Next + we need to find the statically declared type of + the object, so we can find and use the right + vtable map variable in the verification call. */ + tree class_type = extract_object_class_type + (gimple_assign_rhs1 (stmt)); + + if (class_type + && (TREE_CODE (class_type) == RECORD_TYPE) + && TYPE_BINFO (class_type)) + { + /* Get the vtable VAR_DECL for the type. */ + vtbl_var_decl = BINFO_VTABLE (TYPE_BINFO (class_type)); + + if (TREE_CODE (vtbl_var_decl) == POINTER_PLUS_EXPR) + vtbl_var_decl = TREE_OPERAND (TREE_OPERAND (vtbl_var_decl, 0), + 0); + + gcc_assert (vtbl_var_decl); + + vtbl_decl = vtbl_var_decl; + vtable_map_node = vtbl_map_get_node (class_type); + + gcc_assert (verify_vtbl_ptr_fndecl); + + /* Given the vtable pointer for the base class of the + object, build the call to __VLTVerifyVtablePointer to + verify that the object's vtable pointer (contained in + lhs) is in the set of valid vtable pointers for the + base class. */ + + if (vtable_map_node && vtable_map_node->vtbl_map_decl) + { + use_operand_p use_p; + ssa_op_iter iter; + tree expr_tree; + gimple_seq pre_p = NULL; + + vtable_map_node->is_used = true; + vtbl_var_decl = vtable_map_node->vtbl_map_decl; + + if (!var_ann (vtbl_var_decl)) + add_referenced_var (vtbl_var_decl); + + if (TREE_CODE (vtbl_decl) == VAR_DECL) + vtable_name = IDENTIFIER_POINTER (DECL_NAME (vtbl_decl)); + + push_gimplify_context (&gctx); + + /* Call different routines if we are interested in + trace information to debug problems. */ +#ifdef VTV_DEBUG + len1 = IDENTIFIER_LENGTH (DECL_NAME (vtbl_var_decl)); + len2 = strlen (vtable_name); + + expr_tree = build_call_expr + (verify_vtbl_ptr_fndecl, 4, + build1 (ADDR_EXPR, + TYPE_POINTER_TO + (TREE_TYPE (vtbl_var_decl)), + vtbl_var_decl), + SSA_NAME_VAR (lhs), + build_string_literal + (len1 + 1, + IDENTIFIER_POINTER + (DECL_NAME + (vtbl_var_decl))), + build_string_literal (len2 + 1, + vtable_name)); +#else + expr_tree = build_call_expr + (verify_vtbl_ptr_fndecl, 2, + build1 (ADDR_EXPR, + TYPE_POINTER_TO + (TREE_TYPE (vtbl_var_decl)), + vtbl_var_decl), + SSA_NAME_VAR (lhs)); +#endif + /* Assign the result of the call to the original + variable receiving the assignment of the + object's vtable pointer; mark that variable + to be updated by update_ssa. */ + + mark_sym_for_renaming (SSA_NAME_VAR (lhs)); + force_gimple_operand (expr_tree, &pre_p, 1, + SSA_NAME_VAR (lhs)); + + /* Insert the new call just after the original + assignment of the object's vtable pointer. */ + + pop_gimplify_context (NULL); + gsi_vtbl_assign = gsi_for_stmt (stmt); + gsi_insert_seq_after (&gsi_vtbl_assign, pre_p, + GSI_NEW_STMT); + + any_verification_calls_generated = true; + total_num_verified_vcalls++; + } + } + } + } +} + +/* Main function, called from pass->excute(). Loop through all the + basic blocks in the current function, passing them to + verify_bb_vtables, which searches for virtual calls, and inserts + calls to __VLTVerifyVtablePointer. */ + +unsigned int +vtable_verify_main (void) +{ + unsigned int ret = 1; + basic_block bb; + + FOR_ALL_BB (bb) + verify_bb_vtables (bb); + + return ret; +} + +/* Gate function for the pass. */ + +static bool +gate_tree_vtable_verify (void) +{ + return (flag_vtable_verify + && (strcmp (lang_hooks.name, "GNU C++") == 0)); +} + +/* Definition of this optimization pass. */ + +struct gimple_opt_pass pass_vtable_verify = +{ + { + GIMPLE_PASS, + "vtable-verify", /* name */ + gate_tree_vtable_verify, /* gate */ + vtable_verify_main, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + TV_VTABLE_VERIFICATION, /* tv_id */ + PROP_cfg | PROP_ssa, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_dump_func | TODO_update_ssa + | TODO_ggc_collect /* todo_flags_finish */ + } +}; + +#include "gt-tree-vtable-verify.h" diff --git a/gcc/tree-vtable-verify.h b/gcc/tree-vtable-verify.h new file mode 100644 index 00000000000..ed0c05436f0 --- /dev/null +++ b/gcc/tree-vtable-verify.h @@ -0,0 +1,151 @@ +/* Interprocedural constant propagation + Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 + Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +/* Virtual Table Pointer Security. */ + +#ifndef TREE_VTABLE_VERIFY_H +#define TREE_VTABLE_VERIFY_H + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "timevar.h" +#include "cpplib.h" +#include "tree.h" +#include "hashtab.h" +#include "sbitmap.h" + +/* The function decl used to create calls to __VLTVtableVerify. It must + be global because it needs to be initialized in the C++ front end, but + used in the middle end (in the vtable verification pass). */ + +extern tree verify_vtbl_ptr_fndecl; + +/* Global variable keeping track of how many vtable map variables we + have created. */ +extern unsigned num_vtable_map_nodes; + +/* Global variable that records whether or not any vtable verification + calls have been generated. */ +extern bool any_verification_calls_generated; + +/* Keep track of how many virtual calls we are actually verifying. */ +extern int total_num_virtual_calls; +extern int total_num_verified_vcalls; + +/* Each vtable map variable corresponds to a virtual class. Each + vtable map variable has a hash table associated with it, that keeps + track of the vtable pointers for which we have generated a call to + __VLTRegisterPair (with the current vtable map variable). This is + the hash table node that is used for each entry in this hash table + of vtable pointers. + + Sometimes there are multiple valid vtable pointer entries that use + the same vtable pointer decl with different offsets. Therefore, + for each vtable pointer in the hash table, there is also an array + of offsets used with that vtable. */ + +struct vtable_registration +{ + tree vtable_decl; /* The var decl of the vtable. */ + unsigned max_offsets; /* The allocated size of the offsets array. */ + unsigned cur_offset; /* The next availabe entry in the offsets + array. */ + unsigned *offsets; /* The offsets array. */ +}; + +/* This struct is used to represent the class hierarchy information + that we need. Each vtable map variable has an associated class + hierarchy node (struct vtv_graph_node). Note: In this struct, + 'children' means immediate descendants in the class hierarchy; + 'descendant' means any descendant however many levels deep. */ + +struct vtv_graph_node { + tree class_type; /* The record_type of the class. */ + unsigned class_uid; /* A unique, monotonically + ascending id for class node. + Each vtable map node also has + an id. The class uid is the + same as the vtable map node id + for nodes corresponding to the + same class. */ + unsigned max_parents; /* Allocated size of the parents array. */ + unsigned max_children; /* Allocated size of the children array. */ + unsigned num_parents; /* # of entries in the parents array. */ + unsigned num_children; /* # of entries in the children array. */ + unsigned num_processed_children; /* # of children for whom we have + computed the class hierarchy + transitive closure. */ + struct vtv_graph_node **parents; /* Array of parents in the graph. */ + struct vtv_graph_node **children; /* Array of children in the graph. */ + sbitmap descendants; /* Bitmap representing all this node's + descendants in the graph. */ +}; + +/* This is the node used for our hashtable of vtable map variable + information. When we create a vtable map variable (var decl) we + put it into one of these nodes; create a corresponding + vtv_graph_node for our class hierarchy info and store that in this + node; generate a unique (monotonically ascending) id for both the + vtbl_map_node and the vtv_graph_node; and insert the node into + THREE data structures (to make it easy to find in several different + ways): 1). A hash table ("vtbl_map_hash" in tree-vtable-verify.c). + This gives us an easy way to check to see if we already have a node + for the vtable map variable or not. 2). A linked list of all + vtbl_map_nodes ("vtbl_map_nodes") for easy iteration through all of + them; and 3). An array of vtbl_map_nodes, where the array index + corresponds to the unique id of the vtbl_map_node, which gives us + an easy way to use bitmaps to represent and find the vtable map + nodes. */ + +struct vtbl_map_node { + tree vtbl_map_decl; /* The var decl for the vtable map + variable. */ + tree class_name; /* The DECL_ASSEMBLER_NAME of the + class. */ + struct vtv_graph_node *class_info; /* Our class hierarchy info for the + class. */ + unsigned uid; /* The unique id for the vtable map + variable. */ + struct vtbl_map_node *next, *prev; /* Pointers for the linked list + structure. */ + htab_t registered; /* Hashtable of vtable pointers for which we have + generated a _VLTRegisterPair call with this vtable + map variable. */ + bool is_used; /* Boolean indicating if we used this vtable map + variable in a call to __VLTVerifyVtablePointer. */ +}; + +/* The global linked list of vtbl_map_nodes. */ +extern struct vtbl_map_node *vtbl_map_nodes; + +/* The global array of vtbl_map_nodes. */ +extern struct vtbl_map_node **vtbl_map_nodes_array; + +extern struct vtbl_map_node *vtbl_map_get_node (tree); +extern struct vtbl_map_node *find_or_create_vtbl_map_node (tree); +extern void vtbl_map_node_class_insert (struct vtbl_map_node *, unsigned); +extern bool vtbl_map_node_registration_find (struct vtbl_map_node *, + tree, unsigned); +extern bool vtbl_map_node_registration_insert (struct vtbl_map_node *, + tree, unsigned); + +#endif /* TREE_VTABLE_VERIFY_H */ diff --git a/gcc/tree.h b/gcc/tree.h index 592a49813fa..f280f851e7c 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -5920,6 +5920,9 @@ is_tm_safe_or_pure (const_tree x) void init_inline_once (void); +/* In tree-vtable-verify.c */ +extern void save_vtable_map_decl (tree); + /* Compute the number of operands in an expression node NODE. For tcc_vl_exp nodes like CALL_EXPRs, this is stored in the node itself, otherwise it is looked up from the node's code. */ diff --git a/gcc/varasm.c b/gcc/varasm.c index bffe9ee6e30..c6cfcd207c0 100644 --- a/gcc/varasm.c +++ b/gcc/varasm.c @@ -2064,13 +2064,43 @@ assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED, assemble_noswitch_variable (decl, name, sect); else { - switch_to_section (sect); + if (sect->named.name + && (strcmp (sect->named.name, ".vtable_map_vars") == 0)) + { +#if defined (OBJECT_FORMAT_ELF) + targetm.asm_out.named_section (sect->named.name, + sect->named.common.flags + | SECTION_LINKONCE, + DECL_NAME (decl)); + in_section = sect; +#else + switch_to_section (sect); +#endif + } + else + switch_to_section (sect); if (DECL_ALIGN (decl) > BITS_PER_UNIT) ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (DECL_ALIGN_UNIT (decl))); assemble_variable_contents (decl, name, dont_output_data); } } +/* Given a function declaration (FN_DECL), this function assembles the + function into the .preinit_array section. */ + +void +assemble_vtv_preinit_initializer (tree fn_decl) +{ + section *sect; + unsigned flags = SECTION_WRITE; + rtx symbol = XEXP (DECL_RTL (fn_decl), 0); + + flags |= SECTION_NOTYPE; + sect = get_section (".preinit_array", flags, fn_decl); + switch_to_section (sect); + assemble_addr_to_section (symbol, sect); +} + /* Return 1 if type TYPE contains any pointers. */ static int @@ -6346,6 +6376,9 @@ default_section_type_flags (tree decl, const char *name, int reloc) if (decl && DECL_ONE_ONLY (decl)) flags |= SECTION_LINKONCE; + if (strcmp (name, ".vtable_map_vars") == 0) + flags |= SECTION_LINKONCE; + if (decl && TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL_P (decl)) flags |= SECTION_TLS | SECTION_WRITE; diff --git a/libgcc/Makefile.in b/libgcc/Makefile.in index 743960bf4a6..9bf1a6f596e 100644 --- a/libgcc/Makefile.in +++ b/libgcc/Makefile.in @@ -40,6 +40,7 @@ double_type_size = @double_type_size@ long_double_type_size = @long_double_type_size@ decimal_float = @decimal_float@ enable_decimal_float = @enable_decimal_float@ +enable_vtable_verify = @enable_vtable_verify@ fixed_point = @fixed_point@ host_noncanonical = @host_noncanonical@ @@ -980,6 +981,16 @@ crtendS$(objext): $(srcdir)/crtstuff.c # This is a version of crtbegin for -static links. crtbeginT$(objext): $(srcdir)/crtstuff.c $(crt_compile) $(CRTSTUFF_T_CFLAGS) -c $< -DCRT_BEGIN -DCRTSTUFFT_O + +ifeq ($(enable_vtable_verify),yes) +# These are used in vtable verification; see comments in source files for +# more details. +vtv_start$(objext): $(srcdir)/vtv_start.c + $(crt_compile) $(CRTSTUFF_T_CFLAGS_S) -c $(srcdir)/vtv_start.c + +vtv_end$(objext): $(srcdir)/vtv_end.c + $(crt_compile) $(CRTSTUFF_T_CFLAGS_S) -c $(srcdir)/vtv_end.c +endif endif ifeq ($(CUSTOM_CRTIN),) diff --git a/libgcc/config.host b/libgcc/config.host index 19ab4bdf482..68cf0d071f2 100644 --- a/libgcc/config.host +++ b/libgcc/config.host @@ -195,7 +195,7 @@ case ${host} in ;; *-*-linux* | frv-*-*linux* | *-*-kfreebsd*-gnu | *-*-knetbsd*-gnu | *-*-gnu* | *-*-kopensolaris*-gnu) tmake_file="$tmake_file t-crtstuff-pic t-libgcc-pic t-eh-dw2-dip t-slibgcc t-slibgcc-gld t-slibgcc-elf-ver t-linux" - extra_parts="crtbegin.o crtbeginS.o crtbeginT.o crtend.o crtendS.o" + extra_parts="crtbegin.o crtbeginS.o crtbeginT.o crtend.o crtendS.o vtv_start.o vtv_end.o" ;; *-*-lynxos*) tmake_file="$tmake_file t-lynx $cpu_type/t-crtstuff t-crtstuff-pic t-libgcc-pic" diff --git a/libgcc/configure b/libgcc/configure index 0751859c8fc..770483ae8a3 100644 --- a/libgcc/configure +++ b/libgcc/configure @@ -567,6 +567,7 @@ set_have_cc_tls vis_hide fixed_point enable_decimal_float +enable_vtable_verify decimal_float long_double_type_size double_type_size @@ -659,6 +660,7 @@ with_slibdir enable_maintainer_mode with_build_libsubdir enable_decimal_float +enable_vtable_verify with_system_libunwind enable_sjlj_exceptions enable_tls @@ -2137,7 +2139,17 @@ else enable_shared=yes fi +# Check whether --enable-vtable-verify was given. +if test "${enable_vtable_verify+set}" = set; then : + enableval=$enable_vtable_verify; + case "$enableval" in + yes | no) ;; + *) as_fn_error "Argument to enable/disable vtable-verify must be yes or no" "$LINENO" 5 ;; + esac +else + enable_vtable_verify=no +fi # Make sure we can run config.sub. $SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || diff --git a/libgcc/vtv_end.c b/libgcc/vtv_end.c new file mode 100644 index 00000000000..1a3f0416efc --- /dev/null +++ b/libgcc/vtv_end.c @@ -0,0 +1,59 @@ +/* Copyright (C) 2012, 2013 + Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +/* This file is part of the vtable verification feature (for a + detailed description of the feature, see comments in + tree-vtable-verify.c). The vtable verification feature creates + certain global symbols that need to be read-write sometimes during + program execution, and read-only at others. It uses 'mprotect' to + change the memory protections of the pages on which these variables + are stored. In order to not affect the protections of other + program variables, these variables are put into a special named + section, ".vtable_map_vars", which is page-aligned at the start, + and which is padded with a page-sized amount of zeros at the end. + To make this section page aligned, we create a special symbol, + "_vtable_map_vars_start" which we make the very first thing that + goes into the section. That is defined in vtv_start.c (which + contains nothing else). vtv_start.c gest compiled into + vtv_start.o, and vtv_start.o gets inserted into the link line + immediately after crtbegin.o, if the program is compiled with + -fvtable.verify. + + In order to pad the ".vtable_map_vars" section with a page-sized + amount of zeros at the end, there is a second symbol, + _vtable_map_vars_end. This file defines that symbol (and only this + symbol). This second symbol is a page-sized array of chars, + zero-filled, and is the very last thing to go into the section. + When the GCC driver inserts vtv_start.o into the link line (just + after crtbegin.o) it also inserts vtv_end.o into the link line, + just before crtend.o. This has the desired effect of making our + section page-aligned and page-size paded, ensuring that no other + program data lands on our pages. */ + +#ifdef BIG_PAGE_SIZE +/* TODO - Replace '4096' below with correct big page size. */ +#define VTV_PAGE_SIZE 4096 +#else +#define VTV_PAGE_SIZE 4096 +#endif + +/* Page-sized variable to mark end of .vtable_map_vars section. */ +char _vtable_map_vars_end[VTV_PAGE_SIZE] + __attribute__ ((__visibility__ ("protected"), used, + section(".vtable_map_vars"))); diff --git a/libgcc/vtv_start.c b/libgcc/vtv_start.c new file mode 100644 index 00000000000..7899fdbd643 --- /dev/null +++ b/libgcc/vtv_start.c @@ -0,0 +1,59 @@ +/* Copyright (C) 2012, 2013 + Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +/* This file is part of the vtable verification feature (for a + detailed description of the feature, see comments in + tree-vtable-verify.c). The vtable verification feature creates + certain global symbols that need to be read-write sometimes during + program execution, and read-only at others. It uses 'mprotect' to + change the memory protections of the pages on which these variables + are stored. In order to not affect the protections of other + program variables, these variables are put into a special named + section, ".vtable_map_vars", which is page-aligned at the start, + and which is padded with a page-sized amount of zeros at the end. + To make this section page aligned, we create a special symbol, + "_vtable_map_vars_start" which we make the very first thing that + goes into the section. This file defines that symbol (and only + that symbol). GCC compiles this file into vtv_start.o, and + inserts vtv_start.o into the link line immediately after + crtbegin.o, if the program is compiled with -fvtable.verify. + + In order to pad the ".vtable_map_vars" section with a page-sized + amount of zeros at the end, there is a second symbol, + _vtable_map_vars_end, which is defined in another file, vtv_end.c. + This second symbol is a page-sized array of chars, zero-filled, and + is the very last thing to go into the section. When the GCC driver + inserts vtv_start.o into the link line (just after crtbegin.o) it + also inserts vtv_end.o into the link line, just before crtend.o. + This has the desired effect of making our section page-aligned and + page-size paded, ensuring that no other program data lands on our + pages. */ + +#ifdef BIG_PAGE_SIZE +/* TODO - Replace '4096' below with correct big page size. */ +#define VTV_PAGE_SIZE 4096 +#else +#define VTV_PAGE_SIZE 4096 +#endif + +/* Page-aligned symbol to mark beginning of .vtable_map_vars section. */ +char _vtable_map_vars_start [] +__attribute__ ((__visibility__ ("protected"), used, aligned(VTV_PAGE_SIZE), + section(".vtable_map_vars"))) + = { }; diff --git a/libstdc++-v3/acinclude.m4 b/libstdc++-v3/acinclude.m4 index 86f7dfab865..514c5cf7a7f 100644 --- a/libstdc++-v3/acinclude.m4 +++ b/libstdc++-v3/acinclude.m4 @@ -2223,6 +2223,25 @@ AC_DEFUN([GLIBCXX_ENABLE_EXTERN_TEMPLATE], [ ]) dnl +dnl Use vtable verification. +dnl +dnl --enable-vtable-verify defines _GLIBCXX_VTABLE_VERIFY to 1 +dnl --disable-vtable-verify defines _GLIBCXX_VTABLE_VERIFY to 0 + +dnl + Usage: GLIBCXX_ENABLE_VTABLE_VERIFY[(DEFAULT)] +dnl Where DEFAULT is `yes' or `no'. +dnl +AC_DEFUN([GLIBCXX_ENABLE_VTABLE_VERIFY], [ + + GLIBCXX_ENABLE(vtable-verify,$1,,[enable vtable verify]) + + AC_MSG_CHECKING([for vtable verify support]) + AC_MSG_RESULT([$enable_vtable_verify]) + + GLIBCXX_CONDITIONAL(ENABLE_VTABLE_VERIFY, test $enable_vtable_verify = yes) +]) + +dnl dnl Check for parallel mode pre-requisites, including OpenMP support. dnl dnl + Usage: GLIBCXX_ENABLE_PARALLEL diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver index 288152cf36d..90eab2fbfd8 100644 --- a/libstdc++-v3/config/abi/pre/gnu.ver +++ b/libstdc++-v3/config/abi/pre/gnu.ver @@ -1326,6 +1326,20 @@ GLIBCXX_3.4.18 { # operator delete(void*, , unsigned long) _ZdlPv[jmy]; + # Virtual table verification symbols + _Z12__VLTprotectv; + _Z14__VLTunprotectv; + _Z18__VLTInitSetSymbol*; + _Z23__VLTInitSetSymbolDebug*; + _Z17__VLTRegisterPair*; + _Z22__VLTRegisterPairDebug*; + _Z21__VLTChangePermission*; + _Z24__VLTVerifyVtablePointer*; + _Z29__VLTVerifyVtablePointerDebug*; + _Z17__vtv_verify_fail*; + _Z17__vtv_really_fail*; + + } GLIBCXX_3.4.17; # Symbols in the support library (libsupc++) have their own tag. diff --git a/libstdc++-v3/configure b/libstdc++-v3/configure index a317514cb2d..f15b3fed32f 100755 --- a/libstdc++-v3/configure +++ b/libstdc++-v3/configure @@ -661,6 +661,8 @@ LIBICONV OPT_LDFLAGS SECTION_LDFLAGS GLIBCXX_LIBS +ENABLE_VTABLE_VERIFY_FALSE +ENABLE_VTABLE_VERIFY_TRUE ENABLE_WERROR_FALSE ENABLE_WERROR_TRUE ENABLE_PYTHONDIR_FALSE @@ -863,6 +865,7 @@ enable_fully_dynamic_string enable_extern_template with_python_dir enable_werror +enable_vtable_verify enable_libstdcxx_time enable_tls enable_rpath @@ -1553,6 +1556,7 @@ Optional Features: --enable-extern-template enable extern template [default=yes] --enable-werror turns on -Werror [default=yes] + --enable-vtable-verify enable vtable verify [default=no] --enable-libstdcxx-time[=KIND] use KIND for check type [default=no] --enable-tls Use thread-local storage [default=yes] @@ -11515,7 +11519,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 11518 "configure" +#line 11522 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -11621,7 +11625,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 11624 "configure" +#line 11628 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -14907,6 +14911,11 @@ esac +if test "$enable_vtable_verify" = yes; then + predep_objects_CXX="${predep_objects_CXX} ${glibcxx_builddir}/../libgcc/vtv_start.o" + postdep_objects_CXX="${postdep_objects_CXX} ${glibcxx_builddir}/../libgcc/vtv_end.o" +fi + # libtool variables for C++ shared and position-independent compiles. # # Use glibcxx_lt_pic_flag to designate the automake variable @@ -15011,7 +15020,7 @@ fi # # Fake what AC_TRY_COMPILE does. XXX Look at redoing this new-style. cat > conftest.$ac_ext << EOF -#line 15014 "configure" +#line 15023 "configure" struct S { ~S(); }; void bar(); void foo() @@ -15346,7 +15355,7 @@ $as_echo "$glibcxx_cv_atomic_long_long" >&6; } # Fake what AC_TRY_COMPILE does. cat > conftest.$ac_ext << EOF -#line 15349 "configure" +#line 15358 "configure" int main() { typedef bool atomic_type; @@ -15381,7 +15390,7 @@ $as_echo "$glibcxx_cv_atomic_bool" >&6; } rm -f conftest* cat > conftest.$ac_ext << EOF -#line 15384 "configure" +#line 15393 "configure" int main() { typedef short atomic_type; @@ -15416,7 +15425,7 @@ $as_echo "$glibcxx_cv_atomic_short" >&6; } rm -f conftest* cat > conftest.$ac_ext << EOF -#line 15419 "configure" +#line 15428 "configure" int main() { // NB: _Atomic_word not necessarily int. @@ -15452,7 +15461,7 @@ $as_echo "$glibcxx_cv_atomic_int" >&6; } rm -f conftest* cat > conftest.$ac_ext << EOF -#line 15455 "configure" +#line 15464 "configure" int main() { typedef long long atomic_type; @@ -15531,7 +15540,7 @@ $as_echo "$as_me: WARNING: Performance of certain classes will degrade as a resu # unnecessary for this test. cat > conftest.$ac_ext << EOF -#line 15534 "configure" +#line 15543 "configure" int main() { _Decimal32 d1; @@ -15573,7 +15582,7 @@ ac_compiler_gnu=$ac_cv_cxx_compiler_gnu # unnecessary for this test. cat > conftest.$ac_ext << EOF -#line 15576 "configure" +#line 15585 "configure" template<typename T1, typename T2> struct same { typedef T2 type; }; @@ -15607,7 +15616,7 @@ $as_echo "$enable_int128" >&6; } rm -f conftest* cat > conftest.$ac_ext << EOF -#line 15610 "configure" +#line 15619 "configure" template<typename T1, typename T2> struct same { typedef T2 type; }; @@ -17341,6 +17350,29 @@ $as_echo "$enable_werror" >&6; } + + # Check whether --enable-vtable-verify was given. +if test "${enable_vtable_verify+set}" = set; then : + enableval=$enable_vtable_verify; + case "$enableval" in + yes|no) ;; + *) as_fn_error "Argument to enable/disable vtable-verify must be yes or no" "$LINENO" 5 ;; + esac + +else + enable_vtable_verify=no +fi + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for vtable verify support" >&5 +$as_echo_n "checking for vtable verify support... " >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_vtable_verify" >&5 +$as_echo "$enable_vtable_verify" >&6; } + + + + # Checks for operating systems support that doesn't require linking. @@ -66251,6 +66283,15 @@ else fi + if test $enable_vtable_verify = yes; then + ENABLE_VTABLE_VERIFY_TRUE= + ENABLE_VTABLE_VERIFY_FALSE='#' +else + ENABLE_VTABLE_VERIFY_TRUE='#' + ENABLE_VTABLE_VERIFY_FALSE= +fi + + if test $enable_symvers != no; then ENABLE_SYMVERS_TRUE= ENABLE_SYMVERS_FALSE='#' @@ -66698,6 +66739,10 @@ if test -z "${ENABLE_WERROR_TRUE}" && test -z "${ENABLE_WERROR_FALSE}"; then as_fn_error "conditional \"ENABLE_WERROR\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +if test -z "${ENABLE_VTABLE_VERIFY_TRUE}" && test -z "${ENABLE_VTABLE_VERIFY_FALSE}"; then + as_fn_error "conditional \"ENABLE_VTABLE_VERIFY\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi if test -z "${ENABLE_SYMVERS_TRUE}" && test -z "${ENABLE_SYMVERS_FALSE}"; then as_fn_error "conditional \"ENABLE_SYMVERS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 diff --git a/libstdc++-v3/configure.ac b/libstdc++-v3/configure.ac index ce1f6c48165..fb4f5065ae3 100644 --- a/libstdc++-v3/configure.ac +++ b/libstdc++-v3/configure.ac @@ -97,6 +97,11 @@ ACX_LT_HOST_FLAGS AC_SUBST(enable_shared) AC_SUBST(enable_static) +if test "$enable_vtable_verify" = yes; then + predep_objects_CXX="${predep_objects_CXX} ${glibcxx_builddir}/../libgcc/vtv_start.o" + postdep_objects_CXX="${postdep_objects_CXX} ${glibcxx_builddir}/../libgcc/vtv_end.o" +fi + # libtool variables for C++ shared and position-independent compiles. # # Use glibcxx_lt_pic_flag to designate the automake variable @@ -165,6 +170,7 @@ GLIBCXX_ENABLE_FULLY_DYNAMIC_STRING([no]) GLIBCXX_ENABLE_EXTERN_TEMPLATE([yes]) GLIBCXX_ENABLE_PYTHON GLIBCXX_ENABLE_WERROR([yes]) +GLIBCXX_ENABLE_VTABLE_VERIFY([no]) # Checks for operating systems support that doesn't require linking. GLIBCXX_CHECK_MATH_PROTO diff --git a/libstdc++-v3/libsupc++/Makefile.am b/libstdc++-v3/libsupc++/Makefile.am index 0c4339da68d..8b619da41b1 100644 --- a/libstdc++-v3/libsupc++/Makefile.am +++ b/libstdc++-v3/libsupc++/Makefile.am @@ -27,7 +27,11 @@ include $(top_srcdir)/fragment.am # Need this library to both be part of libstdc++.a, and installed # separately too. # 1) separate libsupc++.la +if ENABLE_VTABLE_VERIFY +toolexeclib_LTLIBRARIES = libsupc++.la libvtv_init.la libvtv_stubs.la +else toolexeclib_LTLIBRARIES = libsupc++.la +endif # 2) integrated libsupc++convenience.la that is to be a part of libstdc++.a noinst_LTLIBRARIES = libsupc++convenience.la @@ -45,7 +49,73 @@ if GLIBCXX_HOSTED cp-demangle.c endif -sources = \ +if ENABLE_VTABLE_VERIFY + sources = \ + array_type_info.cc \ + atexit_arm.cc \ + bad_alloc.cc \ + bad_cast.cc \ + bad_typeid.cc \ + class_type_info.cc \ + del_op.cc \ + del_opsz.cc \ + del_opnt.cc \ + del_opv.cc \ + del_opvnt.cc \ + dyncast.cc \ + eh_alloc.cc \ + eh_arm.cc \ + eh_aux_runtime.cc \ + eh_call.cc \ + eh_catch.cc \ + eh_exception.cc \ + eh_globals.cc \ + eh_personality.cc \ + eh_ptr.cc \ + eh_term_handler.cc \ + eh_terminate.cc \ + eh_tm.cc \ + eh_throw.cc \ + eh_type.cc \ + eh_unex_handler.cc \ + enum_type_info.cc \ + function_type_info.cc \ + fundamental_type_info.cc \ + guard.cc \ + guard_error.cc \ + hash_bytes.cc \ + nested_exception.cc \ + new_handler.cc \ + new_op.cc \ + new_opnt.cc \ + new_opv.cc \ + new_opvnt.cc \ + pbase_type_info.cc \ + pmem_type_info.cc \ + pointer_type_info.cc \ + pure.cc \ + si_class_type_info.cc \ + tinfo.cc \ + tinfo2.cc \ + vec.cc \ + vmi_class_type_info.cc \ + vterminate.cc \ + vtv_rts.cc \ + vtv_malloc.cc \ + vtv_utils.cc + + vtv_init_sources = \ + vtv_init.cc + + vtv_stubs_sources = \ + vtv_stubs.cc + + libsupc___la_SOURCES = $(sources) $(c_sources) + libsupc__convenience_la_SOURCES = $(sources) $(c_sources) + libvtv_init_la_SOURCES = $(vtv_init_sources) + libvtv_stubs_la_SOURCES = $(vtv_stubs_sources) +else + sources = \ array_type_info.cc \ atexit_arm.cc \ bad_alloc.cc \ @@ -96,8 +166,9 @@ sources = \ vmi_class_type_info.cc \ vterminate.cc -libsupc___la_SOURCES = $(sources) $(c_sources) -libsupc__convenience_la_SOURCES = $(sources) $(c_sources) + libsupc___la_SOURCES = $(sources) $(c_sources) + libsupc__convenience_la_SOURCES = $(sources) $(c_sources) +endif cp-demangle.c: rm -f $@ diff --git a/libstdc++-v3/libsupc++/Makefile.in b/libstdc++-v3/libsupc++/Makefile.in index a4f93aad9c5..a6c53227df5 100644 --- a/libstdc++-v3/libsupc++/Makefile.in +++ b/libstdc++-v3/libsupc++/Makefile.in @@ -90,26 +90,91 @@ am__installdirs = "$(DESTDIR)$(toolexeclibdir)" "$(DESTDIR)$(bitsdir)" \ "$(DESTDIR)$(stddir)" LTLIBRARIES = $(noinst_LTLIBRARIES) $(toolexeclib_LTLIBRARIES) libsupc___la_LIBADD = -am__objects_1 = array_type_info.lo atexit_arm.lo bad_alloc.lo \ - bad_cast.lo bad_typeid.lo class_type_info.lo del_op.lo \ - del_opsz.lo del_opnt.lo del_opv.lo del_opvnt.lo dyncast.lo \ - eh_alloc.lo eh_arm.lo eh_aux_runtime.lo eh_call.lo eh_catch.lo \ - eh_exception.lo eh_globals.lo eh_personality.lo eh_ptr.lo \ - eh_term_handler.lo eh_terminate.lo eh_tm.lo eh_throw.lo \ - eh_type.lo eh_unex_handler.lo enum_type_info.lo \ - function_type_info.lo fundamental_type_info.lo guard.lo \ - guard_error.lo hash_bytes.lo nested_exception.lo \ - new_handler.lo new_op.lo new_opnt.lo new_opv.lo new_opvnt.lo \ - pbase_type_info.lo pmem_type_info.lo pointer_type_info.lo \ - pure.lo si_class_type_info.lo tinfo.lo tinfo2.lo vec.lo \ - vmi_class_type_info.lo vterminate.lo +@ENABLE_VTABLE_VERIFY_FALSE@am__objects_1 = array_type_info.lo \ +@ENABLE_VTABLE_VERIFY_FALSE@ atexit_arm.lo bad_alloc.lo \ +@ENABLE_VTABLE_VERIFY_FALSE@ bad_cast.lo bad_typeid.lo \ +@ENABLE_VTABLE_VERIFY_FALSE@ class_type_info.lo del_op.lo \ +@ENABLE_VTABLE_VERIFY_FALSE@ del_opsz.lo del_opnt.lo del_opv.lo \ +@ENABLE_VTABLE_VERIFY_FALSE@ del_opvnt.lo dyncast.lo \ +@ENABLE_VTABLE_VERIFY_FALSE@ eh_alloc.lo eh_arm.lo \ +@ENABLE_VTABLE_VERIFY_FALSE@ eh_aux_runtime.lo eh_call.lo \ +@ENABLE_VTABLE_VERIFY_FALSE@ eh_catch.lo eh_exception.lo \ +@ENABLE_VTABLE_VERIFY_FALSE@ eh_globals.lo eh_personality.lo \ +@ENABLE_VTABLE_VERIFY_FALSE@ eh_ptr.lo eh_term_handler.lo \ +@ENABLE_VTABLE_VERIFY_FALSE@ eh_terminate.lo eh_tm.lo \ +@ENABLE_VTABLE_VERIFY_FALSE@ eh_throw.lo eh_type.lo \ +@ENABLE_VTABLE_VERIFY_FALSE@ eh_unex_handler.lo \ +@ENABLE_VTABLE_VERIFY_FALSE@ enum_type_info.lo \ +@ENABLE_VTABLE_VERIFY_FALSE@ function_type_info.lo \ +@ENABLE_VTABLE_VERIFY_FALSE@ fundamental_type_info.lo guard.lo \ +@ENABLE_VTABLE_VERIFY_FALSE@ guard_error.lo hash_bytes.lo \ +@ENABLE_VTABLE_VERIFY_FALSE@ nested_exception.lo new_handler.lo \ +@ENABLE_VTABLE_VERIFY_FALSE@ new_op.lo new_opnt.lo new_opv.lo \ +@ENABLE_VTABLE_VERIFY_FALSE@ new_opvnt.lo pbase_type_info.lo \ +@ENABLE_VTABLE_VERIFY_FALSE@ pmem_type_info.lo \ +@ENABLE_VTABLE_VERIFY_FALSE@ pointer_type_info.lo pure.lo \ +@ENABLE_VTABLE_VERIFY_FALSE@ si_class_type_info.lo tinfo.lo \ +@ENABLE_VTABLE_VERIFY_FALSE@ tinfo2.lo vec.lo \ +@ENABLE_VTABLE_VERIFY_FALSE@ vmi_class_type_info.lo \ +@ENABLE_VTABLE_VERIFY_FALSE@ vterminate.lo +@ENABLE_VTABLE_VERIFY_TRUE@am__objects_1 = array_type_info.lo \ +@ENABLE_VTABLE_VERIFY_TRUE@ atexit_arm.lo bad_alloc.lo \ +@ENABLE_VTABLE_VERIFY_TRUE@ bad_cast.lo bad_typeid.lo \ +@ENABLE_VTABLE_VERIFY_TRUE@ class_type_info.lo del_op.lo \ +@ENABLE_VTABLE_VERIFY_TRUE@ del_opsz.lo del_opnt.lo del_opv.lo \ +@ENABLE_VTABLE_VERIFY_TRUE@ del_opvnt.lo dyncast.lo eh_alloc.lo \ +@ENABLE_VTABLE_VERIFY_TRUE@ eh_arm.lo eh_aux_runtime.lo \ +@ENABLE_VTABLE_VERIFY_TRUE@ eh_call.lo eh_catch.lo \ +@ENABLE_VTABLE_VERIFY_TRUE@ eh_exception.lo eh_globals.lo \ +@ENABLE_VTABLE_VERIFY_TRUE@ eh_personality.lo eh_ptr.lo \ +@ENABLE_VTABLE_VERIFY_TRUE@ eh_term_handler.lo eh_terminate.lo \ +@ENABLE_VTABLE_VERIFY_TRUE@ eh_tm.lo eh_throw.lo eh_type.lo \ +@ENABLE_VTABLE_VERIFY_TRUE@ eh_unex_handler.lo \ +@ENABLE_VTABLE_VERIFY_TRUE@ enum_type_info.lo \ +@ENABLE_VTABLE_VERIFY_TRUE@ function_type_info.lo \ +@ENABLE_VTABLE_VERIFY_TRUE@ fundamental_type_info.lo guard.lo \ +@ENABLE_VTABLE_VERIFY_TRUE@ guard_error.lo hash_bytes.lo \ +@ENABLE_VTABLE_VERIFY_TRUE@ nested_exception.lo new_handler.lo \ +@ENABLE_VTABLE_VERIFY_TRUE@ new_op.lo new_opnt.lo new_opv.lo \ +@ENABLE_VTABLE_VERIFY_TRUE@ new_opvnt.lo pbase_type_info.lo \ +@ENABLE_VTABLE_VERIFY_TRUE@ pmem_type_info.lo \ +@ENABLE_VTABLE_VERIFY_TRUE@ pointer_type_info.lo pure.lo \ +@ENABLE_VTABLE_VERIFY_TRUE@ si_class_type_info.lo tinfo.lo \ +@ENABLE_VTABLE_VERIFY_TRUE@ tinfo2.lo vec.lo \ +@ENABLE_VTABLE_VERIFY_TRUE@ vmi_class_type_info.lo \ +@ENABLE_VTABLE_VERIFY_TRUE@ vterminate.lo vtv_rts.lo \ +@ENABLE_VTABLE_VERIFY_TRUE@ vtv_malloc.lo vtv_utils.lo @GLIBCXX_HOSTED_TRUE@am__objects_2 = cp-demangle.lo -am_libsupc___la_OBJECTS = $(am__objects_1) $(am__objects_2) +@ENABLE_VTABLE_VERIFY_FALSE@am_libsupc___la_OBJECTS = \ +@ENABLE_VTABLE_VERIFY_FALSE@ $(am__objects_1) $(am__objects_2) +@ENABLE_VTABLE_VERIFY_TRUE@am_libsupc___la_OBJECTS = $(am__objects_1) \ +@ENABLE_VTABLE_VERIFY_TRUE@ $(am__objects_2) libsupc___la_OBJECTS = $(am_libsupc___la_OBJECTS) +@ENABLE_VTABLE_VERIFY_FALSE@am_libsupc___la_rpath = -rpath \ +@ENABLE_VTABLE_VERIFY_FALSE@ $(toolexeclibdir) +@ENABLE_VTABLE_VERIFY_TRUE@am_libsupc___la_rpath = -rpath \ +@ENABLE_VTABLE_VERIFY_TRUE@ $(toolexeclibdir) libsupc__convenience_la_LIBADD = -am_libsupc__convenience_la_OBJECTS = $(am__objects_1) $(am__objects_2) +@ENABLE_VTABLE_VERIFY_FALSE@am_libsupc__convenience_la_OBJECTS = \ +@ENABLE_VTABLE_VERIFY_FALSE@ $(am__objects_1) $(am__objects_2) +@ENABLE_VTABLE_VERIFY_TRUE@am_libsupc__convenience_la_OBJECTS = \ +@ENABLE_VTABLE_VERIFY_TRUE@ $(am__objects_1) $(am__objects_2) libsupc__convenience_la_OBJECTS = \ $(am_libsupc__convenience_la_OBJECTS) +libvtv_init_la_LIBADD = +@ENABLE_VTABLE_VERIFY_TRUE@am__objects_3 = vtv_init.lo +@ENABLE_VTABLE_VERIFY_TRUE@am_libvtv_init_la_OBJECTS = \ +@ENABLE_VTABLE_VERIFY_TRUE@ $(am__objects_3) +libvtv_init_la_OBJECTS = $(am_libvtv_init_la_OBJECTS) +@ENABLE_VTABLE_VERIFY_TRUE@am_libvtv_init_la_rpath = -rpath \ +@ENABLE_VTABLE_VERIFY_TRUE@ $(toolexeclibdir) +libvtv_stubs_la_LIBADD = +@ENABLE_VTABLE_VERIFY_TRUE@am__objects_4 = vtv_stubs.lo +@ENABLE_VTABLE_VERIFY_TRUE@am_libvtv_stubs_la_OBJECTS = \ +@ENABLE_VTABLE_VERIFY_TRUE@ $(am__objects_4) +libvtv_stubs_la_OBJECTS = $(am_libvtv_stubs_la_OBJECTS) +@ENABLE_VTABLE_VERIFY_TRUE@am_libvtv_stubs_la_rpath = -rpath \ +@ENABLE_VTABLE_VERIFY_TRUE@ $(toolexeclibdir) DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = am__depfiles_maybe = @@ -122,7 +187,8 @@ LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) CXXLD = $(CXX) -SOURCES = $(libsupc___la_SOURCES) $(libsupc__convenience_la_SOURCES) +SOURCES = $(libsupc___la_SOURCES) $(libsupc__convenience_la_SOURCES) \ + $(libvtv_init_la_SOURCES) $(libvtv_stubs_la_SOURCES) HEADERS = $(bits_HEADERS) $(std_HEADERS) ETAGS = etags CTAGS = ctags @@ -338,11 +404,12 @@ WARN_CXXFLAGS = \ # -I/-D flags to pass when compiling. AM_CPPFLAGS = $(GLIBCXX_INCLUDES) +@ENABLE_VTABLE_VERIFY_FALSE@toolexeclib_LTLIBRARIES = libsupc++.la # Need this library to both be part of libstdc++.a, and installed # separately too. # 1) separate libsupc++.la -toolexeclib_LTLIBRARIES = libsupc++.la +@ENABLE_VTABLE_VERIFY_TRUE@toolexeclib_LTLIBRARIES = libsupc++.la libvtv_init.la libvtv_stubs.la # 2) integrated libsupc++convenience.la that is to be a part of libstdc++.a noinst_LTLIBRARIES = libsupc++convenience.la std_HEADERS = \ @@ -356,59 +423,123 @@ headers = $(std_HEADERS) $(bits_HEADERS) @GLIBCXX_HOSTED_TRUE@c_sources = \ @GLIBCXX_HOSTED_TRUE@ cp-demangle.c -sources = \ - array_type_info.cc \ - atexit_arm.cc \ - bad_alloc.cc \ - bad_cast.cc \ - bad_typeid.cc \ - class_type_info.cc \ - del_op.cc \ - del_opsz.cc \ - del_opnt.cc \ - del_opv.cc \ - del_opvnt.cc \ - dyncast.cc \ - eh_alloc.cc \ - eh_arm.cc \ - eh_aux_runtime.cc \ - eh_call.cc \ - eh_catch.cc \ - eh_exception.cc \ - eh_globals.cc \ - eh_personality.cc \ - eh_ptr.cc \ - eh_term_handler.cc \ - eh_terminate.cc \ - eh_tm.cc \ - eh_throw.cc \ - eh_type.cc \ - eh_unex_handler.cc \ - enum_type_info.cc \ - function_type_info.cc \ - fundamental_type_info.cc \ - guard.cc \ - guard_error.cc \ - hash_bytes.cc \ - nested_exception.cc \ - new_handler.cc \ - new_op.cc \ - new_opnt.cc \ - new_opv.cc \ - new_opvnt.cc \ - pbase_type_info.cc \ - pmem_type_info.cc \ - pointer_type_info.cc \ - pure.cc \ - si_class_type_info.cc \ - tinfo.cc \ - tinfo2.cc \ - vec.cc \ - vmi_class_type_info.cc \ - vterminate.cc - -libsupc___la_SOURCES = $(sources) $(c_sources) -libsupc__convenience_la_SOURCES = $(sources) $(c_sources) +@ENABLE_VTABLE_VERIFY_FALSE@sources = \ +@ENABLE_VTABLE_VERIFY_FALSE@ array_type_info.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ atexit_arm.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ bad_alloc.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ bad_cast.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ bad_typeid.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ class_type_info.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ del_op.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ del_opsz.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ del_opnt.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ del_opv.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ del_opvnt.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ dyncast.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ eh_alloc.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ eh_arm.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ eh_aux_runtime.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ eh_call.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ eh_catch.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ eh_exception.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ eh_globals.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ eh_personality.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ eh_ptr.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ eh_term_handler.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ eh_terminate.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ eh_tm.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ eh_throw.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ eh_type.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ eh_unex_handler.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ enum_type_info.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ function_type_info.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ fundamental_type_info.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ guard.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ guard_error.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ hash_bytes.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ nested_exception.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ new_handler.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ new_op.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ new_opnt.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ new_opv.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ new_opvnt.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ pbase_type_info.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ pmem_type_info.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ pointer_type_info.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ pure.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ si_class_type_info.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ tinfo.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ tinfo2.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ vec.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ vmi_class_type_info.cc \ +@ENABLE_VTABLE_VERIFY_FALSE@ vterminate.cc + +@ENABLE_VTABLE_VERIFY_TRUE@sources = \ +@ENABLE_VTABLE_VERIFY_TRUE@ array_type_info.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ atexit_arm.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ bad_alloc.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ bad_cast.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ bad_typeid.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ class_type_info.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ del_op.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ del_opsz.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ del_opnt.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ del_opv.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ del_opvnt.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ dyncast.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ eh_alloc.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ eh_arm.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ eh_aux_runtime.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ eh_call.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ eh_catch.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ eh_exception.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ eh_globals.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ eh_personality.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ eh_ptr.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ eh_term_handler.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ eh_terminate.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ eh_tm.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ eh_throw.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ eh_type.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ eh_unex_handler.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ enum_type_info.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ function_type_info.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ fundamental_type_info.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ guard.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ guard_error.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ hash_bytes.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ nested_exception.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ new_handler.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ new_op.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ new_opnt.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ new_opv.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ new_opvnt.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ pbase_type_info.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ pmem_type_info.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ pointer_type_info.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ pure.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ si_class_type_info.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ tinfo.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ tinfo2.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ vec.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ vmi_class_type_info.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ vterminate.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ vtv_rts.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ vtv_malloc.cc \ +@ENABLE_VTABLE_VERIFY_TRUE@ vtv_utils.cc + +@ENABLE_VTABLE_VERIFY_TRUE@vtv_init_sources = \ +@ENABLE_VTABLE_VERIFY_TRUE@ vtv_init.cc + +@ENABLE_VTABLE_VERIFY_TRUE@vtv_stubs_sources = \ +@ENABLE_VTABLE_VERIFY_TRUE@ vtv_stubs.cc + +@ENABLE_VTABLE_VERIFY_FALSE@libsupc___la_SOURCES = $(sources) $(c_sources) +@ENABLE_VTABLE_VERIFY_TRUE@libsupc___la_SOURCES = $(sources) $(c_sources) +@ENABLE_VTABLE_VERIFY_FALSE@libsupc__convenience_la_SOURCES = $(sources) $(c_sources) +@ENABLE_VTABLE_VERIFY_TRUE@libsupc__convenience_la_SOURCES = $(sources) $(c_sources) +@ENABLE_VTABLE_VERIFY_TRUE@libvtv_init_la_SOURCES = $(vtv_init_sources) +@ENABLE_VTABLE_VERIFY_TRUE@libvtv_stubs_la_SOURCES = $(vtv_stubs_sources) # AM_CXXFLAGS needs to be in each subdirectory so that it can be # modified in a per-library or per-sub-library way. Need to manually @@ -575,9 +706,13 @@ clean-toolexeclibLTLIBRARIES: rm -f "$${dir}/so_locations"; \ done libsupc++.la: $(libsupc___la_OBJECTS) $(libsupc___la_DEPENDENCIES) - $(CXXLINK) -rpath $(toolexeclibdir) $(libsupc___la_OBJECTS) $(libsupc___la_LIBADD) $(LIBS) + $(CXXLINK) $(am_libsupc___la_rpath) $(libsupc___la_OBJECTS) $(libsupc___la_LIBADD) $(LIBS) libsupc++convenience.la: $(libsupc__convenience_la_OBJECTS) $(libsupc__convenience_la_DEPENDENCIES) $(CXXLINK) $(libsupc__convenience_la_OBJECTS) $(libsupc__convenience_la_LIBADD) $(LIBS) +libvtv_init.la: $(libvtv_init_la_OBJECTS) $(libvtv_init_la_DEPENDENCIES) + $(CXXLINK) $(am_libvtv_init_la_rpath) $(libvtv_init_la_OBJECTS) $(libvtv_init_la_LIBADD) $(LIBS) +libvtv_stubs.la: $(libvtv_stubs_la_OBJECTS) $(libvtv_stubs_la_DEPENDENCIES) + $(CXXLINK) $(am_libvtv_stubs_la_rpath) $(libvtv_stubs_la_OBJECTS) $(libvtv_stubs_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) diff --git a/libstdc++-v3/libsupc++/vtv_fail.h b/libstdc++-v3/libsupc++/vtv_fail.h new file mode 100644 index 00000000000..1048905c639 --- /dev/null +++ b/libstdc++-v3/libsupc++/vtv_fail.h @@ -0,0 +1,53 @@ +/* Copyright (C) 2012 + Free Software Foundation + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GCC is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + <http://www.gnu.org/licenses/>. */ + +#ifndef _VTV_FAIL_H +#define _VTV_FAIL_H 1 + +/* __vtv_really_fail prints a backtrace and a memory dump, then calls + abort. It is here for programmers to call, presumably from + __vtv_verify_fail, if they choose to overwrite the standard + __vtv_verify_fail with one of their own. Programmers should NOT + attempt to rewrite __vtv_really_fail. */ + +extern void +__vtv_really_fail (const char *fail_msg) __attribute__ ((noreturn)); + +/* __vtv_verify_fail is the function that gets called if the vtable + verification code discovers a vtable pointer that it cannot verify + as valid. Normally __vtv_verify_fail calls __vtv_really_fail. + However programmers can write and link in their own version of + __vtv_verify_fail, if they wish to do some kind of secondary + verification, for example. The main verification code assumes that + IF __vtv_verify_fail returns, then some kind of secondary + verification was done AND that the secondary verification succeeded, + i.e. that the vtable pointer is actually valid and ok to use. If + the secondary verification fails, then __vtv_verify_fail should not + return. */ + +extern void +__vtv_verify_fail (void **data_set_ptr, const void *vtbl_pointer) + __attribute__((visibility ("default"))); + +#endif /* _VTV_FAIL_H */ diff --git a/libstdc++-v3/libsupc++/vtv_init.cc b/libstdc++-v3/libsupc++/vtv_init.cc new file mode 100644 index 00000000000..2ab47adf552 --- /dev/null +++ b/libstdc++-v3/libsupc++/vtv_init.cc @@ -0,0 +1,179 @@ +/* Copyright (C) 2012 + Free Software Foundation + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GCC is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + <http://www.gnu.org/licenses/>. */ + + +/* This file contains all the definitions that go into the libvtv_init + library, which is part of the vtable verification feature. This + library should contain exactly two functionsa (__VLTunprotect and + __VLTprotect) and one global variable definition + (__vtv_defined_in_vtv_init_lib). Any program that was compiled + with the option "-fvtable-verify=std" MUST also be linked with + libvtv_init, because the two functions defined here are used by the + vtable verification code. The reason they are in a separate + library rather than in libstdc++ with all the rest of the vtable + verification runtime code is as follows. Each .o file that was + compiled with vtable verification will contain calls into the + runtime (made from constructor initialization functions) to build + the data structures needed for verification. At all times except + when they are being constructed, these data structures need to be + in protected memory, so that attackers cannot corrupt them. + __VLTunprotect sets the memory containing these data structures to + be writable, for updates. __VLTprotect makes the memory read-only, + for all other times. This memory protection and unprotection is + done via calls to mprotect, which are costly. So instead of + calling __VLTunprotect and __VLTprotect once per object file we + want to call them once per executable. Therefore instead of + putting calls to them directly into each object file, we put the + calls to them only in __VLTRegisterPair, in the libstdc++ library. + We give __VLTunprotect an initialization priority to make it run + before all of our data structure construction functions, and we + give __VLTprotect an initialization priority to make it run after + all of our data structure constructiion functions. We put them + into a separate library and link that library with the + "--whole-archive" linker option, to make sure that both functions get + linked in (since the actual calls to them are in the libstdc++ + runtime). We can't put them into libstdc++ because linking + libstdc++ with "--whole-archive" is probably not a good idea. + + The __vtv_defined_in_vtv_lib variable is referenced, but not + defined, in the constructor initialization functions where we have + the calls to build our data structures. The purpose of this + variable is to cause a linker error to occur if the programmer + compiled with -fvtable-verify=std and did not link with the vtv_int + library (better a link-time error than a run-time error). */ + + +/* Needs to build with C++ because the definition of + __VLTChangePermission is in C++. */ +#ifndef __cplusplus +#error "This file must be compiled with a C++ compiler" +#endif + +#include "vtv_rts.h" + +/* Define this dummy symbol to detect at link time the cases where + user is compiling with -fvtable-verify=std and not linking with the + vtv_init library. Note that the visibility needs to be hidden. Each + object module is supposed to link with the vtv_init library and we + don't want this definition to come from a different library */ +unsigned int +__vtv_defined_in_vtv_init_lib __attribute__ ((visibility ("hidden"))) = 0; + +void __VLTunprotect (void) __attribute__ ((constructor(98))); +void __VLTprotect (void) __attribute__ ((constructor(100))); + +void +__VLTunprotect (void) +{ + __VLTChangePermission (__VLTP_READ_WRITE); +} + +void +__VLTprotect (void) +{ + __VLTChangePermission (__VLTP_READ_ONLY); +} + +/* This VTV_STATIC_VERIFY macro is experimental for now. If we are + going to use it we need to put the code below in a common + place. Right now it is a copy of the code in vtv_rts.cc */ +#ifdef VTV_STATIC_VERIFY + +#ifdef VTV_DEBUG + +const void * +__VLTVerifyVtablePointerDebug (void **set_handle_ptr, + const void *vtable_ptr, + const char *set_symbol_name, + const char *vtable_name) +{ +#ifndef VTV_EMPTY_VERIFY + VTV_DEBUG_ASSERT(set_handle_ptr != NULL && *set_handle_ptr != NULL); + int_vptr vtbl_ptr = (int_vptr) vtable_ptr; + + vtv_set_handle *handle_ptr; + if (!is_set_handle_handle (*set_handle_ptr)) + handle_ptr = (vtv_set_handle *) set_handle_ptr; + else + handle_ptr = ptr_from_set_handle_handle (*set_handle_ptr); + + if (vtv_sets::contains (vtbl_ptr, handle_ptr)) + { + if (debug_verify_vtable) + { + if (verify_vtable_log_fd == -1) + vtv_open_log ("vtv_verify_vtable.log"); + vtv_add_to_log (verify_vtable_log_fd, + "Verified %s %s value = %p\n", + set_symbol_name, vtable_name, vtable_ptr); + } + } + else + { + snprintf (debug_log_message, sizeof (debug_log_message), + "Looking for %s in %s\n", vtable_name, set_symbol_name); + __vtv_verify_fail_debug (set_handle_ptr, vtable_ptr, debug_log_message); + + /* Normally __vtv_verify_fail will call abort, so we won't + execute the return below. If we get this far, the assumption + is that the programmer has replace __vtv_verify_fail with + some kind of secondary verification AND this secondary + verification succeeded, so the vtable pointer is valid. */ + } +#endif /* VTV_EMPTY_VERIFY */ + + return vtable_ptr; +} + +#else /* VTV_DEBUG */ + +const void * +__VLTVerifyVtablePointer (void **set_handle_ptr, const void *vtable_ptr) +{ +#ifndef VTV_EMPTY_VERIFY + int_vptr vtbl_ptr = (int_vptr) vtable_ptr; + + vtv_set_handle *handle_ptr; + if (!is_set_handle_handle (*set_handle_ptr)) + handle_ptr = (vtv_set_handle *) set_handle_ptr; + else + handle_ptr = ptr_from_set_handle_handle (*set_handle_ptr); + + if (!vtv_sets::contains (vtbl_ptr, handle_ptr)) + { + __vtv_verify_fail ((void **) handle_ptr, vtable_ptr); + /* Normally __vtv_verify_fail will call abort, so we won't + execute the return below. If we get this far, the assumption + is that the programmer has replaced __vtv_verify_fail with + some kind of secondary verification AND this secondary + verification succeeded, so the vtable pointer is valid. */ + } +#endif /* VTV_EMPTY_VERIFY*/ + + return vtable_ptr; +} + +#endif /* else-clause VTV_DEBUG */ + +#endif /* VTV_STATIC_VERIFY */ diff --git a/libstdc++-v3/libsupc++/vtv_malloc.cc b/libstdc++-v3/libsupc++/vtv_malloc.cc new file mode 100644 index 00000000000..b080c7dd7d4 --- /dev/null +++ b/libstdc++-v3/libsupc++/vtv_malloc.cc @@ -0,0 +1,210 @@ +/* Copyright (C) 2012 + Free Software Foundation + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GCC is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + <http://www.gnu.org/licenses/>. */ + +/* This file is part of the vtable verification runtime library. It + contains our memory allocation and deallocation routines, which we + use in order to keep track of the pages in memory in which our sets + of valid vtable pointes are stored. (We need to know the pages so + we can set the protections on them appropriately). For more + information about the vtable verification feature, see the comments + in vtv_rts.cc. We use the existing obstack implementation in our + memory allocation scheme. */ + +#include <stdlib.h> +#include <unistd.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdio.h> + +#include "vtv_utils.h" +#include "vtv_malloc.h" +#include "obstack.h" + +/* Put the following variables in our ".vtable_map_vars" section so + that they are protected. They are explicitly unprotected and + protected again by calls to VTV_unprotect and VTV_protect */ + +static struct obstack VTV_obstack VTV_PROTECTED_VAR; +static unsigned long page_size VTV_PROTECTED_VAR = 0; +static void *current_chunk VTV_PROTECTED_VAR = 0; +static size_t current_chunk_size VTV_PROTECTED_VAR = 0; +static int malloc_initialized VTV_PROTECTED_VAR = 0; + +/* This function goes through all of the pages we have allocated so + far and calls mprotect to make each page read-only. */ + +void +VTV_malloc_protect (void) +{ + struct _obstack_chunk *ci; + ci = (struct _obstack_chunk *) current_chunk; + while (ci) + { + VTV_DEBUG_ASSERT (((unsigned long) ci & (page_size - 1)) == 0); + if (mprotect (ci, (ci->limit - (char *) ci), PROT_READ) == -1) + VTV_error (); + ci = ci->prev; + } + +#if (VTV_DEBUG_MALLOC == 1) + VTV_malloc_dump_stats (); +#endif +} + +/* This function goes through all of the pages we have allocated so + far and calls mrpotect to make each page read-write. */ + +void +VTV_malloc_unprotect (void) +{ + struct _obstack_chunk * ci; + ci = (struct _obstack_chunk *) current_chunk; + while (ci) + { + VTV_DEBUG_ASSERT (((unsigned long) ci & (page_size - 1)) == 0); + if (mprotect (ci, (ci->limit - (char *) ci), PROT_READ | PROT_WRITE) + == -1) + VTV_error (); + ci = ci->prev; + } +} + +/* Allocates a SIZE-sized chunk of memory that is aligned to a page + boundary. The amount of memory requested (SIZE) must be a multiple + of the page size. Note: We must use mmap to allocate the memory; + using malloc here will cause problems. */ + +static void * +obstack_chunk_alloc (size_t size) +{ + /* Increase size to the next multiple of page_size. */ + size = (size + (page_size - 1)) & (~(page_size - 1)); + VTV_DEBUG_ASSERT ((size & (page_size - 1)) == 0); + void *allocated; + + if ((allocated = mmap (NULL, size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)) == 0) + VTV_error (); + + VTV_DEBUG_ASSERT (((unsigned long) allocated & (page_size - 1)) == 0); + + current_chunk = allocated; + current_chunk_size = size; + return allocated; +} + +static void +obstack_chunk_free (size_t size) +{ + /* Do nothing. For our purposes there should be very little + de-allocation. */ +} + +/* This function sets up and initializes the obstack pieces for our + memory allocation scheme. */ + +void +VTV_malloc_init (void) +{ + /* Make sure we only execute the main body of this function ONCE. */ + if (malloc_initialized) + return; + + page_size = sysconf (_SC_PAGE_SIZE); + if (page_size != 4096) + VTV_error (); + + obstack_chunk_size (&VTV_obstack) = page_size; + obstack_alignment_mask (&VTV_obstack) = sizeof (long) - 1; + /* We guarantee that the obstack alloc failed handler will never be + called because in case the allocation of the chunk fails, it will + never return */ + obstack_alloc_failed_handler = NULL; + + obstack_init (&VTV_obstack); + malloc_initialized = 1; +} + +/* This is our external interface for the memory allocation. SIZE is + the requested number of bytes to be allocated/ */ + +void * +VTV_malloc (size_t size) +{ + return obstack_alloc (&VTV_obstack, size); +} + + +/* This is our external interface for memory deallocation. */ + +void +VTV_free (void *) +{ + /* Do nothing. We dont care about recovering unneded memory at this + time. */ +} + + +/* This is a debugging function tat collects statistics about our + memory allocation. */ +void +VTV_malloc_stats (void) +{ + int count = 0; + struct _obstack_chunk * ci = (struct _obstack_chunk *) current_chunk; + while (ci) + { + count++; + ci = ci->prev; + } + fprintf (stderr, + "VTV_malloc_stats:\n Page Size = %lu bytes\n " + "Number of pages = %d\n", page_size, count); +} + +/* This is a debugging function. It writes out our memory allocation + statistics to a log file. */ + +void +VTV_malloc_dump_stats (void) +{ + static int fd = -1; + + if (fd == -1) + fd = vtv_open_log ("/tmp/vtv_mem_protection.log"); + if (fd == -1) + return; + + int count = 0; + struct _obstack_chunk * ci = (struct _obstack_chunk *) current_chunk; + while (ci) + { + count++; + ci = ci->prev; + } + + vtv_add_to_log (fd, "VTV_malloc_protect protected=%d pages\n", count); +} diff --git a/libstdc++-v3/libsupc++/vtv_malloc.h b/libstdc++-v3/libsupc++/vtv_malloc.h new file mode 100644 index 00000000000..26cab7e5de8 --- /dev/null +++ b/libstdc++-v3/libsupc++/vtv_malloc.h @@ -0,0 +1,49 @@ +/* Copyright (C) 2012, 2013 + Free Software Foundation + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GCC is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + <http://www.gnu.org/licenses/>. */ + +#ifndef _VTV_MALLOC_H +#define _VTV_MALLOC_H 1 + +#include <stdlib.h> + +/* Alignment mask for any object returned by the VTV memory pool */ +#ifdef __LP64__ +#define VTV_ALIGNMENT_MASK (0x7) +#else +#define VTV_ALIGNMENT_MASK (0x3) +#endif + +extern void VTV_malloc_init (void); + +extern void *VTV_malloc (size_t size); +extern void VTV_free (void * ptr); + + +extern void VTV_malloc_protect (void); +extern void VTV_malloc_unprotect (void); + +extern void VTV_malloc_stats (void); +extern void VTV_malloc_dump_stats (void); + +#endif /* vtv_malloc.h */ diff --git a/libstdc++-v3/libsupc++/vtv_map.h b/libstdc++-v3/libsupc++/vtv_map.h new file mode 100644 index 00000000000..ed8769e02da --- /dev/null +++ b/libstdc++-v3/libsupc++/vtv_map.h @@ -0,0 +1,311 @@ +/* Copyright (C) 2012 + Free Software Foundation + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GCC is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + <http://www.gnu.org/licenses/>. */ + +#ifndef _VTV_MAP_H +#define _VTV_MAP_H 1 + +#include <string.h> +#include <vtv_utils.h> + +inline uint64_t +load8bytes (const void *p) +{ + uint64_t result; + memcpy (&result, p, 8); + return result; +} + +/* Insert_only_hash_map maps keys to values. The implementation is a + basic hash table with open addressing. The keys are not "owned" by + the table; it only stores pointers to keys. The key type is + specified below (see insert_only_hash_map::key_type) and is, + roughly speaking, a string of any length with the string length and + a hash code stored at the front. The code here does not compute + any hash codes, but rather uses what's given. */ + +template<typename T, typename Alloc> +class insert_only_hash_map + { + public: + typedef size_t size_type; + typedef T value_type; + typedef Alloc alloc_type; + enum { min_capacity = 4 }; +#if HASHMAP_STATS + enum { stats = true }; +#else + enum { stats = false }; +#endif + + /* Keys are a byte string (up to 2^32 - 1 long) plus a uint32_t + that's used as a hash code. The latter can encode arbitrary + information at the client's discretion, so, e.g., multiple keys + that are the same string still "differ" if the hash codes differ. + Keys are equal if the first 8 bytes are equal and the next n + bytes are equal. */ + struct key_type + { + uint32_t n; + uint32_t hash; + char bytes[0]; + + bool + equals (const key_type *k) const; + }; + + /* Create an empty map with a reasonable number of buckets for the + expected size. Returns NULL if the allocator fails. */ + + static insert_only_hash_map * + create (size_type expected_size); + + /* The opposite of create(). Free the memory for the given map. */ + + static void + destroy (insert_only_hash_map *m) + { Alloc().dealloc (m, m->size_in_bytes_); } + + /* Return a map identical to this except that *k is mapped to v. + Typcially it's done by modifying this in place, but if a resize + is necessary then this is deallocated and a new map is returned. + Requires k to be non-NULL. Does nothing and returns NULL if the + allocator fails. */ + + insert_only_hash_map* + put (const key_type *k, const value_type &v) + { return this->put_internal (k, v, false); } + + /* If *k is a key in this then set *v to point to the corresponding + value. Otherwise, do the equivalent of insert(k, value_type()) + and, if that succeeds, set *v to point to the inserted value. + Requires k to be non-NULL. Does nothing and returns NULL if the + allocator fails. Typically returns this, but will return a new + insert_only_hash_map if a resize occurs. If the return value is + non-NULL, *v is set and it's valid until a resize of the map that + is the return value. */ + + insert_only_hash_map * + find_or_add_key (const key_type *k, value_type **v); + + /* Get the value corresponding to *k. Returns NULL if there is + none. Requires k to be non-NULL. The return value is valid + until any resize. */ + const value_type *get (const key_type *k) const; + + size_type + size () const + { return num_entries_; } + + bool + empty () const + { return this->size () == 0; } + + size_type + bucket_count () const + { return num_buckets_; } + + private: + typedef std::pair <const key_type *, value_type> bucket_type; + + insert_only_hash_map *put_internal (const key_type *, const value_type &, + bool); + + /* This function determines when to resize the table. */ + bool + is_too_full (size_type entries) const + { return entries > (this->bucket_count () * 0.7); } + + /* Return a copy with double the number of buckets. Returns NULL if + the allocator fails. Otherwise, calls destroy (this). */ + insert_only_hash_map *destructive_copy (); + + /* Must be a power of 2 not less than min_capacity. */ + size_type num_buckets_; + size_type num_entries_; + size_type size_in_bytes_; + bucket_type buckets[0]; /* Actual array size is num_buckets. */ +}; + +template <typename T, typename Alloc> +insert_only_hash_map <T, Alloc> * +insert_only_hash_map <T, Alloc>::create (size_type expected_size) +{ + size_t cap = min_capacity; + while (expected_size >= cap) + { + cap *= 2; + } + size_t size_in_bytes = sizeof (insert_only_hash_map <T, Alloc>) + + cap * sizeof (bucket_type); + insert_only_hash_map <T, Alloc>* result = + static_cast <insert_only_hash_map <T, Alloc>*> (Alloc () + .alloc (size_in_bytes)); + if (result != NULL) + { + result->size_in_bytes_ = size_in_bytes; + result->num_buckets_ = cap; + result->num_entries_ = 0; + memset (result->buckets, 0, cap * sizeof (bucket_type)); + } + return result; +} + +template <typename T, typename Alloc> +insert_only_hash_map <T, Alloc>* +insert_only_hash_map <T, Alloc>::destructive_copy () +{ + insert_only_hash_map* copy = create (this->bucket_count ()); + if (copy == NULL) + return NULL; + VTV_DEBUG_ASSERT (copy->bucket_count () == 2 * this->bucket_count ()); + for (size_type i = 0; i < this->bucket_count (); i++) + if (this->buckets[i].first != NULL) + copy->put_internal (this->buckets[i].first, this->buckets[i].second, + true); + VTV_DEBUG_ASSERT (copy->size () == this->size ()); + destroy (this); + return copy; +} + +template <typename T, typename Alloc> +insert_only_hash_map <T, Alloc>* +insert_only_hash_map <T, Alloc>::find_or_add_key (const key_type *k, + value_type **v) +{ + /* Table size is always a power of 2. */ + const size_type mask = this->bucket_count () - 1; + size_type bucket_index = k->hash & mask; + size_type step = 1; + for (;;) + { + bucket_type &bucket = this->buckets[bucket_index]; + if (bucket.first == NULL) + { + /* Key was not present. */ + if (this->is_too_full (this->size () + 1)) + { + insert_only_hash_map <T, Alloc>* result = + this->destructive_copy (); + return result == NULL + ? NULL + : result->find_or_add_key (k, v); + } + else + { + bucket.first = k; + bucket.second = T (); + this->num_entries_++; + *v = &bucket.second; + return this; + } + } + else if (bucket.first->equals (k)) + { + /* Key was present. */ + *v = &bucket.second; + return this; + } + else + bucket_index = (bucket_index + step++) & mask; + } +} + +template <typename T, typename Alloc> +insert_only_hash_map <T, Alloc>* +insert_only_hash_map <T, Alloc>::put_internal ( + const insert_only_hash_map::key_type *k, + const insert_only_hash_map::value_type &v, + bool unique_key_and_resize_not_needed) +{ + /* Table size is always a power of 2. */ + const size_type mask = this->bucket_count () - 1; + size_type bucket_index = k->hash & mask; + size_type step = 1; + for (;;) + { + bucket_type &bucket = this->buckets[bucket_index]; + if (bucket.first == NULL) + { + /* Key was not present. */ + if (!unique_key_and_resize_not_needed + && this->is_too_full (this->size () + 1)) + { + insert_only_hash_map <T, Alloc>* result = + this->destructive_copy (); + return result == NULL + ? NULL + : result->put_internal (k, v, true); + } + else + { + bucket.first = k; + bucket.second = v; + this->num_entries_++; + return this; + } + } + else if (!unique_key_and_resize_not_needed && bucket.first->equals (k)) + { + /* Key was present. Just change the value. */ + bucket.second = v; + return this; + } + else + bucket_index = (bucket_index + step++) & mask; + } +} + +template <typename T, typename Alloc> +const typename insert_only_hash_map <T, Alloc>::value_type* +insert_only_hash_map <T, Alloc>::get (const insert_only_hash_map::key_type *k) + const +{ + /* Table size is always a power of 2. */ + const size_type mask = this->bucket_count () - 1; + size_type bucket_index = k->hash & mask; + size_type step = 1; + for (;;) + { + const bucket_type &bucket = this->buckets[bucket_index]; + if (bucket.first == NULL) + return NULL; + else if (bucket.first->equals (k)) + return &bucket.second; + else + bucket_index = (bucket_index + step++) & mask; + } +} + +template <typename T, typename Alloc> +bool +insert_only_hash_map <T, Alloc>::key_type::equals ( + const typename insert_only_hash_map <T, Alloc>::key_type *k) const +{ + const char* x = reinterpret_cast <const char *> (k); + const char* y = reinterpret_cast <const char *> (this); + return (load8bytes (x) == load8bytes (y) + && memcmp (x + 8, y + 8, this->n) == 0); +} + +#endif /* _VTV_MAP_H */ diff --git a/libstdc++-v3/libsupc++/vtv_rts.cc b/libstdc++-v3/libsupc++/vtv_rts.cc new file mode 100644 index 00000000000..ad9aea4d5ef --- /dev/null +++ b/libstdc++-v3/libsupc++/vtv_rts.cc @@ -0,0 +1,1314 @@ +/* Copyright (C) 2012 + Free Software Foundation + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GCC is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + <http://www.gnu.org/licenses/>. */ + +/* This file is part of the vtable security feature implementation. + The vtable security feature is designed to detect when a virtual + call is about to be made through an invalid vtable pointer + (possibly due to data corruption or malicious attacks). The + compiler finds every virtual call, and inserts a verification call + before the virtual call. The verification call takes the actual + vtable pointer value in the object through which the virtual call + is being made, and compares the vtable pointer against a set of all + valid vtable pointers that the object could contain (this set is + based on the declared type of the object). If the pointer is in + the valid set, execution is allowed to continue; otherwise the + program is halted. + + There are several pieces needed in order to make this work: 1. For + every virtual class in the program (i.e. a class that contains + virtual methods), we need to build the set of all possible valid + vtables that an object of that class could point to. This includes + vtables for any class(es) that inherit from the class under + consideration. 2. For every such data set we build up, we need a + way to find and reference the data set. This is complicated by the + fact that the real vtable addresses are not known until runtime, + when the program is loaded into memory, but we need to reference the + sets at compile time when we are inserting verification calls into + the program. 3. We need to find every virtual call in the program, + and insert the verification call (with the appropriate arguments) + before the virtual call. 4. We need some runtime library pieces: + the code to build up the data sets at runtime; the code to actually + perform the verification using the data sets; and some code to set + protections on the data sets, so they themselves do not become + hacker targets. + + To find and reference the set of valid vtable pointers for any given + virtual class, we create a special global varible for each virtual + class. We refer to this as the "vtable map variable" for that + class. The vtable map variable has the type "void *", and is + initialized by the compiler to NULL. At runtime when the set of + valid vtable pointers for a virtual class, e.g. class Foo, is built, + the vtable map variable for class Foo is made to point to the set. + During compile time, when the compiler is inserting verification + calls into the program, it passes the vtable map variable for the + appropriate class to the verification call, so that at runtime the + verification call can find the appropriate data set. + + The actual set of valid vtable pointers for a virtual class, + e.g. class Foo, cannot be built until runtime, when the vtables get + loaded into memory and their addresses are known. But the knowledge + about which vtables belong in which class' hierarchy is only known + at compile time. Therefore at compile time we collect class + hierarchy and vtable information about every virtual class, and we + generate calls to build up the data sets at runtime. To build the + data sets, we call one of the functions we add to the runtime + library, __VLTRegisterPair. __VLTRegisterPair takes two arguments, + a vtable map variable and the address of a vtable. If the vtable + map variable is currently NULL, it creates a new data set (hash + table), makes the vtable map variable point to the new data set, and + inserts the vtable address into the data set. If the vtable map + variable is not NULL, it just inserts the vtable address into the + data set. In order to make sure that our data sets are built before + any verification calls happen, we create a special constructor + initialization function for each compilation unit, give it a very + high initialization priority, and insert all of our calls to + __VLTRegisterPair into our special constructor initialization + function. */ + +/* This file contains the main externally visible runtime library + functions for vtable verification: __VLTChangePermission, + __VLTRegisterPair, and __VLTVerifyVtablePointer. It also contains + debug versions __VLTRegisterPairDebug and + __VLTVerifyVtablePointerDebug, which have extra parameters in order + to make it easier to debug verification failures. + + This file also contains the failure functions that get called when + a vtable pointer is not found in the data set. Two particularly + important functions are __vtv_verify_fail and __vtv_really_fail. + They are both externally visible. __vtv_verify_fail is defined in + such a way that it can be replaced by a programmer, if desired. It + is the function that __VLTVerifyVtablePointer calls if it can't + find the pointer in the data set. Allowing the programmer to + overwrite this function means that he/she can do some alternate + verification, including NOT failing in certain specific cases, if + desired. This may be the case if the programmer has to deal wtih + unverified third party software, for example. __vtv_really_fail is + available for the programmer to call from his version of + __vtv_verify_fail, if he decides the failure is real. + + The final piece of functionality implemented in this file is symbol + resolution for multiple instances of the same vtable map variable. + If the same virtual class is used in two different compilation + units, then each compilation unit will create a vtable map variable + for the class. We need all instances of the same vtable map + variable to point to the same (single) set of valid vtable + pointters for the class, so we wrote our own hashtable-based symbol + resolution for vtable map variables (with a tiny optimization in + the case where there is only one instance of the variable). + + There are two other important pieces to the runtime for vtable + verification besides the main pieces that go into libstdc++.so: two + special tiny shared libraries, libvtv_init.so and libvtv_stubs.so. + libvtv_init.so is built from vtv_init.cc. It is designed to hel[p + minimize the calls made to mprotect (see the comments in + vtv_init.cc for more details). Anything compiled with + "-fvtable-verify=std" must be linked with libvtv_init.so (the gcc + driver has been modified to do this). vtv_stubs.so is built from + vtv_stubs.cc. It replaces the main runtime functions + (__VLTChangePermissino, __VLTRegisterPair and + __VLTVerifyVtablePoitner) with stub functions that do nothing. If + a programmer has a library that was built with verification, but + wishes to not have verification turned on, the programmer can link + in the vtv_stubs.so library. */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <execinfo.h> + +#include <unistd.h> +#include <sys/mman.h> +#include <errno.h> +#include <link.h> +#include <fcntl.h> +#include <limits.h> + +/* For gthreads suppport */ +#include <bits/c++config.h> +#include <ext/concurrence.h> + +#include "vtv_utils.h" +#include "vtv_malloc.h" +#include "vtv_set.h" +#include "vtv_map.h" +#include "vtv_rts.h" +#include "vtv_fail.h" + +extern "C" { + + /* __fortify_fail is a function in glibc that calls __libc_message, + causing it to print out a program termination error message + (including the name of the binary being terminated), a stack + trace where the error occurred, and a memory map dump. Ideally + we would have called __libc_message directly, but that function + does not appear to be accessible to functions outside glibc, + whereas __fortify_fail is. We call __fortify_fail from + __vtv_really_fail. We looked at calling __libc_fatal, which is + externally accessible, but it does not do the back trace and + memory dump. */ + + extern void __fortify_fail (const char *) __attribute__((noreturn)); + +} /* extern "C" */ + + +/* Be careful about initialization of statics in this file. Some of + the routines below are called before any runtime initialization for + statics in this file will be done. For example, dont try to + initialize any of these statics with a runtime call (for ex: + sysconf. The initialization will happen after calls to the routines + to protect/unprotec the vtabla_map variables */ + +/* No need to mark the following variables with VTV_PROTECTED_VAR. + These are either const or are only used for debugging/tracing. + debugging/tracing will not be ON on production environments */ + +static const bool debug_hash = HASHTABLE_STATS; + +/* TODO: Make sure debugging messages under this guard dont use malloc! */ +static const int debug_functions = 0; +static const int debug_init = 0; +static const int debug_verify_vtable = 0; + + +#ifdef VTV_DEBUG +/* Global file descriptor variables for logging, tracing and debugging. */ +static int init_log_fd = -1; +static int verify_vtable_log_fd = -1; + +/* This holds a formatted error logging message, to be written to the + vtable verify failures log. */ +static char debug_log_message[1024]; +#endif + +/* TODO: should this be under VTV_DEBUG? */ +static int vtv_failures_log_fd = -1; + +#if HASHTABLE_STATS +static int set_log_fd = -1; +#endif + + +#ifdef __GTHREAD_MUTEX_INIT +/* TODO: NEED TO PROTECT THIS VAR !!!!!!!!!!!!!!!!!!! */ +static __gthread_mutex_t change_permissions_lock = __GTHREAD_MUTEX_INIT; +#else +/* TODO: NEED TO PROTECT THIS VAR !!!!!!!!!!!!!!!!!!! */ +static __gthread_mutex_t change_permissions_lock; +#endif + +/* Types needed by insert_only_hash_sets. */ +typedef uintptr_t int_vptr; + +/* The set of valid vtable pointers for each virtual class is stored + in a hash table. This is the hashing function used for the hash + table. For more information on the implementation of the hash + table, see the class insert_only_hash_sets in vtv_set.h. */ + +struct vptr_hash + { + /* Hash function, used to convert vtable pointer, V, (a memory + address) into an index into the hash table. */ + size_t + operator() (int_vptr v) const + { + const uint32_t x = 0x7a35e4d9; + const int shift = (sizeof (v) == 8) ? 23 : 21; + v = x * v; + return v ^ (v >> shift); + } + }; + +/* This is the memory allocator used to create the hash table data + sets of valid vtable pointers. We use VTV_malloc in order to keep + track of which pages have been allocated, so we can update the + protections on those pages appropriately. See the class + insert_only_hash_sets in vtv_set.h for more information. */ + +struct vptr_set_alloc + { + /* Memory allocator operator. N is the number of bytes to be + allocated. */ + void * + operator() (size_t n) const + { + return VTV_malloc (n); + } + }; + +/* Instantiate the template classes (in vtv_set.h) for our particular + hash table needs. */ +typedef insert_only_hash_sets<int_vptr, vptr_hash, vptr_set_alloc> vtv_sets; +typedef vtv_sets::insert_only_hash_set vtv_set; +typedef vtv_set * vtv_set_handle; +typedef vtv_set_handle * vtv_set_handle_handle; + +/* Data structure passed to our dl_iterate_phdr callback function, + indicating whether mprotect should make the pages readonly or + read/write, and what page size to use. */ + +struct mprotect_data + { + int prot_mode; + unsigned long page_size; + }; + +/* Records for caching teh section header information that we have + read out of the file(s) on disk (in dl_iterate_phdr_callback), to + avoid having to re-open and re-read the same file multiple + times. */ + +struct sect_hdr_data +{ + ElfW (Addr) dlpi_addr; /* The header address in the INFO record, + passed in from dl_iterate_phdr. */ + ElfW (Addr) mp_low; /* Start address of the .vtable_map_vars + section in memory. */ + size_t mp_size; /* Size of the .vtable_map_vars section in + memory. */ +}; + +/* Array for caching the section header information, read from file, + to avoid re-opening and re-reading the same file over-and-over + again. */ + +#define MAX_ENTRIES 250 +struct sect_hdr_data sect_info_cache[MAX_ENTRIES]; + +unsigned int num_cache_entries = 0; + +/* This function takes the LOAD_ADDR for an object opened by the + dynamic loader, and checks the array of cached file data to see if + there is an entry with the same addres. If it finds such an entry, + it returns the record for that entry; otherwise it returns + NULL. */ + +struct sect_hdr_data * +search_cached_file_data (ElfW (Addr) load_addr) +{ + unsigned int i; + for (i = 0; i < num_cache_entries; ++i) + { + if (sect_info_cache[i].dlpi_addr == load_addr) + return &(sect_info_cache[i]); + } + + return NULL; +} + +/* This function tries to read COUNT bytes out of the file referred to + by FD into the buffer BUF. It returns the actual number of bytes + it succeeded in reading. */ + +static ssize_t +ReadPersistent (int fd, void *buf, size_t count) +{ + char *buf0 = (char *) buf; + size_t num_bytes = 0; + while (num_bytes < count) + { + int len; + len = read (fd, buf0 + num_bytes, count - num_bytes); + if (len < 0) + return -1; + if (len == 0) + break; + num_bytes += len; + } + + return num_bytes; +} + +/* This function tries to read COUNT bytes, starting at OFFSET from + the file referred to by FD, and put them into BUF. It calls + ReadPersistent to help it do so. It returns the actual number of + bytes read, or -1 if it fails altogether. */ + +static ssize_t +ReadFromOffset (int fd, void *buf, const size_t count, const off_t offset) +{ + off_t off = lseek (fd, offset, SEEK_SET); + if (off != (off_t) -1) + return ReadPersistent (fd, buf, count); + return -1; +} + +/* The function takes a MESSAGE and attempts to write it to the vtable + memory protection log (for debugging purposes). If the file is not + open, it attempts to open the file first. Sometimes multiple + processes may be attempting to write to the log file at the same + time, so we may attempt to open multiple log files (with versioned + names) if the first open fails. */ + +static void +log_memory_protection_data (char *message) +{ + static int log_fd = -1; + + if (log_fd == -1) + log_fd = vtv_open_log ("vtv_memory_protection_data.log"); + + vtv_add_to_log (log_fd, "%s", message); +} + +/* This is the callback function used by dl_iterate_phdr (which is + called from VTV_unprotect_vtable_vars and VTV_protect_vtable_vars). + It attempts to find the binary file on disk for the INFO record + that dl_iterate_phdr passes in; open the binary file, and read its + section header information. If the file contains a + ".vtable_map_vars" section, read the section offset and size. Use + the secttion offset and size, in conjunction with the data in INFO + to locate the pages in memory where the section is. Call + 'mprotect' on those pages, setting the protection either to + read-only or read-write, depending on what's in DATA. */ + +static int +dl_iterate_phdr_callback (struct dl_phdr_info *info, + size_t unused __attribute__((__unused__)), + void *data) +{ + mprotect_data * mdata = (mprotect_data *) data; + off_t map_sect_offset = 0; + ElfW (Word) map_sect_len = 0; + ElfW (Addr) start_addr = 0; + struct sect_hdr_data *cached_data = NULL; + bool found = false; + char buffer[PATH_MAX]; + char program_name[PATH_MAX]; + char *cptr; + static bool first_time = true; + const ElfW (Phdr) *phdr_info = info->dlpi_phdr; + const ElfW (Ehdr) *ehdr_info = + (const ElfW (Ehdr) *) (info->dlpi_addr + info->dlpi_phdr[0].p_vaddr + - info->dlpi_phdr[0].p_offset); + + /* Check to see if this is the record for the Linux Virtual Dynamic + Shared Object (linux-vdso.so.1), which exists only in memory (and + therefore cannot be read from disk). */ + + if (strcmp (info->dlpi_name, "linux-vdso.so.1") == 0) + return 0; + + if (strlen (info->dlpi_name) == 0 + && info->dlpi_addr != 0) + return 0; + + if (first_time) + { + int i; + for (i = 0; i < MAX_ENTRIES; ++i) + { + sect_info_cache[i].dlpi_addr = (ElfW (Addr)) 0; + sect_info_cache[i].dlpi_addr = (ElfW (Addr)) 0; + sect_info_cache[i].dlpi_addr = 0; + } + first_time = false; + } + + /* Get the name of the main executable. This may or may not include + arguments passed to the program. Find the first space, assume it + is the start of the argument list, and change it to a '\0'. */ + snprintf (program_name, sizeof (program_name), program_invocation_name); + + /* Check to see if we already have the data for this file. */ + cached_data = search_cached_file_data (info->dlpi_addr); + + if (cached_data) + { + /* We already read the section header data and calculated the + appropriate addresses; use the cached data to set the + appropriate protections and return. */ + if (mprotect ((void *) cached_data->mp_low, cached_data->mp_size, + mdata->prot_mode) == -1) + { + if (debug_functions) + { + snprintf (buffer, sizeof (buffer), + "Failed called to mprotect for %s error: ", + (mdata->prot_mode & PROT_WRITE) ? + "READ/WRITE" : "READ-ONLY"); + log_memory_protection_data (buffer); + perror(NULL); + } + VTV_error(); + } + else if (debug_functions) + { + snprintf (buffer, sizeof (buffer), + "mprotect'ed range [%p, %p]\n", + (void *) cached_data->mp_low, + (char *) cached_data->mp_low + cached_data->mp_size); + log_memory_protection_data (buffer); + } + return 0; + } + + /* Find the first non-escaped space in the program name and make it + the end of the string. */ + cptr = strchr (program_name, ' '); + if (cptr != NULL && cptr[-1] != '\\') + cptr[0] = '\0'; + + if ((phdr_info->p_type == PT_PHDR || phdr_info->p_type == PT_LOAD) + && (ehdr_info->e_shoff && ehdr_info->e_shnum)) + { + const char *map_sect_name = ".vtable_map_vars"; + int name_len = strlen (map_sect_name); + int fd = -1; + + /* Attempt to open the binary file on disk. */ + if (strlen (info->dlpi_name) == 0) + { + /* If the constructor initialization function was put into + the preinit array, then this function will get called + while handling preinit array stuff, in which case + program_invocation_name has not been initialized. In + that case we can get the filename of the executable from + "/proc/self/exe". */ + if (strlen (program_name) > 0) + { + if (phdr_info->p_type == PT_PHDR) + fd = open (program_name, O_RDONLY); + } + else + fd = open ("/proc/self/exe", O_RDONLY); + } + else + fd = open (info->dlpi_name, O_RDONLY); + + /* VTV_ASSERT (fd != -1); */ + if (fd != -1) + { + + /* Find the section header information in the file. */ + ElfW (Half) strtab_idx = ehdr_info->e_shstrndx; + ElfW (Shdr) shstrtab; + off_t shstrtab_offset = ehdr_info->e_shoff + + (ehdr_info->e_shentsize * strtab_idx); + size_t bytes_read = ReadFromOffset (fd, &shstrtab, sizeof (shstrtab), + shstrtab_offset); + VTV_ASSERT (bytes_read == sizeof (shstrtab)); + + ElfW (Shdr) sect_hdr; + + /* Loop through all the section headers, looking for one whose + name is ".vtable_map_vars". */ + + for (int i = 0; i < ehdr_info->e_shnum && !found; ++i) + { + off_t offset = ehdr_info->e_shoff + (ehdr_info->e_shentsize * i); + + bytes_read = ReadFromOffset (fd, §_hdr, sizeof (sect_hdr), + offset); + + VTV_ASSERT (bytes_read == sizeof (sect_hdr)); + + char header_name[64]; + off_t name_offset = shstrtab.sh_offset + sect_hdr.sh_name; + + bytes_read = ReadFromOffset (fd, &header_name, 64, name_offset); + + VTV_ASSERT (bytes_read > 0); + + if (memcmp (header_name, map_sect_name, name_len) == 0) + { + /* We found the section; get its load offset and + size. */ + map_sect_offset = sect_hdr.sh_addr; + map_sect_len = sect_hdr.sh_size - mdata->page_size; + found = true; + } + } + close (fd); + + } + /* Calculate the start address of the section in memory. */ + start_addr = (const ElfW (Addr)) info->dlpi_addr + map_sect_offset; + } + + if (debug_functions) + { + snprintf (buffer, sizeof(buffer), + " Looking at load module %s to change permissions to %s\n", + ((strlen (info->dlpi_name) == 0) ? program_name + : info->dlpi_name), + (mdata->prot_mode & PROT_WRITE) ? "READ/WRITE" : "READ-ONLY"); + log_memory_protection_data (buffer); + } + + /* See if we actually found the section. */ + if (start_addr && map_sect_len) + { + ElfW (Addr) relocated_start_addr = start_addr; + ElfW (Word) size_in_memory = map_sect_len; + + if ((relocated_start_addr != 0) + && (size_in_memory != 0)) + { + /* Calculate the address & size to pass to mprotect. */ + ElfW (Addr) mp_low = relocated_start_addr & ~(mdata->page_size - 1); + size_t mp_size = size_in_memory - 1; + + if (debug_functions) + { + snprintf (buffer, sizeof (buffer), + " (%s): Protecting %p to %p\n", + ((strlen (info->dlpi_name) == 0) ? program_name + : info->dlpi_name), + (void *) mp_low, + ((void *) mp_low + mp_size)); + log_memory_protection_data (buffer); + } + + /* Change the protections on the pages for the section. */ + if (mprotect ((void *) mp_low, mp_size, mdata->prot_mode) == -1) + { + if (debug_functions) + { + snprintf (buffer, sizeof (buffer), + "Failed called to mprotect for %s error: ", + (mdata->prot_mode & PROT_WRITE) ? + "READ/WRITE" : "READ-ONLY"); + log_memory_protection_data (buffer); + perror(NULL); + } + VTV_error(); + } + else if (debug_functions) + { + if (num_cache_entries < MAX_ENTRIES) + { + sect_info_cache[num_cache_entries].dlpi_addr = + info->dlpi_addr; + sect_info_cache[num_cache_entries].mp_low = mp_low; + sect_info_cache[num_cache_entries].mp_size = mp_size; + num_cache_entries++; + } + snprintf (buffer, sizeof (buffer), + "mprotect'ed range [%p, %p]\n", + (void *) mp_low, (char *) mp_low + mp_size); + log_memory_protection_data (buffer); + } + } + } + + return 0; +} + +/* Unprotect all the vtable map vars and other side data that is used + to keep the core hash_map data. All of these data have been put + into relro sections */ + +static void +VTV_unprotect_vtable_vars (void) +{ + mprotect_data mdata; + + mdata.prot_mode = PROT_READ | PROT_WRITE; + mdata.page_size = sysconf (_SC_PAGE_SIZE); + dl_iterate_phdr (dl_iterate_phdr_callback, (void *) &mdata); +} + +/* Protect all the vtable map vars and other side data that is used + to keep the core hash_map data. All of these data have been put + into relro sections */ + +static void +VTV_protect_vtable_vars (void) +{ + mprotect_data mdata; + + mdata.prot_mode = PROT_READ; + mdata.page_size = sysconf (_SC_PAGE_SIZE); + dl_iterate_phdr (dl_iterate_phdr_callback, (void *) &mdata); +} + +#ifndef __GTHREAD_MUTEX_INIT +static void +initialize_change_permissions_mutexes () +{ + __GTHREAD_MUTEX_INIT_FUNCTION (&change_permissions_lock); +} +#endif + +/* Variables needed for getting the statistics about the hashtable set. */ +#if HASHTABLE_STATS +_AtomicStatCounter stat_contains = 0; +_AtomicStatCounter stat_insert = 0; +_AtomicStatCounter stat_resize = 0; +_AtomicStatCounter stat_create = 0; +_AtomicStatCounter stat_probes_in_non_trivial_set = 0; +_AtomicStatCounter stat_contains_size0 = 0; +_AtomicStatCounter stat_contains_size1 = 0; +_AtomicStatCounter stat_contains_size2 = 0; +_AtomicStatCounter stat_contains_size3 = 0; +_AtomicStatCounter stat_contains_size4 = 0; +_AtomicStatCounter stat_contains_size5 = 0; +_AtomicStatCounter stat_contains_size6 = 0; +_AtomicStatCounter stat_contains_size7 = 0; +_AtomicStatCounter stat_contains_size8 = 0; +_AtomicStatCounter stat_contains_size9 = 0; +_AtomicStatCounter stat_contains_size10 = 0; +_AtomicStatCounter stat_contains_size11 = 0; +_AtomicStatCounter stat_contains_size12 = 0; +_AtomicStatCounter stat_contains_size13_or_more = 0; +_AtomicStatCounter stat_contains_sizes = 0; +_AtomicStatCounter stat_grow_from_size0_to_1 = 0; +_AtomicStatCounter stat_grow_from_size1_to_2 = 0; +_AtomicStatCounter stat_double_the_number_of_buckets = 0; +_AtomicStatCounter stat_insert_found_hash_collision = 0; +_AtomicStatCounter stat_contains_in_non_trivial_set = 0; +_AtomicStatCounter stat_insert_key_that_was_already_present = 0; +#endif + +/* Record statistics about the hash table sets, for debugging. */ + +static void +log_set_stats (void) +{ +#if HASHTABLE_STATS + if (set_log_fd == -1) + set_log_fd = vtv_open_log ("vtv_set_stats.log"); + + vtv_add_to_log (set_log_fd, "---\n%s\n", + insert_only_hash_tables_stats().c_str()); +#endif +} + +/* Change the permissions on all the pages we have allocated for the + data sets and all the ".vtable_map_var" sections in memory (which + contain our vtable map variables). PERM indicates whether to make + the permissions read-only or read-write. */ + +void +__VLTChangePermission (int perm) +{ + if (debug_functions) + { + if (perm == __VLTP_READ_WRITE) + fprintf (stdout, "Changing VLT permisisons to Read-Write.\n"); + else if (perm == __VLTP_READ_ONLY) + fprintf (stdout, "Changing VLT permissions to Read-only.\n"); + else + fprintf (stdout, "Unrecognized permissions value: %d\n", perm); + } + +#ifndef __GTHREAD_MUTEX_INIT + static __gthread_once_t mutex_once VTV_PROTECTED_VAR = __GTHREAD_ONCE_INIT; + + __gthread_once (&mutex_once, initialize_change_permissions_mutexes); +#endif + + /* Ordering of these unprotect/protect calls is very important. + You first need to unprotect all the map vars and side + structures before you do anything with the core data + structures (hash_maps) */ + + if (perm == __VLTP_READ_WRITE) + { + /* TODO: Meed to revisit this code for dlopen. It most probably + is not unlocking the protected vtable vars after for a load + module that is not the first load module. */ + __gthread_mutex_lock (&change_permissions_lock); + + VTV_unprotect_vtable_vars (); + VTV_malloc_init (); + VTV_malloc_unprotect (); + + } + else if (perm == __VLTP_READ_ONLY) + { + if (debug_hash) + log_set_stats(); + + VTV_malloc_protect (); + VTV_protect_vtable_vars (); + + __gthread_mutex_unlock (&change_permissions_lock); + } +} + +/* This is the memory allocator used to create the hash table that + maps from vtable map variable name to the data set that vtable map + variable should point to. This is part of our vtable map variable + symbol resolution, which is necessary because the same vtable map + variable may be created by multiple compilation units and we need a + method to make sure that all vtable map variables for a particular + class point to the same data set at runtime. */ + +struct insert_only_hash_map_allocator + { + /* N is the number of bytes to allocate. */ + void * + alloc (size_t n) const + { + return VTV_malloc (n); + } + + /* P points to the memory to be deallocated; N is the number of + bytes to deallocate. */ + void + dealloc (void *p, size_t n __attribute__((__unused__))) const + { + VTV_free (p); + } + }; + +/* Explicitly instantiate this class since this file is compiled with + -fno-implicit-templates. These are for the hash table that is used + to do vtable map variable symbol resolution. */ +template class insert_only_hash_map <vtv_set_handle *, + insert_only_hash_map_allocator >; +typedef insert_only_hash_map <vtv_set_handle *, + insert_only_hash_map_allocator > s2s; +typedef const s2s::key_type vtv_symbol_key; + +static s2s * vtv_symbol_unification_map VTV_PROTECTED_VAR = NULL; + +const unsigned long SET_HANDLE_HANDLE_BIT = 0x2; + +/* In the case where a vtable map variable is the only instance of the + variable we have seen, it points directly to the set of valid + vtable pointers. All subsequent instances of the 'same' vtable map + variable point to the first vtable map variable. This function, + given a vtable map variable PTR, checks a bit to see whether it's + pointing directly to the data set or to the first vtable map + variable. */ + +static inline bool +is_set_handle_handle (void * ptr) +{ + return ((unsigned long) ptr & SET_HANDLE_HANDLE_BIT) + == SET_HANDLE_HANDLE_BIT; +} + +/* Returns the actual pointer value of a vtable map variable, PTR (see + comments for is_set_handle_handle for more details). */ + +static inline vtv_set_handle * +ptr_from_set_handle_handle (void * ptr) +{ + return (vtv_set_handle *) ((unsigned long) ptr & ~SET_HANDLE_HANDLE_BIT); +} + +/* Given a vtable map variable, PTR, this function sets the bit that + says this is the second (or later) instance of a vtable map + variable. */ + +static inline vtv_set_handle_handle +set_handle_handle (vtv_set_handle * ptr) +{ + return (vtv_set_handle_handle) ((unsigned long) ptr | SET_HANDLE_HANDLE_BIT); +} + +/* Open error logging file, if not already open, and write vtable + verification failure messages (LOG_MSG) to the log file. Also + generate a backtrace in the log file, if GENERATE_BACKTRACE is + set. */ + +static void +log_error_message (const char *log_msg, bool generate_backtrace) +{ + if (vtv_failures_log_fd == -1) + vtv_failures_log_fd = vtv_open_log ("vtable_verification_failures.log"); + + if (vtv_failures_log_fd == -1) + return; + + vtv_add_to_log (vtv_failures_log_fd, "%s", log_msg); + + if (generate_backtrace) + { +#define STACK_DEPTH 20 + void *callers[STACK_DEPTH]; + int actual_depth = backtrace (callers, STACK_DEPTH); + backtrace_symbols_fd (callers, actual_depth, vtv_failures_log_fd); + } +} + +/* Ideally it would be nice if the library always provided the 2 + versions of the runtime libraries. However, when we use VTV_DEBUG + we want to make sure that only the debug versions are being + used. We could change this once the code is more stable. */ + +#ifdef VTV_DEBUG + +/* This routine initializes a set handle to a vtable set. It makes + sure that there is only one set handle for a particular set by + using a map from set name to pointer to set handle. Since there + will be multiple copies of the pointer to the set handle (one per + compilation unit that uses it), it makes sure to initialize all the + pointers to the set handle so that the set handle is unique. To + make this a little more efficient and avoid a level of indirection + in some cases, the first pointer to handle for a particular handle + becomes the handle itself and the other pointers will point to the + set handle. This is the debug version of this function, so it + outputs extra debugging messages and logging. SET_HANDLE_PTR is + the address of the vtable map variable, SET_SYMBOL_KEY is the hash + table key (containing the name of the map variable and the hash + value) and SIZE_HINT is a guess for the best initial size for the + set of vtable pointers that SET_HANDLE_POINTER will point to. */ + +void __VLTInitSetSymbolDebug (void **set_handle_ptr, + const void *set_symbol_key, + size_t size_hint) +{ + VTV_DEBUG_ASSERT (set_handle_ptr); + + if (vtv_symbol_unification_map == NULL) + { + /* TODO: For now we have chosen 1024, but we need to come up with a + better initial size for this. */ + vtv_symbol_unification_map = s2s::create (1024); + VTV_DEBUG_ASSERT(vtv_symbol_unification_map); + } + + vtv_set_handle *handle_ptr = (vtv_set_handle *) set_handle_ptr; + vtv_symbol_key *symbol_key_ptr = (vtv_symbol_key *) set_symbol_key; + + const s2s::value_type * map_value_ptr = + vtv_symbol_unification_map->get (symbol_key_ptr); + char buffer[200]; + if (map_value_ptr == NULL) + { + if (*handle_ptr != NULL) + { + snprintf (buffer, sizeof(buffer), + "*** Found non-NULL local set ptr %p missing for symbol" + " %.*s", + *handle_ptr, symbol_key_ptr->n, symbol_key_ptr->bytes); + log_error_message (buffer, true); + VTV_DEBUG_ASSERT (0); + } + } + else if (*handle_ptr != NULL && + (handle_ptr != *map_value_ptr && + ptr_from_set_handle_handle (*handle_ptr) != *map_value_ptr)) + { + VTV_DEBUG_ASSERT (*map_value_ptr != NULL); + snprintf (buffer, sizeof(buffer), + "*** Found diffence between local set ptr %p and set ptr %p" + "for symbol %.*s", + *handle_ptr, *map_value_ptr, + symbol_key_ptr->n, symbol_key_ptr->bytes); + log_error_message (buffer, true); + VTV_DEBUG_ASSERT (0); + } + else if (*handle_ptr == NULL) + { + /* Execution should not reach this point. */ + } + + if (*handle_ptr != NULL) + { + if (!is_set_handle_handle (*set_handle_ptr)) + handle_ptr = (vtv_set_handle *) set_handle_ptr; + else + handle_ptr = ptr_from_set_handle_handle (*set_handle_ptr); + vtv_sets::resize (size_hint, handle_ptr); + return; + } + + VTV_DEBUG_ASSERT (*handle_ptr == NULL); + if (map_value_ptr != NULL) + { + if (*map_value_ptr == handle_ptr) + vtv_sets::resize (size_hint, *map_value_ptr); + else + { + /* The one level handle to the set already exists. So, we + are adding one level of indirection here and we will + store a pointer to the one level handle here. */ + + vtv_set_handle_handle * handle_handle_ptr = + (vtv_set_handle_handle *)handle_ptr; + *handle_handle_ptr = set_handle_handle(*map_value_ptr); + VTV_DEBUG_ASSERT(*handle_handle_ptr != NULL); + + /* The handle can itself be NULL if the set has only + been initiazlied with size hint == 1. */ + vtv_sets::resize (size_hint, *map_value_ptr); + } + } + else + { + /* We will create a new set. So, in this case handle_ptr is the + one level pointer to the set handle. Create copy of map name + in case the memory where this comes from gets unmapped by + dlclose. */ + size_t map_key_len = symbol_key_ptr->n + sizeof (vtv_symbol_key); + void *map_key = VTV_malloc (map_key_len); + + memcpy (map_key, symbol_key_ptr, map_key_len); + + s2s::value_type *value_ptr; + vtv_symbol_unification_map = + vtv_symbol_unification_map->find_or_add_key ((vtv_symbol_key *)map_key, + &value_ptr); + *value_ptr = handle_ptr; + + /* TODO: We should verify the return value. */ + vtv_sets::create (size_hint, handle_ptr); + VTV_DEBUG_ASSERT (size_hint <= 1 || *handle_ptr != NULL); + } + + if (debug_init) + { + if (init_log_fd == -1) + init_log_fd = vtv_open_log ("vtv_init.log"); + + vtv_add_to_log (init_log_fd, + "Init handle:%p for symbol:%.*s hash:%u size_hint:%lu" + "number of symbols:%lu \n", + set_handle_ptr, symbol_key_ptr->n, + symbol_key_ptr->bytes, symbol_key_ptr->hash, size_hint, + vtv_symbol_unification_map->size ()); + } +} + +/* This function takes a the address of a vtable map variable + (SET_HANDLE_PTR), a VTABLE_PTR to add to the data set, the name of + the vtable map variable (SET_SYMBOL_NAME) and the name of the + vtable (VTABLE_NAME) being pointed to. If the vtable map variable + is NULL it creates a new data set and initializes the variable, + otherwise it uses our symbol unification to find the right data + set; in either case it then adds the vtable pointer to the set. + The other two parameters are used for debugging information. */ + +void +__VLTRegisterPairDebug (void **set_handle_ptr, const void *vtable_ptr, + const char *set_symbol_name, const char *vtable_name) + +{ + VTV_DEBUG_ASSERT(set_handle_ptr != NULL); + /* set_handle_ptr can be NULL if the call to InitSetSymbol had a + size hint of 1. */ + + int_vptr vtbl_ptr = (int_vptr) vtable_ptr; + VTV_DEBUG_ASSERT (vtv_symbol_unification_map != NULL); + vtv_set_handle *handle_ptr; + if (!is_set_handle_handle (*set_handle_ptr)) + handle_ptr = (vtv_set_handle *) set_handle_ptr; + else + handle_ptr = ptr_from_set_handle_handle (*set_handle_ptr); + + /* TODO: We should verify the return value. */ + vtv_sets::insert (vtbl_ptr, handle_ptr); + + if (debug_init) + { + if (init_log_fd == -1) + init_log_fd = vtv_open_log("vtv_init.log"); + + vtv_add_to_log(init_log_fd, + "Registered %s : %s (%p) 2 level deref = %s\n", + set_symbol_name, vtable_name, vtbl_ptr, + is_set_handle_handle(*set_handle_ptr) ? "yes" : "no" ); + } +} + +/* This function is called from __VLTVerifyVtablePointerDebug; it + sends as much debugging information as it can to the error log + file, then calls __vtv_verify_fail. SET_HANDLE_PTR is the pointer + to the set of valid vtable pointers, VTBL_PTR is the pointer that + was not found in the set, and DEBUG_MSG is the message to be + written to the log file before failing. n */ + +static void +__vtv_verify_fail_debug (void **set_handle_ptr, const void *vtbl_ptr, + const char *debug_msg) +{ + log_error_message (debug_msg, false); + + /* Call the public interface in case it has been overwritten by + user. */ + __vtv_verify_fail (set_handle_ptr, vtbl_ptr); + + log_error_message ("Returned from __vtv_verify_fail." + " Secondary verification succeeded.\n", false); +} + +#ifndef VTV_STATIC_VERIFY + +/* This is the debug version of the verification function. It takes + the address of a vtable map variable (SET_HANDLE_PTR) and a + VTABLE_PTR to validate, as well as the name of the vtable map + variable (SET_SYMBOL_NAME) and VTABLE_NAME, which are used for + debugging messages. It checks to see if VTABLE_PTR is in the set + pointed to by SET_HANDLE_PTR. If so, it returns VTABLE_PTR, + otherwise it calls __vtv_verify_fail, which usually logs error + messages and calls abort. */ + +const void * +__VLTVerifyVtablePointerDebug (void **set_handle_ptr, const void *vtable_ptr, + const char *set_symbol_name, + const char *vtable_name) +{ +#ifndef VTV_EMPTY_VERIFY + VTV_DEBUG_ASSERT (set_handle_ptr != NULL && *set_handle_ptr != NULL); + int_vptr vtbl_ptr = (int_vptr) vtable_ptr; + + vtv_set_handle *handle_ptr; + if (!is_set_handle_handle (*set_handle_ptr)) + handle_ptr = (vtv_set_handle *) set_handle_ptr; + else + handle_ptr = ptr_from_set_handle_handle (*set_handle_ptr); + + if (vtv_sets::contains (vtbl_ptr, handle_ptr)) + { + if (debug_verify_vtable) + { + if (verify_vtable_log_fd == -1) + vtv_open_log ("vtv_verify_vtable.log"); + vtv_add_to_log (verify_vtable_log_fd, + "Verified %s %s value = %p\n", + set_symbol_name, vtable_name, vtable_ptr); + } + } + else + { + /* We failed to find the vtable pointer in the set of valid + pointers. Log the error data and call the failure + function. */ + snprintf (debug_log_message, sizeof (debug_log_message), + "Looking for %s in %s\n", vtable_name, set_symbol_name); + __vtv_verify_fail_debug (set_handle_ptr, vtable_ptr, debug_log_message); + + /* Normally __vtv_verify_fail_debug will call abort, so we won't + execute the return below. If we get this far, the assumption + is that the programmer has replaced __vtv_verify_fail_debug + with some kind of secondary verification AND this secondary + verification succeeded, so the vtable pointer is valid. */ + } +#endif /* ifndef VTV_EMPTY_VERIFY*/ + + return vtable_ptr; +} + +#endif /* ifndef VTV_STATIC_VERIFY */ + +#else /* ifdef VTV_DEBUG */ + +/* This routine initializes a set handle to a vtable set. It makes + sure that there is only one set handle for a particular set by + using a map from set name to pointer to set handle. Since there + will be multiple copies of the pointer to the set handle (one per + compilation unit that uses it), it makes sure to initialize all the + pointers to the set handle so that the set handle is unique. To + make this a little more efficient and avoid a level of indirection + in some cases, the first pointer to handle for a particular handle + becomes the handle itself and the other pointers will point to the + set handle. SET_HANDLE_PTR is the address of the vtable map + variable, SET_SYMBOL_KEY is the hash table key (containing the name + of the map variable and the hash value) and SIZE_HINT is a guess + for the best initial size for the set of vtable pointers that + SET_HANDLE_POINTER will point to.*/ + +void __VLTInitSetSymbol (void **set_handle_ptr, const void *set_symbol_key, + size_t size_hint) +{ + vtv_set_handle *handle_ptr = (vtv_set_handle *) set_handle_ptr; + if (*handle_ptr != NULL) + { + if (!is_set_handle_handle (*set_handle_ptr)) + handle_ptr = (vtv_set_handle *) set_handle_ptr; + else + handle_ptr = ptr_from_set_handle_handle (*set_handle_ptr); + vtv_sets::resize (size_hint, handle_ptr); + return; + } + + if (vtv_symbol_unification_map == NULL) + vtv_symbol_unification_map = s2s::create (1024); + + vtv_symbol_key *symbol_key_ptr = (vtv_symbol_key *) set_symbol_key; + const s2s::value_type *map_value_ptr = + vtv_symbol_unification_map->get (symbol_key_ptr); + + if (map_value_ptr != NULL) + { + if (*map_value_ptr == handle_ptr) + vtv_sets::resize (size_hint, *map_value_ptr); + else + { + /* The one level handle to the set already exists. So, we + are adding one level of indirection here and we will + store a pointer to the one level pointer here. */ + vtv_set_handle_handle *handle_handle_ptr = + (vtv_set_handle_handle *) handle_ptr; + *handle_handle_ptr = set_handle_handle (*map_value_ptr); + vtv_sets::resize (size_hint, *map_value_ptr); + } + } + else + { + /* We will create a new set. So, in this case handle_ptr is the + one level pointer to the set handle. Create copy of map name + in case the memory where this comes from gets unmapped by + dlclose. */ + size_t map_key_len = symbol_key_ptr->n + sizeof (vtv_symbol_key); + void * map_key = VTV_malloc (map_key_len); + memcpy (map_key, symbol_key_ptr, map_key_len); + + s2s::value_type * value_ptr; + vtv_symbol_unification_map = + vtv_symbol_unification_map->find_or_add_key ((vtv_symbol_key *)map_key, + &value_ptr); + + *value_ptr = handle_ptr; + + /* TODO: We should verify the return value. */ + vtv_sets::create (size_hint, handle_ptr); + } +} + +/* This function takes a the address of a vtable map variable + (SET_HANDLE_PTR) and a VTABLE_PTR. If the vtable map variable is + NULL it creates a new data set and initializes the variable, + otherwise it uses our symbol unification to find the right data + set; in either case it then adds the vtable pointer to the set. */ + +void +__VLTRegisterPair (void **set_handle_ptr, const void *vtable_ptr) +{ + int_vptr vtbl_ptr = (int_vptr) vtable_ptr; + vtv_set_handle *handle_ptr; + if (!is_set_handle_handle (*set_handle_ptr)) + handle_ptr = (vtv_set_handle *) set_handle_ptr; + else + handle_ptr = ptr_from_set_handle_handle (*set_handle_ptr); + + /* TODO: We should verify the return value. */ + vtv_sets::insert (vtbl_ptr, handle_ptr); +} + +#ifndef VTV_STATIC_VERIFY + +/* This is the main verification function. It takes the address of a + vtable map variable (SET_HANDLE_PTR) and a VTABLE_PTR to validate. + It checks to see if VTABLE_PTR is in the set pointed to by + SET_HANDLE_PTR. If so, it returns VTABLE_PTR, otherwise it calls + __vtv_verify_fail, which usually logs error messages and calls + abort. Since this function gets called VERY frequently, it is + important for it to be as efficient as possible. */ + +const void * +__VLTVerifyVtablePointer (void ** set_handle_ptr, const void * vtable_ptr) +{ +#ifndef VTV_EMPTY_VERIFY + int_vptr vtbl_ptr = (int_vptr) vtable_ptr; + + vtv_set_handle *handle_ptr; + if (!is_set_handle_handle (*set_handle_ptr)) + handle_ptr = (vtv_set_handle *) set_handle_ptr; + else + handle_ptr = ptr_from_set_handle_handle (*set_handle_ptr); + + if (!vtv_sets::contains (vtbl_ptr, handle_ptr)) + { + __vtv_verify_fail ((void **) handle_ptr, vtable_ptr); + /* Normally __vtv_verify_fail will call abort, so we won't + execute the return below. If we get this far, the assumption + is that the programmer has replaced __vtv_verify_fail with + some kind of secondary verification AND this secondary + verification succeeded, so the vtable pointer is valid. */ + } +#endif /* ifndef VTV_EMPTY_VERIFY */ + + return vtable_ptr; +} + +#endif /* ifndef VTV_STATIC_VERIFY */ + +#endif /* else-clause of ifdef VTV_DEBUG */ + +/* This function calls __fortify_fail with a FAILURE_MSG and then + calls abort. */ + +void +__vtv_really_fail (const char *failure_msg) +{ + __fortify_fail (failure_msg); + + /* We should never get this far; __fortify_fail calls __libc_message + which prints out a back trace and a memory dump and then is + supposed to call abort, but let's play it safe anyway and call abort + ourselves. */ + abort (); +} + +/* This function takes an error MSG, a vtable map variable + (DATA_SET_PTR) and a vtable pointer (VTBL_PTR). It is called when + an attempt to verify VTBL_PTR with the set pointed to by + DATA_SET_PTR failed. It outputs a failure message with the + addresses involved, and calls __vtv_really_fail. */ + +static void +vtv_fail (const char *msg, void **data_set_ptr, const void *vtbl_ptr) +{ + char buffer[128]; + int buf_len; + const char *format_str = + "*** Unable to verify vtable pointer (%p) in set (%p) *** \n"; + + snprintf (buffer, sizeof (buffer), format_str, vtbl_ptr, + is_set_handle_handle(*data_set_ptr) ? + ptr_from_set_handle_handle (*data_set_ptr) : + *data_set_ptr); + buf_len = strlen (buffer); + /* Send this to to stderr. */ + write (2, buffer, buf_len); + +#ifndef VTV_NO_ABORT + __vtv_really_fail (msg); +#endif +} + +/* Send information about what we were trying to do when verification + failed to the error log, then call vtv_fail. This function can be + overwritten/replaced by the user, to implement a secondary + verification function instead. DATA_SET_PTR is the vtable map + variable used for the failed verification, and VTBL_PTR is the + vtable pointer that was not found in the set. */ + +void +__vtv_verify_fail (void **data_set_ptr, const void *vtbl_ptr) +{ + char log_msg[256]; + snprintf (log_msg, sizeof (log_msg), "Looking for vtable %p in set %p.\n", + vtbl_ptr, + is_set_handle_handle (*data_set_ptr) ? + ptr_from_set_handle_handle (*data_set_ptr) : + *data_set_ptr); + log_error_message (log_msg, false); + + const char *format_str = + "*** Unable to verify vtable pointer (%p) in set (%p) *** \n"; + snprintf (log_msg, sizeof (log_msg), format_str, vtbl_ptr, *data_set_ptr); + log_error_message (log_msg, false); + log_error_message (" Backtrace: \n", true); + + const char *fail_msg = "Potential vtable pointer corruption detected!!\n"; + vtv_fail (fail_msg, data_set_ptr, vtbl_ptr); +} diff --git a/libstdc++-v3/libsupc++/vtv_rts.h b/libstdc++-v3/libsupc++/vtv_rts.h new file mode 100644 index 00000000000..4409d7a52c0 --- /dev/null +++ b/libstdc++-v3/libsupc++/vtv_rts.h @@ -0,0 +1,56 @@ +/* Copyright (C) 2012 + Free Software Foundation + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GCC is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + <http://www.gnu.org/licenses/>. */ + +#ifndef _VTV_RTS_H +#define _VTV_RTS_H 1 + +#include <cstdlib> + +/* This prototype needs to be kept in sync with the compiler- + generated declaration in vtable-class-hierarchy.c. */ + +/* We could have used an enumeration here but it just makes it more + difficult for the compiler to generate a call to this. */ +#define __VLTP_READ_ONLY 0 +#define __VLTP_READ_WRITE 1 + +extern void __VLTChangePermission (int); + +#ifdef VTV_DEBUG + +extern void __VLTInitSetSymbolDebug (void **, const void *, std::size_t); +extern void __VLTRegisterPairDebug (void **, const void *, const char *, + const char *); +extern const void *__VLTVerifyVtablePointerDebug (void **, const void *, + const char *, const char *); + +#else + +extern void __VLTRegisterPair (void **, const void *); +extern const void *__VLTVerifyVtablePointer (void **, const void *); +extern void __VLTInitSetSymbol (void **, const void *, std::size_t ); + +#endif + +#endif /* _VTV_RTS_H */ diff --git a/libstdc++-v3/libsupc++/vtv_set.h b/libstdc++-v3/libsupc++/vtv_set.h new file mode 100644 index 00000000000..81bbba3fbe4 --- /dev/null +++ b/libstdc++-v3/libsupc++/vtv_set.h @@ -0,0 +1,652 @@ +/* Copyright (C) 2012 + Free Software Foundation + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GCC is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License + and a copy of the GCC Runtime Library Exception along with this + program; see the files COPYING3 and COPYING.RUNTIME respectively. + If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef _VTV_SET_H +#define _VTV_SET_H 1 + +/* Code in this file manages a collection of insert-only sets. We + have only tested the case where Key is uintptr_t, though it + theoretically should work for some other cases. All odd keys are + reserved, and must not be inserted into any of the sets. This code + is intended primarily for sets of pointers, and the code is + optimized for small sets (including size 0 and 1), but regardless + of the set size, insert() and contains() have close to O(1) speed + in practice. + + TODO(gpike): fix this comment. + + Recommended multithreaded use of a set: + + For speed, we want to use a lock-free test for set membership. The + code handles simultaneous reads and inserts, as long as at most one + insertion is in progress at a time. After an insert, other threads + may not immediately "see" the inserted key if they perform a + lock-free read, so we recommend retrying, as explained below. + + Also, to make data corruption less likely, we recommend using a + "normal" RW page as well as one or pages that are typically RO + but that can be switched to RW and back as needed. The latter + pages should contain sets. The former should contain a lock, L, + and an int or similar, num_writers. Then, to insert, something + like this would be safe: + o Acquire L. + o Increment num_writers; if that made it 1, change pages to RW. + o Release L. + o while (there are insertions to do in some set, S) { + acquire L; + do some insertions in S; + release L; + } + o Acquire L. + o Decrement num_writers; if that made it 0, change pages to RO. + o Release L. + + And to check if the set contains some key, one could use + set.contains(key) || + ({ Acquire L; bool b = set.contains(key); Release L; b; }) + + In this scheme, the number of threads with reads in progress isn't + tracked, so old sets can never be deleted. In addition, on some + architectures the intentionally racy reads might cause contains() + to return true when it should have returned false. This should be + no problem on x86, and most other machines, where reading or + writing an aligned uintptr_t is atomic. E.g., on those machines, + if *p is 0 and one thread does *p = x while another reads *p, the + read will see either 0 or x. + + To make the above easier, the insert_only_hash_sets class provides + an interface to manipulate any number of hash sets. One shouldn't + create objects of that class, as it has no member data and its + methods are static. + + So the recommended model is to have a single lock, a single + num_writers variable, and some number of sets. If lock contention + becomes a problem then the sets can be divided into k groups, each + of which has a lock and a num_writers variable; or each set can be + represented as a set of values that equal 0 mod m, a set of values + that equal 1 mod m, ..., plus a set of values that equal m-1 mod m. + + However, we expect most or all uses of this code to call contains() + much more frequently than anything else, so lock contention is + likely to be low. */ + +#include <algorithm> + +#ifndef HASHTABLE_STATS +#define HASHTABLE_STATS 0 +#endif + +#ifndef HASHTABLE_STATS_ATOMIC +#define HASHTABLE_STATS_ATOMIC 0 +#endif + +#if HASHTABLE_STATS +#if HASHTABLE_STATS_ATOMIC +/* Stat counters, with atomics. */ +#include <bits/atomic_word.h> + +typedef _Atomic_word _AtomicStatCounter; + +void +inc_by (_AtomicStatCounter &stat, int amount) +{ + __atomic_add_fetch (&stat, amount, __ATOMIC_ACQ_REL); +} + +#else + +/* Stat counters, but without atomics. */ +typedef int _AtomicStatCounter; + +void +inc_by (_AtomicStatCounter& stat, int amount) +{ + stat += amount; +} + +#endif + + +/* Number of calls to contains(), insert(), etc. */ +extern _AtomicStatCounter stat_insert; +extern _AtomicStatCounter stat_contains; +extern _AtomicStatCounter stat_resize; +extern _AtomicStatCounter stat_create; + +/* Sum of set size over all calls to contains(). */ +extern _AtomicStatCounter stat_contains_sizes; + +/* contains() calls in a set whose capacity is more than 1. */ +extern _AtomicStatCounter stat_contains_in_non_trivial_set; + +/* Probes in a set whose capacity is more than 1. Ideally, this will + be pretty close to stat_contains_in_non_trivial_set. That will + happen if our hash function is good and/or important keys were + inserted before unimportant keys. */ +extern _AtomicStatCounter stat_probes_in_non_trivial_set; + +/* number of calls to contains() with size=0, 1, etc. */ +extern _AtomicStatCounter stat_contains_size0; +extern _AtomicStatCounter stat_contains_size1; +extern _AtomicStatCounter stat_contains_size2; +extern _AtomicStatCounter stat_contains_size3; +extern _AtomicStatCounter stat_contains_size4; +extern _AtomicStatCounter stat_contains_size5; +extern _AtomicStatCounter stat_contains_size6; +extern _AtomicStatCounter stat_contains_size7; +extern _AtomicStatCounter stat_contains_size8; +extern _AtomicStatCounter stat_contains_size9; +extern _AtomicStatCounter stat_contains_size10; +extern _AtomicStatCounter stat_contains_size11; +extern _AtomicStatCounter stat_contains_size12; +extern _AtomicStatCounter stat_contains_size13_or_more; +extern _AtomicStatCounter stat_grow_from_size0_to_1; +extern _AtomicStatCounter stat_grow_from_size1_to_2; +extern _AtomicStatCounter stat_double_the_number_of_buckets; +extern _AtomicStatCounter stat_insert_key_that_was_already_present; + +/* Hash collisions detected during insert_no_resize(). Only counts + hasher(k) == hasher(k'); hasher(k) % tablesize == hasher(k') % + tablesize is not sufficient. Will count collisions that are + detected during table resizes etc., so the same two keys may add to + this stat multiple times. */ +extern _AtomicStatCounter stat_insert_found_hash_collision; + +#include <string> + +struct insert_only_hash_sets_logger +{ + static char * + log (char c, char *buf) + { + *buf++ = c; + return buf; + } + + static char * + log (const char *s, char *buf) + { return strcpy (buf, s) + strlen (s); } + + static char * + log (_AtomicStatCounter i, char *buf) + { + if (i < 10) + return log ((char) ('0' + i), buf); + else + return log ((char) ('0' + i % 10), log (i / 10, buf)); + } + + static char * + log (const char *label, _AtomicStatCounter i, char *buf) + { + buf = log (label, buf); + buf = log (": ", buf); + buf = log (i, buf); + return log ('\n', buf); + } +}; + +// Write stats to the given buffer, which should be at least 4000 bytes. +static inline void +insert_only_hash_tables_stats (char *buf) +{ + buf = insert_only_hash_sets_logger::log ("insert", stat_insert, buf); + buf = insert_only_hash_sets_logger::log ("contains", stat_contains, buf); + buf = insert_only_hash_sets_logger::log ("resize", stat_resize, buf); + buf = insert_only_hash_sets_logger::log ("create", stat_create, buf); + buf = insert_only_hash_sets_logger::log ("insert_key_that_was_already_" + "present", + stat_insert_key_that_was_already_present, + buf); + buf = insert_only_hash_sets_logger::log ("contains_sizes", + stat_contains_sizes, buf); + buf = insert_only_hash_sets_logger::log ("contains_in_non_trivial_set", + stat_contains_in_non_trivial_set, + buf); + buf = insert_only_hash_sets_logger::log ("probes_in_non_trivial_set", + stat_probes_in_non_trivial_set, + buf); + buf = insert_only_hash_sets_logger::log ("contains_size0", + stat_contains_size0, buf); + buf = insert_only_hash_sets_logger::log ("contains_size1", + stat_contains_size1, buf); + buf = insert_only_hash_sets_logger::log ("contains_size2", + stat_contains_size2, buf); + buf = insert_only_hash_sets_logger::log ("contains_size3", + stat_contains_size3, buf); + buf = insert_only_hash_sets_logger::log ("contains_size4", + stat_contains_size4, buf); + buf = insert_only_hash_sets_logger::log ("contains_size5", + stat_contains_size5, buf); + buf = insert_only_hash_sets_logger::log ("contains_size6", + stat_contains_size6, buf); + buf = insert_only_hash_sets_logger::log ("contains_size7", + stat_contains_size7, buf); + buf = insert_only_hash_sets_logger::log ("contains_size8", + stat_contains_size8, buf); + buf = insert_only_hash_sets_logger::log ("contains_size9", + stat_contains_size9, buf); + buf = insert_only_hash_sets_logger::log ("contains_size10", + stat_contains_size10, buf); + buf = insert_only_hash_sets_logger::log ("contains_size11", + stat_contains_size11, buf); + buf = insert_only_hash_sets_logger::log ("contains_size12", + stat_contains_size12, buf); + buf = insert_only_hash_sets_logger::log ("contains_size13_or_more", + stat_contains_size13_or_more, buf); + buf = insert_only_hash_sets_logger::log ("grow_from_size0_to_1", + stat_grow_from_size0_to_1, buf); + buf = insert_only_hash_sets_logger::log ("grow_from_size1_to_2", + stat_grow_from_size1_to_2, buf); + buf = insert_only_hash_sets_logger::log ("insert_found_hash_collision", + stat_insert_found_hash_collision, + buf); + buf = insert_only_hash_sets_logger::log ("double_the_number_of_buckets", + stat_double_the_number_of_buckets, + buf); + *buf = '\0'; +} + +#else + +/* No stats. */ +#define inc_by(statname, amount) do { } while (false && (amount)) + +#endif + +#define inc(statname) inc_by (statname, 1) + +template <typename Key, class HashFcn, class Alloc> +class insert_only_hash_sets +{ + public: + typedef Key key_type; + typedef size_t size_type; + typedef Alloc alloc_type; + enum { illegal_key = 1 }; + enum { min_capacity = 4 }; +#if HASHTABLE_STATS + enum { stats = true }; +#else + enum { stats = false }; +#endif + + /* Do not directly use insert_only_hash_set. Instead, use the + static methods below to create and manipulate objects of the + following class. + + Implementation details: each set is represented by a pointer + plus, perhaps, out-of-line data, which would be an object of type + insert_only_hash_set. For a pointer, s, the interpretation is: s + == NULL means empty set, lsb(s) == 1 means a set with one + element, which is (uintptr_t)s - 1, and otherwise s is a pointer + of type insert_only_hash_set*. So, to increase the size of a set + we have to change s and/or *s. To check if a set contains some + key we have to examine s and possibly *s. */ + class insert_only_hash_set + { + public: + /* Insert a key. The key must not be a reserved key. */ + static inline insert_only_hash_set *insert (key_type key, + insert_only_hash_set *s); + + + /* Create an empty set. */ + static inline insert_only_hash_set *create (size_type capacity); + + /* Return whether the given key is present. If key is illegal_key + then either true or false may be returned, but for all other + reserved keys false will be returned. */ + static bool + contains (key_type key, const insert_only_hash_set *s) + { + if (stats) + { + inc (stat_contains); + switch (size (s)) + { + case 0: inc (stat_contains_size0); break; + case 1: inc (stat_contains_size1); break; + case 2: inc (stat_contains_size2); break; + case 3: inc (stat_contains_size3); break; + case 4: inc (stat_contains_size4); break; + case 5: inc (stat_contains_size5); break; + case 6: inc (stat_contains_size6); break; + case 7: inc (stat_contains_size7); break; + case 8: inc (stat_contains_size8); break; + case 9: inc (stat_contains_size9); break; + case 10: inc (stat_contains_size10); break; + case 11: inc (stat_contains_size11); break; + case 12: inc (stat_contains_size12); break; + default: inc (stat_contains_size13_or_more); break; + } + inc_by (stat_contains_sizes, size (s)); + } + + return (singleton (s) ? + singleton_key (key) == s : + ((s != NULL) && s->contains (key))); + } + + /* Return a set's size. */ + static size_type + size (const insert_only_hash_set *s) + { return (s == NULL) ? 0 : (singleton (s) ? 1 : s->num_entries); } + + static inline insert_only_hash_set *resize (size_type target_num_buckets, + insert_only_hash_set *s); + + + private: + /* Return whether a set has size 1. */ + static bool + singleton (const insert_only_hash_set *s) + { return (uintptr_t) s & 1; } + + /* Return the representation of a singleton set containing the + given key. */ + static insert_only_hash_set * + singleton_key (key_type key) + { return (insert_only_hash_set *) ((uintptr_t) key + 1); } + + /* Given a singleton set, what key does it contain? */ + static key_type + extract_singleton_key (const insert_only_hash_set *s) + { + VTV_DEBUG_ASSERT (singleton (s)); + return (key_type) ((uintptr_t) s - 1); + } + + volatile key_type & + key_at_index (size_type index) + { return buckets[index]; } + + key_type + key_at_index (size_type index) const + { return buckets[index]; } + + size_type + next_index (size_type index, size_type indices_examined) const + { return (index + indices_examined) & (num_buckets - 1); } + + inline void insert_no_resize (key_type key); + + inline bool contains (key_type key) const; + + inline insert_only_hash_set *resize_if_necessary (void); + + size_type num_buckets; /* Must be a power of 2 not less than + min_capacity. */ + volatile size_type num_entries; + volatile key_type buckets[0]; /* Actual array size is num_buckets. */ + }; + + /* Create an empty set with the given capacity. Requires that n be + 0 or a power of 2. If 1 < n < min_capacity then treat n as + min_capacity. Sets *handle. Returns true unless the allocator + fails. Subsequent operations on this set should use the same + handle. */ + + static inline bool create (size_type n, insert_only_hash_set **handle); + + /* Force the capacity of a set to be n, unless it was more than n + already. Requires that n be 0 or a power of 2. Sets *handle + unless the current capacity is n or more. Returns true unless + the allocator fails. */ + + static inline bool resize (size_type n, insert_only_hash_set **handle); + + /* Insert a key. *handle is unmodified unless (1) a resize occurs, + or (2) the set was initially empty. Returns true unless the + allocator fails during a resize. If the allocator fails during a + resize then the set is reset to be the empty set. The key must + not be a reserved key. */ + + static inline bool insert (key_type key, insert_only_hash_set **handle); + + /* Check for the presence of a key. If key is illegal_key then + either true or false may be returned, but for all other reserved + keys false will be returned. */ + + static inline bool + contains (key_type key, /* const */ insert_only_hash_set **handle) + { return insert_only_hash_set::contains (key, *handle); } + + /* Return the size of the given set. */ + static size_type + size (const insert_only_hash_set **handle) + { return insert_only_hash_set::size (*handle); } + + static bool + is_reserved_key (key_type key) + { return ((uintptr_t) key % 2) == 1; } +}; + +template <typename Key, class HashFcn, class Alloc> +typename insert_only_hash_sets <Key, HashFcn, Alloc>::insert_only_hash_set * +insert_only_hash_sets <Key, HashFcn, Alloc>::insert_only_hash_set::resize + (size_type n, insert_only_hash_set *s) +{ + if (s == NULL) + return create (n); + + size_type capacity = singleton (s) ? 1 : s->num_buckets; + + if (n <= capacity) + return s; + + insert_only_hash_set *result = + create (std::max<size_type> (n, min_capacity)); + if (result != NULL) + { + if (singleton (s)) + { + result->insert_no_resize (extract_singleton_key (s)); + } + else + { + for (size_type i = 0; i < s->num_buckets; i++) + if (s->buckets[i] != (key_type) illegal_key) + result->insert_no_resize (s->buckets[i]); + } + VTV_DEBUG_ASSERT (size (result) == size (s)); + } + return result; +} + +template <typename Key, class HashFcn, class Alloc> +typename insert_only_hash_sets <Key, HashFcn, Alloc>::insert_only_hash_set * +insert_only_hash_sets <Key, HashFcn, Alloc>::insert_only_hash_set::insert + (key_type key, insert_only_hash_set *s) +{ + VTV_DEBUG_ASSERT (!is_reserved_key (key)); + inc_by (stat_grow_from_size0_to_1, s == NULL); + + if (s == NULL) + return singleton_key (key); + + if (singleton (s)) + { + const key_type old_key = extract_singleton_key (s); + if (old_key == key) + return s; + + /* Grow from size 1 to size 2. */ + inc (stat_grow_from_size1_to_2); + s = create (2); + if (s == NULL) + return NULL; + + s->insert_no_resize (old_key); + s->insert_no_resize (key); + VTV_DEBUG_ASSERT (size (s) == 2); + return s; + } + s = s->resize_if_necessary(); + if (s != NULL) + s->insert_no_resize (key); + return s; +} + +template <typename Key, class HashFcn, class Alloc> +typename insert_only_hash_sets <Key, HashFcn, Alloc>::insert_only_hash_set * +insert_only_hash_sets <Key, HashFcn, Alloc>::insert_only_hash_set::create + (size_type capacity) +{ + if (capacity <= 1) + return NULL; + + VTV_DEBUG_ASSERT (capacity > 1 && (capacity & (capacity - 1)) == 0); + VTV_DEBUG_ASSERT (sizeof (insert_only_hash_set) == 2 * sizeof (size_type)); + capacity = std::max <size_type> (capacity, min_capacity); + const size_t num_bytes = sizeof (insert_only_hash_set) + + sizeof (key_type) * capacity; + alloc_type alloc; + insert_only_hash_set *result = (insert_only_hash_set *) alloc (num_bytes); + result->num_buckets = capacity; + result->num_entries = 0; + for (size_type i = 0; i < capacity; i++) + result->buckets[i] = (key_type) illegal_key; + return result; +} + +template <typename Key, class HashFcn, class Alloc> +void +insert_only_hash_sets<Key, HashFcn, + Alloc>::insert_only_hash_set::insert_no_resize + (key_type key) +{ + HashFcn hasher; + const size_type capacity = num_buckets; + VTV_DEBUG_ASSERT (capacity >= min_capacity); + VTV_DEBUG_ASSERT (!is_reserved_key (key)); + size_type index = hasher (key) & (capacity - 1); + key_type k = key_at_index (index); + size_type indices_examined = 0; + while (k != key) + { + ++indices_examined; + if (k == (key_type) illegal_key) + { + key_at_index (index) = key; + ++num_entries; + return; + } + else + { + inc_by (stat_insert_found_hash_collision, + hasher (k) == hasher (key)); + } + VTV_DEBUG_ASSERT (indices_examined < capacity); + index = next_index (index, indices_examined); + k = key_at_index (index); + } +} + +template<typename Key, class HashFcn, class Alloc> +bool +insert_only_hash_sets<Key, HashFcn, Alloc>::insert_only_hash_set::contains + (key_type key) const +{ + inc (stat_contains_in_non_trivial_set); + HashFcn hasher; + const size_type capacity = num_buckets; + size_type index = hasher (key) & (capacity - 1); + key_type k = key_at_index (index); + size_type indices_examined = 0; + inc (stat_probes_in_non_trivial_set); + while (k != key) + { + ++indices_examined; + if (/*UNLIKELY*/(k == (key_type) illegal_key + || indices_examined == capacity)) + return false; + + index = next_index (index, indices_examined); + k = key_at_index (index); + inc (stat_probes_in_non_trivial_set); + } + return true; +} + +template <typename Key, class HashFcn, class Alloc> +typename insert_only_hash_sets <Key, HashFcn, Alloc>::insert_only_hash_set * + insert_only_hash_sets<Key, HashFcn, + Alloc>::insert_only_hash_set::resize_if_necessary (void) +{ + VTV_DEBUG_ASSERT (num_buckets >= min_capacity); + size_type unused = num_buckets - num_entries; + if (unused < (num_buckets >> 2)) + { + inc (stat_double_the_number_of_buckets); + size_type new_num_buckets = num_buckets * 2; + insert_only_hash_set *s = create (new_num_buckets); + for (size_type i = 0; i < num_buckets; i++) + if (buckets[i] != (key_type) illegal_key) + s->insert_no_resize (buckets[i]); + VTV_DEBUG_ASSERT (size (this) == size (s)); + return s; + } + else + return this; +} + +template<typename Key, class HashFcn, class Alloc> +bool +insert_only_hash_sets<Key, HashFcn, Alloc>::create (size_type n, + insert_only_hash_set **handle) + +{ + inc (stat_create); + *handle = insert_only_hash_set::create (n); + return (n <= 1) || (*handle != NULL); +} + +template<typename Key, class HashFcn, class Alloc> +bool +insert_only_hash_sets<Key, HashFcn, Alloc>::resize (size_type n, + insert_only_hash_set **handle) +{ + inc (stat_resize); + *handle = insert_only_hash_set::resize (n, *handle); + return (n <= 1) || (*handle != NULL); +} + +template<typename Key, class HashFcn, class Alloc> +bool +insert_only_hash_sets<Key, HashFcn, Alloc>::insert (key_type key, + insert_only_hash_set **handle) +{ + inc (stat_insert); + const size_type old_size = insert_only_hash_set::size (*handle); + *handle = insert_only_hash_set::insert (key, *handle); + if (*handle != NULL) + { + const size_type delta = insert_only_hash_set::size (*handle) - old_size; + inc_by (stat_insert_key_that_was_already_present, delta == 0); + } + return *handle != NULL; +} + +#endif /* VTV_SET_H */ diff --git a/libstdc++-v3/libsupc++/vtv_stubs.cc b/libstdc++-v3/libsupc++/vtv_stubs.cc new file mode 100644 index 00000000000..7b50db39a81 --- /dev/null +++ b/libstdc++-v3/libsupc++/vtv_stubs.cc @@ -0,0 +1,97 @@ +/* Copyright (C) 2012 + Free Software Foundation + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GCC is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + <http://www.gnu.org/licenses/>. */ + +#include "vtv_rts.h" + +/* The is part of the vtable verification runtime library. For more + information about this feature, see the comments in vtv_rts.cc. */ + +/* The functions in this file are used to create the libvtv_stubs + library, as part of the vtable verification feature. When building + a binary without vtable verification, and linking it with a + (possibly pre-built third-party) library that was built with + verification, it is possible that vtable verification will fail due + to incomplete data (rather than due to corrupt vtable pointers). In + this case we need to give programmers a way of turning off the + vtable verification in their libraries. They can do so by linking + with the libvtv_stubs library, which (as you can see) will replace + the real verification functions with a set of functions that do + nothing (so no more verification failures/aborts). */ + +void +__VLTChangePermission (int perm __attribute__((__unused__))) +{ +} + +#ifdef VTV_DEBUG + +void __VLTInitSetSymbolDebug + (void **set_handle_ptr __attribute__((__unused__)), + const void *set_symbol_key __attribute__((__unused__)), + std::size_t size_hint __attribute__((__unused__))) +{ +} + +void +__VLTRegisterPairDebug (void **set_handle_ptrr __attribute__((__unused__)), + const void *vtable_ptr __attribute__((__unused__)), + const char *set_symbol_name __attribute__((__unused__)), + const char *vtable_name __attribute__((__unused__))) +{ +} + +const void * +__VLTVerifyVtablePointerDebug + (void **set_handle_ptr __attribute__((__unused__)), + const void *vtable_ptr, + const char *set_symbol_name __attribute__((__unused__)), + const char *vtable_name __attribute__((__unused__))) + +{ + return vtable_ptr; +} + +#else + +void __VLTInitSetSymbol + (void **set_handle_ptr __attribute__((__unused__)), + const void *set_symbol_key __attribute__((__unused__)), + std::size_t size_hint __attribute__((__unused__))) +{ +} + +void +__VLTRegisterPair (void **set_handle_ptr __attribute__((__unused__)), + const void *vtable_ptr __attribute__((__unused__))) +{ +} + +const void * +__VLTVerifyVtablePointer (void **set_handle_ptr __attribute__((__unused__)), + const void *vtable_ptr) +{ + return vtable_ptr; +} + +#endif diff --git a/libstdc++-v3/libsupc++/vtv_utils.cc b/libstdc++-v3/libsupc++/vtv_utils.cc new file mode 100644 index 00000000000..2dddb401fa2 --- /dev/null +++ b/libstdc++-v3/libsupc++/vtv_utils.cc @@ -0,0 +1,122 @@ +/* Copyright (C) 2012 + Free Software Foundation + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GCC is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + <http://www.gnu.org/licenses/>. */ + +/* This file is part of the vtable verication runtime library (see + comments in vtv_rts.cc for more information about vtable + verification). This file contains log file utilities. */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> + +#include "vtv_utils.h" + +/* This is the directory into which all vtable verication log files + get written, if possible. */ +static const char * const logs_dir = "/var/log/chrome/vtv_logs"; + + +/* This is the directory into which the vtable verification log files + will get written if they can't be written to the directory + above. */ +static const char * const alt_logs_dir = "/tmp/vtv_logs"; + + +/* This function takes the NAME of a log file to open, attempts to + open it in the logs_dir directory, and returns the resulting file + decriptor. */ + +int +vtv_open_log (const char *name) +{ + /* Try to create the logs under /var/log/chrome first, which is + persistent across reboots. This location only exists on + ChromeOS. This code should not be commited upstream GCC. */ + char log_name[256]; + snprintf (log_name, sizeof (log_name), "%s/%s", logs_dir, name); + mkdir (logs_dir, S_IRWXU); + int fd = open (log_name, O_WRONLY | O_APPEND | O_CREAT, S_IRWXU); + if (fd != -1) + return fd; + + /* Otherwise, try to open in /tmp. */ + snprintf(log_name, sizeof(log_name), "%s/%s", alt_logs_dir, name); + mkdir(alt_logs_dir, S_IRWXU); + fd = open(log_name, O_WRONLY | O_APPEND | O_CREAT, S_IRWXU); + if (fd == -1) + vtv_add_to_log (2, "Cannot open log file %s %s\n", name, + strerror (errno)); + return fd; +} + +/* This function takes a file descriptor (FD) and a string (STR) and + tries to write the string to the file. */ + +static int +vtv_log_write (int fd, const char *str) +{ + if (write (fd, str, strlen (str)) != -1) + return 0; + + if (fd != 2) /* Make sure we dont get in a loop. */ + vtv_add_to_log (2, "Error writing to log: %s\n", strerror (errno)); + return -1; +} + + +/* This function takes a file decriptor (LOG_FILE) and an output + format string (FORMAT), followed by zero or more print format + arguments (the same as fprintf, for example). It gets the current + process ID and PPID, pre-pends them to the formatted message, and + writes write it out to the log file referenced by LOG_FILE via calles + to vtv_log_write. */ + +int +vtv_add_to_log (int log_file, const char * format, ...) +{ + /* We dont want to dynamically allocate this buffer. This should be + more than enough in most cases. It if isn't we are careful not to + do a buffer overflow. */ + char output[1024]; + + va_list ap; + va_start (ap, format); + + snprintf (output, sizeof (output), "VTV: PID=%d PPID=%d ", getpid (), + getppid ()); + vtv_log_write (log_file, output); + vsnprintf (output, sizeof (output), format, ap); + vtv_log_write (log_file, output); + + /* fdatasync is quite expensive. Only enable if you suspect you are + loosing log data in in a program crash? */ + /* fdatasync(log_file); */ + + return 0; +} diff --git a/libstdc++-v3/libsupc++/vtv_utils.h b/libstdc++-v3/libsupc++/vtv_utils.h new file mode 100644 index 00000000000..962274c331c --- /dev/null +++ b/libstdc++-v3/libsupc++/vtv_utils.h @@ -0,0 +1,55 @@ +/* Copyright (C) 2012 + Free Software Foundation + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GCC is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + <http://www.gnu.org/licenses/>. */ + +#ifndef _VTV_UTILS_H +#define _VTV_UTILS_H 1 + +#include <stdlib.h> + +/* Handler for verification runtime errors. */ +#define VTV_error abort + +/* Assertion macros used in vtable verification runtime. */ +#define VTV_ASSERT(EXPR) \ + if (!(EXPR)) VTV_error(); + +#ifdef VTV_DEBUG +#define VTV_DEBUG_ASSERT(EXPR) ((bool) (!(EXPR)) ? VTV_error() : (void) 0) +#else +#define VTV_DEBUG_ASSERT(EXPR) ((void) 0) +#endif + +/* Name of the section where we put general VTV variables for protection */ +#define VTV_PROTECTED_VARS_SECTION ".vtable_map_vars" +#define VTV_PROTECTED_VAR \ + __attribute__ ((section (VTV_PROTECTED_VARS_SECTION))) + +/* The following logging routines try to use low level file access + routines and avoid calling malloc. We need this so that we dont + disturb the order of calls to dlopen. Changing the order of dlopen + calls may lead to deadlocks */ +int vtv_open_log (const char * name); +int vtv_add_to_log (int log, const char * format, ...); + +#endif /* VTV_UTILS_H */ diff --git a/libstdc++-v3/src/Makefile.am b/libstdc++-v3/src/Makefile.am index cc17c962eae..6c005f2c4c3 100644 --- a/libstdc++-v3/src/Makefile.am +++ b/libstdc++-v3/src/Makefile.am @@ -73,10 +73,23 @@ libstdc___la_DEPENDENCIES = \ $(top_builddir)/src/c++98/libc++98convenience.la \ $(top_builddir)/src/c++11/libc++11convenience.la +if ENABLE_VTABLE_VERIFY libstdc___la_LDFLAGS = \ - -version-info $(libtool_VERSION) ${version_arg} -lm + -version-info $(libtool_VERSION) ${version_arg} -lm -Wl,-u_vtable_map_vars_start,-u_vtable_map_vars_end libstdc___la_LINK = $(CXXLINK) $(libstdc___la_LDFLAGS) +libvtv___la_LIBADD = \ + $(top_builddir)/libsupc++/.libs +LIBVTV_FLAGS = \ + -L$(libvtv___la_LIBADD) --whole-archive -lvtv_init --no-whole-archive +else +libstdc___la_LDFLAGS = \ + -version-info $(libtool_VERSION) ${version_arg} -lm +libstdc___la_LINK = $(CXXLINK) $(libstdc___la_LDFLAGS) + +libvtv___la_LIBADD = +LIBVTV_FLAGS = +endif # Use special rules for parallel mode compilation. @@ -193,6 +206,7 @@ CXXLINK = \ $(LIBTOOL) --tag CXX \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ --mode=link $(CXX) \ + $(LIBVTV_FLAGS) \ $(OPT_LDFLAGS) $(SECTION_LDFLAGS) $(AM_CXXFLAGS) $(LTLDFLAGS) -o $@ diff --git a/libstdc++-v3/src/Makefile.in b/libstdc++-v3/src/Makefile.in index ab989e8d800..d8bb653f546 100644 --- a/libstdc++-v3/src/Makefile.in +++ b/libstdc++-v3/src/Makefile.in @@ -368,10 +368,22 @@ libstdc___la_DEPENDENCIES = \ $(top_builddir)/src/c++98/libc++98convenience.la \ $(top_builddir)/src/c++11/libc++11convenience.la -libstdc___la_LDFLAGS = \ - -version-info $(libtool_VERSION) ${version_arg} -lm +@ENABLE_VTABLE_VERIFY_FALSE@libstdc___la_LDFLAGS = \ +@ENABLE_VTABLE_VERIFY_FALSE@ -version-info $(libtool_VERSION) ${version_arg} -lm + +@ENABLE_VTABLE_VERIFY_TRUE@libstdc___la_LDFLAGS = \ +@ENABLE_VTABLE_VERIFY_TRUE@ -version-info $(libtool_VERSION) ${version_arg} -lm -Wl,-u_vtable_map_vars_start,-u_vtable_map_vars_end + +@ENABLE_VTABLE_VERIFY_FALSE@libstdc___la_LINK = $(CXXLINK) $(libstdc___la_LDFLAGS) +@ENABLE_VTABLE_VERIFY_TRUE@libstdc___la_LINK = $(CXXLINK) $(libstdc___la_LDFLAGS) +@ENABLE_VTABLE_VERIFY_FALSE@libvtv___la_LIBADD = +@ENABLE_VTABLE_VERIFY_TRUE@libvtv___la_LIBADD = \ +@ENABLE_VTABLE_VERIFY_TRUE@ $(top_builddir)/libsupc++/.libs + +@ENABLE_VTABLE_VERIFY_FALSE@LIBVTV_FLAGS = +@ENABLE_VTABLE_VERIFY_TRUE@LIBVTV_FLAGS = \ +@ENABLE_VTABLE_VERIFY_TRUE@ -L$(libvtv___la_LIBADD) --whole-archive -lvtv_init --no-whole-archive -libstdc___la_LINK = $(CXXLINK) $(libstdc___la_LDFLAGS) # Use special rules for parallel mode compilation. PARALLEL_FLAGS = -fopenmp -D_GLIBCXX_PARALLEL -I$(glibcxx_builddir)/../libgomp @@ -454,6 +466,7 @@ CXXLINK = \ $(LIBTOOL) --tag CXX \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ --mode=link $(CXX) \ + $(LIBVTV_FLAGS) \ $(OPT_LDFLAGS) $(SECTION_LDFLAGS) $(AM_CXXFLAGS) $(LTLDFLAGS) -o $@ @ENABLE_SYMVERS_TRUE@CLEANFILES = libstdc++-symbols.ver $(version_dep) diff --git a/libstdc++-v3/src/c++11/Makefile.am b/libstdc++-v3/src/c++11/Makefile.am index 14a412711c6..e5eb30f5a61 100644 --- a/libstdc++-v3/src/c++11/Makefile.am +++ b/libstdc++-v3/src/c++11/Makefile.am @@ -61,6 +61,18 @@ vpath % $(top_srcdir)/src/c++11 libc__11convenience_la_SOURCES = $(sources) $(inst_sources) +if ENABLE_VTABLE_VERIFY +EXTRA_VTV_LDFLAGS = \ + -Wl,-u_vtable_map_vars_start,-u_vtable_map_vars_end +LIBVTV_FLAGS = \ + -L$(top_builddir)/libsupc++/.libs --whole-archive -lvtv_init --no-whole-archive +VTV_CXXFLAGS = -fvtable-verify=std $(EXTRA_VTV_LDFLAGS) +else +EXTRA_VTV_LDFLAGS = +LIBVTV_FLAGS = +VTV_CXXFLAGS = +endif + # AM_CXXFLAGS needs to be in each subdirectory so that it can be # modified in a per-library or per-sub-library way. Need to manually # set this option because CONFIG_CXXFLAGS has to be after @@ -69,7 +81,7 @@ libc__11convenience_la_SOURCES = $(sources) $(inst_sources) AM_CXXFLAGS = \ -std=gnu++11 \ $(glibcxx_lt_pic_flag) $(glibcxx_compiler_shared_flag) \ - $(XTEMPLATE_FLAGS) \ + $(XTEMPLATE_FLAGS) $(VTV_CXXFLAGS) \ $(WARN_CXXFLAGS) $(OPTIMIZE_CXXFLAGS) $(CONFIG_CXXFLAGS) \ $($(@)_no_omit_frame_pointer) @@ -120,6 +132,7 @@ CXXLINK = \ $(LIBTOOL) --tag CXX --tag disable-shared \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ --mode=link $(CXX) \ + $(LIBVTV_FLAGS) \ $(OPT_LDFLAGS) $(SECTION_LDFLAGS) $(AM_CXXFLAGS) $(LTLDFLAGS) -o $@ # Google-specific pessimization diff --git a/libstdc++-v3/src/c++11/Makefile.in b/libstdc++-v3/src/c++11/Makefile.in index be91b30acf3..bf59ed88b17 100644 --- a/libstdc++-v3/src/c++11/Makefile.in +++ b/libstdc++-v3/src/c++11/Makefile.in @@ -325,6 +325,16 @@ sources = \ @ENABLE_EXTERN_TEMPLATE_TRUE@ wstring-inst.cc libc__11convenience_la_SOURCES = $(sources) $(inst_sources) +@ENABLE_VTABLE_VERIFY_FALSE@EXTRA_VTV_LDFLAGS = +@ENABLE_VTABLE_VERIFY_TRUE@EXTRA_VTV_LDFLAGS = \ +@ENABLE_VTABLE_VERIFY_TRUE@ -Wl,-u_vtable_map_vars_start,-u_vtable_map_vars_end + +@ENABLE_VTABLE_VERIFY_FALSE@LIBVTV_FLAGS = +@ENABLE_VTABLE_VERIFY_TRUE@LIBVTV_FLAGS = \ +@ENABLE_VTABLE_VERIFY_TRUE@ -L$(top_builddir)/libsupc++/.libs --whole-archive -lvtv_init --no-whole-archive + +@ENABLE_VTABLE_VERIFY_FALSE@VTV_CXXFLAGS = +@ENABLE_VTABLE_VERIFY_TRUE@VTV_CXXFLAGS = -fvtable-verify=std $(EXTRA_VTV_LDFLAGS) # AM_CXXFLAGS needs to be in each subdirectory so that it can be # modified in a per-library or per-sub-library way. Need to manually @@ -334,7 +344,7 @@ libc__11convenience_la_SOURCES = $(sources) $(inst_sources) AM_CXXFLAGS = \ -std=gnu++11 \ $(glibcxx_lt_pic_flag) $(glibcxx_compiler_shared_flag) \ - $(XTEMPLATE_FLAGS) \ + $(XTEMPLATE_FLAGS) $(VTV_CXXFLAGS) \ $(WARN_CXXFLAGS) $(OPTIMIZE_CXXFLAGS) $(CONFIG_CXXFLAGS) \ $($(@)_no_omit_frame_pointer) @@ -386,6 +396,7 @@ CXXLINK = \ $(LIBTOOL) --tag CXX --tag disable-shared \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ --mode=link $(CXX) \ + $(LIBVTV_FLAGS) \ $(OPT_LDFLAGS) $(SECTION_LDFLAGS) $(AM_CXXFLAGS) $(LTLDFLAGS) -o $@ diff --git a/libstdc++-v3/src/c++98/Makefile.am b/libstdc++-v3/src/c++98/Makefile.am index c153a59336d..89606bc82b6 100644 --- a/libstdc++-v3/src/c++98/Makefile.am +++ b/libstdc++-v3/src/c++98/Makefile.am @@ -143,6 +143,18 @@ vpath % $(top_srcdir)/src/c++98 libc__98convenience_la_SOURCES = $(sources) +if ENABLE_VTABLE_VERIFY +EXTRA_VTV_LDFLAGS = \ + -Wl,-u_vtable_map_vars_start,-u_vtable_map_vars_end +LIBVTV_FLAGS = \ + -L$(top_builddir)/libsupc++/.libs --whole-archive -lvtv_init --no-whole-archive +VTV_CXXFLAGS = -fvtable-verify=std $(EXTRA_VTV_LDFLAGS) +else +EXTRA_VTV_LDFLAGS = +LIBVTV_FLAGS = +VTV_CXXFLAGS = +endif + # Use special rules for the deprecated source files so that they find # deprecated include files. GLIBCXX_INCLUDE_DIR=$(glibcxx_builddir)/include @@ -173,7 +185,7 @@ parallel_settings.o: parallel_settings.cc # as the occasion calls for it. AM_CXXFLAGS = \ $(glibcxx_lt_pic_flag) $(glibcxx_compiler_shared_flag) \ - $(XTEMPLATE_FLAGS) \ + $(XTEMPLATE_FLAGS) $(VTV_CXXFLAGS) \ $(WARN_CXXFLAGS) $(OPTIMIZE_CXXFLAGS) $(CONFIG_CXXFLAGS) AM_MAKEFLAGS = \ @@ -223,4 +235,5 @@ CXXLINK = \ $(LIBTOOL) --tag CXX --tag disable-shared \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ --mode=link $(CXX) \ + $(LIBVTV_FLAGS) \ $(OPT_LDFLAGS) $(SECTION_LDFLAGS) $(AM_CXXFLAGS) $(LTLDFLAGS) -o $@ diff --git a/libstdc++-v3/src/c++98/Makefile.in b/libstdc++-v3/src/c++98/Makefile.in index c3173bc91f2..fb86e6655cb 100644 --- a/libstdc++-v3/src/c++98/Makefile.in +++ b/libstdc++-v3/src/c++98/Makefile.in @@ -388,6 +388,16 @@ sources = \ ${host_sources_extra} libc__98convenience_la_SOURCES = $(sources) +@ENABLE_VTABLE_VERIFY_FALSE@EXTRA_VTV_LDFLAGS = +@ENABLE_VTABLE_VERIFY_TRUE@EXTRA_VTV_LDFLAGS = \ +@ENABLE_VTABLE_VERIFY_TRUE@ -Wl,-u_vtable_map_vars_start,-u_vtable_map_vars_end + +@ENABLE_VTABLE_VERIFY_FALSE@LIBVTV_FLAGS = +@ENABLE_VTABLE_VERIFY_TRUE@LIBVTV_FLAGS = \ +@ENABLE_VTABLE_VERIFY_TRUE@ -L$(top_builddir)/libsupc++/.libs --whole-archive -lvtv_init --no-whole-archive + +@ENABLE_VTABLE_VERIFY_FALSE@VTV_CXXFLAGS = +@ENABLE_VTABLE_VERIFY_TRUE@VTV_CXXFLAGS = -fvtable-verify=std $(EXTRA_VTV_LDFLAGS) # Use special rules for the deprecated source files so that they find # deprecated include files. @@ -403,7 +413,7 @@ PARALLEL_FLAGS = -fopenmp -D_GLIBCXX_PARALLEL -I$(glibcxx_builddir)/../libgomp # as the occasion calls for it. AM_CXXFLAGS = \ $(glibcxx_lt_pic_flag) $(glibcxx_compiler_shared_flag) \ - $(XTEMPLATE_FLAGS) \ + $(XTEMPLATE_FLAGS) $(VTV_CXXFLAGS) \ $(WARN_CXXFLAGS) $(OPTIMIZE_CXXFLAGS) $(CONFIG_CXXFLAGS) AM_MAKEFLAGS = \ @@ -454,6 +464,7 @@ CXXLINK = \ $(LIBTOOL) --tag CXX --tag disable-shared \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ --mode=link $(CXX) \ + $(LIBVTV_FLAGS) \ $(OPT_LDFLAGS) $(SECTION_LDFLAGS) $(AM_CXXFLAGS) $(LTLDFLAGS) -o $@ all: all-am |