Index: libstdc++-v3/configure =================================================================== --- libstdc++-v3/configure (revision 195903) +++ libstdc++-v3/configure (working copy) @@ -13205,8 +13205,8 @@ $as_echo "$ld_shlibs_CXX" >&6; } ## the running order or otherwise move them around unless you know exactly ## what you are doing... # Dependencies to place before and after the object being linked: -predep_objects_CXX= -postdep_objects_CXX= +predep_objects_CXX="${glibcxx_builddir}/../libgcc/vtv_start.o" +postdep_objects_CXX="${glibcxx_builddir}/../libgcc/vtv_end.o" predeps_CXX= postdeps_CXX= compiler_lib_search_path_CXX= Index: libstdc++-v3/src/Makefile.in =================================================================== --- libstdc++-v3/src/Makefile.in (revision 195903) +++ libstdc++-v3/src/Makefile.in (working copy) @@ -367,9 +367,15 @@ libstdc___la_DEPENDENCIES = \ $(top_builddir)/src/c++11/libc++11convenience.la libstdc___la_LDFLAGS = \ - -version-info $(libtool_VERSION) ${version_arg} -lm + -version-info $(libtool_VERSION) ${version_arg} -lm -Wl,-u_vtable_map_vars_start,-u_vtable_map_vars_end libstdc___la_LINK = $(CXXLINK) $(libstdc___la_LDFLAGS) +libvtv___la_LIBADD = \ + $(top_builddir)/libsupc++/.libs + +LIBVTV_FLAGS = \ + -L$(libvtv___la_LIBADD) --whole-archive -lvtv_init --no-whole-archive + # Use special rules for parallel mode compilation. PARALLEL_FLAGS = -fopenmp -D_GLIBCXX_PARALLEL -I$(glibcxx_builddir)/../libgomp Index: libstdc++-v3/src/Makefile.am =================================================================== --- libstdc++-v3/src/Makefile.am (revision 195903) +++ libstdc++-v3/src/Makefile.am (working copy) @@ -74,9 +74,13 @@ libstdc___la_DEPENDENCIES = \ $(top_builddir)/src/c++11/libc++11convenience.la libstdc___la_LDFLAGS = \ - -version-info $(libtool_VERSION) ${version_arg} -lm + -version-info $(libtool_VERSION) ${version_arg} -lm -Wl,-u_vtable_map_vars_start,-u_vtable_map_vars_end libstdc___la_LINK = $(CXXLINK) $(libstdc___la_LDFLAGS) +libvtv___la_LIBADD = \ + $(top_builddir)/libsupc++/.libs +LIBVTV_FLAGS = \ + -L$(libvtv___la_LIBADD) --whole-archive -lvtv_init --no-whole-archive # Use special rules for parallel mode compilation. @@ -160,7 +164,7 @@ LTLDFLAGS = $(shell $(SHELL) $(top_srcdi CXXLINK = \ $(LIBTOOL) --tag CXX \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ - --mode=link $(CXX) \ + --mode=link $(CXX) \ $(OPT_LDFLAGS) $(SECTION_LDFLAGS) $(AM_CXXFLAGS) $(LTLDFLAGS) -o $@ Index: libstdc++-v3/libsupc++/Makefile.in =================================================================== --- libstdc++-v3/libsupc++/Makefile.in (revision 195903) +++ libstdc++-v3/libsupc++/Makefile.in (working copy) @@ -102,7 +102,8 @@ am__objects_1 = array_type_info.lo atexi 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 + vmi_class_type_info.lo vterminate.lo vtv_rts.lo 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) libsupc___la_OBJECTS = $(am_libsupc___la_OBJECTS) @@ -110,6 +111,14 @@ libsupc__convenience_la_LIBADD = am_libsupc__convenience_la_OBJECTS = $(am__objects_1) $(am__objects_2) libsupc__convenience_la_OBJECTS = \ $(am_libsupc__convenience_la_OBJECTS) +libvtv_init_la_LIBADD = +am__objects_3 = vtv_init.lo +am_libvtv_init_la_OBJECTS = $(am__objects_3) +libvtv_init_la_OBJECTS = $(am_libvtv_init_la_OBJECTS) +libvtv_stubs_la_LIBADD = +am__objects_4 = vtv_stubs.lo +am_libvtv_stubs_la_OBJECTS = $(am__objects_4) +libvtv_stubs_la_OBJECTS = $(am_libvtv_stubs_la_OBJECTS) DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = am__depfiles_maybe = @@ -122,7 +131,8 @@ LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLF 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 @@ -340,7 +350,7 @@ AM_CPPFLAGS = $(GLIBCXX_INCLUDES) # Need this library to both be part of libstdc++.a, and installed # separately too. # 1) separate libsupc++.la -toolexeclib_LTLIBRARIES = libsupc++.la +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 = \ @@ -402,10 +412,21 @@ sources = \ tinfo2.cc \ vec.cc \ vmi_class_type_info.cc \ - vterminate.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) # 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 @@ -569,6 +590,10 @@ libsupc++.la: $(libsupc___la_OBJECTS) $( $(CXXLINK) -rpath $(toolexeclibdir) $(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) -rpath $(toolexeclibdir) $(libvtv_init_la_OBJECTS) $(libvtv_init_la_LIBADD) $(LIBS) +libvtv_stubs.la: $(libvtv_stubs_la_OBJECTS) $(libvtv_stubs_la_DEPENDENCIES) + $(CXXLINK) -rpath $(toolexeclibdir) $(libvtv_stubs_la_OBJECTS) $(libvtv_stubs_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) Index: libstdc++-v3/libsupc++/vtv_rts.cc =================================================================== --- libstdc++-v3/libsupc++/vtv_rts.cc (revision 0) +++ libstdc++-v3/libsupc++/vtv_rts.cc (revision 0) @@ -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 + . */ + +/* 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* For gthreads suppport */ +#include +#include + +#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 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 ; +typedef insert_only_hash_map 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); +} Index: libstdc++-v3/libsupc++/vtv_malloc.h =================================================================== --- libstdc++-v3/libsupc++/vtv_malloc.h (revision 0) +++ libstdc++-v3/libsupc++/vtv_malloc.h (revision 0) @@ -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 + . */ + +#ifndef _VTV_MALLOC_H +#define _VTV_MALLOC_H 1 + +#include + +/* 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 */ Index: libstdc++-v3/libsupc++/vtv_rts.h =================================================================== --- libstdc++-v3/libsupc++/vtv_rts.h (revision 0) +++ libstdc++-v3/libsupc++/vtv_rts.h (revision 0) @@ -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 + . */ + +#ifndef _VTV_RTS_H +#define _VTV_RTS_H 1 + +#include + +/* 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 */ Index: libstdc++-v3/libsupc++/vtv_fail.h =================================================================== --- libstdc++-v3/libsupc++/vtv_fail.h (revision 0) +++ libstdc++-v3/libsupc++/vtv_fail.h (revision 0) @@ -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 + . */ + +#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 */ Index: libstdc++-v3/libsupc++/vtv_map.h =================================================================== --- libstdc++-v3/libsupc++/vtv_map.h (revision 0) +++ libstdc++-v3/libsupc++/vtv_map.h (revision 0) @@ -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 + . */ + +#ifndef _VTV_MAP_H +#define _VTV_MAP_H 1 + +#include +#include + +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 +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 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 +insert_only_hash_map * +insert_only_hash_map ::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 ) + + cap * sizeof (bucket_type); + insert_only_hash_map * result = + static_cast *> (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 +insert_only_hash_map * +insert_only_hash_map ::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 +insert_only_hash_map * +insert_only_hash_map ::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 * 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 +insert_only_hash_map * +insert_only_hash_map ::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 * 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 +const typename insert_only_hash_map ::value_type* +insert_only_hash_map ::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 +bool +insert_only_hash_map ::key_type::equals ( + const typename insert_only_hash_map ::key_type *k) const +{ + const char* x = reinterpret_cast (k); + const char* y = reinterpret_cast (this); + return (load8bytes (x) == load8bytes (y) + && memcmp (x + 8, y + 8, this->n) == 0); +} + +#endif /* _VTV_MAP_H */ Index: libstdc++-v3/libsupc++/vtv_set.h =================================================================== --- libstdc++-v3/libsupc++/vtv_set.h (revision 0) +++ libstdc++-v3/libsupc++/vtv_set.h (revision 0) @@ -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 . */ + +#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 + +#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 + +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 + +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 +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 insert_only_hash_sets ::insert_only_hash_set * +insert_only_hash_sets ::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 (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 insert_only_hash_sets ::insert_only_hash_set * +insert_only_hash_sets ::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 insert_only_hash_sets ::insert_only_hash_set * +insert_only_hash_sets ::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 (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 +void +insert_only_hash_sets::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 +bool +insert_only_hash_sets::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 insert_only_hash_sets ::insert_only_hash_set * + insert_only_hash_sets::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 +bool +insert_only_hash_sets::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 +bool +insert_only_hash_sets::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 +bool +insert_only_hash_sets::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 */ Index: libstdc++-v3/libsupc++/vtv_stubs.cc =================================================================== --- libstdc++-v3/libsupc++/vtv_stubs.cc (revision 0) +++ libstdc++-v3/libsupc++/vtv_stubs.cc (revision 0) @@ -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 + . */ + +#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 Index: libstdc++-v3/libsupc++/vtv_utils.cc =================================================================== --- libstdc++-v3/libsupc++/vtv_utils.cc (revision 0) +++ libstdc++-v3/libsupc++/vtv_utils.cc (revision 0) @@ -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 + . */ + +/* 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 +#include +#include +#include +#include +#include +#include +#include + +#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; +} Index: libstdc++-v3/libsupc++/vtv_utils.h =================================================================== --- libstdc++-v3/libsupc++/vtv_utils.h (revision 0) +++ libstdc++-v3/libsupc++/vtv_utils.h (revision 0) @@ -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 + . */ + +#ifndef _VTV_UTILS_H +#define _VTV_UTILS_H 1 + +#include + +/* 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 */ Index: libstdc++-v3/libsupc++/vtv_init.cc =================================================================== --- libstdc++-v3/libsupc++/vtv_init.cc (revision 0) +++ libstdc++-v3/libsupc++/vtv_init.cc (revision 0) @@ -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 + . */ + + +/* 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 */ Index: libstdc++-v3/libsupc++/vtv_malloc.cc =================================================================== --- libstdc++-v3/libsupc++/vtv_malloc.cc (revision 0) +++ libstdc++-v3/libsupc++/vtv_malloc.cc (revision 0) @@ -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 + . */ + +/* 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 +#include +#include +#include +#include +#include +#include + +#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); +} Index: libstdc++-v3/libsupc++/Makefile.am =================================================================== --- libstdc++-v3/libsupc++/Makefile.am (revision 195903) +++ libstdc++-v3/libsupc++/Makefile.am (working copy) @@ -27,7 +27,7 @@ include $(top_srcdir)/fragment.am # Need this library to both be part of libstdc++.a, and installed # separately too. # 1) separate libsupc++.la -toolexeclib_LTLIBRARIES = libsupc++.la +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 @@ -93,10 +93,21 @@ sources = \ tinfo2.cc \ vec.cc \ vmi_class_type_info.cc \ - vterminate.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) # 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 Index: libstdc++-v3/config/abi/pre/gnu.ver =================================================================== --- libstdc++-v3/config/abi/pre/gnu.ver (revision 195903) +++ libstdc++-v3/config/abi/pre/gnu.ver (working copy) @@ -1326,6 +1326,20 @@ GLIBCXX_3.4.18 { # operator delete(void*, , unsigned long) _ZdlPv[jmy]; + # Virtual table verification symbols + _Z12__VLTprotectv; + _Z14__VLTunprotectv; + _Z18__VLTInitSetSymbol*; + _Z23__VLTInitSetSymbolDebug*; + _Z17__VLTRegisterPair*; + _Z22__VLTRegisterPairDebug*; + _Z21__VLTChangePermission*; + _Z24__VLTVerifyVtablePointer*; + _Z29__VLTVerifyVtablePointerDebug*; + _Z17__vtv_verify_fail*; + _Z17__vtv_really_fail*; + + } GLIBCXX_3.4.17; # Symbols in the support library (libsupc++) have their own tag. Index: libgcc/config.host =================================================================== --- libgcc/config.host (revision 195903) +++ libgcc/config.host (working copy) @@ -195,7 +195,7 @@ case ${host} in ;; *-*-linux* | frv-*-*linux* | *-*-kfreebsd*-gnu | *-*-knetbsd*-gnu | *-*-gnu* | *-*-kopensolaris*-gnu) tmake_file="$tmake_file t-crtstuff-pic t-libgcc-pic t-eh-dw2-dip t-slibgcc t-slibgcc-gld t-slibgcc-elf-ver t-linux" - extra_parts="crtbegin.o crtbeginS.o crtbeginT.o crtend.o crtendS.o" + extra_parts="crtbegin.o crtbeginS.o crtbeginT.o crtend.o crtendS.o vtv_start.o vtv_end.o" ;; *-*-lynxos*) tmake_file="$tmake_file t-lynx $cpu_type/t-crtstuff t-crtstuff-pic t-libgcc-pic" Index: libgcc/Makefile.in =================================================================== --- libgcc/Makefile.in (revision 195903) +++ libgcc/Makefile.in (working copy) @@ -976,6 +976,14 @@ crtendS$(objext): $(srcdir)/crtstuff.c # This is a version of crtbegin for -static links. crtbeginT$(objext): $(srcdir)/crtstuff.c $(crt_compile) $(CRTSTUFF_T_CFLAGS) -c $< -DCRT_BEGIN -DCRTSTUFFT_O + +# These are used in vtable verification; see comments in source files for +# more details. +vtv_start$(objext): $(srcdir)/vtv_start.c + $(crt_compile) $(CRTSTUFF_T_CFLAGS_S) -c $(srcdir)/vtv_start.c + +vtv_end$(objext): $(srcdir)/vtv_end.c + $(crt_compile) $(CRTSTUFF_T_CFLAGS_S) -c $(srcdir)/vtv_end.c endif ifeq ($(CUSTOM_CRTIN),) Index: libgcc/vtv_start.c =================================================================== --- libgcc/vtv_start.c (revision 0) +++ libgcc/vtv_start.c (revision 0) @@ -0,0 +1,59 @@ +/* Copyright (C) 2012, 2013 + Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +/* This file is part of the vtable verification feature (for a + detailed description of the feature, see comments in + tree-vtable-verify.c). The vtable verification feature creates + certain global symbols that need to be read-write sometimes during + program execution, and read-only at others. It uses 'mprotect' to + change the memory protections of the pages on which these variables + are stored. In order to not affect the protections of other + program variables, these variables are put into a special named + section, ".vtable_map_vars", which is page-aligned at the start, + and which is padded with a page-sized amount of zeros at the end. + To make this section page aligned, we create a special symbol, + "_vtable_map_vars_start" which we make the very first thing that + goes into the section. This file defines that symbol (and only + that symbol). GCC compiles this file into vtv_start.o, and + inserts vtv_start.o into the link line immediately after + crtbegin.o, if the program is compiled with -fvtable.verify. + + In order to pad the ".vtable_map_vars" section with a page-sized + amount of zeros at the end, there is a second symbol, + _vtable_map_vars_end, which is defined in another file, vtv_end.c. + This second symbol is a page-sized array of chars, zero-filled, and + is the very last thing to go into the section. When the GCC driver + inserts vtv_start.o into the link line (just after crtbegin.o) it + also inserts vtv_end.o into the link line, just before crtend.o. + This has the desired effect of making our section page-aligned and + page-size paded, ensuring that no other program data lands on our + pages. */ + +#ifdef BIG_PAGE_SIZE +/* TODO - Replace '4096' below with correct big page size. */ +#define VTV_PAGE_SIZE 4096 +#else +#define VTV_PAGE_SIZE 4096 +#endif + +/* Page-aligned symbol to mark beginning of .vtable_map_vars section. */ +char _vtable_map_vars_start [] +__attribute__ ((__visibility__ ("protected"), used, aligned(VTV_PAGE_SIZE), + section(".vtable_map_vars"))) + = { }; Index: libgcc/vtv_end.c =================================================================== --- libgcc/vtv_end.c (revision 0) +++ libgcc/vtv_end.c (revision 0) @@ -0,0 +1,59 @@ +/* Copyright (C) 2012, 2013 + Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +/* This file is part of the vtable verification feature (for a + detailed description of the feature, see comments in + tree-vtable-verify.c). The vtable verification feature creates + certain global symbols that need to be read-write sometimes during + program execution, and read-only at others. It uses 'mprotect' to + change the memory protections of the pages on which these variables + are stored. In order to not affect the protections of other + program variables, these variables are put into a special named + section, ".vtable_map_vars", which is page-aligned at the start, + and which is padded with a page-sized amount of zeros at the end. + To make this section page aligned, we create a special symbol, + "_vtable_map_vars_start" which we make the very first thing that + goes into the section. That is defined in vtv_start.c (which + contains nothing else). vtv_start.c gest compiled into + vtv_start.o, and vtv_start.o gets inserted into the link line + immediately after crtbegin.o, if the program is compiled with + -fvtable.verify. + + In order to pad the ".vtable_map_vars" section with a page-sized + amount of zeros at the end, there is a second symbol, + _vtable_map_vars_end. This file defines that symbol (and only this + symbol). This second symbol is a page-sized array of chars, + zero-filled, and is the very last thing to go into the section. + When the GCC driver inserts vtv_start.o into the link line (just + after crtbegin.o) it also inserts vtv_end.o into the link line, + just before crtend.o. This has the desired effect of making our + section page-aligned and page-size paded, ensuring that no other + program data lands on our pages. */ + +#ifdef BIG_PAGE_SIZE +/* TODO - Replace '4096' below with correct big page size. */ +#define VTV_PAGE_SIZE 4096 +#else +#define VTV_PAGE_SIZE 4096 +#endif + +/* Page-sized variable to mark end of .vtable_map_vars section. */ +char _vtable_map_vars_end[VTV_PAGE_SIZE] + __attribute__ ((__visibility__ ("protected"), used, + section(".vtable_map_vars"))); Index: gcc/cgraph.c =================================================================== --- gcc/cgraph.c (revision 195903) +++ gcc/cgraph.c (working copy) @@ -2498,7 +2498,7 @@ cgraph_add_new_function (tree fndecl, bo case CGRAPH_STATE_FINISHED: /* At the very end of compilation we have to do all the work up to expansion. */ - node = cgraph_create_node (fndecl); + node = cgraph_get_create_node (fndecl); if (lowered) node->lowered = true; cgraph_analyze_function (node); Index: gcc/tree.h =================================================================== --- gcc/tree.h (revision 195903) +++ gcc/tree.h (working copy) @@ -5913,6 +5913,9 @@ is_tm_safe_or_pure (const_tree x) void init_inline_once (void); +/* In tree-vtable-verify.c */ +extern void save_vtable_map_decl (tree); + /* Compute the number of operands in an expression node NODE. For tcc_vl_exp nodes like CALL_EXPRs, this is stored in the node itself, otherwise it is looked up from the node's code. */ Index: gcc/tree-pass.h =================================================================== --- gcc/tree-pass.h (revision 195903) +++ gcc/tree-pass.h (working copy) @@ -460,6 +460,7 @@ extern struct gimple_opt_pass pass_split extern struct gimple_opt_pass pass_feedback_split_functions; extern struct gimple_opt_pass pass_threadsafe_analyze; extern struct gimple_opt_pass pass_tree_convert_builtin_dispatch; +extern struct gimple_opt_pass pass_vtable_verify; /* IPA Passes */ extern struct simple_ipa_opt_pass pass_ipa_lower_emutls; Index: gcc/cp/init.c =================================================================== --- gcc/cp/init.c (revision 195903) +++ gcc/cp/init.c (working copy) @@ -46,7 +46,6 @@ static tree initializing_context (tree); static void expand_cleanup_for_base (tree, tree); static tree dfs_initialize_vtbl_ptrs (tree, void *); static tree build_field_list (tree, tree, int *); -static tree build_vtbl_address (tree); static int diagnose_uninitialized_cst_or_ref_member_1 (tree, tree, bool, bool); /* We are about to generate some complex initialization code. @@ -1105,7 +1104,7 @@ emit_mem_initializers (tree mem_inits) /* Returns the address of the vtable (i.e., the value that should be assigned to the vptr) for BINFO. */ -static tree +tree build_vtbl_address (tree binfo) { tree binfo_for = binfo; Index: gcc/cp/class.c =================================================================== --- gcc/cp/class.c (revision 195903) +++ gcc/cp/class.c (working copy) @@ -6136,6 +6136,9 @@ finish_struct_1 (tree t) maybe_suppress_debug_info (t); + if (flag_vtable_verify) + vtv_save_class_info (t); + dump_class_hierarchy (t); /* Finish debugging output for this type. */ Index: gcc/cp/Make-lang.in =================================================================== --- gcc/cp/Make-lang.in (revision 195903) +++ gcc/cp/Make-lang.in (working copy) @@ -82,7 +82,7 @@ CXX_AND_OBJCXX_OBJS = cp/call.o cp/decl. cp/typeck.o cp/cvt.o cp/except.o cp/friend.o cp/init.o cp/method.o \ cp/search.o cp/semantics.o cp/tree.o cp/repo.o cp/dump.o cp/optimize.o \ cp/mangle.o cp/cp-objcp-common.o cp/name-lookup.o cp/cxx-pretty-print.o \ - cp/cp-gimplify.o tree-mudflap.o $(CXX_C_OBJS) + cp/cp-gimplify.o tree-mudflap.o cp/vtable-class-hierarchy.o $(CXX_C_OBJS) # Language-specific object files for C++. CXX_OBJS = cp/cp-lang.o c-family/stub-objc.o $(CXX_AND_OBJCXX_OBJS) @@ -340,7 +340,12 @@ cp/parser.o: cp/parser.c $(CXX_TREE_H) $ tree-threadsafe-analyze.h cp/cp-gimplify.o: cp/cp-gimplify.c $(CXX_TREE_H) $(C_COMMON_H) \ $(TM_H) coretypes.h pointer-set.h tree-iterator.h $(SPLAY_TREE_H) - +cp/vtable-class-hierarchy.o: cp/vtable-class-hierarchy.c \ + $(TM_H) $(TIMEVAR_H) $(CXX_TREE_H) intl.h $(CXX_PARSER_H) cp/decl.h \ + $(FLAGS_H) $(DIAGNOSTIC_CORE_H) output.h $(CGRAPH_H) c-family/c-common.h \ + c-family/c-objc.h $(PLUGIN_H) \ + tree-iterator.h tree-vtable-verify.h $(GIMPLE_H) \ + gt-cp-vtable-class-hierarchy.h cp/name-lookup.o: cp/name-lookup.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ $(TM_H) $(CXX_TREE_H) $(TIMEVAR_H) gt-cp-name-lookup.h \ $(DIAGNOSTIC_CORE_H) $(FLAGS_H) debug.h pointer-set.h Index: gcc/cp/g++spec.c =================================================================== --- gcc/cp/g++spec.c (revision 195903) +++ gcc/cp/g++spec.c (working copy) @@ -51,6 +51,8 @@ along with GCC; see the file COPYING3. #define LIBSTDCXX_STATIC NULL #endif +#define VTABLE_LOAD_MODULE_INIT "--whole-archive,-lvtv_init,--no-whole-archive" + void lang_specific_driver (struct cl_decoded_option **in_decoded_options, unsigned int *in_decoded_options_count, @@ -112,6 +114,11 @@ lang_specific_driver (struct cl_decoded_ /* The total number of arguments with the new stuff. */ unsigned int num_args = 1; + /* The command line contains a -fvtable_verify. We need to add the + init library if we are linking and if we are adding the stdc++ + library. */ + int saw_vtable_verify = 0; + argc = *in_decoded_options_count; decoded_options = *in_decoded_options; added_libraries = *in_added_libraries; @@ -237,6 +244,13 @@ lang_specific_driver (struct cl_decoded_ } } break; + + case OPT_fvtable_verify_: + if (strcmp (arg, "std") == 0) + saw_vtable_verify = 1; + else if (strcmp (arg, "preinit") == 0) + saw_vtable_verify = 2; + break; } } @@ -248,6 +262,12 @@ lang_specific_driver (struct cl_decoded_ /* Add one for shared_libgcc or extra static library. */ num_args = argc + added + need_math + (library > 0) * 4 + 1; + + /* Add two more linker args, '-Wl,-u_vtable_map_vars_start and + '-Wl,-u_vtable_map_vars_end. */ + if (saw_vtable_verify && library > 0) + num_args += 2; + new_decoded_options = XNEWVEC (struct cl_decoded_option, num_args); i = 0; @@ -310,6 +330,33 @@ lang_specific_driver (struct cl_decoded_ j++; } + /* Add option to make sure that if we are doing 'std' vtable + verification then we link with the libvtv_init library. */ + + if (saw_vtable_verify == 1 && library > 0) + { + generate_option(OPT_Wl_, VTABLE_LOAD_MODULE_INIT, 1, + CL_DRIVER, &new_decoded_options[j]); + added_libraries++; + j++; + } + + /* If we are doing vtable verification, make sure the linker does + not garbage-collect the special symbols that mark the start and + end of the ".vtable_map_vars" section in the binary. (See + comments in vtv_start.c and vtv_end.c for more details). */ + + if (saw_vtable_verify > 0 && library > 0) + { + generate_option (OPT_Wl_,"-u_vtable_map_vars_start", 1, + CL_DRIVER, &new_decoded_options[j]); + j++; + + generate_option (OPT_Wl_,"-u_vtable_map_vars_end", 1, + CL_DRIVER, &new_decoded_options[j]); + j++; + } + /* Add `-lstdc++' if we haven't already done so. */ if (library > 0) { Index: gcc/cp/pt.c =================================================================== --- gcc/cp/pt.c (revision 195903) +++ gcc/cp/pt.c (working copy) @@ -18124,6 +18124,10 @@ mark_class_instantiated (tree t, int ext if (! extern_p) { CLASSTYPE_DEBUG_REQUESTED (t) = 1; + + if (flag_vtable_verify) + vtv_save_class_info (t); + rest_of_type_compilation (t, 1); } } Index: gcc/cp/decl2.c =================================================================== --- gcc/cp/decl2.c (revision 195903) +++ gcc/cp/decl2.c (working copy) @@ -2782,7 +2782,8 @@ set_guard (tree guard) } /* Start the process of running a particular set of global constructors - or destructors. Subroutine of do_[cd]tors. */ + or destructors. Subroutine of do_[cd]tors. Also called from + vtv_start_verification_constructor_init_function. */ static tree start_objects (int method_type, int initp) @@ -2834,8 +2835,12 @@ start_objects (int method_type, int init return body; } -/* Finish the process of running a particular set of global constructors - or destructors. Subroutine of do_[cd]tors. */ +/* Finish the process of running a particular set of global + constructors or destructors. Subroutine of do_[cd]tors. Also + called from vtv_finish_verification_constructor_init_function. + + This function returns a tree containing the functino decl for the + functin it finished creating. */ static void finish_objects (int method_type, int initp, tree body) @@ -4125,8 +4130,22 @@ cp_write_global_declarations (void) timevar_start (TV_PHASE_CGRAPH); + if (flag_vtable_verify) + { + vtv_recover_class_info (); + vtv_compute_class_hierarchy_transitive_closure (); + } + cgraph_finalize_compilation_unit (); + if (flag_vtable_verify) + { + /* Generate the special constructor initialization function that + calls __VLTRegisterPairs, and give it a very high initialization + priority. */ + vtv_generate_init_routine (); + } + timevar_stop (TV_PHASE_CGRAPH); timevar_start (TV_PHASE_CHECK_DBGINFO); @@ -4475,4 +4494,24 @@ mark_used (tree decl) return true; } +tree +vtv_start_verification_constructor_init_function (void) +{ + return start_objects ('I', MAX_RESERVED_INIT_PRIORITY - 1); +} + +tree +vtv_finish_verification_constructor_init_function (tree function_body) +{ + /* return finish_objects ('I', MAX_RESERVED_INIT_PRIORITY - 1, function_body); */ + tree fn; + + finish_compound_stmt (function_body); + fn = finish_function (0); + DECL_STATIC_CONSTRUCTOR (fn) = 1; + decl_init_priority_insert (fn, MAX_RESERVED_INIT_PRIORITY - 1); + + return fn; +} + #include "gt-cp-decl2.h" Index: gcc/cp/config-lang.in =================================================================== --- gcc/cp/config-lang.in (revision 195903) +++ gcc/cp/config-lang.in (working copy) @@ -30,4 +30,4 @@ compilers="cc1plus\$(exeext)" target_libs="target-libstdc++-v3" -gtfiles="\$(srcdir)/cp/rtti.c \$(srcdir)/cp/mangle.c \$(srcdir)/cp/name-lookup.h \$(srcdir)/cp/name-lookup.c \$(srcdir)/cp/cp-tree.h \$(srcdir)/cp/decl.h \$(srcdir)/cp/call.c \$(srcdir)/cp/decl.c \$(srcdir)/cp/decl2.c \$(srcdir)/cp/pt.c \$(srcdir)/cp/repo.c \$(srcdir)/cp/semantics.c \$(srcdir)/cp/tree.c \$(srcdir)/cp/parser.h \$(srcdir)/cp/parser.c \$(srcdir)/cp/method.c \$(srcdir)/cp/typeck2.c \$(srcdir)/c-family/c-common.c \$(srcdir)/c-family/c-common.h \$(srcdir)/c-family/c-objc.h \$(srcdir)/c-family/c-lex.c \$(srcdir)/c-family/c-pragma.h \$(srcdir)/c-family/c-pragma.c \$(srcdir)/cp/class.c \$(srcdir)/cp/cp-objcp-common.c \$(srcdir)/cp/cp-lang.c \$(srcdir)/cp/except.c" +gtfiles="\$(srcdir)/cp/rtti.c \$(srcdir)/cp/mangle.c \$(srcdir)/cp/name-lookup.h \$(srcdir)/cp/name-lookup.c \$(srcdir)/cp/cp-tree.h \$(srcdir)/cp/decl.h \$(srcdir)/cp/call.c \$(srcdir)/cp/decl.c \$(srcdir)/cp/decl2.c \$(srcdir)/cp/pt.c \$(srcdir)/cp/repo.c \$(srcdir)/cp/semantics.c \$(srcdir)/cp/tree.c \$(srcdir)/cp/parser.h \$(srcdir)/cp/parser.c \$(srcdir)/cp/method.c \$(srcdir)/cp/typeck2.c \$(srcdir)/c-family/c-common.c \$(srcdir)/c-family/c-common.h \$(srcdir)/c-family/c-objc.h \$(srcdir)/c-family/c-lex.c \$(srcdir)/c-family/c-pragma.h \$(srcdir)/c-family/c-pragma.c \$(srcdir)/cp/class.c \$(srcdir)/cp/cp-objcp-common.c \$(srcdir)/cp/cp-lang.c \$(srcdir)/cp/except.c \$(srcdir)/cp/vtable-class-hierarchy.c" Index: gcc/cp/vtable-class-hierarchy.c =================================================================== --- gcc/cp/vtable-class-hierarchy.c (revision 0) +++ gcc/cp/vtable-class-hierarchy.c (revision 0) @@ -0,0 +1,1265 @@ +/* Copyright (C) 2012 Free Software Foundation, Inc. + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GCC is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +/* Virtual Table Pointer Security Pass - Detect corruption of vtable pointers + before using them for virtual method dispatches. */ + +/* This file is part of the vtable security feature implementation. + The vtable security feature is designed to detect when a virtual + call is about to be made through an invalid vtable pointer + (possibly due to data corruption or malicious attacks). The + compiler finds every virtual call, and inserts a verification call + before the virtual call. The verification call takes the actual + vtable pointer value in the object through which the virtual call + is being made, and compares the vtable pointer against a set of all + valid vtable pointers that the object could contain (this set is + based on the declared type of the object). If the pointer is in + the valid set, execution is allowed to continue; otherwise the + program is halted. + + There are several pieces needed in order to make this work: 1. For + every virtual class in the program (i.e. a class that contains + virtual methods), we need to build the set of all possible valid + vtables that an object of that class could point to. This includes + vtables for any class(es) that inherit from the class under + consideration. 2. For every such data set we build up, we need a + way to find and reference the data set. This is complicated by the + fact that the real vtable addresses are not known until runtime, + when the program is loaded into memory, but we need to reference the + sets at compile time when we are inserting verification calls into + the program. 3. We need to find every virtual call in the program, + and insert the verification call (with the appropriate arguments) + before the virtual call. 4. We need some runtime library pieces: + the code to build up the data sets at runtime; the code to actually + perform the verification using the data sets; and some code to set + protections on the data sets, so they themselves do not become + hacker targets. + + To find and reference the set of valid vtable pointers for any given + virtual class, we create a special global varible for each virtual + class. We refer to this as the "vtable map variable" for that + class. The vtable map variable has the type "void *", and is + initialized by the compiler to NULL. At runtime when the set of + valid vtable pointers for a virtual class, e.g. class Foo, is built, + the vtable map variable for class Foo is made to point to the set. + During compile time, when the compiler is inserting verification + calls into the program, it passes the vtable map variable for the + appropriate class to the verification call, so that at runtime the + verification call can find the appropriate data set. + + The actual set of valid vtable pointers for a virtual class, + e.g. class Foo, cannot be built until runtime, when the vtables get + loaded into memory and their addresses are known. But the knowledge + about which vtables belong in which class' hierarchy is only known + at compile time. Therefore at compile time we collect class + hierarchy and vtable information about every virtual class, and we + generate calls to build up the data sets at runtime. To build the + data sets, we call one of the functions we add to the runtime + library, __VLTRegisterPair. __VLTRegisterPair takes two arguments, + a vtable map variable and the address of a vtable. If the vtable + map variable is currently NULL, it creates a new data set (hash + table), makes the vtable map variable point to the new data set, and + inserts the vtable address into the data set. If the vtable map + variable is not NULL, it just inserts the vtable address into the + data set. In order to make sure that our data sets are built before + any verification calls happen, we create a special constructor + initialization function for each compilation unit, give it a very + high initialization priority, and insert all of our calls to + __VLTRegisterPair into our special constructor initialization + function. + + The vtable verification feature is controlled by the flag + '-fvtable-verify='. There are three flavors of this: + '-fvtable-verify=std', '-fvtable-verify=preinit', and + '-fvtable-verify=none'. If the option '-fvtable-verfy=preinit' is + used, then our constructor initialization function gets put into the + preinit array. This is necessary if there are data sets that need + to be built very early in execution. If the constructor + initialization function gets put into the preinit array, the we also + add calls to __VLTChangePermission at the beginning and end of the + function. The call at the beginning sets the permissions on the + data sets and vtable map variables to read/write, and the one at the + end makes them read-only. If the '-fvtable-verify=std' option is + used, the constructor initialization functions are executed at their + normal time, and the __VLTChangePermission calls are handled + differently (see the comments in libstdc++-v3/libsupc++/vtv_rts.cc). + The option '-fvtable-verify=none' turns off vtable verification. + + This file contains code to find and record the class hierarchies for + the virtual classes in a program, and all the vtables associated + with each such class; to generate the vtable map variables; and to + generate the constructor initialization function (with the calls to + __VLTRegisterPair, and __VLTChangePermission). The main data + structures used for collecting the class hierarchy data and + building/maintaining the vtable map variable data are defined in + gcc/tree-vtable-verify.h, because they are used both here and in + gcc/tree-vtable-verify.c. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "timevar.h" +#include "cpplib.h" +#include "tree.h" +#include "cp-tree.h" +#include "intl.h" +#include "c-family/c-pragma.h" +#include "decl.h" +#include "flags.h" +#include "diagnostic-core.h" +#include "output.h" +#include "target.h" +#include "cgraph.h" +#include "c-family/c-common.h" +#include "c-family/c-objc.h" +#include "plugin.h" +#include "tree-iterator.h" +#include "tree-vtable-verify.h" +#include "gimple.h" + +/* Mark these specially since they need to be stored in precompiled + header IR. */ +static GTY (()) tree vlt_saved_class_info = NULL_TREE; +static GTY (()) tree vlt_register_pairs_fndecl = NULL_TREE; +static GTY (()) tree vlt_init_set_symbol_fndecl = NULL_TREE; +static GTY (()) tree vlt_change_permission_fndecl = NULL_TREE; + +struct work_node { + struct vtv_graph_node *node; + struct work_node *next; +}; + +struct vtbl_map_node *vtable_find_or_create_map_decl (tree); + +/* As part of vtable verification the compiler generates and inserts calls + to __VLTRegisterPair and __VLTChangePermission, which are in libsupc++. + This function builds and initializes the function decls that are used + in generating those function calls. + + In addition to __VLTRegisterPair there is also __VLTRegisterPairDebug + which can be used in place of __VLTRegisterPair, and which takes extra + parameters and outputs extra information, to help debug problems. The + debug version of this function is generated and used if VTV_DEBUG is + defined. + + The signatures for these functions are: + + void __VLTChangePermission (int); + void __VLTRegisterPair (void **, void*, int); + void __VLTRegisterPairDebug (void**, void *, int, char *, int, char *, int); +*/ + +static void +init_functions (void) +{ + tree void_ptr_type = build_pointer_type (void_type_node); + tree arg_types = NULL_TREE; + tree change_permission_type = void_type_node; + tree register_pairs_type = void_type_node; + tree init_set_symbol_type = void_type_node; +#ifdef VTV_DEBUG + tree const_char_ptr_type = build_pointer_type (build_qualified_type + (char_type_node, + TYPE_QUAL_CONST)); +#endif + + if (vlt_change_permission_fndecl != NULL_TREE) + return; + + gcc_assert (vlt_register_pairs_fndecl == NULL_TREE); + + arg_types = build_tree_list (NULL_TREE, integer_type_node); + arg_types = chainon (arg_types, build_tree_list (NULL_TREE, void_type_node)); + + change_permission_type = build_function_type (change_permission_type, + arg_types); + vlt_change_permission_fndecl = build_fn_decl ("__VLTChangePermission", + change_permission_type); + TREE_NOTHROW (vlt_change_permission_fndecl) = 1; + DECL_ATTRIBUTES (vlt_change_permission_fndecl) = + tree_cons (get_identifier ("leaf"), NULL, + DECL_ATTRIBUTES (vlt_change_permission_fndecl)); + TREE_PUBLIC (vlt_change_permission_fndecl) = 1; + DECL_PRESERVE_P (vlt_change_permission_fndecl) = 1; + retrofit_lang_decl (vlt_change_permission_fndecl); + SET_DECL_LANGUAGE (vlt_change_permission_fndecl, lang_cplusplus); + + arg_types = build_tree_list (NULL_TREE, build_pointer_type (void_ptr_type)); + arg_types = chainon (arg_types, build_tree_list (NULL_TREE, + const_ptr_type_node)); + +#ifdef VTV_DEBUG + /* These arguments are only used by the debug version of RegisterPair */ + arg_types = chainon (arg_types, build_tree_list (NULL_TREE, + const_char_ptr_type)); + arg_types = chainon (arg_types, build_tree_list (NULL_TREE, + const_char_ptr_type)); +#endif + + arg_types = chainon (arg_types, build_tree_list (NULL_TREE, void_type_node)); + + register_pairs_type = build_function_type (register_pairs_type, arg_types); + +#ifdef VTV_DEBUG + /* void + __VLTRegisterPairDebug (void ** set_handle_ptr, const void * vtable_ptr, + const char * set_symbol_name, const char * vtable_name) + */ + + vlt_register_pairs_fndecl = build_fn_decl ("__VLTRegisterPairDebug", + register_pairs_type); +#else + /* void __VLTRegisterPair (void **set_handle_ptr, const void *vtable_ptr) */ + vlt_register_pairs_fndecl = build_fn_decl ("__VLTRegisterPair", + register_pairs_type); +#endif + + TREE_NOTHROW (vlt_register_pairs_fndecl) = 1; + DECL_ATTRIBUTES (vlt_register_pairs_fndecl) = + tree_cons (get_identifier ("leaf"), NULL, + DECL_ATTRIBUTES (vlt_register_pairs_fndecl)); + TREE_PUBLIC (vlt_register_pairs_fndecl) = 1; + DECL_PRESERVE_P (vlt_register_pairs_fndecl) = 1; + retrofit_lang_decl (vlt_register_pairs_fndecl); + SET_DECL_LANGUAGE (vlt_register_pairs_fndecl, lang_cplusplus); + + arg_types = build_tree_list (NULL_TREE, build_pointer_type (void_ptr_type)); + arg_types = chainon (arg_types, build_tree_list (NULL_TREE, + const_ptr_type_node)); + arg_types = chainon (arg_types, build_tree_list (NULL_TREE, + size_type_node)); + arg_types = chainon (arg_types, build_tree_list (NULL_TREE, void_type_node)); + + init_set_symbol_type = build_function_type (init_set_symbol_type, arg_types); + +#ifdef VTV_DEBUG + /* void __VLTInitSetSymbolDebug(void ** set_handle_ptr, + const void * set_symbol_key, + size_t size_hint) + */ + vlt_init_set_symbol_fndecl = build_fn_decl ("__VLTInitSetSymbolDebug", + init_set_symbol_type); +#else + /* void __VLTInitSetSymbol(void ** set_handle_ptr, + const void * set_symbol_key, + size_t size_hint) + */ + vlt_init_set_symbol_fndecl = build_fn_decl ("__VLTInitSetSymbol", + init_set_symbol_type); +#endif + + TREE_NOTHROW (vlt_init_set_symbol_fndecl) = 1; + DECL_ATTRIBUTES (vlt_init_set_symbol_fndecl) = + tree_cons (get_identifier ("leaf"), NULL, + DECL_ATTRIBUTES (vlt_init_set_symbol_fndecl)); + TREE_PUBLIC (vlt_init_set_symbol_fndecl) = 1; + DECL_PRESERVE_P (vlt_init_set_symbol_fndecl) = 1; + retrofit_lang_decl (vlt_init_set_symbol_fndecl); + SET_DECL_LANGUAGE (vlt_init_set_symbol_fndecl, lang_cplusplus); +} + +/* This is a helper function for + vtv_compute_class_hierarchy_transitive_closure. It adds a + vtv_graph_node to the WORKLIST, which is a linked list of + seen-but-not-yet-processed nodes. INSERTED is a bitmap, one bit + per node, to help make sure that we don't insert a node into the + worklist more than once. Each node represents a class somewhere in + our class hierarchy information. Every node in the graph gets added + to the worklist exactly once and removed from the worklist exactly + once (when all of its children have been processed). */ + +static void +add_to_worklist (struct work_node **worklist, struct vtv_graph_node *node, + sbitmap inserted) +{ + struct work_node *new_work_node; + + if (TEST_BIT (inserted, node->class_uid)) + return; + + new_work_node = XNEW (struct work_node); + new_work_node->next = *worklist; + new_work_node->node = node; + *worklist = new_work_node; + + SET_BIT (inserted, node->class_uid); +} + +/* This is a helper function for + vtv_compute_class_hierarchy_transitive_closure. It goes through + the WORKLIST of class hierarchy nodes looking for a "leaf" node, + i.e. a node whose children in the hierarchy have all been + processed. When it finds the next leaf node, it removes it from + the linked list (WORKLIST) and returns the node. */ + +static struct vtv_graph_node * +find_and_remove_next_leaf_node (struct work_node **worklist) +{ + struct work_node *prev, *cur; + + for (prev = NULL, cur = *worklist; cur; prev = cur, cur = cur->next) + { + if (cur->node->num_children == cur->node->num_processed_children) + { + if (prev == NULL) + (*worklist) = cur->next; + else + prev->next = cur->next; + + cur->next = NULL; + return cur->node; + } + } + + return NULL; +} + +/* In our class hierarchy graph, each class node contains a bitmap, + with one bit for each class in the hierarchy. The bits are set for + classes that are descendants in the graph of the current node. + Initially the descendants bitmap is only set for immediate + descendants. This function traverses the class hierarchy graph, + bottom up, filling in the transitive closures for the descendants + as we rise up the graph. */ + +void +vtv_compute_class_hierarchy_transitive_closure (void) +{ + struct work_node *worklist = NULL; + struct vtbl_map_node *cur; + sbitmap inserted = sbitmap_alloc (num_vtable_map_nodes); + unsigned i; + + /* Note: Every node in the graph gets added to the worklist exactly + once and removed from the worklist exactly once (when all of its + children have been processed). Each node's children edges are + followed exactly once, and each node's parent edges are followed + exactly once. So this algorithm is roughly O(V + 2E), i.e. + O(E + V). */ + + /* Set-up: */ + /* Find all the "leaf" nodes in the graph, and add them to the worklist. */ + sbitmap_zero (inserted); + for (cur = vtbl_map_nodes; cur; cur = cur->next) + { + if (cur->class_info + && (cur->class_info->num_children == 0) + && ! (TEST_BIT (inserted, cur->class_info->class_uid))) + add_to_worklist (&worklist, cur->class_info, inserted); + } + + /* Main work: pull next leaf node off work list, process it, add its + parents to the worklist, where a 'leaf' node is one that has no + children, or all of its children have been processed. */ + while (worklist) + { + struct vtv_graph_node *temp_node = + find_and_remove_next_leaf_node (&worklist); + + gcc_assert (temp_node != NULL); + temp_node->descendants = sbitmap_alloc (num_vtable_map_nodes); + sbitmap_zero (temp_node->descendants); + SET_BIT (temp_node->descendants, temp_node->class_uid); + for (i = 0; i < temp_node->num_children; ++i) + sbitmap_a_or_b (temp_node->descendants, temp_node->descendants, + temp_node->children[i]->descendants); + for (i = 0; i < temp_node->num_parents; ++i) + { + temp_node->parents[i]->num_processed_children = + temp_node->parents[i]->num_processed_children + 1; + if (!TEST_BIT (inserted, temp_node->parents[i]->class_uid)) + add_to_worklist (&worklist, temp_node->parents[i], inserted); + } + } +} + +/* Keep track of which pairs we have already created __VLTRegisterPair + calls for, to prevent creating duplicate calls within the same + compilation unit. VTABLE_DECL is the var decl for the vtable of + the (descendant) class that we are adding to our class hierarchy + data. VPTR_ADDRESS is and expression for calculating the correct + offset into the vtable (VTABLE_DECL). It is the actual vtable + pointer address that will be stored in our list of valid vtable + pointers for BASE_CLASS. BASE_CLASS is the record_type node for + the base class to whose hiearchy we want to add + VPTR_ADDRESS. (VTABLE_DECL should be the vtable for BASE_CLASS or + one of BASE_CLASS' descendents. */ + +static bool +record_register_pairs (tree vtable_decl, tree vptr_address, + tree base_class) +{ + unsigned offset; + struct vtbl_map_node *base_vtable_map_node; + + if (TREE_OPERAND_LENGTH (vptr_address) == 1) + { + tree tmp_address = TREE_OPERAND (vptr_address, 0); + offset = TREE_INT_CST_LOW (TREE_OPERAND (tmp_address, 1)); + } + else + offset = TREE_INT_CST_LOW (TREE_OPERAND (vptr_address, 1)); + + base_vtable_map_node = vtbl_map_get_node (base_class); + + if (vtbl_map_node_registration_find (base_vtable_map_node, vtable_decl, + offset)) + return true; + + vtbl_map_node_registration_insert (base_vtable_map_node, vtable_decl, + offset); + return false; +} + +/* A class may contain secondary vtables in it, for various reasons. + This function goes through the decl chain of a class record looking + for any fields that point to secondary vtables, and adding calls to + __VLTRegisterPair for the secondary vtable pointers. + + BASE_CLASS_DECL_ARG is an expression for the address of the vtable + map variable for the BASE_CLASS (whose hierarchy we are currently + updating). BASE_CLASS is the record_type node for the base class. + RECORD_TYPE is the record_type node for the descendant class that + we are possibly adding to BASE_CLASS's hierarchy. BODY is the + function body for the constructor init function to which we are + adding our calls to __VLTRegisterPair. */ + +static void +register_vptr_fields (tree base_class_decl_arg, tree base_class, + tree record_type, tree body) +{ + tree vtbl_var_decl; + + if (TREE_CODE (record_type) != RECORD_TYPE) + return; + + vtbl_var_decl = get_vtbl_decl_for_binfo (TYPE_BINFO (record_type)); + + if (vtbl_var_decl) + { + tree ztt_decl = DECL_CHAIN (vtbl_var_decl); + bool already_registered = false; + + /* Check to see if we have found a constructor vtable. Add its + data if appropriate. */ + if (ztt_decl != NULL_TREE && (DECL_NAME (ztt_decl)) + && (strncmp (IDENTIFIER_POINTER (DECL_NAME (ztt_decl)), + "_ZTT", 4) == 0)) + { + tree values = DECL_INITIAL (ztt_decl); + struct varpool_node *vp_node = varpool_node (ztt_decl); + if (vp_node->finalized + && TREE_ASM_WRITTEN (ztt_decl) + && values != NULL_TREE + && TREE_CODE (values) == CONSTRUCTOR + && TREE_CODE (TREE_TYPE (values)) == ARRAY_TYPE) + { + tree call_expr = NULL_TREE; + unsigned HOST_WIDE_INT cnt; + constructor_elt *ce; +#ifdef VTV_DEBUG + int len1 = strlen (IDENTIFIER_POINTER + (DECL_NAME (TREE_OPERAND (base_class_decl_arg, + 0)))); + tree arg1 = build_string_literal (len1 + 1, + IDENTIFIER_POINTER + (DECL_NAME + (TREE_OPERAND + (base_class_decl_arg, + 0)))); +#endif + /* Loop through the initialization values for this vtable to + get all the correct vtable pointer addresses that we need + to add to our set of valid vtable pointers for the current + base class. */ + + for (cnt = 0; + VEC_iterate (constructor_elt, CONSTRUCTOR_ELTS (values), + cnt, ce); + cnt++) + { + tree value = ce->value; + tree val_vtbl_decl; + + /* We need to check value and find the bit where we have + something with 2 arguments, the first argument of which + is an ADDR_EXPR and the second argument of which is + an INTEGER_CST. */ + + while (value && TREE_OPERAND_LENGTH (value) == 1 + && TREE_CODE (TREE_OPERAND (value, 0)) == ADDR_EXPR) + value = TREE_OPERAND (value, 0); + + /* + gcc_assert (TREE_OPERAND_LENGTH (value) >= 2 + && TREE_CODE (TREE_OPERAND (value, 0)) == ADDR_EXPR + && TREE_CODE (TREE_OPERAND (value, 1)) == INTEGER_CST); + */ + /* The VAR_DECL for the vtable should be the first argument of + the ADDR_EXPR, which is the first argument of value.*/ + + if (TREE_OPERAND (value, 0)) + val_vtbl_decl = TREE_OPERAND (value, 0); + + while (TREE_CODE (val_vtbl_decl) != VAR_DECL + && TREE_OPERAND (val_vtbl_decl, 0)) + val_vtbl_decl = TREE_OPERAND (val_vtbl_decl, 0); + + gcc_assert (TREE_CODE (val_vtbl_decl) == VAR_DECL); + + /* Check to see if we already have this vtable pointer in + our valid set for this base class. */ + already_registered = record_register_pairs (val_vtbl_decl, + value, + base_class); + + if (already_registered) + continue; +#ifdef VTV_DEBUG + { + int len2 = strlen (IDENTIFIER_POINTER + (DECL_NAME (val_vtbl_decl))); + tree arg2 = build_string_literal (len2 + 1, + IDENTIFIER_POINTER + (DECL_NAME + (val_vtbl_decl))); + + /* Generate the call to __VLTRegisterPairDebug to + add this vtable pointer to our set of valid + pointers for the base class. */ + + call_expr = build_call_expr (vlt_register_pairs_fndecl, 4, + base_class_decl_arg, value, + arg1, arg2); + } +#else + /* Generate the call to __VLTRegisterPair to add + this vtable pointer to our set of valid pointers + for the base class. */ + + call_expr = build_call_expr (vlt_register_pairs_fndecl, 2, + base_class_decl_arg, value); +#endif + append_to_statement_list (call_expr, &body); + } + } + } + } +} + +/* This function iterates through all the vtables it can find from the + BINFO of a class, to make sure we have found ALL of the vtables + that an object of that class could point to. Generate calls to + __VLTRegisterPair for those vtable pointers that we find. + + BINFO is the tree_binfo node for the BASE_CLASS. BODY is the + function body for the constructor init function to which we are + adding calls to __VLTRegisterPair. ARG1 is an expression for the + address of the vtable map variable (for the BASE_CLASS), that will + point to the updated data set. BASE_CLASS is the record_type node + for the base class whose set of valid vtable pointers we are + updating. STR1 and STR2 are all debugging information, to be passed + as parameters to __VLTRegisterPairDebug. STR1 represents the name + of the vtable map variable to be updated by the call. Similarly, + STR2 represents the name of the class whose vtable pointer is being + added to the hierarchy. */ + +static void +register_other_binfo_vtables (tree binfo, tree body, tree arg1, tree str1, + tree str2, tree base_class) +{ + unsigned ix; + tree base_binfo; + tree vtable_decl; + bool already_registered; + + if (binfo == NULL_TREE) + return; + + for (ix = 0; BINFO_BASE_ITERATE (binfo, ix, base_binfo); ix++) + { + if ((!BINFO_PRIMARY_P (base_binfo) + || BINFO_VIRTUAL_P (base_binfo)) + && (vtable_decl = get_vtbl_decl_for_binfo (base_binfo)) + && !(DECL_VTABLE_OR_VTT_P (vtable_decl) + && DECL_CONSTRUCTION_VTABLE_P (vtable_decl))) + { + tree vtable_address = build_vtbl_address (base_binfo); + tree call_expr; + + already_registered = record_register_pairs (vtable_decl, + vtable_address, + base_class); + if (!already_registered) + { +#ifdef VTV_DEBUG + call_expr = build_call_expr (vlt_register_pairs_fndecl, 4, + arg1, vtable_address, + str1, str2); +#else + call_expr = build_call_expr (vlt_register_pairs_fndecl, 2, + arg1, vtable_address); +#endif + append_to_statement_list (call_expr, &body); + } + } + + register_other_binfo_vtables (base_binfo, body, arg1, str1, str2, + base_class); + } +} + +/* The set of valid vtable pointers for any given class are stored in + a hash table. For reasons of efficiency, that hash table size is + always a power of two. In order to try to prevent re-sizing the + hash tables very often, we pass __VLTRegisterPair an initial guess + as to the number of entries the hashtable will eventually need + (rounded up to the nearest power of two). This function takes the + class information we have collected for a particular class, + CLASS_NODE, and calculates the hash table size guess. */ + +static int +guess_num_vtable_pointers (struct vtv_graph_node *class_node) +{ + tree vtbl; + int total_num_vtbls = 0; + int num_vtbls_power_of_two = 1; + unsigned i; + + for (i = 0; i < num_vtable_map_nodes; ++i) + if (TEST_BIT (class_node->descendants, i)) + { + tree class_type = vtbl_map_nodes_array[i]->class_info->class_type; + for (vtbl = CLASSTYPE_VTABLES (class_type); vtbl; + vtbl = DECL_CHAIN (vtbl)) + { + total_num_vtbls++; + if (total_num_vtbls > num_vtbls_power_of_two) + num_vtbls_power_of_two <<= 1; + } + } + return num_vtbls_power_of_two; +} + +/* This function goes through our internal class hierarchy & vtable + pointer data structure and outputs calls to __VLTRegisterPair for + every class-vptr pair (for those classes whose vtable would be + output in the current compilation unit). These calls get put into + our constructor initialization function. BODY is the function + body, so far, of our constructor initialization function, to which we + add the calls. */ + +static bool +register_all_pairs (tree body) +{ + struct vtbl_map_node *current; + bool registered_at_least_one = false; + + for (current = vtbl_map_nodes; current; current = current->next) + { + unsigned i; + tree base_class = current->class_info->class_type; + tree base_ptr_var_decl = current->vtbl_map_decl; + tree str1 = NULL_TREE; + + gcc_assert (current->class_info != NULL); + + +#ifdef VTV_DEBUG + str1 = build_string_literal + (IDENTIFIER_LENGTH (DECL_NAME (base_ptr_var_decl)) + 1, + IDENTIFIER_POINTER (DECL_NAME (base_ptr_var_decl))); +#endif + + for (i = 0; i < num_vtable_map_nodes; ++i) + if (TEST_BIT (current->class_info->descendants, i)) + { + struct vtbl_map_node *vtbl_class_node = vtbl_map_nodes_array[i]; + tree class_type = vtbl_class_node->class_info->class_type; + + if (class_type + && (TREE_CODE (class_type) == RECORD_TYPE)) + { + tree new_type; + tree arg1; + tree call_expr; + bool already_registered; + + tree binfo = TYPE_BINFO (class_type); + tree vtable_decl; + bool vtable_should_be_output = false; + + vtable_decl = CLASSTYPE_VTABLES (class_type); + + /* Handle main vtable for this class. */ + + if (vtable_decl) + vtable_should_be_output = TREE_ASM_WRITTEN (vtable_decl); + + if (vtable_decl && vtable_should_be_output + && BINFO_VTABLE (binfo)) + { + tree vtable_address = build_vtbl_address (binfo); + + already_registered = record_register_pairs (vtable_decl, + vtable_address, + base_class); + + if (!already_registered) + { + tree str2 = NULL_TREE; + new_type = build_pointer_type (TREE_TYPE + (base_ptr_var_decl)); + arg1 = build1 (ADDR_EXPR, new_type, base_ptr_var_decl); + +#ifdef VTV_DEBUG + str2 = build_string_literal (IDENTIFIER_LENGTH + (DECL_NAME (vtable_decl)) + + 1, + IDENTIFIER_POINTER + (DECL_NAME (vtable_decl))); + + /* This call expr has the 2 "real" arguments, + plus 2 debugging arguments. */ + call_expr = build_call_expr (vlt_register_pairs_fndecl, + 4, arg1, vtable_address, + str1, str2); +#else + call_expr = build_call_expr (vlt_register_pairs_fndecl, + 2, arg1, vtable_address); +#endif + + append_to_statement_list (call_expr, &body); + + registered_at_least_one = true; + + /* Find and handle any 'extra' vtables associated + with this class, via virtual inheritance. */ + register_vptr_fields (arg1, base_class, class_type, + body); + + /* Find and handle any 'extra' vtables associated + with this class, via multiple inheritance. */ + register_other_binfo_vtables (binfo, body, arg1, str1, + str2, base_class); + } + } + } + } + } + + return registered_at_least_one; +} + +/* Given a tree containing a class type (CLASS_TYPE), this function + finds and returns the class hierarchy node for that class in our + data structure. */ + +static struct vtv_graph_node * +find_graph_node (tree class_type) +{ + struct vtbl_map_node *vtbl_node; + + vtbl_node = vtbl_map_get_node (class_type); + if (vtbl_node) + return vtbl_node->class_info; + + return NULL; +} + +/* This function adds an edge to our class hierarchy graph. + EDGE_ARRAY will either be an array of parent nodes or an array of + children nodes for a particular class. NUM_ENTRIES is the current + number of entries in the array. MAX_WENTRIES is the maximum number + of entries the array can hold. NEW_ENTRY is a vtv_graph_node + representing the new child or parent node to be added to the + EDGE_ARRAY. */ + +static void +add_edge_to_graph (struct vtv_graph_node ***edge_array, unsigned *num_entries, + unsigned *max_entries, struct vtv_graph_node *new_entry) +{ + /* Check array size, and re-size it if necessary. */ + if (*num_entries >= ((*max_entries) - 1)) + { + unsigned new_size = 2 * (*max_entries); + unsigned i; + *edge_array = (struct vtv_graph_node **) + xrealloc (*edge_array, new_size * sizeof (struct vtv_graph_node *)); + + for (i = *max_entries; i < new_size; ++i) + (*edge_array)[i] = NULL; + *max_entries = new_size; + } + + (*edge_array)[*num_entries] = new_entry; + *num_entries = (*num_entries) + 1; +} + +/* Add base class/derived class pair to our internal class hierarchy + data structure. BASE_NODE is our vtv_graph_node that corresponds + to a base class. DERIVED_NODE is our vtv_graph_node that + corresponds to a class that is a descendant of the base class + (possibly the base class itself). */ + +static void +add_hierarchy_pair (struct vtv_graph_node *base_node, + struct vtv_graph_node *derived_node) +{ + add_edge_to_graph (&(base_node->children), &(base_node->num_children), + &(base_node->max_children), derived_node); + add_edge_to_graph (&(derived_node->parents), &(derived_node->num_parents), + &(derived_node->max_parents), base_node); +} + +/* This functions adds a new base class/derived class relationship to + our class hierarchy data structure. Both parameters are trees + representing the class types, i.e. RECORD_TYPE trees. + DERIVED_CLASS can be the same as BASE_CLASS. */ + +static void +update_class_hierarchy_information (tree base_class, + tree derived_class) +{ + struct vtv_graph_node *base_node = find_graph_node (base_class); + struct vtv_graph_node *derived_node = find_graph_node (derived_class); + + add_hierarchy_pair (base_node, derived_node); +} + +/* Generate an undefined variable (a reference) to a varible defined + in the vtv_init libraty. In that way, if the a module is not linked + with the vtv_init library, the linker will generate an undefined + symbol error. Which is much better that getting a segmentation + violation at runtime. The parameter, INIT_ROUTINE_BODY, is the + function body of our constructor initialization function, to which + we add the reference to this symbol (and all of our calls to + __VLTRegisterPair). + + For more information, see comments in + libstdc++-v3/libsupc++/vtv_init.cc. */ + +static void +create_undef_reference_to_vtv_init (tree init_routine_body) +{ + const char *vtv_init_undef_var = "__vtv_defined_in_vtv_init_lib"; + tree var_decl; + tree init_zero; + + var_decl = build_decl (UNKNOWN_LOCATION, VAR_DECL, + get_identifier (vtv_init_undef_var), + int32_type_node); + TREE_PUBLIC (var_decl) = 1; + DECL_EXTERNAL (var_decl) = 1; + TREE_STATIC (var_decl) = 1; + SET_DECL_ASSEMBLER_NAME (var_decl, get_identifier (vtv_init_undef_var)); + DECL_ARTIFICIAL (var_decl) = 1; + TREE_READONLY (var_decl) = 0; + DECL_IGNORED_P (var_decl) = 1; + DECL_PRESERVE_P (var_decl) = 1; + varpool_finalize_decl (var_decl); + + /* Store a value in the undefined variable to force the creation of a + a reference. */ + init_zero = build2 (MODIFY_EXPR, TREE_TYPE (var_decl), var_decl, + integer_zero_node); + append_to_statement_list (init_zero, &init_routine_body); + +} + +/* A simple hash function on strings */ +/* Be careful about changing this routine. The values generated will + be stored in the calls to InitSet. So, changing this routine may + cause a binary incompatibility. */ + +static uint32_t +vtv_string_hash(const char *in) +{ + const char *s = in; + uint32_t h = 0; + + gcc_assert (in != NULL); + for ( ; *s; ++s) + h = 5 * h + *s; + return h; +} + +/* This function goes through all of our vtable map nodes, and for + each one that is actually used, it generates a call to + __VLTInitSetSymbol, with the appropriate arguments, and inserts the + calls as the start of our constructor initialization function + (INIT_ROUTINE_BODY). */ + +static bool +init_all_sets (tree init_routine_body) +{ + struct vtbl_map_node *current; + bool inited_at_least_one = false; + tree_stmt_iterator i = tsi_start (init_routine_body); + + for (current = vtbl_map_nodes; current; current = current->next) + { + if (!(current->is_used || (htab_elements (current->registered) > 0))) + continue; + + size_t size_hint = guess_num_vtable_pointers (current->class_info); + tree set_handle_var_decl = current->vtbl_map_decl; + + tree void_ptr_type = build_pointer_type (TREE_TYPE (set_handle_var_decl)); + tree arg1 = build1 (ADDR_EXPR, void_ptr_type, set_handle_var_decl); + + uint32_t len1 = IDENTIFIER_LENGTH (DECL_NAME (set_handle_var_decl)); + uint32_t hash_value = vtv_string_hash (IDENTIFIER_POINTER + (DECL_NAME (set_handle_var_decl))); + tree arg2, arg3, init_set_call; + + /* Build a buffer with the memory representation of + insert_only_hash_map::key_value as defined in vtv_map.h. This + will be passed as the second argument to InitSet. */ + #define KEY_TYPE_FIXED_SIZE 8 + + void *key_buffer = xmalloc (len1 + KEY_TYPE_FIXED_SIZE); + uint32_t *value_ptr = (uint32_t *) key_buffer; + + /* Set the len and hash for the string. */ + *value_ptr = len1; + value_ptr++; + *value_ptr = hash_value; + + /* Now copy the string representation of the vtbl map name... */ + memcpy ((char *) key_buffer + KEY_TYPE_FIXED_SIZE, + IDENTIFIER_POINTER (DECL_NAME (set_handle_var_decl)), + len1); + + /* ... and build a string literal from it. This will make a copy + so the key_bufffer is not needed anymore after this. */ + arg2 = build_string_literal (len1 + KEY_TYPE_FIXED_SIZE, + (char *) key_buffer); + free (key_buffer); + + /* size_t maybe different at compile time vs at runtime but + there should not be a problem in here. We dont expect such + large number of elements in the set. */ + arg3 = build_int_cst (size_type_node, size_hint); + init_set_call = build_call_expr (vlt_init_set_symbol_fndecl, + 3, arg1, arg2, arg3); + gcc_assert (size_hint != 0); + tsi_link_before (&i, init_set_call, TSI_SAME_STMT); + + inited_at_least_one = true; + } + return inited_at_least_one; +} + + +/* This function calls register_all_pairs, which actually generates + all the calls to __VLTRegisterPair (in the verification constructor + init function). It also generates the calls to + __VLTChangePermission, if the verification constructor init + function is going into the preinit array. INIT_ROUTINE_BODY is + the body of our constructior initialization function, to which we + add our function calls.*/ + +static bool +vtv_register_class_hierarchy_information (tree init_routine_body) +{ + bool registered_something = false; + bool inited_some_sets = true; + + init_functions (); + + /* TODO: Temp fix. Needs to be tightened. */ + if (num_vtable_map_nodes == 0) + return false;; + + /* Add class hierarchy pairs to the vtable map data structure. */ + registered_something = register_all_pairs (init_routine_body); + + /* Initialialize all vtable map variables (pointers to our data + sets. */ + inited_some_sets = init_all_sets (init_routine_body); + + if (registered_something || inited_some_sets) + { + /* If this function is going into the preinit_array, then we + need to manually call __VLTChangePermission, rather than + depending on initialization prioritys in vtv_init. */ + if (flag_vtable_verify == VTV_PREINIT_PRIORITY) + { + /* Pass __VLTP_READ_WRITE value as defined in vtv_rts.h. */ + tree arg_read_write = build_int_cst (integer_type_node, 1); + tree arg_read_only = build_int_cst (integer_type_node, 0); + + tree call_rw_expr = build_call_expr (vlt_change_permission_fndecl, + 1, arg_read_write); + tree call_r_expr = build_call_expr (vlt_change_permission_fndecl, + 1, arg_read_only); + tree_stmt_iterator i = tsi_start (init_routine_body); + /* Insert the call to make permissions read-write at the + beginning of the init routine. */ + tsi_link_before (&i, call_rw_expr, TSI_SAME_STMT); + + /* Append the call to make permissions read-only at the + end of the init routine. */ + append_to_statement_list (call_r_expr, &init_routine_body); + } + + if (flag_vtable_verify == VTV_STANDARD_PRIORITY) + create_undef_reference_to_vtv_init (init_routine_body); + } + + return registered_something || inited_some_sets; +} + +/* This function writes records data about the number of virtual calls + we found and the number of verification calls we generated. It is + primarily for debugging purposes. */ + +static void +write_out_counters (void) +{ + if (total_num_virtual_calls == 0) + return; + + FILE *fp = fopen ("/tmp/vtable-verification-counters.log", "a"); + double pct_done = (total_num_verified_vcalls * 100) / total_num_virtual_calls; + + if (fp) + { + fprintf (fp, "%s %d %d (%.2f %%)\n", main_input_filename, + total_num_virtual_calls, total_num_verified_vcalls, + pct_done); + fclose (fp); + } +} + +/* Generate the special constructor function that calls + __VLTChangePermission and __VLTRegisterPairs, and give it a very + high initialization priority. */ + +void +vtv_generate_init_routine (void) +{ + tree init_routine_body; + bool vtable_classes_found = false; +#ifdef VTV_COUNT + bool debug_num_verified = true; +#else + bool debug_num_verified = false; +#endif + + if (debug_num_verified) + write_out_counters (); + + push_lang_context (lang_name_c); + + /* The priority for this init function (constructor) is carefully + chosen so that it will happen after the calls to unprotect the + memory used for vtable verification and before the memory is + protected again. */ + init_routine_body = vtv_start_verification_constructor_init_function (); + + vtable_classes_found = + vtv_register_class_hierarchy_information (init_routine_body); + + if (vtable_classes_found) + { + current_function_decl = + vtv_finish_verification_constructor_init_function (init_routine_body); + allocate_struct_function (current_function_decl, false); + TREE_STATIC (current_function_decl) = 1; + TREE_USED (current_function_decl) = 1; + DECL_PRESERVE_P (current_function_decl) = 1; + if (flag_vtable_verify == VTV_PREINIT_PRIORITY) + { + DECL_STATIC_CONSTRUCTOR (current_function_decl) = 0; + assemble_vtv_preinit_initializer (current_function_decl); + } + + gimplify_function_tree (current_function_decl); + cgraph_add_new_function (current_function_decl, false); + + cgraph_process_new_functions (); + } + pop_lang_context (); +} + +/* This funtion takes a tree containing a class type (BASE_TYPE), and + it either finds the existing vtbl_map_node for that class in our + data structure, or it creates a new node and adds it to the data + structure if there is not one for the class already. As part of + this process it also creates the global vtable map variable for the + class. */ + +struct vtbl_map_node * +vtable_find_or_create_map_decl (tree base_type) +{ + tree base_decl = TREE_CHAIN (base_type); + tree base_id; + struct vtbl_map_node *vtable_map_node = NULL; + tree base_decl_type; + unsigned int save_quals; + unsigned int null_quals = TYPE_UNQUALIFIED; + + /* Verify the type has an associated vtable. */ + if (!TYPE_BINFO (base_type) || !BINFO_VTABLE (TYPE_BINFO (base_type))) + return NULL; + + if (!base_decl) + base_decl = TYPE_NAME (base_type); + + /* Temporarily remove any type qualifiers on the type. */ + base_decl_type = TREE_TYPE (base_decl); + save_quals = TYPE_QUALS (base_decl_type); + reset_type_qualifiers (null_quals, base_decl_type); + + base_id = DECL_ASSEMBLER_NAME (base_decl); + + /* Restore the type qualifiers. */ + reset_type_qualifiers (save_quals, base_decl_type); + + /* We've already created the variable; just look it. */ + vtable_map_node = vtbl_map_get_node (base_type); + + if (!vtable_map_node || (vtable_map_node->vtbl_map_decl == NULL_TREE)) + { + /* If we haven't already created the *__vtable_map global + variable for this class, do so now, and add it to the + varpool, to make sure it gets saved and written out. */ + char *var_name = NULL; + tree var_decl = NULL; + tree var_type = build_pointer_type (void_type_node); + tree initial_value = build_int_cst (make_node (INTEGER_TYPE), 0); + + /* Create map lookup symbol for base class */ + var_name = ACONCAT (("_ZN4_VTVI", IDENTIFIER_POINTER (base_id), + "E12__vtable_mapE", NULL)); + var_decl = build_decl (UNKNOWN_LOCATION, VAR_DECL, + get_identifier (var_name), var_type); + TREE_PUBLIC (var_decl) = 1; + DECL_EXTERNAL (var_decl) = 0; + TREE_STATIC (var_decl) = 1; + DECL_VISIBILITY (var_decl) = VISIBILITY_HIDDEN; + SET_DECL_ASSEMBLER_NAME (var_decl, get_identifier (var_name)); + DECL_ARTIFICIAL (var_decl) = 1; + /* We cannot mark this variable as read-only otherwise the gold + linker will not put it in the relro section. It seems if it + is marked as read-only, gold will put it in the .text + segment. */ + TREE_READONLY (var_decl) = 0; + DECL_IGNORED_P (var_decl) = 1; + + /* Put these mmap variables in to .vtable_map_vars sections, so + we can find and protect them. */ + + DECL_SECTION_NAME (var_decl) = build_string (strlen (".vtable_map_vars"), + ".vtable_map_vars"); + DECL_HAS_IMPLICIT_SECTION_NAME_P (var_decl) = true; + DECL_COMDAT_GROUP (var_decl) = get_identifier (var_name); + DECL_INITIAL (var_decl) = initial_value; + + varpool_finalize_decl (var_decl); + if (!vtable_map_node) + vtable_map_node = find_or_create_vtbl_map_node (base_type); + if (vtable_map_node->vtbl_map_decl == NULL_TREE) + vtable_map_node->vtbl_map_decl = var_decl; + } + + gcc_assert (vtable_map_node); + return vtable_map_node; +} + +/* This function is used to build up our class hierarchy data for a + particular class. TYPE is the record_type tree node for the + class. */ + +static void +vtv_save_base_class_info (tree type) +{ + if (flag_vtable_verify) + { + tree binfo = TYPE_BINFO (type); + tree base_binfo; + struct vtbl_map_node *own_map; + int i; + + /* First make sure to create the map for this record type. */ + own_map = vtable_find_or_create_map_decl (type); + if (own_map == NULL) + return; + + /* Go through the list of all base classes for the current + (derived) type, make sure the *__vtable_map global variable + for the base class exists, and add the base class/derived + class pair to the class hierarchy information we are + accumulating (for vtable pointer verification). */ + for (i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); i++) + { + tree tree_val = BINFO_TYPE (base_binfo); + struct vtbl_map_node *vtable_map_node = NULL; + + vtable_map_node = vtable_find_or_create_map_decl (tree_val); + + if (vtable_map_node != NULL) + update_class_hierarchy_information (tree_val, type); + } + } +} + +/* This function adds classes we are interested in to a list of + classes that is saved during pre-compiled header generation. + RECORD is the record_type node for the class we are adding to the + list. */ + +void +vtv_save_class_info (tree record) +{ + if (!flag_vtable_verify || TREE_CODE (record) == UNION_TYPE) + return; + + gcc_assert (TREE_CODE (record) == RECORD_TYPE); + + vlt_saved_class_info = tree_cons (NULL_TREE, record, vlt_saved_class_info); +} + + +/* This function goes through the list of classes we saved before the + pre-compiled header generation and calls vtv_save_base_class_info + on each one, to build up our class hierarchy data structure. */ + +void +vtv_recover_class_info (void) +{ + tree current_class; + tree class_chain = vlt_saved_class_info; + while (class_chain != NULL_TREE) + { + current_class = TREE_VALUE (class_chain); + gcc_assert (TREE_CODE (current_class) == RECORD_TYPE); + + vtv_save_base_class_info (current_class); + class_chain = TREE_CHAIN (class_chain); + } + + /* Let the garbabe collector collect the memory associated with the + chain. */ + vlt_saved_class_info = NULL_TREE; +} + +#include "gt-cp-vtable-class-hierarchy.h" Index: gcc/cp/mangle.c =================================================================== --- gcc/cp/mangle.c (revision 195903) +++ gcc/cp/mangle.c (working copy) @@ -3347,7 +3347,7 @@ mangle_decl_string (const tree decl) /* Return an identifier for the external mangled name of DECL. */ -static tree +tree get_mangled_id (tree decl) { tree id = mangle_decl_string (decl); @@ -3413,7 +3413,7 @@ mangle_decl (const tree decl) DECL_WEAK (alias) = 1; if (TREE_CODE (decl) == FUNCTION_DECL) cgraph_same_body_alias (cgraph_get_create_node (decl), alias, decl); - else + else if (TREE_CODE (decl) == VAR_DECL) varpool_extra_name_alias (alias, decl); #endif } Index: gcc/cp/cp-tree.h =================================================================== --- gcc/cp/cp-tree.h (revision 195903) +++ gcc/cp/cp-tree.h (working copy) @@ -5151,6 +5151,8 @@ extern void note_vague_linkage_fn (tree extern tree build_artificial_parm (tree, tree); extern bool possibly_inlined_p (tree); extern int parm_index (tree); +extern tree vtv_start_verification_constructor_init_function (void); +extern tree vtv_finish_verification_constructor_init_function (tree); /* in error.c */ extern void init_error (void); @@ -5238,6 +5240,7 @@ extern tree build_java_class_ref (tree) extern tree integral_constant_value (tree); extern tree decl_constant_value_safe (tree); extern int diagnose_uninitialized_cst_or_ref_member (tree, bool, bool); +extern tree build_vtbl_address (tree); /* in lex.c */ extern void cxx_dup_lang_specific_decl (tree); @@ -5919,6 +5922,7 @@ extern tree mangle_thunk (tree, int, t extern tree mangle_conv_op_name_for_type (tree); extern tree mangle_guard_variable (tree); extern tree mangle_ref_init_variable (tree); +extern tree get_mangled_id (tree); /* in dump.c */ extern bool cp_dump_tree (void *, tree); @@ -5966,6 +5970,12 @@ extern bool cxx_omp_privatize_by_referen extern void suggest_alternatives_for (location_t, tree); extern tree strip_using_decl (tree); +/* in vtable-class-hierarchy.c */ +extern void vtv_compute_class_hierarchy_transitive_closure (void); +extern void vtv_generate_init_routine (void); +extern void vtv_save_class_info (tree); +extern void vtv_recover_class_info (void); + /* -- end of C++ */ #endif /* ! GCC_CP_TREE_H */ Index: gcc/tree-ssa-copyrename.c =================================================================== --- gcc/tree-ssa-copyrename.c (revision 195903) +++ gcc/tree-ssa-copyrename.c (working copy) @@ -245,7 +245,7 @@ copy_rename_partition_coalesce (var_map } /* Don't coalesce if the two variables aren't type compatible . */ - if (!types_compatible_p (TREE_TYPE (root1), TREE_TYPE (root2)) + if (!gimple_types_compatible_p (TREE_TYPE (root1), TREE_TYPE (root2)) /* There is a disconnect between the middle-end type-system and VRP, avoid coalescing enum types with different bounds. */ || ((TREE_CODE (TREE_TYPE (root1)) == ENUMERAL_TYPE Index: gcc/timevar.def =================================================================== --- gcc/timevar.def (revision 195903) +++ gcc/timevar.def (working copy) @@ -256,6 +256,7 @@ DEFTIMEVAR (TV_TREE_THREADSAFE , " DEFTIMEVAR (TV_PLUGIN_INIT , "plugin initialization") DEFTIMEVAR (TV_PLUGIN_RUN , "plugin execution") DEFTIMEVAR (TV_SIMPLIFY_GOT , "simplify got") +DEFTIMEVAR (TV_VTABLE_VERIFICATION , "vtable verification") /* Everything else in rest_of_compilation not included above. */ DEFTIMEVAR (TV_EARLY_LOCAL , "early local passes") Index: gcc/flag-types.h =================================================================== --- gcc/flag-types.h (revision 195903) +++ gcc/flag-types.h (working copy) @@ -223,4 +223,11 @@ enum opt_info_verbosity_levels { OPT_INFO_MED = 2, OPT_INFO_MAX = 3 }; + +/* flag_vtable_verify initialization levels. */ +enum vtv_priority { + VTV_NO_PRIORITY = 0, /* i.E. Do NOT do vtable verification. */ + VTV_STANDARD_PRIORITY = 1, + VTV_PREINIT_PRIORITY = 2 +}; #endif /* ! GCC_FLAG_TYPES_H */ Index: gcc/tree-vtable-verify.c =================================================================== --- gcc/tree-vtable-verify.c (revision 0) +++ gcc/tree-vtable-verify.c (revision 0) @@ -0,0 +1,1131 @@ +/* Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 + Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +/* Virtual Table Pointer Security Pass - Detect corruption of vtable pointers + before using them for virtual method dispatches. */ + +/* This file is part of the vtable security feature implementation. + The vtable security feature is designed to detect when a virtual + call is about to be made through an invalid vtable pointer + (possibly due to data corruption or malicious attacks). The + compiler finds every virtual call, and inserts a verification call + before the virtual call. The verification call takes the actual + vtable pointer value in the object through which the virtual call + is being made, and compares the vtable pointer against a set of all + valid vtable pointers that the object could contain (this set is + based on the declared type of the object). If the pointer is in + the valid set, execution is allowed to continue; otherwise the + program is halted. + + There are several pieces needed in order to make this work: 1. For + every virtual class in the program (i.e. a class that contains + virtual methods), we need to build the set of all possible valid + vtables that an object of that class could point to. This includes + vtables for any class(es) that inherit from the class under + consideration. 2. For every such data set we build up, we need a + way to find and reference the data set. This is complicated by the + fact that the real vtable addresses are not known until runtime, + when the program is loaded into memory, but we need to reference the + sets at compile time when we are inserting verification calls into + the program. 3. We need to find every virtual call in the program, + and insert the verification call (with the appropriate arguments) + before the virtual call. 4. We need some runtime library pieces: + the code to build up the data sets at runtime; the code to actually + perform the verification using the data sets; and some code to set + protections on the data sets, so they themselves do not become + hacker targets. + + To find and reference the set of valid vtable pointers for any given + virtual class, we create a special global varible for each virtual + class. We refer to this as the "vtable map variable" for that + class. The vtable map variable has the type "void *", and is + initialized by the compiler to NULL. At runtime when the set of + valid vtable pointers for a virtual class, e.g. class Foo, is built, + the vtable map variable for class Foo is made to point to the set. + During compile time, when the compiler is inserting verification + calls into the program, it passes the vtable map variable for the + appropriate class to the verification call, so that at runtime the + verification call can find the appropriate data set. + + The actual set of valid vtable pointers for a virtual class, + e.g. class Foo, cannot be built until runtime, when the vtables get + loaded into memory and their addresses are known. But the knowledge + about which vtables belong in which class' hierarchy is only known + at compile time. Therefore at compile time we collect class + hierarchy and vtable information about every virtual class, and we + generate calls to build up the data sets at runtime. To build the + data sets, we call one of the functions we add to the runtime + library, __VLTRegisterPair. __VLTRegisterPair takes two arguments, + a vtable map variable and the address of a vtable. If the vtable + map variable is currently NULL, it creates a new data set (hash + table), makes the vtable map variable point to the new data set, and + inserts the vtable address into the data set. If the vtable map + variable is not NULL, it just inserts the vtable address into the + data set. In order to make sure that our data sets are built before + any verification calls happen, we create a special constructor + initialization function for each compilation unit, give it a very + high initialization priority, and insert all of our calls to + __VLTRegisterPair into our special constructor initialization + function. + + The vtable verification feature is controlled by the flag + '-fvtable-verify='. There are three flavors of this: + '-fvtable-verify=std', '-fvtable-verify=preinit', and + '-fvtable-verify=none'. If the option '-fvtable-verfy=preinit' is + used, then our constructor initialization function gets put into the + preinit array. This is necessary if there are data sets that need + to be built very early in execution. If the constructor + initialization function gets put into the preinit array, the we also + add calls to __VLTChangePermission at the beginning and end of the + function. The call at the beginning sets the permissions on the + data sets and vtable map variables to read/write, and the one at the + end makes them read-only. If the '-fvtable-verify=std' option is + used, the constructor initialization functions are executed at their + normal time, and the __VLTChangePermission calls are handled + differently (see the comments in libstdc++-v3/libsupc++/vtv_rts.cc). + The option '-fvtable-verify=none' turns off vtable verification. + + This file contains code for the tree pass that goes through all the + statements in each basic block, looking for virtual calls, and + inserting a call to __VLTVerifyVtablePointer (with appropriate + arguments) before each one. It also contains the hash table + functions for the data structures used for collecting the class + hierarchy data and building/maintaining the vtable map variable data + are defined in gcc/tree-vtable-verify.h. These data structures are + shared with the code in the C++ front end that collects the class + hierarchy & vtable information and generates the vtable map + variables (see cp/vtable-class-hierarchy.c). This tree pass should + run just before the gimple is converted to RTL. + + Some implementation details for this pass: + + To find the all of the virtual calls, we iterate through all the + gimple statements in each basic block, looking for any call + statement with the code "OBJ_TYPE_REF". Once we have found the + virtual call, we need to find the vtable pointer through which the + call is being made, and the type of the object containing the + pointer (to find the appropriate vtable map variable). We then use + these to build a call to __VLTVerifyVtablePointer, passing the + vtable map variable, and the vtable pointer. We insert the + verification call just after the gimple statement that gets the + vtable pointer out of the object, and we update the next + statement to depend on the result returned from + __VLTVerifyVtablePointer (the vtable pointer value), to ensure + subsequent compiler phases don't remove or reorder the call (it's no + good to have the verification occur after the virtual call, for + example). To find the vtable pointer being used (and the type of + the object) we search backwards through the def_stmts chain from the + virtual call (see verify_bb_vtables for more details). */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "tree.h" +#include "cp/cp-tree.h" +#include "tm_p.h" +#include "basic-block.h" +#include "output.h" +#include "tree-flow.h" +#include "tree-dump.h" +#include "tree-pass.h" +#include "timevar.h" +#include "cfgloop.h" +#include "flags.h" +#include "tree-inline.h" +#include "tree-scalar-evolution.h" +#include "diagnostic-core.h" +#include "gimple-pretty-print.h" +#include "toplev.h" +#include "langhooks.h" + +#include "tree-vtable-verify.h" + +int total_num_virtual_calls = 0; +int total_num_verified_vcalls = 0; + +unsigned num_vtable_map_nodes = 0; +bool any_verification_calls_generated = false; + +static GTY(()) tree verify_vtbl_ptr_fndecl = NULL_TREE; + +unsigned int vtable_verify_main (void); + +/* The following few functions are for the vtbl pointer hash table + in the 'registered' field of the struct vtable_map_node. The hash + table keeps track of which vtable pointers have been used in + calls to __VLTRegisterPair with that particular vtable map variable. */ + +/* This function checks to see if a particular VTABLE_DECL and OFFSET are + already in the 'registered' hash table for NODE. */ + +bool +vtbl_map_node_registration_find (struct vtbl_map_node *node, + tree vtable_decl, + unsigned offset) +{ + struct vtable_registration key; + struct vtable_registration **slot; + + gcc_assert (node && node->registered); + + key.vtable_decl = vtable_decl; + slot = (struct vtable_registration **) htab_find_slot (node->registered, + &key, NO_INSERT); + + if (slot && (*slot) && (*slot)->offsets) + { + unsigned i; + for (i = 0; i < (*slot)->cur_offset; ++i) + if ((*slot)->offsets[i] == offset) + return true; + } + + return false; +} + +/* This function inserts VTABLE_DECL and OFFSET into the 'registered' + hash table for NODE. */ + +void +vtbl_map_node_registration_insert (struct vtbl_map_node *node, + tree vtable_decl, + unsigned offset) +{ + struct vtable_registration key; + struct vtable_registration **slot; + + if (!node || !node->registered) + return; + + key.vtable_decl = vtable_decl; + slot = (struct vtable_registration **) htab_find_slot (node->registered, + &key, INSERT); + + if (! *slot) + { + unsigned i; + struct vtable_registration *node; + node = XNEW (struct vtable_registration); + node->vtable_decl = vtable_decl; + + /* We expect the number of different offsets in any given vtable + that will be valid vtable pointers to be small (but we know + it can be greater than 1). To avoid having to resize this + very often, we randomly chose 10 as a reasonable-seeming + size. */ + node->offsets = (unsigned *) xmalloc (10 * sizeof (unsigned)); + for (i= 0; i < 10; ++i) + node->offsets[i] = 0; + node->offsets[0] = offset; + node->cur_offset = 1; + node->max_offsets = 10; + *slot = node; + } + else + { + /* We found the vtable_decl slot; we need to see if it already + contains the offset. If not, we need to add the offset. */ + unsigned i; + bool found = false; + for (i = 0; i < (*slot)->cur_offset && !found; ++i) + if ((*slot)->offsets[i] == offset) + found = true; + + if (!found) + { + /* Re-size the offset array if necessary. */ + if ((*slot)->cur_offset == (*slot)->max_offsets) + { + unsigned new_max = 2 * (*slot)->max_offsets; + (*slot)->offsets = (unsigned *) + xrealloc ((*slot)->offsets, new_max * sizeof (unsigned)); + + for (i = (*slot)->max_offsets; i < new_max; ++i) + (*slot)->offsets[i] = 0; + (*slot)->max_offsets = new_max; + } + /* Insert the new offset. */ + (*slot)->offsets[(*slot)->cur_offset] = offset; + (*slot)->cur_offset = (*slot)->cur_offset + 1; + } + } +} + +/* Hashtable functions for vtable_registration hashtables. */ + +static hashval_t +hash_vtable_registration (const void *p) +{ + const struct vtable_registration *n = (const struct vtable_registration *) p; + return (hashval_t) (DECL_UID (n->vtable_decl)); +} + +static int +eq_vtable_registration (const void *p1, const void *p2) +{ + const struct vtable_registration *n1 = + (const struct vtable_registration *) p1; + const struct vtable_registration *n2 = + (const struct vtable_registration *) p2; + return (DECL_UID (n1->vtable_decl) == DECL_UID (n2->vtable_decl)); +} + +/* End of hashtable functions for "registered" hashtables. */ + + +/* Here are the three data structures into which we insert vtable map nodes. + We use three data structures because of the vastly different ways we need + to find the nodes for various tasks (see comments in tree-vtable-verify.h + for more details. */ + +/* Vtable map variable nodes stored in a hash table. */ +static htab_t vtbl_map_hash = NULL; + +/* Vtable map variable nodes stored in a linked list. */ +struct vtbl_map_node *vtbl_map_nodes = NULL; + +/* Vtable map variable nodes stored in an array. */ +struct vtbl_map_node **vtbl_map_nodes_array = NULL; + +/* This function take a vtbl_map_node, NODE, and inserts it in the + array of vtable map nodes, using it's uid as the position in the + array into which to insert it. (The uids are unique and + monotonically grow as the nodes are generated.) */ + +static void +vtable_map_array_insert (struct vtbl_map_node *node) +{ + static unsigned array_size = 0; + unsigned i; + + /* If the array is NULL, allocate it with an initial size of 16. */ + if (vtbl_map_nodes_array == NULL || array_size == 0) + { + array_size = 16; + vtbl_map_nodes_array = (struct vtbl_map_node **) + xmalloc (array_size * sizeof (struct vtbl_map_node *)); + memset (vtbl_map_nodes_array, 0, + array_size * sizeof (struct vtbl_map_node *)); + } + else if (node->uid >= array_size) + { + /* Check to see if the array is large enough to hold another + node; resize it if it is not. */ + unsigned new_size = 2 * array_size; + vtbl_map_nodes_array = (struct vtbl_map_node **) + xrealloc (vtbl_map_nodes_array, + new_size * sizeof (struct vtbl_map_node *)); + + for (i = array_size; i < new_size; ++i) + vtbl_map_nodes_array[i] = NULL; + + array_size = new_size; + } + + gcc_assert (node->uid < array_size + && vtbl_map_nodes_array[node->uid] == NULL); + + /* Insert the node into the array. */ + vtbl_map_nodes_array[node->uid] = node; +} + +/* Hashtable functions for vtbl_map_hash. */ + +/* Returns a hash code for P. */ + +static hashval_t +hash_vtbl_map_node (const void *p) +{ + const struct vtbl_map_node *n = (const struct vtbl_map_node *) p; + return (hashval_t) IDENTIFIER_HASH_VALUE (n->class_name); +} + +/* Returns nonzero if P1 and P2 are equal. */ + +static int +eq_vtbl_map_node (const void *p1, const void *p2) +{ + const struct vtbl_map_node *n1 = (const struct vtbl_map_node *) p1; + const struct vtbl_map_node *n2 = (const struct vtbl_map_node *) p2; + return (IDENTIFIER_HASH_VALUE (n1->class_name) == + IDENTIFIER_HASH_VALUE (n2->class_name)); +} + +/* Return vtbl_map node for CLASS_TYPE without creating a new one. */ + +struct vtbl_map_node * +vtbl_map_get_node (tree class_type) +{ + struct vtbl_map_node key; + struct vtbl_map_node **slot; + + tree class_type_decl; + tree type_decl_type; + tree class_name; + unsigned int save_quals; + unsigned int null_quals = TYPE_UNQUALIFIED; + + if (!vtbl_map_hash) + return NULL; + + gcc_assert (TREE_CODE (class_type) == RECORD_TYPE); + + /* Use the mangled name for the *unqualified* class as the key in our + hashtable. First we need to find and temporarily remove any type + qualifiers on the type. */ + + /* Find the TYPE_DECL for the class. */ + if (TREE_CHAIN (class_type)) + class_type_decl = TREE_CHAIN (class_type); + else + class_type_decl = TYPE_NAME (class_type); + + /* Temporarily remove any qualifiers on the type. */ + type_decl_type = TREE_TYPE (class_type_decl); + save_quals = TYPE_QUALS (type_decl_type); + reset_type_qualifiers (null_quals, type_decl_type); + + /* Get the mangled name for the unqualified type. */ + class_name = DECL_ASSEMBLER_NAME (class_type_decl); + + /* Restore any type qualifiers. */ + reset_type_qualifiers (save_quals, type_decl_type); + + key.class_name = class_name; + slot = (struct vtbl_map_node **) htab_find_slot (vtbl_map_hash, &key, + NO_INSERT); + if (!slot) + return NULL; + return *slot; +} + +/* Return vtbl_map node assigned to BASE_CLASS_TYPE. Create new one + when needed. */ + +struct vtbl_map_node * +find_or_create_vtbl_map_node (tree base_class_type) +{ + struct vtbl_map_node key; + struct vtbl_map_node *node; + struct vtbl_map_node **slot; + unsigned i; + tree class_type_decl; + tree type_decl_type; + unsigned int save_quals; + unsigned int null_quals = TYPE_UNQUALIFIED; + /* Our data shows 90% of classes have no more than 4 parents or children, + so we will use 4 as our default hierarchy array size. */ + unsigned int default_array_size = 4; + + if (!vtbl_map_hash) + vtbl_map_hash = htab_create (10, hash_vtbl_map_node, + eq_vtbl_map_node, NULL); + + if (TREE_CHAIN (base_class_type)) + class_type_decl = TREE_CHAIN (base_class_type); + else + class_type_decl = TYPE_NAME (base_class_type); + + /* Temporarily remove any type qualifiers on type. */ + type_decl_type = TREE_TYPE (class_type_decl); + save_quals = TYPE_QUALS (type_decl_type); + reset_type_qualifiers (null_quals, type_decl_type); + + key.class_name = DECL_ASSEMBLER_NAME (class_type_decl); + slot = (struct vtbl_map_node **) htab_find_slot (vtbl_map_hash, &key, + INSERT); + + /* Restore any type qualifiers. */ + reset_type_qualifiers (save_quals, type_decl_type); + + if (*slot) + return *slot; + + node = XNEW (struct vtbl_map_node); + node->vtbl_map_decl = NULL_TREE; + node->class_name = key.class_name; + node->uid = num_vtable_map_nodes++; + + node->class_info = XNEW (struct vtv_graph_node); + node->class_info->class_type = base_class_type; + node->class_info->class_uid = node->uid; + node->class_info->max_parents = default_array_size; + node->class_info->max_children = default_array_size; + node->class_info->num_parents = 0; + node->class_info->num_children = 0; + node->class_info->num_processed_children = 0; + node->class_info->parents = + (struct vtv_graph_node **) + xmalloc (default_array_size * sizeof (struct vtv_graph_node *)); + node->class_info->children = + (struct vtv_graph_node **) + xmalloc (default_array_size * sizeof (struct vtv_graph_node *)); + + for (i = 0; i < default_array_size; ++i) + { + node->class_info->parents[i] = NULL; + node->class_info->children[i] = NULL; + } + + node->registered = htab_create (16, hash_vtable_registration, + eq_vtable_registration, NULL); + node->is_used = false; + node->next = vtbl_map_nodes; + if (vtbl_map_nodes) + vtbl_map_nodes->prev = node; + + vtable_map_array_insert (node); + + vtbl_map_nodes = node; + *slot = node; + return node; +} + +/* End of hashtable functions for vtable_map variables hash table. */ + +/* As part of vtable verification the compiler generates and inserts + calls to __VLTVerifyVtablePointer, which is in libstdc++. This + function builds and initializes the function decl that is used + in generating those function calls. + + In addition to __VLTVerifyVtablePointer there is also + __VLTVerifyVtablePointerDebug which can be used in place of + __VLTVerifyVtablePointer, and which takes extra parameters and + outputs extra information, to help debug problems. The debug + version of this function is generated and used if vtv_debug is + true. + + The signatures for these functions are: + + void * __VLTVerifyVtablePointer (void **, void*); + void * __VLTVerifyVtablePointerDebug (void**, void *, char *, int, char *, + int); +*/ + +static void +build_vtable_verify_fndecl (void) +{ + tree void_ptr_type = build_pointer_type (void_type_node); + tree arg_types = NULL_TREE; + tree func_type = NULL_TREE; + struct lang_decl *ld; +#ifdef VTV_DEBUG + tree const_char_ptr_type = build_pointer_type + (build_qualified_type (char_type_node, + TYPE_QUAL_CONST)); +#endif + + if (verify_vtbl_ptr_fndecl != NULL_TREE) + return; + + ld = ggc_alloc_cleared_lang_decl (sizeof (struct lang_decl_fn)); + ld->u.base.selector = 1; + + arg_types = build_tree_list (NULL_TREE, build_pointer_type (void_ptr_type)); + arg_types = chainon (arg_types, build_tree_list (NULL_TREE, + const_ptr_type_node)); + +#ifdef VTV_DEBUG + /* Arg types for the debugging version of function. */ + arg_types = chainon (arg_types, build_tree_list (NULL_TREE, + const_char_ptr_type)); + arg_types = chainon (arg_types, build_tree_list (NULL_TREE, + const_char_ptr_type)); +#endif + + arg_types = chainon (arg_types, build_tree_list (NULL_TREE, void_type_node)); + func_type = build_function_type (const_ptr_type_node, arg_types); + +#ifdef VTV_DEBUG + /* const void * + __VLTVerifyVtablePointerDebug (void ** set_handle_ptr, + const void * vtable_ptr, + const char * set_symbol_name, + const char * vtable_name) */ + + verify_vtbl_ptr_fndecl = build_fn_decl ("__VLTVerifyVtablePointerDebug", + func_type); +#else + /* const void * + __VLTVerifyVtablePointerDebug (void ** set_handle_ptr, + const void * vtable_ptr) */ + + verify_vtbl_ptr_fndecl = build_fn_decl ("__VLTVerifyVtablePointer", + func_type); +#endif + + TREE_NOTHROW (verify_vtbl_ptr_fndecl) = 1; + DECL_ATTRIBUTES (verify_vtbl_ptr_fndecl) + = tree_cons (get_identifier ("leaf"), NULL, + DECL_ATTRIBUTES (verify_vtbl_ptr_fndecl)); + DECL_PURE_P (verify_vtbl_ptr_fndecl) = 1; + TREE_PUBLIC (verify_vtbl_ptr_fndecl) = 1; +#ifdef VTV_STATIC_VERIFY + DECL_VISIBILITY (verify_vtbl_ptr_fndecl) = 1; +#endif + DECL_PRESERVE_P (verify_vtbl_ptr_fndecl) = 1; + DECL_LANG_SPECIFIC (verify_vtbl_ptr_fndecl) = ld; + SET_DECL_LANGUAGE (verify_vtbl_ptr_fndecl, lang_cplusplus); +} + +/* This function takes a tree type, TYPE_NODE, and a set of type qualifier + flags, NEW_QUALS, and set the type qualifiers on TYPE_NODE to those + specified in NEW_QUALS. */ + +void +reset_type_qualifiers (unsigned int new_quals, tree type_node) +{ + if (new_quals & TYPE_QUAL_CONST) + TYPE_READONLY (type_node) = 1; + else + TYPE_READONLY (type_node) = 0; + + if (new_quals & TYPE_QUAL_VOLATILE) + TYPE_VOLATILE (type_node) = 1; + else + TYPE_VOLATILE (type_node) = 0; + + if (new_quals & TYPE_QUAL_RESTRICT) + TYPE_RESTRICT (type_node) = 1; + else + TYPE_RESTRICT (type_node) = 0; +} + +/* This function takes the LHS of a gimple assignment statement, which + has already been verified to be an ssa_name, finds the type name of + it, and checks to see if the name of the type is + "__vtbl_ptr_type". */ + +static bool +type_name_is_vtable_pointer (tree lhs) +{ + tree node; + + if (!POINTER_TYPE_P (TREE_TYPE (lhs)) + || !POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (lhs)))) + return false; + + node = TREE_TYPE (TREE_TYPE (lhs)); + + if (TYPE_NAME (node)) + { + if (TREE_CODE (TYPE_NAME (node)) == IDENTIFIER_NODE) + return (strcmp (IDENTIFIER_POINTER (TYPE_NAME (node)), + "__vtbl_ptr_type") == 0); + else if (TREE_CODE (TYPE_NAME (node)) == TYPE_DECL + && DECL_NAME (TYPE_NAME (node))) + return (strcmp (IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (node))), + "__vtbl_ptr_type") == 0); + else + return false; + } + + return false; +} + +/* Given a gimple STMT, this function checks to see if the statement + is an assignment, the rhs of which is getting the vtable pointer + value out of an object. (i.e. it's the value we need to verify + because its the vtable pointer that will be used for a virtual + call). */ + +static bool +is_vtable_assignment_stmt (gimple stmt) +{ + + if (gimple_code (stmt) != GIMPLE_ASSIGN) + return false; + else + { + tree lhs = gimple_assign_lhs (stmt); + tree rhs = gimple_assign_rhs1 (stmt); + + if (TREE_CODE (lhs) != SSA_NAME) + return false; + + if (! type_name_is_vtable_pointer (lhs)) + return false; + + if (TREE_CODE (rhs) != COMPONENT_REF) + return false; + + if (! (TREE_OPERAND (rhs, 1)) + || (TREE_CODE (TREE_OPERAND (rhs, 1)) != FIELD_DECL)) + return false; + + if (! (DECL_NAME (TREE_OPERAND (rhs, 1))) + || (strncmp (IDENTIFIER_POINTER (DECL_NAME (TREE_OPERAND (rhs, 1))), + "_vptr.", 6) != 0)) + return false; + } + + return true; +} + +/* Given the BINFO for a virtual type, gets the vtable decl for that + BINFO. */ + +static tree +my_get_vtbl_decl_for_binfo (tree binfo) +{ + tree decl; + + decl = BINFO_VTABLE (binfo); + if (decl && TREE_CODE (decl) == POINTER_PLUS_EXPR) + { + gcc_assert (TREE_CODE (TREE_OPERAND (decl, 0)) == ADDR_EXPR); + decl = TREE_OPERAND (TREE_OPERAND (decl, 0), 0); + } + if (decl) + gcc_assert (TREE_CODE (decl) == VAR_DECL); + + return decl; +} + +/* Given a virtual funciton call (FNCALL) in gimple STMT, this + function chains back through the def stmts of the virtual call, + looking for the most recent statement that assigned a _vptr field + out of the object used in the function call. This is the vtable + pointer value that needs to be verified. */ + +static gimple +find_vtable_ptr_assignment_stmt (tree fncall, gimple stmt, gimple *prev_use) +{ + gimple def_stmt; + + /* Find the first operand of the function call; this should be the + SSA_NAME variable that contains the pointer to the virtual + function. */ + if (TREE_OPERAND (fncall, 0) + && TREE_CODE (TREE_OPERAND (fncall, 0)) == SSA_NAME) + { + tree rhs = NULL_TREE; + tree func_ptr_var = NULL_TREE; + + func_ptr_var = TREE_OPERAND (fncall, 0); + *prev_use = stmt; + + /* Find the statment that calculated the function pointer, i.e. + func_ptr_var = *(vtbl_ptr + offset). */ + + def_stmt = SSA_NAME_DEF_STMT (func_ptr_var); + + /* Search backwards through the def_stmt chain, to try to find + the assignment statement where the rhs of the assignment + contains the "._vptr" field (the vtable pointer). */ + + /* TODO: In the future we need to handle def_stmts that are phi + stmts. For now we punt on them. */ + + if (def_stmt && gimple_code (def_stmt) == GIMPLE_PHI) + return NULL; + + gcc_assert ((def_stmt != NULL) + && (gimple_code (def_stmt) == GIMPLE_ASSIGN)); + + + /* def_stmt should now be of the form: var1 = *var2 + (dereferencding var 2 to get the actual address of the + virtual method. We need to extract 'var2' from the rhs, and + find its def_stmt. */ + + rhs = gimple_assign_rhs1 (def_stmt); + if (rhs + && TREE_CODE (rhs) == MEM_REF + && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME) + { + *prev_use = def_stmt; + def_stmt = SSA_NAME_DEF_STMT (TREE_OPERAND (rhs, 0)); + } + + if (gimple_code (def_stmt) == GIMPLE_PHI) + def_stmt = NULL; + + /* Search backwards, through the SSA_NAME_DEF_STMT statements in + the RHS of our def_stmt(s), until we find an assignment + statement whose lhs is an SSA_NAME and whose rhs contains a + "._vptr" field. */ + + while (def_stmt + && !is_vtable_assignment_stmt (def_stmt)) + { + tree lhs = gimple_assign_lhs (def_stmt); + if (!lhs || TREE_CODE (lhs) != SSA_NAME) + { + def_stmt = NULL; + break; + } + + /* Try to find the SSA_NAME variable in the RHS; start by trying + the first (only) rhs piece. */ + if (gimple_assign_rhs1 (def_stmt) + && TREE_CODE (gimple_assign_rhs1 (def_stmt)) == SSA_NAME) + { + *prev_use = def_stmt; + rhs = gimple_assign_rhs1 (def_stmt); + + /* Get our new def_stmt. */ + def_stmt = SSA_NAME_DEF_STMT (rhs); + } + /* The first piece didn't work, so try the second piece, if + there is one. */ + else if ((get_gimple_rhs_class (gimple_expr_code (def_stmt)) + != GIMPLE_SINGLE_RHS) + && gimple_assign_rhs2 (def_stmt) + && TREE_CODE (gimple_assign_rhs2 (def_stmt)) == SSA_NAME) + { + *prev_use = def_stmt; + rhs = gimple_assign_rhs2 (def_stmt); + def_stmt = SSA_NAME_DEF_STMT (rhs); + } + else + def_stmt = NULL; + + if (def_stmt && gimple_code (def_stmt) == GIMPLE_PHI) + def_stmt = NULL; + + if (!def_stmt) + break; + } + } + + return def_stmt; +} + +/* This function takes a gimple statment (STMT) and an iteratpr + (GSI_TEMP) and makes the iterator point to the STMTs location in + its basic block's sequence of statements.*/ + +static bool +find_stmt_in_bb_stmts (gimple stmt, gimple_stmt_iterator *gsi_temp) +{ + basic_block def_bb; + gimple_seq def_bb_stmts; + gimple tmp_stmt; + bool found = false; + + def_bb = gimple_bb (stmt); + def_bb_stmts = bb_seq (def_bb); + *gsi_temp = gsi_start (def_bb_stmts); + for (; !gsi_end_p (*gsi_temp) && !found; gsi_next (gsi_temp)) + { + tmp_stmt = gsi_stmt (*gsi_temp); + if (tmp_stmt == stmt) + { + found = true; + break; /* Exit loop immediately; do no do another gsi_next. */ + } + } + + return found; +} + +/* This function attempts to recover the declared class of an object + that is used in making a virtual call. First we try looking + directly at the THIS_OBJECT itself. If that does not work, we try to + get the type from the gimple assignment statement that extracts the + vtable pointer from the object (DEF_STMT). The gimple + statment usually looks something like this: + + D.2201_4 = MEM[(struct Event *)this_1(D)]._vptr.Event */ + +static tree +extract_object_class_type (tree this_object, gimple def_stmt) +{ + tree object_rhs = TREE_TYPE (this_object); + tree rhs = NULL_TREE; + + /* First try to get the type out of the 'this' object. */ + + if (POINTER_TYPE_P (object_rhs) + && TREE_CODE (TREE_TYPE (object_rhs)) == RECORD_TYPE) + rhs = TREE_TYPE (object_rhs); + else if (TREE_CODE (object_rhs) == REFERENCE_TYPE + && TREE_CODE (TREE_TYPE (object_rhs)) == RECORD_TYPE) + rhs = TREE_TYPE (object_rhs); + + /* Check to see if the type from the 'this' object will work or not. + Sometimes the type of the 'this' object is not usable (usually + due to optimizations which change the type of the 'this' object + to 'void *'); try to get the type out of the type cast in the rhs + of the vtable pointer assignment statement. */ + + if (!rhs || TREE_CODE (rhs) != RECORD_TYPE || !TYPE_BINFO (rhs) + || !BINFO_VTABLE (TYPE_BINFO (rhs)) + || !my_get_vtbl_decl_for_binfo (TYPE_BINFO (rhs))) + { + /* The type of the 'this' object did not work, so try to find + the type from the rhs of the def_stmt. Try to find and + extract the type cast from that stmt. */ + + rhs = gimple_assign_rhs1 (def_stmt); + if (TREE_CODE (rhs) == COMPONENT_REF) + { + while (TREE_CODE (rhs) == COMPONENT_REF + && (TREE_CODE (TREE_OPERAND (rhs, 0)) == COMPONENT_REF)) + rhs = TREE_OPERAND (rhs, 0); + + if (TREE_CODE (rhs) == COMPONENT_REF + && TREE_CODE (TREE_OPERAND (rhs, 0)) == MEM_REF + && TREE_CODE (TREE_TYPE (TREE_OPERAND (rhs, 0)))== RECORD_TYPE) + rhs = TREE_TYPE (TREE_OPERAND (rhs, 0)); + else + rhs = NULL_TREE; + } + else + rhs = NULL_TREE; + } + + return rhs; +} + +/* Search through all the statements in a basic block (BB), searching + for virtual method calls. For each virtual method dispatch, find + the vptr value used, and the statically declared type of the + object; retrieve the vtable map variable for the type of the + object; generate a call to __VLTVerifyVtablePointer; and insert the + generated call into the basic block, after the point where the vptr + value is gotten out of the object and before the virtual method + dispatch. Make the virtual method dispatch depend on the return + value from the verification call, so that subsequent optimizations + cannot reorder the two calls. */ + +static void +verify_bb_vtables (basic_block bb) +{ + gimple_seq stmts; + gimple stmt = NULL; + gimple_stmt_iterator gsi_vtbl_assign; + gimple_stmt_iterator gsi_virtual_call; + tree this_object; + + stmts = bb_seq (bb); + gsi_virtual_call = gsi_start (stmts); + this_object = NULL_TREE; + for (; !gsi_end_p (gsi_virtual_call); gsi_next (&gsi_virtual_call)) + { + stmt = gsi_stmt (gsi_virtual_call); + if (gimple_code (stmt) == GIMPLE_CALL) + { + /* Found a call; see if it's a virtual call. */ + tree fncall = gimple_call_fn (stmt); + if (TREE_CODE (fncall) == OBJ_TYPE_REF) + { + bool found = false; + tree lhs = NULL_TREE; + gimple vptr_stmt; + gimple prev_use = NULL; + + /* We have found a virtual call; search backwards, + through the object's SSA_NAME_DEF_STMTs, to find the place + were the vtable pointer is gotten out of the object + (also need to find the object's declared static + type). */ + + total_num_virtual_calls++; + + /* The first argument to the function must be "this", a + pointer to the object itself. */ + this_object = gimple_call_arg (stmt, 0); + + /* Find the previousg statement that gets the "_vptr" + field out of the object. */ + vptr_stmt = find_vtable_ptr_assignment_stmt (fncall, + stmt, + &prev_use); + + if (!vptr_stmt) + continue; + + lhs = gimple_assign_lhs (vptr_stmt); + + /* Find the vptr_stmt in its basic block's sequence, so + we have an insertion point for adding our + verification call. */ + found = find_stmt_in_bb_stmts (vptr_stmt, &gsi_vtbl_assign); + + if (found) + { + tree vtbl_var_decl = NULL_TREE; + tree vtbl = NULL_TREE; + gimple_seq pre_p = NULL; + struct vtbl_map_node *vtable_map_node = NULL; + tree vtbl_decl = NULL_TREE; + tree expr_tree = NULL_TREE; + struct gimplify_ctx gctx; + const char *vtable_name = ""; + int len1 = 0; + int len2 = 0; + + /* Now we have found the virtual method dispatch and + the preceding access of the _vptr.* field... Next + we need to find the statically declared type of + the object, so we can find and use the right + vtable map variable in the verification call. */ + tree class_type = extract_object_class_type (this_object, + vptr_stmt); + + /* Make sure we found a valid type. */ + gcc_assert (class_type + && (TREE_CODE (class_type) == RECORD_TYPE) + && TYPE_BINFO (class_type)); + + /* Get the vtable VAR_DECL for the type. */ + vtbl_var_decl = my_get_vtbl_decl_for_binfo + (TYPE_BINFO (class_type)); + vtbl = BINFO_VTABLE (TYPE_BINFO (class_type)); + + gcc_assert (vtbl_var_decl && vtbl); + + vtbl_decl = vtbl_var_decl; + + if (POINTER_TYPE_P (TREE_TYPE (vtbl))) + force_gimple_operand (vtbl, &pre_p, 1, NULL); + + vtable_map_node = vtbl_map_get_node (class_type); + + /* Build the FUNC_DECL to use for the verification + call. */ + build_vtable_verify_fndecl (); + gcc_assert (verify_vtbl_ptr_fndecl); + + /* Given the vtable pointer for the base class of + the object, build the call to + __VLTVerifyVtablePointer to verify that the + object's vtable pointer (contained in lhs) is in + the set of valid vtable pointers for the base + class. */ + + if (vtable_map_node && vtable_map_node->vtbl_map_decl) + { + vtable_map_node->is_used = true; + vtbl_var_decl = vtable_map_node->vtbl_map_decl; + if (!var_ann (vtbl_var_decl)) + add_referenced_var (vtbl_var_decl); + + if (TREE_CODE (vtbl_decl) == VAR_DECL) + vtable_name = IDENTIFIER_POINTER + (DECL_NAME (vtbl_decl)); + + push_gimplify_context (&gctx); + len1 = strlen (IDENTIFIER_POINTER + (DECL_NAME (vtbl_var_decl))); + len2 = strlen (vtable_name); + + /* Call different routines if we are interested + in trace information to debug problems. */ +#ifdef VTV_DEBUG + expr_tree = build_call_expr + (verify_vtbl_ptr_fndecl, 4, + build1 (ADDR_EXPR, + TYPE_POINTER_TO + (TREE_TYPE (vtbl_var_decl)), + vtbl_var_decl), + SSA_NAME_VAR (lhs), + build_string_literal + (len1 + 1, + IDENTIFIER_POINTER + (DECL_NAME + (vtbl_var_decl))), + build_string_literal (len2 + 1, + vtable_name)); +#else + expr_tree = build_call_expr + (verify_vtbl_ptr_fndecl, 2, + build1 (ADDR_EXPR, + TYPE_POINTER_TO + (TREE_TYPE (vtbl_var_decl)), + vtbl_var_decl), + SSA_NAME_VAR (lhs)); +#endif + + /* Assign the result of the call to the original + variable receiving the assignment of the + object's vtable pointer; mark that variable + to be updated by update_ssa. */ + + mark_sym_for_renaming (SSA_NAME_VAR (lhs)); + force_gimple_operand (expr_tree, &pre_p, 1, + SSA_NAME_VAR (lhs)); + + /* Insert the new call just after the original + assignment of the object's vtable pointer. */ + + pop_gimplify_context (NULL); + gsi_insert_seq_after (&gsi_vtbl_assign, pre_p, + GSI_NEW_STMT); + any_verification_calls_generated = true; + total_num_verified_vcalls++; + } + } + } + } + } +} + +/* Main function, called from pass->excute(). Loop through all the + basic blocks in the current function, passing them to + verify_bb_vtables, which searches for virtual calls, and inserts + calls to __VLTVerifyVtablePointer. */ + +unsigned int +vtable_verify_main (void) +{ + unsigned int ret = 1; + basic_block bb; + + FOR_ALL_BB (bb) + verify_bb_vtables (bb); + + return ret; +} + +/* Gate function for the pass. */ + +static bool +gate_tree_vtable_verify (void) +{ + return (flag_vtable_verify + && (strcmp (lang_hooks.name, "GNU C++") == 0)); +} + +/* Definition of this optimization pass. */ + +struct gimple_opt_pass pass_vtable_verify = +{ + { + GIMPLE_PASS, + "vtable-verify", /* name */ + gate_tree_vtable_verify, /* gate */ + vtable_verify_main, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + TV_VTABLE_VERIFICATION, /* tv_id */ + PROP_cfg | PROP_ssa, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_dump_func | TODO_update_ssa + | TODO_ggc_collect /* todo_flags_finish */ + } +}; + +#include "gt-tree-vtable-verify.h" Index: gcc/tree-vtable-verify.h =================================================================== --- gcc/tree-vtable-verify.h (revision 0) +++ gcc/tree-vtable-verify.h (revision 0) @@ -0,0 +1,146 @@ +/* Interprocedural constant propagation + Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 + Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +/* Virtual Table Pointer Security. */ + +#ifndef TREE_VTABLE_VERIFY_H +#define TREE_VTABLE_VERIFY_H + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "timevar.h" +#include "cpplib.h" +#include "tree.h" +#include "hashtab.h" +#include "sbitmap.h" + +/* Global variable keeping track of how many vtable map variables we + have created. */ +extern unsigned num_vtable_map_nodes; + +/* Global variable that records whether or not any vtable verification + calls have been generated. */ +extern bool any_verification_calls_generated; + +/* Keep track of how many virtual calls we are actually verifying. */ +extern int total_num_virtual_calls; +extern int total_num_verified_vcalls; + +/* Each vtable map variable corresponds to a virtual class. Each + vtable map variable has a hash table associated with it, that keeps + track of the vtable pointers for which we have generated a call to + __VLTRegisterPair (with the current vtable map variable). This is + the hash table node that is used for each entry in this hash table + of vtable pointers. + + Sometimes there are multiple valid vtable pointer entries that use + the same vtable pointer decl with different offsets. Therefore, + for each vtable pointer in the hash table, there is also an array + of offsets used with that vtable. */ + +struct vtable_registration +{ + tree vtable_decl; /* The var decl of the vtable. */ + unsigned max_offsets; /* The allocated size of the offsets array. */ + unsigned cur_offset; /* The next availabe entry in the offsets + array. */ + unsigned *offsets; /* The offsets array. */ +}; + +/* This struct is used to represent the class hierarchy information + that we need. Each vtable map variable has an associated class + hierarchy node (struct vtv_graph_node). Note: In this struct, + 'children' means immediate descendants in the class hierarchy; + 'descendant' means any descendant however many levels deep. */ + +struct vtv_graph_node { + tree class_type; /* The record_type of the class. */ + unsigned class_uid; /* A unique, monotonically + ascending id for class node. + Each vtable map node also has + an id. The class uid is the + same as the vtable map node id + for nodes corresponding to the + same class. */ + unsigned max_parents; /* Allocated size of the parents array. */ + unsigned max_children; /* Allocated size of the children array. */ + unsigned num_parents; /* # of entries in the parents array. */ + unsigned num_children; /* # of entries in the children array. */ + unsigned num_processed_children; /* # of children for whom we have + computed the class hierarchy + transitive closure. */ + struct vtv_graph_node **parents; /* Array of parents in the graph. */ + struct vtv_graph_node **children; /* Array of children in the graph. */ + sbitmap descendants; /* Bitmap representing all this node's + descendants in the graph. */ +}; + +/* This is the node used for our hashtable of vtable map variable + information. When we create a vtable map variable (var decl) we + put it into one of these nodes; create a corresponding + vtv_graph_node for our class hierarchy info and store that in this + node; generate a unique (monotonically ascending) id for both the + vtbl_map_node and the vtv_graph_node; and insert the node into + THREE data structures (to make it easy to find in several different + ways): 1). A hash table ("vtbl_map_hash" in tree-vtable-verify.c). + This gives us an easy way to check to see if we already have a node + for the vtable map variable or not. 2). A linked list of all + vtbl_map_nodes ("vtbl_map_nodes") for easy iteration through all of + them; and 3). An array of vtbl_map_nodes, where the array index + corresponds to the unique id of the vtbl_map_node, which gives us + an easy way to use bitmaps to represent and find the vtable map + nodes. */ + +struct vtbl_map_node { + tree vtbl_map_decl; /* The var decl for the vtable map + variable. */ + tree class_name; /* The DECL_ASSEMBLER_NAME of the + class. */ + struct vtv_graph_node *class_info; /* Our class hierarchy info for the + class. */ + unsigned uid; /* The unique id for the vtable map + variable. */ + struct vtbl_map_node *next, *prev; /* Pointers for the linked list + structure. */ + htab_t registered; /* Hashtable of vtable pointers for which we have + generated a _VLTRegisterPair call with this vtable + map variable. */ + bool is_used; /* Boolean indicating if we used this vtable map + variable in a call to __VLTVerifyVtablePointer. */ +}; + +/* The global linked list of vtbl_map_nodes. */ +extern struct vtbl_map_node *vtbl_map_nodes; + +/* The global array of vtbl_map_nodes. */ +extern struct vtbl_map_node **vtbl_map_nodes_array; + +extern struct vtbl_map_node *vtbl_map_get_node (tree); +extern struct vtbl_map_node *find_or_create_vtbl_map_node (tree); +extern void vtbl_map_node_class_insert (struct vtbl_map_node *, unsigned); +extern bool vtbl_map_node_registration_find (struct vtbl_map_node *, + tree, unsigned); +extern void vtbl_map_node_registration_insert (struct vtbl_map_node *, + tree, unsigned); +extern void reset_type_qualifiers (unsigned int, tree); + +#endif /* TREE_VTABLE_VERIFY_H */ Index: gcc/common.opt =================================================================== --- gcc/common.opt (revision 195903) +++ gcc/common.opt (working copy) @@ -2368,6 +2368,22 @@ Enum(symbol_visibility) String(hidden) V EnumValue Enum(symbol_visibility) String(protected) Value(VISIBILITY_PROTECTED) +fvtable-verify= +Common Joined RejectNegative Enum(vtv_priority) Var(flag_vtable_verify) Init(VTV_NO_PRIORITY) +Validate vtable pointers before using them. + +Enum +Name(vtv_priority) Type(enum vtv_priority) UnknownError(unknown vtable verify initialization priority %qs) + +EnumValue +Enum(vtv_priority) String(none) Value(VTV_NO_PRIORITY) + +EnumValue +Enum(vtv_priority) String(std) Value(VTV_STANDARD_PRIORITY) + +EnumValue +Enum(vtv_priority) String(preinit) Value(VTV_PREINIT_PRIORITY) + fvpt Common Report Var(flag_value_profile_transformations) Optimization Use expression value profiles in optimizations Index: gcc/varasm.c =================================================================== --- gcc/varasm.c (revision 195903) +++ gcc/varasm.c (working copy) @@ -2064,13 +2064,43 @@ assemble_variable (tree decl, int top_le assemble_noswitch_variable (decl, name, sect); else { - switch_to_section (sect); + if (sect->named.name + && (strcmp (sect->named.name, ".vtable_map_vars") == 0)) + { +#if defined (OBJECT_FORMAT_ELF) + targetm.asm_out.named_section (sect->named.name, + sect->named.common.flags + | SECTION_LINKONCE, + DECL_NAME (decl)); + in_section = sect; +#else + switch_to_section (sect); +#endif + } + else + switch_to_section (sect); if (DECL_ALIGN (decl) > BITS_PER_UNIT) ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (DECL_ALIGN_UNIT (decl))); assemble_variable_contents (decl, name, dont_output_data); } } +/* Given a function declaration (FN_DECL), this function assembles the + function into the .preinit_array section. */ + +void +assemble_vtv_preinit_initializer (tree fn_decl) +{ + section *sect; + unsigned flags = SECTION_WRITE; + rtx symbol = XEXP (DECL_RTL (fn_decl), 0); + + flags |= SECTION_NOTYPE; + sect = get_section (".preinit_array", flags, fn_decl); + switch_to_section (sect); + assemble_addr_to_section (symbol, sect); +} + /* Return 1 if type TYPE contains any pointers. */ static int @@ -6338,6 +6368,9 @@ default_section_type_flags (tree decl, c if (decl && DECL_ONE_ONLY (decl)) flags |= SECTION_LINKONCE; + if (strcmp (name, ".vtable_map_vars") == 0) + flags |= SECTION_LINKONCE; + if (decl && TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL_P (decl)) flags |= SECTION_TLS | SECTION_WRITE; Index: gcc/output.h =================================================================== --- gcc/output.h (revision 195903) +++ gcc/output.h (working copy) @@ -215,6 +215,10 @@ extern void assemble_end_function (tree, initial value (that will be done by the caller). */ extern void assemble_variable (tree, int, int, int); +/* Put the vtable verification constructor initialization function + into the preinit array. */ +extern void assemble_vtv_preinit_initializer (tree); + /* Compute the alignment of variable specified by DECL. DONT_OUTPUT_DATA is from assemble_variable. */ extern void align_variable (tree decl, bool dont_output_data); Index: gcc/Makefile.in =================================================================== --- gcc/Makefile.in (revision 195903) +++ gcc/Makefile.in (working copy) @@ -866,6 +866,7 @@ TREE_H = tree.h all-tree.def tree.def c- $(INPUT_H) statistics.h $(VEC_H) treestruct.def $(HASHTAB_H) \ double-int.h alias.h $(SYMTAB_H) $(FLAGS_H) vecir.h \ $(REAL_H) $(FIXED_VALUE_H) +CP_TREE_H = cp/cp-tree.h REGSET_H = regset.h $(BITMAP_H) hard-reg-set.h BASIC_BLOCK_H = basic-block.h $(PREDICT_H) $(VEC_H) $(FUNCTION_H) cfghooks.h GIMPLE_H = gimple.h gimple.def gsstruct.def pointer-set.h $(VEC_H) \ @@ -1446,6 +1447,7 @@ OBJS = \ tree-vect-loop-manip.o \ tree-vect-slp.o \ tree-vectorizer.o \ + tree-vtable-verify.o \ tree-vrp.o \ tree.o \ value-prof.o \ @@ -2692,6 +2694,12 @@ tree-vectorizer.o: tree-vectorizer.c $(C $(TM_H) $(GGC_H) $(TREE_H) $(DIAGNOSTIC_H) $(TREE_FLOW_H) $(TREE_DUMP_H) \ $(CFGLOOP_H) $(TREE_PASS_H) $(TREE_VECTORIZER_H) $(TIMEVAR_H) \ tree-pretty-print.h +tree-vtable-verify.o: tree-vtable-verify.c tree-vtable-verify.h +# $(CONFIG_H) \ +# $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) $(CP_TREE_H) $(TM_P_H) \ +# $(BASIC_BLOCK_H) output.h $(TREE_FLOW_H) $(TREE_DUMP_H) $(TREE_PASS_H) \ +# $(TIMEVAR_H) $(CFGLOOP_H) $(FLAGS_H) $(TREE_INLINE_H) $(SCEV_H) \ +# $(DIAGNOSTIC_CORE_H) gimple-pretty-print.h toplev.h langhooks.h tree-loop-distribution.o: tree-loop-distribution.c $(CONFIG_H) $(SYSTEM_H) \ coretypes.h $(TREE_FLOW_H) $(CFGLOOP_H) $(TREE_DATA_REF_H) $(TREE_PASS_H) tree-parloops.o: tree-parloops.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ @@ -3790,6 +3798,7 @@ GTFILES = $(CPP_ID_DATA_H) $(srcdir)/inp $(srcdir)/lto-streamer.h \ $(srcdir)/target-globals.h \ $(srcdir)/ipa-inline.h \ + $(srcdir)/tree-vtable-verify.c \ @all_gtfiles@ # Compute the list of GT header files from the corresponding C sources, Index: gcc/gimple.c =================================================================== --- gcc/gimple.c (revision 195903) +++ gcc/gimple.c (working copy) @@ -3813,7 +3813,8 @@ pop: FOR_MERGING_P is true the an incomplete type and a complete type are considered different, otherwise they are considered compatible. */ -static bool +/* static bool */ +bool gimple_types_compatible_p (tree t1, tree t2) { VEC(type_pair_t, heap) *sccstack = NULL; Index: gcc/gimple.h =================================================================== --- gcc/gimple.h (revision 195903) +++ gcc/gimple.h (working copy) @@ -5221,5 +5221,7 @@ extern tree maybe_fold_and_comparisons ( extern tree maybe_fold_or_comparisons (enum tree_code, tree, tree, enum tree_code, tree, tree); +extern bool gimple_types_compatible_p (tree, tree); + bool gimple_val_nonnegative_real_p (tree); #endif /* GCC_GIMPLE_H */ Index: gcc/passes.c =================================================================== --- gcc/passes.c (revision 195903) +++ gcc/passes.c (working copy) @@ -1433,6 +1433,7 @@ init_optimization_passes (void) NEXT_PASS (pass_tm_memopt); NEXT_PASS (pass_tm_edges); } + NEXT_PASS (pass_vtable_verify); NEXT_PASS (pass_lower_complex_O0); NEXT_PASS (pass_cleanup_eh); NEXT_PASS (pass_lower_resx); Index: gcc/config/i386/gnu-user.h =================================================================== --- gcc/config/i386/gnu-user.h (revision 195903) +++ gcc/config/i386/gnu-user.h (working copy) @@ -111,7 +111,8 @@ along with GCC; see the file COPYING3. /* Similar to standard GNU userspace, but adding -ffast-math support. */ #undef ENDFILE_SPEC #define ENDFILE_SPEC \ - "%{Ofast|ffast-math|funsafe-math-optimizations:crtfastmath.o%s} \ + "%{fvtable-verify*:vtv_end.o%s} \ + %{Ofast|ffast-math|funsafe-math-optimizations:crtfastmath.o%s} \ %{mpc32:crtprec32.o%s} \ %{mpc64:crtprec64.o%s} \ %{mpc80:crtprec80.o%s} \ Index: gcc/config/i386/gnu-user64.h =================================================================== --- gcc/config/i386/gnu-user64.h (revision 195903) +++ gcc/config/i386/gnu-user64.h (working copy) @@ -88,7 +88,8 @@ see the files COPYING3 and COPYING.RUNTI /* Similar to standard GNU userspace, but adding -ffast-math support. */ #undef ENDFILE_SPEC #define ENDFILE_SPEC \ - "%{Ofast|ffast-math|funsafe-math-optimizations:crtfastmath.o%s} \ + "%{fvtable-verify*:vtv_end.o%s} \ + %{Ofast|ffast-math|funsafe-math-optimizations:crtfastmath.o%s} \ %{mpc32:crtprec32.o%s} \ %{mpc64:crtprec64.o%s} \ %{mpc80:crtprec80.o%s} \ Index: gcc/config/gnu-user.h =================================================================== --- gcc/config/gnu-user.h (revision 195903) +++ gcc/config/gnu-user.h (working copy) @@ -44,11 +44,13 @@ see the files COPYING3 and COPYING.RUNTI #if defined HAVE_LD_PIE #define GNU_USER_TARGET_STARTFILE_SPEC \ "%{!shared: %{pg|p|profile:gcrt1.o%s;pie:Scrt1.o%s;:crt1.o%s}} \ - crti.o%s %{static:crtbeginT.o%s;shared|pie:crtbeginS.o%s;:crtbegin.o%s}" + crti.o%s %{static:crtbeginT.o%s;shared|pie:crtbeginS.o%s;:crtbegin.o%s} \ + %{fvtable-verify*:vtv_start.o%s}" #else #define GNU_USER_TARGET_STARTFILE_SPEC \ "%{!shared: %{pg|p|profile:gcrt1.o%s;:crt1.o%s}} \ - crti.o%s %{static:crtbeginT.o%s;shared|pie:crtbeginS.o%s;:crtbegin.o%s}" + crti.o%s %{static:crtbeginT.o%s;shared|pie:crtbeginS.o%s;:crtbegin.o%s} \ + %{fvtable-verify*:vtv_start.o%s}" #endif #undef STARTFILE_SPEC #define STARTFILE_SPEC GNU_USER_TARGET_STARTFILE_SPEC @@ -60,7 +62,8 @@ see the files COPYING3 and COPYING.RUNTI GNU userspace "finalizer" file, `crtn.o'. */ #define GNU_USER_TARGET_ENDFILE_SPEC \ - "%{shared|pie:crtendS.o%s;:crtend.o%s} crtn.o%s" + "%{fvtable-verify*:vtv_end.o%s} \ + %{shared|pie:crtendS.o%s;:crtend.o%s} crtn.o%s" #undef ENDFILE_SPEC #define ENDFILE_SPEC GNU_USER_TARGET_ENDFILE_SPEC