aboutsummaryrefslogtreecommitdiffstats
path: root/gcc-4.9/libvtv/vtv_rts.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc-4.9/libvtv/vtv_rts.cc')
-rw-r--r--gcc-4.9/libvtv/vtv_rts.cc1523
1 files changed, 1523 insertions, 0 deletions
diff --git a/gcc-4.9/libvtv/vtv_rts.cc b/gcc-4.9/libvtv/vtv_rts.cc
new file mode 100644
index 000000000..1af000d8e
--- /dev/null
+++ b/gcc-4.9/libvtv/vtv_rts.cc
@@ -0,0 +1,1523 @@
+/* Copyright (C) 2012-2013
+ Free Software Foundation
+
+ This file is part of GCC.
+
+ GCC is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GCC is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ Under Section 7 of GPL version 3, you are granted additional
+ permissions described in the GCC Runtime Library Exception, version
+ 3.1, as published by the Free Software Foundation.
+
+ You should have received a copy of the GNU General Public License and
+ a copy of the GCC Runtime Library Exception along with this program;
+ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+ <http://www.gnu.org/licenses/>. */
+
+/* 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 polymorphic 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.
+
+ 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
+ pointers 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 help
+ 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
+ __VLTVerifyVtablePointer) with stub functions that do nothing. If
+ a programmer has a library that was built with verification, but
+ wishes to not have verification turned on, the programmer can link
+ in the vtv_stubs.so library. */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <execinfo.h>
+
+#include <unistd.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <link.h>
+#include <fcntl.h>
+#include <limits.h>
+
+/* For gthreads suppport */
+#include <bits/c++config.h>
+#include <ext/concurrence.h>
+
+#include "vtv_utils.h"
+#include "vtv_malloc.h"
+#include "vtv_set.h"
+#include "vtv_map.h"
+#include "vtv_rts.h"
+#include "vtv_fail.h"
+
+#include "vtv-change-permission.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" */
+
+/* The following variables are used only for debugging and performance
+ tuning purposes. Therefore they do not need to be "protected".
+ They cannot be used to attack the vtable verification system and if
+ they become corrupted it will not affect the correctness or
+ security of any of the rest of the vtable verification feature. */
+
+unsigned int num_calls_to_regset = 0;
+unsigned int num_calls_to_regpair = 0;
+unsigned int num_calls_to_verify_vtable = 0;
+unsigned long long regset_cycles = 0;
+unsigned long long regpair_cycles = 0;
+unsigned long long verify_vtable_cycles = 0;
+
+/* 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;
+static const int debug_functions = 0;
+static const int debug_init = 0;
+static const int debug_verify_vtable = 0;
+
+#ifdef VTV_DEBUG
+static const int debug_functions = 1;
+static const int debug_init = 1;
+static const int debug_verify_vtable = 1;
+#endif
+
+/* 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];
+
+
+#ifdef __GTHREAD_MUTEX_INIT
+static __gthread_mutex_t change_permissions_lock = __GTHREAD_MUTEX_INIT;
+#else
+static __gthread_mutex_t change_permissions_lock;
+#endif
+
+
+#ifndef VTV_STATS
+#define VTV_STATS 0
+#endif
+
+#if VTV_STATS
+
+static inline unsigned long long
+get_cycle_count (void)
+{
+ return rdtsc();
+}
+
+static inline void
+accumulate_cycle_count (unsigned long long *sum, unsigned long long start)
+{
+ unsigned long long end = rdtsc();
+ *sum = *sum + (end - start);
+}
+
+static inline void
+increment_num_calls (unsigned int *num_calls)
+{
+ *num_calls = *num_calls + 1;
+}
+
+#else
+
+static inline unsigned long long
+get_cycle_count (void)
+{
+ return (unsigned long long) 0;
+}
+
+static inline void
+accumulate_cycle_count (unsigned long long *sum __attribute__((__unused__)),
+ unsigned long long start __attribute__((__unused__)))
+{
+ /* Do nothing. */
+}
+
+static inline void
+increment_num_calls (unsigned int *num_calls __attribute__((__unused__)))
+{
+ /* Do nothing. */
+}
+
+#endif
+
+/* Types needed by insert_only_hash_sets. */
+typedef uintptr_t int_vptr;
+
+/* The set of valid vtable pointers for each virtual class is stored
+ in a hash table. This is the hashing function used for the hash
+ table. For more information on the implementation of the hash
+ table, see the class insert_only_hash_sets in vtv_set.h. */
+
+struct vptr_hash
+ {
+ /* Hash function, used to convert vtable pointer, V, (a memory
+ address) into an index into the hash table. */
+ size_t
+ operator() (int_vptr v) const
+ {
+ const uint32_t x = 0x7a35e4d9;
+ const int shift = (sizeof (v) == 8) ? 23 : 21;
+ v = x * v;
+ return v ^ (v >> shift);
+ }
+ };
+
+/* This is the memory allocator used to create the hash table data
+ sets of valid vtable pointers. We use VTV_malloc in order to keep
+ track of which pages have been allocated, so we can update the
+ protections on those pages appropriately. See the class
+ insert_only_hash_sets in vtv_set.h for more information. */
+
+struct vptr_set_alloc
+ {
+ /* Memory allocator operator. N is the number of bytes to be
+ allocated. */
+ void *
+ operator() (size_t n) const
+ {
+ return __vtv_malloc (n);
+ }
+ };
+
+/* Instantiate the template classes (in vtv_set.h) for our particular
+ hash table needs. */
+typedef insert_only_hash_sets<int_vptr, vptr_hash, vptr_set_alloc> vtv_sets;
+typedef vtv_sets::insert_only_hash_set vtv_set;
+typedef vtv_set * vtv_set_handle;
+typedef vtv_set_handle * vtv_set_handle_handle;
+
+/* Records for caching the 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
+static struct sect_hdr_data vtv_sect_info_cache[MAX_ENTRIES] VTV_PROTECTED_VAR;
+
+unsigned int num_cache_entries VTV_PROTECTED_VAR = 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 (vtv_sect_info_cache[i].dlpi_addr == load_addr)
+ return &(vtv_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 size_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 size_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. */
+
+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);
+}
+
+static void
+read_section_offset_and_length (struct dl_phdr_info *info,
+ const char *sect_name,
+ int mprotect_flags,
+ off_t *sect_offset,
+ ElfW (Word) *sect_len)
+{
+ char program_name[PATH_MAX];
+ char *cptr;
+ bool found = false;
+ struct sect_hdr_data *cached_data = NULL;
+ 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);
+
+
+ /* 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)
+ {
+ *sect_offset = cached_data->mp_low;
+ *sect_len = cached_data->mp_size;
+ return;
+ }
+
+ /* 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))
+ {
+ int name_len = strlen (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);
+
+ 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;
+
+ /* This code will be needed once we have crated libvtv.so. */
+ bool is_libvtv = false;
+
+ /*
+ if (strstr (info->dlpi_name, "libvtv.so"))
+ is_libvtv = true;
+ */
+
+ /* 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, &sect_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, sect_name, name_len) == 0)
+ {
+ /* We found the section; get its load offset and
+ size. */
+ *sect_offset = sect_hdr.sh_addr;
+ if (!is_libvtv)
+ *sect_len = sect_hdr.sh_size - VTV_PAGE_SIZE;
+ else
+ *sect_len = sect_hdr.sh_size;
+ found = true;
+ }
+ }
+ close (fd);
+ }
+ }
+
+ if (*sect_offset != 0 && *sect_len != 0)
+ {
+ /* Calculate the page location in memory, making sure the
+ address is page-aligned. */
+ ElfW (Addr) start_addr = (const ElfW (Addr)) info->dlpi_addr
+ + *sect_offset;
+ *sect_offset = start_addr & ~(VTV_PAGE_SIZE - 1);
+ *sect_len = *sect_len - 1;
+
+ /* Since we got this far, we must not have found these pages in
+ the cache, so add them to it. NOTE: We could get here either
+ while making everything read-only or while making everything
+ read-write. We will only update the cache if we get here on
+ a read-write (to make absolutely sure the cache is writable
+ -- also the read-write pass should come before the read-only
+ pass). */
+ if ((mprotect_flags & PROT_WRITE)
+ && num_cache_entries < MAX_ENTRIES)
+ {
+ vtv_sect_info_cache[num_cache_entries].dlpi_addr = info->dlpi_addr;
+ vtv_sect_info_cache[num_cache_entries].mp_low = *sect_offset;
+ vtv_sect_info_cache[num_cache_entries].mp_size = *sect_len;
+ num_cache_entries++;
+ }
+ }
+}
+
+/* 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 section 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, void *data)
+{
+ int * mprotect_flags = (int *) data;
+ off_t map_sect_offset = 0;
+ ElfW (Word) map_sect_len = 0;
+ char buffer[1024];
+ char program_name[1024];
+ const char *map_sect_name = VTV_PROTECTED_VARS_SECTION;
+
+ /* 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;
+
+ /* 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);
+
+ read_section_offset_and_length (info, map_sect_name, *mprotect_flags,
+ &map_sect_offset, &map_sect_len);
+
+ 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),
+ (*mprotect_flags & PROT_WRITE) ? "READ/WRITE" : "READ-ONLY");
+ log_memory_protection_data (buffer);
+ }
+
+ /* See if we actually found the section. */
+ if (map_sect_offset && map_sect_len)
+ {
+ unsigned long long start;
+ int result;
+
+ if (debug_functions)
+ {
+ snprintf (buffer, sizeof (buffer),
+ " (%s): Protecting %p to %p\n",
+ ((strlen (info->dlpi_name) == 0) ? program_name
+ : info->dlpi_name),
+ (void *) map_sect_offset,
+ (void *) (map_sect_offset + map_sect_len));
+ log_memory_protection_data (buffer);
+ }
+
+ /* Change the protections on the pages for the section. */
+
+ start = get_cycle_count ();
+ result = mprotect ((void *) map_sect_offset, map_sect_len,
+ *mprotect_flags);
+ accumulate_cycle_count (&mprotect_cycles, start);
+ if (result == -1)
+ {
+ if (debug_functions)
+ {
+ snprintf (buffer, sizeof (buffer),
+ "Failed called to mprotect for %s error: ",
+ (*mprotect_flags & 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 *) map_sect_offset,
+ (char *) map_sect_offset + map_sect_len);
+ log_memory_protection_data (buffer);
+ }
+ }
+ increment_num_calls (&num_calls_to_mprotect);
+ /* num_pages_protected += (map_sect_len + VTV_PAGE_SIZE - 1) / VTV_PAGE_SIZE; */
+ num_pages_protected += (map_sect_len + 4096 - 1) / 4096;
+ }
+
+ return 0;
+}
+
+/* This function explicitly changes the protection (read-only or read-write)
+ on the vtv_sect_info_cache, which is used for speeding up look ups in the
+ function dl_iterate_phdr_callback. This data structure needs to be
+ explicitly made read-write before any calls to dl_iterate_phdr_callback,
+ because otherwise it may still be read-only when dl_iterate_phdr_callback
+ attempts to write to it.
+
+ More detailed explanation: dl_iterate_phdr_callback finds all the
+ .vtable_map_vars sections in all loaded objects (including the main program)
+ and (depending on where it was called from) either makes all the pages in the
+ sections read-write or read-only. The vtv_sect_info_cache should be in the
+ .vtable_map_vars section for libstdc++.so, which means that normally it would
+ be read-only until libstdc++.so is processed by dl_iterate_phdr_callback
+ (on the read-write pass), after which it will be writable. But if any loaded
+ object gets processed before libstdc++.so, it will attempt to update the
+ data cache, which will still be read-only, and cause a seg fault. Hence
+ we need a special function, called before dl_iterate_phdr_callback, that
+ will make the data cache writable. */
+
+static void
+change_protections_on_phdr_cache (int protection_flag)
+{
+ char * low_address = (char *) &(vtv_sect_info_cache);
+ size_t cache_size = MAX_ENTRIES * sizeof (struct sect_hdr_data);
+
+ low_address = (char *) ((unsigned long) low_address & ~(VTV_PAGE_SIZE - 1));
+
+ if (mprotect ((void *) low_address, cache_size, protection_flag) == -1)
+ VTV_error ();
+}
+
+/* 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)
+{
+ int mprotect_flags;
+
+ mprotect_flags = PROT_READ | PROT_WRITE;
+ change_protections_on_phdr_cache (mprotect_flags);
+ dl_iterate_phdr (dl_iterate_phdr_callback, (void *) &mprotect_flags);
+}
+
+/* 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)
+{
+ int mprotect_flags;
+
+ mprotect_flags = PROT_READ;
+ dl_iterate_phdr (dl_iterate_phdr_callback, (void *) &mprotect_flags);
+ change_protections_on_phdr_cache (mprotect_flags);
+}
+
+#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. */
+
+extern "C" /* This is only being applied to __VLTChangePermission*/
+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: Need to revisit this code for dlopen. It most probably
+ is not unlocking the protected vtable vars after for 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) const
+ {
+ __vtv_free (p);
+ }
+ };
+
+/* Explicitly instantiate this class since this file is compiled with
+ -fno-implicit-templates. These are for the hash table that is used
+ to do vtable map variable symbol resolution. */
+template class insert_only_hash_map <vtv_set_handle *,
+ insert_only_hash_map_allocator >;
+typedef insert_only_hash_map <vtv_set_handle *,
+ insert_only_hash_map_allocator > s2s;
+typedef const s2s::key_type vtv_symbol_key;
+
+static s2s * vtv_symbol_unification_map VTV_PROTECTED_VAR = NULL;
+
+const unsigned long SET_HANDLE_HANDLE_BIT = 0x2;
+
+/* In the case where a vtable map variable is the only instance of the
+ variable we have seen, it points directly to the set of valid
+ vtable pointers. All subsequent instances of the 'same' vtable map
+ variable point to the first vtable map variable. This function,
+ given a vtable map variable PTR, checks a bit to see whether it's
+ pointing directly to the data set or to the first vtable map
+ variable. */
+
+static inline bool
+is_set_handle_handle (void * ptr)
+{
+ return ((unsigned long) ptr & SET_HANDLE_HANDLE_BIT)
+ == SET_HANDLE_HANDLE_BIT;
+}
+
+/* Returns the actual pointer value of a vtable map variable, PTR (see
+ comments for is_set_handle_handle for more details). */
+
+static inline vtv_set_handle *
+ptr_from_set_handle_handle (void * ptr)
+{
+ return (vtv_set_handle *) ((unsigned long) ptr & ~SET_HANDLE_HANDLE_BIT);
+}
+
+/* Given a vtable map variable, PTR, this function sets the bit that
+ says this is the second (or later) instance of a vtable map
+ variable. */
+
+static inline vtv_set_handle_handle
+set_handle_handle (vtv_set_handle * ptr)
+{
+ return (vtv_set_handle_handle) ((unsigned long) ptr | SET_HANDLE_HANDLE_BIT);
+}
+
+static inline void
+register_set_common (void **set_handle_ptr, size_t num_args,
+ void **vtable_ptr_array, bool debug)
+{
+ /* Now figure out what pointer to use for the set pointer, for the
+ inserts. */
+ vtv_set_handle *handle_ptr = (vtv_set_handle *) set_handle_ptr;
+
+ if (debug)
+ VTV_DEBUG_ASSERT (vtv_symbol_unification_map != 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);
+
+ /* Now we've got the set and it's initialized, add the vtable
+ pointers. */
+ for (size_t index = 0; index < num_args; ++index)
+ {
+ int_vptr vtbl_ptr = (int_vptr) vtable_ptr_array[index];
+ vtv_sets::insert (vtbl_ptr, handle_ptr);
+ }
+}
+
+static inline void
+register_pair_common (void **set_handle_ptr, const void *vtable_ptr,
+ const char *set_symbol_name, const char *vtable_name,
+ bool debug)
+{
+ /* Now we've got the set and it's initialized, add the vtable
+ pointer (assuming that it's not NULL...It may be NULL, as we may
+ have called this function merely to initialize the set
+ pointer). */
+ int_vptr vtbl_ptr = (int_vptr) vtable_ptr;
+ if (vtbl_ptr)
+ {
+ vtv_set_handle *handle_ptr = (vtv_set_handle *) set_handle_ptr;
+ if (debug)
+ VTV_DEBUG_ASSERT (vtv_symbol_unification_map != 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::insert (vtbl_ptr, handle_ptr);
+ }
+
+ if (debug && 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 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. */
+
+static inline void
+init_set_symbol_debug (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);
+ __vtv_log_verification_failure (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);
+ __vtv_log_verification_failure (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 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
+__VLTRegisterSetDebug (void **set_handle_ptr, const void *set_symbol_key,
+ size_t size_hint, size_t num_args,
+ void **vtable_ptr_array)
+{
+ unsigned long long start = get_cycle_count ();
+ increment_num_calls (&num_calls_to_regset);
+
+ VTV_DEBUG_ASSERT(set_handle_ptr != NULL);
+ init_set_symbol_debug (set_handle_ptr, set_symbol_key, size_hint);
+
+ register_set_common (set_handle_ptr, num_args, vtable_ptr_array, true);
+
+ accumulate_cycle_count (&regset_cycles, start);
+}
+
+/* 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 *set_symbol_key,
+ size_t size_hint, const void *vtable_ptr,
+ const char *set_symbol_name, const char *vtable_name)
+{
+ unsigned long long start = get_cycle_count ();
+ increment_num_calls (&num_calls_to_regpair);
+
+ VTV_DEBUG_ASSERT(set_handle_ptr != NULL);
+ init_set_symbol_debug (set_handle_ptr, set_symbol_key, size_hint);
+
+ register_pair_common (set_handle_ptr, vtable_ptr, set_symbol_name, vtable_name,
+ true);
+
+ accumulate_cycle_count (&regpair_cycles, start);
+}
+
+
+/* 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)
+{
+ unsigned long long start = get_cycle_count ();
+ VTV_DEBUG_ASSERT (set_handle_ptr != NULL && *set_handle_ptr != NULL);
+ int_vptr vtbl_ptr = (int_vptr) vtable_ptr;
+
+ increment_num_calls (&num_calls_to_verify_vtable);
+ 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. */
+ }
+ accumulate_cycle_count (&verify_vtable_cycles, start);
+
+ return vtable_ptr;
+}
+
+/* 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.*/
+
+static inline void
+init_set_symbol (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 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
+__VLTRegisterSet (void **set_handle_ptr, const void *set_symbol_key,
+ size_t size_hint, size_t num_args, void **vtable_ptr_array)
+{
+ unsigned long long start = get_cycle_count ();
+ increment_num_calls (&num_calls_to_regset);
+
+ init_set_symbol (set_handle_ptr, set_symbol_key, size_hint);
+ register_set_common (set_handle_ptr, num_args, vtable_ptr_array, false);
+
+ accumulate_cycle_count (&regset_cycles, start);
+}
+
+
+
+/* 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 *set_symbol_key,
+ size_t size_hint, const void *vtable_ptr)
+{
+ unsigned long long start = get_cycle_count ();
+ increment_num_calls (&num_calls_to_regpair);
+
+ init_set_symbol (set_handle_ptr, set_symbol_key, size_hint);
+ register_pair_common (set_handle_ptr, vtable_ptr, NULL, NULL, false);
+
+ accumulate_cycle_count (&regpair_cycles, start);
+}
+
+/* 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)
+{
+ unsigned long long start = get_cycle_count ();
+ int_vptr vtbl_ptr = (int_vptr) vtable_ptr;
+
+ vtv_set_handle *handle_ptr;
+ increment_num_calls (&num_calls_to_verify_vtable);
+ 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. */
+ }
+ accumulate_cycle_count (&verify_vtable_cycles, start);
+
+ return vtable_ptr;
+}
+
+static int page_count_2 = 0;
+
+static int
+dl_iterate_phdr_count_pages (struct dl_phdr_info *info,
+ size_t unused __attribute__ ((__unused__)),
+ void *data)
+{
+ int *mprotect_flags = (int *) data;
+ off_t map_sect_offset = 0;
+ ElfW (Word) map_sect_len = 0;
+ const char *map_sect_name = VTV_PROTECTED_VARS_SECTION;
+
+ /* 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;
+
+ read_section_offset_and_length (info, map_sect_name, *mprotect_flags,
+ &map_sect_offset, &map_sect_len);
+
+ /* See if we actually found the section. */
+ if (map_sect_len)
+ page_count_2 += (map_sect_len + VTV_PAGE_SIZE - 1) / VTV_PAGE_SIZE;
+
+ return 0;
+}
+
+static void
+count_all_pages (void)
+{
+ int mprotect_flags;
+
+ mprotect_flags = PROT_READ;
+ page_count_2 = 0;
+
+ dl_iterate_phdr (dl_iterate_phdr_count_pages, (void *) &mprotect_flags);
+ page_count_2 += __vtv_count_mmapped_pages ();
+}
+
+void
+__VLTDumpStats (void)
+{
+ int log_fd = __vtv_open_log ("vtv-runtime-stats.log");
+
+ if (log_fd != -1)
+ {
+ count_all_pages ();
+ __vtv_add_to_log (log_fd,
+ "Calls: mprotect (%d) regset (%d) regpair (%d)"
+ " verify_vtable (%d)\n",
+ num_calls_to_mprotect, num_calls_to_regset,
+ num_calls_to_regpair, num_calls_to_verify_vtable);
+ __vtv_add_to_log (log_fd,
+ "Cycles: mprotect (%lld) regset (%lld) "
+ "regpair (%lld) verify_vtable (%lld)\n",
+ mprotect_cycles, regset_cycles, regpair_cycles,
+ verify_vtable_cycles);
+ __vtv_add_to_log (log_fd,
+ "Pages protected (1): %d\n", num_pages_protected);
+ __vtv_add_to_log (log_fd, "Pages protected (2): %d\n", page_count_2);
+
+ close (log_fd);
+ }
+}
+
+/* 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 */
+
+void
+__vtv_verify_fail_debug (void **set_handle_ptr, const void *vtbl_ptr,
+ const char *debug_msg)
+{
+ __vtv_log_verification_failure (debug_msg, false);
+
+ /* Call the public interface in case it has been overwritten by
+ user. */
+ __vtv_verify_fail (set_handle_ptr, vtbl_ptr);
+
+ __vtv_log_verification_failure ("Returned from __vtv_verify_fail."
+ " Secondary verification succeeded.\n", false);
+}
+
+/* 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);
+ __vtv_log_verification_failure (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);
+ __vtv_log_verification_failure (log_msg, false);
+ __vtv_log_verification_failure (" Backtrace: \n", true);
+
+ const char *fail_msg = "Potential vtable pointer corruption detected!!\n";
+ vtv_fail (fail_msg, data_set_ptr, vtbl_ptr);
+}