diff options
Diffstat (limited to 'gcc/cp/vtable-class-hierarchy.c')
-rw-r--r-- | gcc/cp/vtable-class-hierarchy.c | 1346 |
1 files changed, 0 insertions, 1346 deletions
diff --git a/gcc/cp/vtable-class-hierarchy.c b/gcc/cp/vtable-class-hierarchy.c deleted file mode 100644 index 4c6e2f2..0000000 --- a/gcc/cp/vtable-class-hierarchy.c +++ /dev/null @@ -1,1346 +0,0 @@ -/* Copyright (C) 2012-2022 Free Software Foundation, Inc. - - This file is part of GCC. - - GCC is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3, or (at your option) - any later version. - - GCC is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GCC; see the file COPYING3. If not see -<http://www.gnu.org/licenses/>. */ - -/* Virtual Table Pointer Security Pass - Detect corruption of vtable pointers - before using them for virtual method dispatches. */ - -/* This file is part of the vtable security feature implementation. - The vtable security feature is designed to detect when a virtual - call is about to be made through an invalid vtable pointer - (possibly due to data corruption or malicious attacks). The - compiler finds every virtual call, and inserts a verification call - before the virtual call. The verification call takes the actual - vtable pointer value in the object through which the virtual call - is being made, and compares the vtable pointer against a set of all - valid vtable pointers that the object could contain (this set is - based on the declared type of the object). If the pointer is in - the valid set, execution is allowed to continue; otherwise the - program is halted. - - There are several pieces needed in order to make this work: 1. For - every virtual class in the program (i.e. a class that contains - virtual methods), we need to build the set of all possible valid - vtables that an object of that class could point to. This includes - vtables for any class(es) that inherit from the class under - consideration. 2. For every such data set we build up, we need a - way to find and reference the data set. This is complicated by the - fact that the real vtable addresses are not known until runtime, - when the program is loaded into memory, but we need to reference the - sets at compile time when we are inserting verification calls into - the program. 3. We need to find every virtual call in the program, - and insert the verification call (with the appropriate arguments) - before the virtual call. 4. We need some runtime library pieces: - the code to build up the data sets at runtime; the code to actually - perform the verification using the data sets; and some code to set - protections on the data sets, so they themselves do not become - hacker targets. - - To find and reference the set of valid vtable pointers for any given - virtual class, we create a special global varible for each virtual - class. We refer to this as the "vtable map variable" for that - class. The vtable map variable has the type "void *", and is - initialized by the compiler to NULL. At runtime when the set of - valid vtable pointers for a virtual class, e.g. class Foo, is built, - the vtable map variable for class Foo is made to point to the set. - During compile time, when the compiler is inserting verification - calls into the program, it passes the vtable map variable for the - appropriate class to the verification call, so that at runtime the - verification call can find the appropriate data set. - - The actual set of valid vtable pointers for a virtual class, - e.g. class Foo, cannot be built until runtime, when the vtables get - loaded into memory and their addresses are known. But the knowledge - about which vtables belong in which class' hierarchy is only known - at compile time. Therefore at compile time we collect class - hierarchy and vtable information about every virtual class, and we - generate calls to build up the data sets at runtime. To build the - data sets, we call one of the functions we add to the runtime - library, __VLTRegisterPair. __VLTRegisterPair takes two arguments, - a vtable map variable and the address of a vtable. If the vtable - map variable is currently NULL, it creates a new data set (hash - table), makes the vtable map variable point to the new data set, and - inserts the vtable address into the data set. If the vtable map - variable is not NULL, it just inserts the vtable address into the - data set. In order to make sure that our data sets are built before - any verification calls happen, we create a special constructor - initialization function for each compilation unit, give it a very - high initialization priority, and insert all of our calls to - __VLTRegisterPair into our special constructor initialization - function. - - The vtable verification feature is controlled by the flag - '-fvtable-verify='. There are three flavors of this: - '-fvtable-verify=std', '-fvtable-verify=preinit', and - '-fvtable-verify=none'. If the option '-fvtable-verfy=preinit' is - used, then our constructor initialization function gets put into the - preinit array. This is necessary if there are data sets that need - to be built very early in execution. If the constructor - initialization function gets put into the preinit array, the we also - add calls to __VLTChangePermission at the beginning and end of the - function. The call at the beginning sets the permissions on the - data sets and vtable map variables to read/write, and the one at the - end makes them read-only. If the '-fvtable-verify=std' option is - used, the constructor initialization functions are executed at their - normal time, and the __VLTChangePermission calls are handled - differently (see the comments in libstdc++-v3/libsupc++/vtv_rts.cc). - The option '-fvtable-verify=none' turns off vtable verification. - - This file contains code to find and record the class hierarchies for - the virtual classes in a program, and all the vtables associated - with each such class; to generate the vtable map variables; and to - generate the constructor initialization function (with the calls to - __VLTRegisterPair, and __VLTChangePermission). The main data - structures used for collecting the class hierarchy data and - building/maintaining the vtable map variable data are defined in - gcc/vtable-verify.h, because they are used both here and in - gcc/vtable-verify.c. */ - -#include "config.h" -#include "system.h" -#include "coretypes.h" -#include "vtable-verify.h" -#include "cp-tree.h" -#include "stringpool.h" -#include "cgraph.h" -#include "output.h" -#include "tree-iterator.h" -#include "gimplify.h" -#include "stor-layout.h" - -static int num_calls_to_regset = 0; -static int num_calls_to_regpair = 0; -static int current_set_size; - -/* Mark these specially since they need to be stored in precompiled - header IR. */ -static GTY (()) vec<tree, va_gc> *vlt_saved_class_info; -static GTY (()) tree vlt_register_pairs_fndecl = NULL_TREE; -static GTY (()) tree vlt_register_set_fndecl = NULL_TREE; - -struct work_node { - struct vtv_graph_node *node; - struct work_node *next; -}; - -struct vtbl_map_node *vtable_find_or_create_map_decl (tree); - -/* As part of vtable verification the compiler generates and inserts - calls to __VLTVerifyVtablePointer, which is in libstdc++. This - function builds and initializes the function decl that is used - in generating those function calls. - - In addition to __VLTVerifyVtablePointer there is also - __VLTVerifyVtablePointerDebug which can be used in place of - __VLTVerifyVtablePointer, and which takes extra parameters and - outputs extra information, to help debug problems. The debug - version of this function is generated and used if flag_vtv_debug is - true. - - The signatures for these functions are: - - void * __VLTVerifyVtablePointer (void **, void*); - void * __VLTVerifyVtablePointerDebug (void**, void *, char *, char *); -*/ - -void -vtv_build_vtable_verify_fndecl (void) -{ - tree func_type = NULL_TREE; - - if (verify_vtbl_ptr_fndecl != NULL_TREE - && TREE_CODE (verify_vtbl_ptr_fndecl) != ERROR_MARK) - return; - - if (flag_vtv_debug) - { - func_type = build_function_type_list (const_ptr_type_node, - build_pointer_type (ptr_type_node), - const_ptr_type_node, - const_string_type_node, - const_string_type_node, - NULL_TREE); - verify_vtbl_ptr_fndecl = - build_lang_decl (FUNCTION_DECL, - get_identifier ("__VLTVerifyVtablePointerDebug"), - func_type); - } - else - { - func_type = build_function_type_list (const_ptr_type_node, - build_pointer_type (ptr_type_node), - const_ptr_type_node, - NULL_TREE); - verify_vtbl_ptr_fndecl = - build_lang_decl (FUNCTION_DECL, - get_identifier ("__VLTVerifyVtablePointer"), - func_type); - } - - 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; - DECL_PRESERVE_P (verify_vtbl_ptr_fndecl) = 1; -} - -/* As part of vtable verification the compiler generates and inserts - calls to __VLTRegisterSet and __VLTRegisterPair, which are in - libsupc++. This function builds and initializes the function decls - that are used in generating those function calls. - - The signatures for these functions are: - - void __VLTRegisterSetDebug (void **, const void *, std::size_t, - size_t, void **); - - void __VLTRegisterSet (void **, const void *, std::size_t, - size_t, void **); - - void __VLTRegisterPairDebug (void **, const void *, size_t, - const void *, const char *, const char *); - - void __VLTRegisterPair (void **, const void *, size_t, const void *); -*/ - -static void -init_functions (void) -{ - tree register_set_type; - tree register_pairs_type; - - if (vlt_register_set_fndecl != NULL_TREE) - return; - - gcc_assert (vlt_register_pairs_fndecl == NULL_TREE); - gcc_assert (vlt_register_set_fndecl == NULL_TREE); - - /* Build function decl for __VLTRegisterSet*. */ - - register_set_type = build_function_type_list - (void_type_node, - build_pointer_type (ptr_type_node), - const_ptr_type_node, - size_type_node, - size_type_node, - build_pointer_type (ptr_type_node), - NULL_TREE); - - if (flag_vtv_debug) - vlt_register_set_fndecl = build_lang_decl - (FUNCTION_DECL, - get_identifier ("__VLTRegisterSetDebug"), - register_set_type); - else - vlt_register_set_fndecl = build_lang_decl - (FUNCTION_DECL, - get_identifier ("__VLTRegisterSet"), - register_set_type); - - - TREE_NOTHROW (vlt_register_set_fndecl) = 1; - DECL_ATTRIBUTES (vlt_register_set_fndecl) = - tree_cons (get_identifier ("leaf"), NULL, - DECL_ATTRIBUTES (vlt_register_set_fndecl)); - DECL_EXTERNAL(vlt_register_set_fndecl) = 1; - TREE_PUBLIC (vlt_register_set_fndecl) = 1; - DECL_PRESERVE_P (vlt_register_set_fndecl) = 1; - SET_DECL_LANGUAGE (vlt_register_set_fndecl, lang_cplusplus); - - /* Build function decl for __VLTRegisterPair*. */ - - if (flag_vtv_debug) - { - register_pairs_type = build_function_type_list (void_type_node, - build_pointer_type - (ptr_type_node), - const_ptr_type_node, - size_type_node, - const_ptr_type_node, - const_string_type_node, - const_string_type_node, - NULL_TREE); - - vlt_register_pairs_fndecl = build_lang_decl - (FUNCTION_DECL, - get_identifier ("__VLTRegisterPairDebug"), - register_pairs_type); - } - else - { - register_pairs_type = build_function_type_list (void_type_node, - build_pointer_type - (ptr_type_node), - const_ptr_type_node, - size_type_node, - const_ptr_type_node, - NULL_TREE); - - vlt_register_pairs_fndecl = build_lang_decl - (FUNCTION_DECL, - get_identifier ("__VLTRegisterPair"), - register_pairs_type); - } - - 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)); - DECL_EXTERNAL(vlt_register_pairs_fndecl) = 1; - TREE_PUBLIC (vlt_register_pairs_fndecl) = 1; - DECL_PRESERVE_P (vlt_register_pairs_fndecl) = 1; - SET_DECL_LANGUAGE (vlt_register_pairs_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 (bitmap_bit_p (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; - - bitmap_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; - struct vtv_graph_node *ret_val = NULL; - - for (prev = NULL, cur = *worklist; cur; prev = cur, cur = cur->next) - { - if ((cur->node->children).length() == cur->node->num_processed_children) - { - if (prev == NULL) - (*worklist) = cur->next; - else - prev->next = cur->next; - - cur->next = NULL; - ret_val = cur->node; - free (cur); - return ret_val; - } - } - - 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; - sbitmap inserted = sbitmap_alloc (num_vtable_map_nodes); - unsigned i; - unsigned j; - - /* 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. */ - bitmap_clear (inserted); - for (j = 0; j < num_vtable_map_nodes; ++j) - { - struct vtbl_map_node *cur = vtbl_map_nodes_vec[j]; - if (cur->class_info - && ((cur->class_info->children).length() == 0) - && ! (bitmap_bit_p (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); - bitmap_clear (temp_node->descendants); - bitmap_set_bit (temp_node->descendants, temp_node->class_uid); - for (i = 0; i < (temp_node->children).length(); ++i) - bitmap_ior (temp_node->descendants, temp_node->descendants, - temp_node->children[i]->descendants); - for (i = 0; i < (temp_node->parents).length(); ++i) - { - temp_node->parents[i]->num_processed_children = - temp_node->parents[i]->num_processed_children + 1; - if (!bitmap_bit_p (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 an expression for calculating the correct - offset into the vtable (VTABLE_DECL). It is the actual vtable - pointer address that will be stored in our list of valid vtable - pointers for BASE_CLASS. BASE_CLASS is the record_type node for - the base class to whose hiearchy we want to add - VPTR_ADDRESS. (VTABLE_DECL should be the vtable for BASE_CLASS or - one of BASE_CLASS' descendents. */ - -static bool -check_and_record_registered_pairs (tree vtable_decl, tree vptr_address, - tree base_class) -{ - unsigned offset; - struct vtbl_map_node *base_vtable_map_node; - bool inserted_something = false; - - - if (TREE_CODE (vptr_address) == ADDR_EXPR - && TREE_CODE (TREE_OPERAND (vptr_address, 0)) == MEM_REF) - vptr_address = TREE_OPERAND (vptr_address, 0); - - if (TREE_OPERAND_LENGTH (vptr_address) > 1) - offset = TREE_INT_CST_LOW (TREE_OPERAND (vptr_address, 1)); - else - offset = 0; - - base_vtable_map_node = vtbl_map_get_node (TYPE_MAIN_VARIANT (base_class)); - - inserted_something = vtbl_map_node_registration_insert - (base_vtable_map_node, - vtable_decl, - offset); - return !inserted_something; -} - -/* Given an IDENTIFIER_NODE, build and return a string literal based on it. */ - -static tree -build_string_from_id (tree identifier) -{ - int len; - - gcc_assert (TREE_CODE (identifier) == IDENTIFIER_NODE); - - len = IDENTIFIER_LENGTH (identifier); - return build_string_literal (len + 1, IDENTIFIER_POINTER (identifier)); -} - -/* A class may contain secondary vtables in it, for various reasons. - This function goes through the decl chain of a class record looking - for any fields that point to secondary vtables, and adding calls to - __VLTRegisterPair for the secondary vtable pointers. - - BASE_CLASS_DECL_ARG is an expression for the address of the vtable - map variable for the BASE_CLASS (whose hierarchy we are currently - updating). BASE_CLASS is the record_type node for the base class. - RECORD_TYPE is the record_type node for the descendant class that - we are possibly adding to BASE_CLASS's hierarchy. BODY is the - function body for the constructor init function to which we are - adding our calls to __VLTRegisterPair. */ - -static void -register_construction_vtables (tree base_class, tree record_type, - vec<tree> *vtable_ptr_array) -{ - tree vtbl_var_decl; - - if (TREE_CODE (record_type) != RECORD_TYPE) - return; - - vtbl_var_decl = CLASSTYPE_VTABLES (record_type); - - if (CLASSTYPE_VBASECLASSES (record_type)) - { - tree vtt_decl; - bool already_registered = false; - tree val_vtbl_decl = NULL_TREE; - - vtt_decl = DECL_CHAIN (vtbl_var_decl); - - /* Check to see if we have found a VTT. Add its data if appropriate. */ - if (vtt_decl) - { - tree values = DECL_INITIAL (vtt_decl); - if (TREE_ASM_WRITTEN (vtt_decl) - && values != NULL_TREE - && TREE_CODE (values) == CONSTRUCTOR - && TREE_CODE (TREE_TYPE (values)) == ARRAY_TYPE) - { - unsigned HOST_WIDE_INT cnt; - constructor_elt *ce; - - /* 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. This may - result in adding more than just the element assigned - to the primary vptr of the class, so we may end up - with more vtable pointers than are strictly - necessary. */ - - for (cnt = 0; - vec_safe_iterate (CONSTRUCTOR_ELTS (values), - cnt, &ce); - cnt++) - { - tree value = ce->value; - - /* Search for the ADDR_EXPR operand within the value. */ - - while (value - && TREE_OPERAND (value, 0) - && TREE_CODE (TREE_OPERAND (value, 0)) == ADDR_EXPR) - value = TREE_OPERAND (value, 0); - - /* The VAR_DECL for the vtable should be the first - argument of the ADDR_EXPR, which is the first - argument of value.*/ - - if (TREE_OPERAND (value, 0)) - val_vtbl_decl = TREE_OPERAND (value, 0); - - while (!VAR_P (val_vtbl_decl) - && TREE_OPERAND (val_vtbl_decl, 0)) - val_vtbl_decl = TREE_OPERAND (val_vtbl_decl, 0); - - gcc_assert (VAR_P (val_vtbl_decl)); - - /* Check to see if we already have this vtable pointer in - our valid set for this base class. */ - - already_registered = check_and_record_registered_pairs - (val_vtbl_decl, - value, - base_class); - - if (already_registered) - continue; - - /* Add this vtable pointer to our set of valid - pointers for the base class. */ - - vtable_ptr_array->safe_push (value); - current_set_size++; - } - } - } - } -} - -/* 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 base_class, - vec<tree> *vtable_ptr_array) -{ - 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))) - { - tree vtable_address = build_vtbl_address (base_binfo); - - already_registered = check_and_record_registered_pairs - (vtable_decl, - vtable_address, - base_class); - if (!already_registered) - { - vtable_ptr_array->safe_push (vtable_address); - current_set_size++; - } - } - - register_other_binfo_vtables (base_binfo, base_class, vtable_ptr_array); - } -} - -/* 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 (bitmap_bit_p (class_node->descendants, i)) - { - tree class_type = vtbl_map_nodes_vec[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; -} - -/* 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; -} - -static char * -get_log_file_name (const char *fname) -{ - const char *tmp_dir = concat (dump_dir_name, NULL); - char *full_name; - int dir_len; - int fname_len; - - dir_len = strlen (tmp_dir); - fname_len = strlen (fname); - - full_name = XNEWVEC (char, dir_len + fname_len + 1); - strcpy (full_name, tmp_dir); - strcpy (full_name + dir_len, fname); - - return full_name; -} - -static void -write_out_current_set_data (tree base_class, int set_size) -{ - static int class_data_log_fd = -1; - char buffer[1024]; - int bytes_written __attribute__ ((unused)); - char *file_name = get_log_file_name ("vtv_class_set_sizes.log"); - - if (class_data_log_fd == -1) - class_data_log_fd = open (file_name, - O_WRONLY | O_APPEND | O_CREAT, S_IRWXU); - - if (class_data_log_fd == -1) - { - warning_at (UNKNOWN_LOCATION, 0, - "unable to open log file %<vtv_class_set_sizes.log%>: %m"); - return; - } - - snprintf (buffer, sizeof (buffer), "%s %d\n", - IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (TYPE_NAME (base_class))), - set_size); - bytes_written = write (class_data_log_fd, buffer, strlen (buffer)); -} - -static tree -build_key_buffer_arg (tree base_ptr_var_decl) -{ - const int key_type_fixed_size = 8; - uint32_t len1 = IDENTIFIER_LENGTH (DECL_NAME (base_ptr_var_decl)); - uint32_t hash_value = vtv_string_hash (IDENTIFIER_POINTER - (DECL_NAME (base_ptr_var_decl))); - void *key_buffer = xmalloc (len1 + key_type_fixed_size); - uint32_t *value_ptr = (uint32_t *) key_buffer; - tree ret_value; - - /* 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 (base_ptr_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. */ - ret_value = build_string_literal (len1 + key_type_fixed_size, - (char *) key_buffer); - free (key_buffer); - return ret_value; -} - -static void -insert_call_to_register_set (tree class_name, - vec<tree> *vtbl_ptr_array, tree body, tree arg1, - tree arg2, tree size_hint_arg) -{ - tree call_expr; - int num_args = vtbl_ptr_array->length(); - char *array_arg_name = ACONCAT (("__vptr_array_", - IDENTIFIER_POINTER (class_name), NULL)); - tree array_arg_type = build_array_type_nelts (build_pointer_type - (build_pointer_type - (void_type_node)), - num_args); - tree array_arg = build_decl (UNKNOWN_LOCATION, VAR_DECL, - get_identifier (array_arg_name), - array_arg_type); - int k; - - vec<constructor_elt, va_gc> *array_elements; - vec_alloc (array_elements, num_args); - - tree initial = NULL_TREE; - tree arg3 = NULL_TREE; - - TREE_PUBLIC (array_arg) = 0; - DECL_EXTERNAL (array_arg) = 0; - TREE_STATIC (array_arg) = 1; - DECL_ARTIFICIAL (array_arg) = 0; - TREE_READONLY (array_arg) = 1; - DECL_IGNORED_P (array_arg) = 0; - DECL_PRESERVE_P (array_arg) = 0; - DECL_VISIBILITY (array_arg) = VISIBILITY_HIDDEN; - - for (k = 0; k < num_args; ++k) - { - CONSTRUCTOR_APPEND_ELT (array_elements, NULL_TREE, (*vtbl_ptr_array)[k]); - } - - initial = build_constructor (TREE_TYPE (array_arg), array_elements); - - TREE_CONSTANT (initial) = 1; - TREE_STATIC (initial) = 1; - DECL_INITIAL (array_arg) = initial; - relayout_decl (array_arg); - varpool_node::finalize_decl (array_arg); - - arg3 = build1 (ADDR_EXPR, TYPE_POINTER_TO (TREE_TYPE (array_arg)), array_arg); - - TREE_TYPE (arg3) = build_pointer_type (TREE_TYPE (array_arg)); - - call_expr = build_call_expr (vlt_register_set_fndecl, 5, arg1, - arg2, /* set_symbol_key */ - size_hint_arg, build_int_cst (size_type_node, - num_args), - arg3); - append_to_statement_list (call_expr, &body); - num_calls_to_regset++; -} - -static void -insert_call_to_register_pair (vec<tree> *vtbl_ptr_array, tree arg1, - tree arg2, tree size_hint_arg, tree str1, - tree str2, tree body) -{ - tree call_expr; - int num_args = vtbl_ptr_array->length(); - tree vtable_address = NULL_TREE; - - if (num_args == 0) - vtable_address = build_int_cst (build_pointer_type (void_type_node), 0); - else - vtable_address = (*vtbl_ptr_array)[0]; - - if (flag_vtv_debug) - call_expr = build_call_expr (vlt_register_pairs_fndecl, 6, arg1, arg2, - size_hint_arg, vtable_address, str1, str2); - else - call_expr = build_call_expr (vlt_register_pairs_fndecl, 4, arg1, arg2, - size_hint_arg, vtable_address); - - append_to_statement_list (call_expr, &body); - num_calls_to_regpair++; -} - -static void -output_set_info (tree record_type, vec<tree> vtbl_ptr_array) -{ - static int vtv_debug_log_fd = -1; - char buffer[1024]; - int bytes_written __attribute__ ((unused)); - int array_len = vtbl_ptr_array.length(); - const char *class_name = - IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (TYPE_NAME (record_type))); - char *file_name = get_log_file_name ("vtv_set_ptr_data.log"); - - if (vtv_debug_log_fd == -1) - vtv_debug_log_fd = open (file_name, - O_WRONLY | O_APPEND | O_CREAT, S_IRWXU); - if (vtv_debug_log_fd == -1) - { - warning_at (UNKNOWN_LOCATION, 0, - "unable to open log file %<vtv_set_ptr_data.log%>: %m"); - return; - } - - for (int i = 0; i < array_len; ++i) - { - const char *vptr_name = "unknown"; - int vptr_offset = 0; - - if (TREE_CODE (vtbl_ptr_array[i]) == POINTER_PLUS_EXPR) - { - tree arg0 = TREE_OPERAND (vtbl_ptr_array[i], 0); - tree arg1 = TREE_OPERAND (vtbl_ptr_array[i], 1); - - if (TREE_CODE (arg0) == ADDR_EXPR) - arg0 = TREE_OPERAND (arg0, 0); - - if (VAR_P (arg0)) - vptr_name = IDENTIFIER_POINTER (DECL_NAME (arg0)); - - if (TREE_CODE (arg1) == INTEGER_CST) - vptr_offset = TREE_INT_CST_LOW (arg1); - } - - snprintf (buffer, sizeof (buffer), "%s %s %s + %d\n", - main_input_filename, class_name, vptr_name, vptr_offset); - bytes_written = write (vtv_debug_log_fd, buffer, strlen(buffer)); - } - -} - -/* 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) -{ - bool registered_at_least_one = false; - vec<tree> *vtbl_ptr_array = NULL; - unsigned j; - - for (j = 0; j < num_vtable_map_nodes; ++j) - { - struct vtbl_map_node *current = vtbl_map_nodes_vec[j]; - unsigned i = 0; - tree base_class = current->class_info->class_type; - tree base_ptr_var_decl = current->vtbl_map_decl; - tree arg1; - tree arg2; - tree new_type; - tree str1 = NULL_TREE; - tree str2 = NULL_TREE; - size_t size_hint; - tree size_hint_arg; - - gcc_assert (current->class_info != NULL); - - - if (flag_vtv_debug) - str1 = build_string_from_id (DECL_NAME (base_ptr_var_decl)); - - new_type = build_pointer_type (TREE_TYPE (base_ptr_var_decl)); - arg1 = build1 (ADDR_EXPR, new_type, base_ptr_var_decl); - - /* We need a fresh vector for each iteration. */ - if (vtbl_ptr_array) - vec_free (vtbl_ptr_array); - - vec_alloc (vtbl_ptr_array, 10); - - for (i = 0; i < num_vtable_map_nodes; ++i) - if (bitmap_bit_p (current->class_info->descendants, i)) - { - struct vtbl_map_node *vtbl_class_node = vtbl_map_nodes_vec[i]; - tree class_type = vtbl_class_node->class_info->class_type; - - if (class_type - && (TREE_CODE (class_type) == RECORD_TYPE)) - { - 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); - str2 = build_string_from_id (DECL_NAME (vtable_decl)); - } - - if (vtable_decl && vtable_should_be_output) - { - tree vtable_address = build_vtbl_address (binfo); - - already_registered = check_and_record_registered_pairs - (vtable_decl, - vtable_address, - base_class); - - - if (!already_registered) - { - vtbl_ptr_array->safe_push (vtable_address); - - /* Find and handle any 'extra' vtables associated - with this class, via virtual inheritance. */ - register_construction_vtables (base_class, class_type, - vtbl_ptr_array); - - /* Find and handle any 'extra' vtables associated - with this class, via multiple inheritance. */ - register_other_binfo_vtables (binfo, base_class, - vtbl_ptr_array); - } - } - } - } - current_set_size = vtbl_ptr_array->length(); - - /* Sometimes we need to initialize the set symbol even if we are - not adding any vtable pointers to the set in the current - compilation unit. In that case, we need to initialize the - set to our best guess as to what the eventual size of the set - hash table will be (to prevent having to re-size the hash - table later). */ - - size_hint = guess_num_vtable_pointers (current->class_info); - - /* If we have added vtable pointers to the set in this - compilation unit, adjust the size hint for the set's hash - table appropriately. */ - if (vtbl_ptr_array->length() > 0) - { - unsigned len = vtbl_ptr_array->length(); - while ((size_t) len > size_hint) - size_hint <<= 1; - } - size_hint_arg = build_int_cst (size_type_node, size_hint); - - /* Get the key-buffer argument. */ - arg2 = build_key_buffer_arg (base_ptr_var_decl); - - if (str2 == NULL_TREE) - str2 = build_string_literal (strlen ("unknown") + 1, - "unknown"); - - if (flag_vtv_debug) - output_set_info (current->class_info->class_type, - *vtbl_ptr_array); - - if (vtbl_ptr_array->length() > 1) - { - insert_call_to_register_set (current->class_name, - vtbl_ptr_array, body, arg1, arg2, - size_hint_arg); - registered_at_least_one = true; - } - else - { - - if (vtbl_ptr_array->length() > 0 - || (current->is_used - || (current->registered->size() > 0))) - { - insert_call_to_register_pair (vtbl_ptr_array, - arg1, arg2, size_hint_arg, str1, - str2, body); - registered_at_least_one = true; - } - } - - if (flag_vtv_counts && current_set_size > 0) - write_out_current_set_data (base_class, current_set_size); - - } - - 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 (TYPE_MAIN_VARIANT (class_type)); - if (vtbl_node) - return vtbl_node->class_info; - - return NULL; -} - -/* 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) -{ - (base_node->children).safe_push (derived_node); - (derived_node->parents).safe_push (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); -} - - -static void -write_out_vtv_count_data (void) -{ - static int vtv_count_log_fd = -1; - char buffer[1024]; - int unused_vtbl_map_vars = 0; - int bytes_written __attribute__ ((unused)); - char *file_name = get_log_file_name ("vtv_count_data.log"); - - if (vtv_count_log_fd == -1) - vtv_count_log_fd = open (file_name, - O_WRONLY | O_APPEND | O_CREAT, S_IRWXU); - if (vtv_count_log_fd == -1) - { - warning_at (UNKNOWN_LOCATION, 0, - "unable to open log file %<vtv_count_data.log%>: %m"); - return; - } - - for (unsigned i = 0; i < num_vtable_map_nodes; ++i) - { - struct vtbl_map_node *current = vtbl_map_nodes_vec[i]; - if (!current->is_used - && current->registered->size() == 0) - unused_vtbl_map_vars++; - } - - snprintf (buffer, sizeof (buffer), "%s %d %d %d %d %d\n", - main_input_filename, total_num_virtual_calls, - total_num_verified_vcalls, num_calls_to_regset, - num_calls_to_regpair, unused_vtbl_map_vars); - - bytes_written = write (vtv_count_log_fd, buffer, strlen (buffer)); -} - -/* 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.*/ - -bool -vtv_register_class_hierarchy_information (tree init_routine_body) -{ - bool registered_something = false; - - init_functions (); - - 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); - - if (flag_vtv_counts) - write_out_vtv_count_data (); - - return registered_something; -} - - -/* 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; - - 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) - { - tree vtv_fndecl = - vtv_finish_verification_constructor_init_function (init_routine_body); - TREE_STATIC (vtv_fndecl) = 1; - TREE_USED (vtv_fndecl) = 1; - DECL_PRESERVE_P (vtv_fndecl) = 1; - /* We are running too late to generate any meaningful debug information - for this routine. */ - DECL_IGNORED_P (vtv_fndecl) = 1; - if (flag_vtable_verify == VTV_PREINIT_PRIORITY && !TARGET_PECOFF) - DECL_STATIC_CONSTRUCTOR (vtv_fndecl) = 0; - - gimplify_function_tree (vtv_fndecl); - cgraph_node::add_new_function (vtv_fndecl, false); - - if (flag_vtable_verify == VTV_PREINIT_PRIORITY && !TARGET_PECOFF) - assemble_vtv_preinit_initializer (vtv_fndecl); - - } - 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) -{ - char *var_name = NULL; - struct vtbl_map_node *vtable_map_node = NULL; - - /* Verify the type has an associated vtable. */ - if (!TYPE_BINFO (base_type) || !BINFO_VTABLE (TYPE_BINFO (base_type))) - return NULL; - - /* Create map lookup symbol for base class */ - var_name = get_mangled_vtable_map_var_name (base_type); - - /* We've already created the variable; just look it. */ - vtable_map_node = vtbl_map_get_node (TYPE_MAIN_VARIANT (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. */ - - tree var_decl = NULL; - tree var_type = build_pointer_type (void_type_node); - tree initial_value = integer_zero_node; - - var_decl = build_decl (UNKNOWN_LOCATION, VAR_DECL, - get_identifier (var_name), var_type); - - 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 because we want to be - able to write to it at runtime. */ - TREE_READONLY (var_decl) = 0; - DECL_IGNORED_P (var_decl) = 1; - DECL_PRESERVE_P (var_decl) = 1; - - /* Put these mmap variables in thr .vtable_map_vars section, so - we can find and protect them. */ - - set_decl_section_name (var_decl, ".vtable_map_vars"); - symtab_node::get (var_decl)->implicit_section = true; - DECL_INITIAL (var_decl) = initial_value; - - comdat_linkage (var_decl); - - varpool_node::finalize_decl (var_decl); - if (!vtable_map_node) - vtable_map_node = - find_or_create_vtbl_map_node (TYPE_MAIN_VARIANT (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_insert_single_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. 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; - - if (!vlt_saved_class_info) - vec_alloc (vlt_saved_class_info, 10); - - gcc_assert (TREE_CODE (record) == RECORD_TYPE); - - vec_safe_push (vlt_saved_class_info, record); -} - - -/* This function goes through the list of classes we saved and calls - vtv_insert_single_class_info on each one, to build up our class - hierarchy data structure. */ - -void -vtv_recover_class_info (void) -{ - tree current_class; - unsigned i; - - if (vlt_saved_class_info) - { - for (i = 0; i < vlt_saved_class_info->length(); ++i) - { - current_class = (*vlt_saved_class_info)[i]; - gcc_assert (TREE_CODE (current_class) == RECORD_TYPE); - vtv_insert_single_class_info (current_class); - } - } -} - -#include "gt-cp-vtable-class-hierarchy.h" |