diff options
Diffstat (limited to 'libstdc++-v3/libsupc++')
-rw-r--r-- | libstdc++-v3/libsupc++/Makefile.am | 77 | ||||
-rw-r--r-- | libstdc++-v3/libsupc++/Makefile.in | 277 | ||||
-rw-r--r-- | libstdc++-v3/libsupc++/vtv_fail.h | 53 | ||||
-rw-r--r-- | libstdc++-v3/libsupc++/vtv_init.cc | 179 | ||||
-rw-r--r-- | libstdc++-v3/libsupc++/vtv_malloc.cc | 210 | ||||
-rw-r--r-- | libstdc++-v3/libsupc++/vtv_malloc.h | 49 | ||||
-rw-r--r-- | libstdc++-v3/libsupc++/vtv_map.h | 311 | ||||
-rw-r--r-- | libstdc++-v3/libsupc++/vtv_rts.cc | 1314 | ||||
-rw-r--r-- | libstdc++-v3/libsupc++/vtv_rts.h | 56 | ||||
-rw-r--r-- | libstdc++-v3/libsupc++/vtv_set.h | 652 | ||||
-rw-r--r-- | libstdc++-v3/libsupc++/vtv_stubs.cc | 97 | ||||
-rw-r--r-- | libstdc++-v3/libsupc++/vtv_utils.cc | 122 | ||||
-rw-r--r-- | libstdc++-v3/libsupc++/vtv_utils.h | 55 |
13 files changed, 3378 insertions, 74 deletions
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 */ |