8d9254fc8a
From-SVN: r279813
1347 lines
49 KiB
C
1347 lines
49 KiB
C
/* Copyright (C) 2012-2020 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"
|