vtable-class-hierarchy.cc revision 1.1 1 1.1 mrg /* Copyright (C) 2012-2022 Free Software Foundation, Inc.
2 1.1 mrg
3 1.1 mrg This file is part of GCC.
4 1.1 mrg
5 1.1 mrg GCC is free software; you can redistribute it and/or modify it
6 1.1 mrg under the terms of the GNU General Public License as published by
7 1.1 mrg the Free Software Foundation; either version 3, or (at your option)
8 1.1 mrg any later version.
9 1.1 mrg
10 1.1 mrg GCC is distributed in the hope that it will be useful, but
11 1.1 mrg WITHOUT ANY WARRANTY; without even the implied warranty of
12 1.1 mrg MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 1.1 mrg General Public License for more details.
14 1.1 mrg
15 1.1 mrg You should have received a copy of the GNU General Public License
16 1.1 mrg along with GCC; see the file COPYING3. If not see
17 1.1 mrg <http://www.gnu.org/licenses/>. */
18 1.1 mrg
19 1.1 mrg /* Virtual Table Pointer Security Pass - Detect corruption of vtable pointers
20 1.1 mrg before using them for virtual method dispatches. */
21 1.1 mrg
22 1.1 mrg /* This file is part of the vtable security feature implementation.
23 1.1 mrg The vtable security feature is designed to detect when a virtual
24 1.1 mrg call is about to be made through an invalid vtable pointer
25 1.1 mrg (possibly due to data corruption or malicious attacks). The
26 1.1 mrg compiler finds every virtual call, and inserts a verification call
27 1.1 mrg before the virtual call. The verification call takes the actual
28 1.1 mrg vtable pointer value in the object through which the virtual call
29 1.1 mrg is being made, and compares the vtable pointer against a set of all
30 1.1 mrg valid vtable pointers that the object could contain (this set is
31 1.1 mrg based on the declared type of the object). If the pointer is in
32 1.1 mrg the valid set, execution is allowed to continue; otherwise the
33 1.1 mrg program is halted.
34 1.1 mrg
35 1.1 mrg There are several pieces needed in order to make this work: 1. For
36 1.1 mrg every virtual class in the program (i.e. a class that contains
37 1.1 mrg virtual methods), we need to build the set of all possible valid
38 1.1 mrg vtables that an object of that class could point to. This includes
39 1.1 mrg vtables for any class(es) that inherit from the class under
40 1.1 mrg consideration. 2. For every such data set we build up, we need a
41 1.1 mrg way to find and reference the data set. This is complicated by the
42 1.1 mrg fact that the real vtable addresses are not known until runtime,
43 1.1 mrg when the program is loaded into memory, but we need to reference the
44 1.1 mrg sets at compile time when we are inserting verification calls into
45 1.1 mrg the program. 3. We need to find every virtual call in the program,
46 1.1 mrg and insert the verification call (with the appropriate arguments)
47 1.1 mrg before the virtual call. 4. We need some runtime library pieces:
48 1.1 mrg the code to build up the data sets at runtime; the code to actually
49 1.1 mrg perform the verification using the data sets; and some code to set
50 1.1 mrg protections on the data sets, so they themselves do not become
51 1.1 mrg hacker targets.
52 1.1 mrg
53 1.1 mrg To find and reference the set of valid vtable pointers for any given
54 1.1 mrg virtual class, we create a special global varible for each virtual
55 1.1 mrg class. We refer to this as the "vtable map variable" for that
56 1.1 mrg class. The vtable map variable has the type "void *", and is
57 1.1 mrg initialized by the compiler to NULL. At runtime when the set of
58 1.1 mrg valid vtable pointers for a virtual class, e.g. class Foo, is built,
59 1.1 mrg the vtable map variable for class Foo is made to point to the set.
60 1.1 mrg During compile time, when the compiler is inserting verification
61 1.1 mrg calls into the program, it passes the vtable map variable for the
62 1.1 mrg appropriate class to the verification call, so that at runtime the
63 1.1 mrg verification call can find the appropriate data set.
64 1.1 mrg
65 1.1 mrg The actual set of valid vtable pointers for a virtual class,
66 1.1 mrg e.g. class Foo, cannot be built until runtime, when the vtables get
67 1.1 mrg loaded into memory and their addresses are known. But the knowledge
68 1.1 mrg about which vtables belong in which class' hierarchy is only known
69 1.1 mrg at compile time. Therefore at compile time we collect class
70 1.1 mrg hierarchy and vtable information about every virtual class, and we
71 1.1 mrg generate calls to build up the data sets at runtime. To build the
72 1.1 mrg data sets, we call one of the functions we add to the runtime
73 1.1 mrg library, __VLTRegisterPair. __VLTRegisterPair takes two arguments,
74 1.1 mrg a vtable map variable and the address of a vtable. If the vtable
75 1.1 mrg map variable is currently NULL, it creates a new data set (hash
76 1.1 mrg table), makes the vtable map variable point to the new data set, and
77 1.1 mrg inserts the vtable address into the data set. If the vtable map
78 1.1 mrg variable is not NULL, it just inserts the vtable address into the
79 1.1 mrg data set. In order to make sure that our data sets are built before
80 1.1 mrg any verification calls happen, we create a special constructor
81 1.1 mrg initialization function for each compilation unit, give it a very
82 1.1 mrg high initialization priority, and insert all of our calls to
83 1.1 mrg __VLTRegisterPair into our special constructor initialization
84 1.1 mrg function.
85 1.1 mrg
86 1.1 mrg The vtable verification feature is controlled by the flag
87 1.1 mrg '-fvtable-verify='. There are three flavors of this:
88 1.1 mrg '-fvtable-verify=std', '-fvtable-verify=preinit', and
89 1.1 mrg '-fvtable-verify=none'. If the option '-fvtable-verfy=preinit' is
90 1.1 mrg used, then our constructor initialization function gets put into the
91 1.1 mrg preinit array. This is necessary if there are data sets that need
92 1.1 mrg to be built very early in execution. If the constructor
93 1.1 mrg initialization function gets put into the preinit array, the we also
94 1.1 mrg add calls to __VLTChangePermission at the beginning and end of the
95 1.1 mrg function. The call at the beginning sets the permissions on the
96 1.1 mrg data sets and vtable map variables to read/write, and the one at the
97 1.1 mrg end makes them read-only. If the '-fvtable-verify=std' option is
98 1.1 mrg used, the constructor initialization functions are executed at their
99 1.1 mrg normal time, and the __VLTChangePermission calls are handled
100 1.1 mrg differently (see the comments in libstdc++-v3/libsupc++/vtv_rts.cc).
101 1.1 mrg The option '-fvtable-verify=none' turns off vtable verification.
102 1.1 mrg
103 1.1 mrg This file contains code to find and record the class hierarchies for
104 1.1 mrg the virtual classes in a program, and all the vtables associated
105 1.1 mrg with each such class; to generate the vtable map variables; and to
106 1.1 mrg generate the constructor initialization function (with the calls to
107 1.1 mrg __VLTRegisterPair, and __VLTChangePermission). The main data
108 1.1 mrg structures used for collecting the class hierarchy data and
109 1.1 mrg building/maintaining the vtable map variable data are defined in
110 1.1 mrg gcc/vtable-verify.h, because they are used both here and in
111 1.1 mrg gcc/vtable-verify.cc. */
112 1.1 mrg
113 1.1 mrg #include "config.h"
114 1.1 mrg #include "system.h"
115 1.1 mrg #include "coretypes.h"
116 1.1 mrg #include "vtable-verify.h"
117 1.1 mrg #include "cp-tree.h"
118 1.1 mrg #include "stringpool.h"
119 1.1 mrg #include "cgraph.h"
120 1.1 mrg #include "output.h"
121 1.1 mrg #include "tree-iterator.h"
122 1.1 mrg #include "gimplify.h"
123 1.1 mrg #include "stor-layout.h"
124 1.1 mrg
125 1.1 mrg static int num_calls_to_regset = 0;
126 1.1 mrg static int num_calls_to_regpair = 0;
127 1.1 mrg static int current_set_size;
128 1.1 mrg
129 1.1 mrg /* Mark these specially since they need to be stored in precompiled
130 1.1 mrg header IR. */
131 1.1 mrg static GTY (()) vec<tree, va_gc> *vlt_saved_class_info;
132 1.1 mrg static GTY (()) tree vlt_register_pairs_fndecl = NULL_TREE;
133 1.1 mrg static GTY (()) tree vlt_register_set_fndecl = NULL_TREE;
134 1.1 mrg
135 1.1 mrg struct work_node {
136 1.1 mrg struct vtv_graph_node *node;
137 1.1 mrg struct work_node *next;
138 1.1 mrg };
139 1.1 mrg
140 1.1 mrg struct vtbl_map_node *vtable_find_or_create_map_decl (tree);
141 1.1 mrg
142 1.1 mrg /* As part of vtable verification the compiler generates and inserts
143 1.1 mrg calls to __VLTVerifyVtablePointer, which is in libstdc++. This
144 1.1 mrg function builds and initializes the function decl that is used
145 1.1 mrg in generating those function calls.
146 1.1 mrg
147 1.1 mrg In addition to __VLTVerifyVtablePointer there is also
148 1.1 mrg __VLTVerifyVtablePointerDebug which can be used in place of
149 1.1 mrg __VLTVerifyVtablePointer, and which takes extra parameters and
150 1.1 mrg outputs extra information, to help debug problems. The debug
151 1.1 mrg version of this function is generated and used if flag_vtv_debug is
152 1.1 mrg true.
153 1.1 mrg
154 1.1 mrg The signatures for these functions are:
155 1.1 mrg
156 1.1 mrg void * __VLTVerifyVtablePointer (void **, void*);
157 1.1 mrg void * __VLTVerifyVtablePointerDebug (void**, void *, char *, char *);
158 1.1 mrg */
159 1.1 mrg
160 1.1 mrg void
161 1.1 mrg vtv_build_vtable_verify_fndecl (void)
162 1.1 mrg {
163 1.1 mrg tree func_type = NULL_TREE;
164 1.1 mrg
165 1.1 mrg if (verify_vtbl_ptr_fndecl != NULL_TREE
166 1.1 mrg && TREE_CODE (verify_vtbl_ptr_fndecl) != ERROR_MARK)
167 1.1 mrg return;
168 1.1 mrg
169 1.1 mrg if (flag_vtv_debug)
170 1.1 mrg {
171 1.1 mrg func_type = build_function_type_list (const_ptr_type_node,
172 1.1 mrg build_pointer_type (ptr_type_node),
173 1.1 mrg const_ptr_type_node,
174 1.1 mrg const_string_type_node,
175 1.1 mrg const_string_type_node,
176 1.1 mrg NULL_TREE);
177 1.1 mrg verify_vtbl_ptr_fndecl =
178 1.1 mrg build_lang_decl (FUNCTION_DECL,
179 1.1 mrg get_identifier ("__VLTVerifyVtablePointerDebug"),
180 1.1 mrg func_type);
181 1.1 mrg }
182 1.1 mrg else
183 1.1 mrg {
184 1.1 mrg func_type = build_function_type_list (const_ptr_type_node,
185 1.1 mrg build_pointer_type (ptr_type_node),
186 1.1 mrg const_ptr_type_node,
187 1.1 mrg NULL_TREE);
188 1.1 mrg verify_vtbl_ptr_fndecl =
189 1.1 mrg build_lang_decl (FUNCTION_DECL,
190 1.1 mrg get_identifier ("__VLTVerifyVtablePointer"),
191 1.1 mrg func_type);
192 1.1 mrg }
193 1.1 mrg
194 1.1 mrg TREE_NOTHROW (verify_vtbl_ptr_fndecl) = 1;
195 1.1 mrg DECL_ATTRIBUTES (verify_vtbl_ptr_fndecl)
196 1.1 mrg = tree_cons (get_identifier ("leaf"), NULL,
197 1.1 mrg DECL_ATTRIBUTES (verify_vtbl_ptr_fndecl));
198 1.1 mrg DECL_PURE_P (verify_vtbl_ptr_fndecl) = 1;
199 1.1 mrg TREE_PUBLIC (verify_vtbl_ptr_fndecl) = 1;
200 1.1 mrg DECL_PRESERVE_P (verify_vtbl_ptr_fndecl) = 1;
201 1.1 mrg }
202 1.1 mrg
203 1.1 mrg /* As part of vtable verification the compiler generates and inserts
204 1.1 mrg calls to __VLTRegisterSet and __VLTRegisterPair, which are in
205 1.1 mrg libsupc++. This function builds and initializes the function decls
206 1.1 mrg that are used in generating those function calls.
207 1.1 mrg
208 1.1 mrg The signatures for these functions are:
209 1.1 mrg
210 1.1 mrg void __VLTRegisterSetDebug (void **, const void *, std::size_t,
211 1.1 mrg size_t, void **);
212 1.1 mrg
213 1.1 mrg void __VLTRegisterSet (void **, const void *, std::size_t,
214 1.1 mrg size_t, void **);
215 1.1 mrg
216 1.1 mrg void __VLTRegisterPairDebug (void **, const void *, size_t,
217 1.1 mrg const void *, const char *, const char *);
218 1.1 mrg
219 1.1 mrg void __VLTRegisterPair (void **, const void *, size_t, const void *);
220 1.1 mrg */
221 1.1 mrg
222 1.1 mrg static void
223 1.1 mrg init_functions (void)
224 1.1 mrg {
225 1.1 mrg tree register_set_type;
226 1.1 mrg tree register_pairs_type;
227 1.1 mrg
228 1.1 mrg if (vlt_register_set_fndecl != NULL_TREE)
229 1.1 mrg return;
230 1.1 mrg
231 1.1 mrg gcc_assert (vlt_register_pairs_fndecl == NULL_TREE);
232 1.1 mrg gcc_assert (vlt_register_set_fndecl == NULL_TREE);
233 1.1 mrg
234 1.1 mrg /* Build function decl for __VLTRegisterSet*. */
235 1.1 mrg
236 1.1 mrg register_set_type = build_function_type_list
237 1.1 mrg (void_type_node,
238 1.1 mrg build_pointer_type (ptr_type_node),
239 1.1 mrg const_ptr_type_node,
240 1.1 mrg size_type_node,
241 1.1 mrg size_type_node,
242 1.1 mrg build_pointer_type (ptr_type_node),
243 1.1 mrg NULL_TREE);
244 1.1 mrg
245 1.1 mrg if (flag_vtv_debug)
246 1.1 mrg vlt_register_set_fndecl = build_lang_decl
247 1.1 mrg (FUNCTION_DECL,
248 1.1 mrg get_identifier ("__VLTRegisterSetDebug"),
249 1.1 mrg register_set_type);
250 1.1 mrg else
251 1.1 mrg vlt_register_set_fndecl = build_lang_decl
252 1.1 mrg (FUNCTION_DECL,
253 1.1 mrg get_identifier ("__VLTRegisterSet"),
254 1.1 mrg register_set_type);
255 1.1 mrg
256 1.1 mrg
257 1.1 mrg TREE_NOTHROW (vlt_register_set_fndecl) = 1;
258 1.1 mrg DECL_ATTRIBUTES (vlt_register_set_fndecl) =
259 1.1 mrg tree_cons (get_identifier ("leaf"), NULL,
260 1.1 mrg DECL_ATTRIBUTES (vlt_register_set_fndecl));
261 1.1 mrg DECL_EXTERNAL(vlt_register_set_fndecl) = 1;
262 1.1 mrg TREE_PUBLIC (vlt_register_set_fndecl) = 1;
263 1.1 mrg DECL_PRESERVE_P (vlt_register_set_fndecl) = 1;
264 1.1 mrg SET_DECL_LANGUAGE (vlt_register_set_fndecl, lang_cplusplus);
265 1.1 mrg
266 1.1 mrg /* Build function decl for __VLTRegisterPair*. */
267 1.1 mrg
268 1.1 mrg if (flag_vtv_debug)
269 1.1 mrg {
270 1.1 mrg register_pairs_type = build_function_type_list (void_type_node,
271 1.1 mrg build_pointer_type
272 1.1 mrg (ptr_type_node),
273 1.1 mrg const_ptr_type_node,
274 1.1 mrg size_type_node,
275 1.1 mrg const_ptr_type_node,
276 1.1 mrg const_string_type_node,
277 1.1 mrg const_string_type_node,
278 1.1 mrg NULL_TREE);
279 1.1 mrg
280 1.1 mrg vlt_register_pairs_fndecl = build_lang_decl
281 1.1 mrg (FUNCTION_DECL,
282 1.1 mrg get_identifier ("__VLTRegisterPairDebug"),
283 1.1 mrg register_pairs_type);
284 1.1 mrg }
285 1.1 mrg else
286 1.1 mrg {
287 1.1 mrg register_pairs_type = build_function_type_list (void_type_node,
288 1.1 mrg build_pointer_type
289 1.1 mrg (ptr_type_node),
290 1.1 mrg const_ptr_type_node,
291 1.1 mrg size_type_node,
292 1.1 mrg const_ptr_type_node,
293 1.1 mrg NULL_TREE);
294 1.1 mrg
295 1.1 mrg vlt_register_pairs_fndecl = build_lang_decl
296 1.1 mrg (FUNCTION_DECL,
297 1.1 mrg get_identifier ("__VLTRegisterPair"),
298 1.1 mrg register_pairs_type);
299 1.1 mrg }
300 1.1 mrg
301 1.1 mrg TREE_NOTHROW (vlt_register_pairs_fndecl) = 1;
302 1.1 mrg DECL_ATTRIBUTES (vlt_register_pairs_fndecl) =
303 1.1 mrg tree_cons (get_identifier ("leaf"), NULL,
304 1.1 mrg DECL_ATTRIBUTES (vlt_register_pairs_fndecl));
305 1.1 mrg DECL_EXTERNAL(vlt_register_pairs_fndecl) = 1;
306 1.1 mrg TREE_PUBLIC (vlt_register_pairs_fndecl) = 1;
307 1.1 mrg DECL_PRESERVE_P (vlt_register_pairs_fndecl) = 1;
308 1.1 mrg SET_DECL_LANGUAGE (vlt_register_pairs_fndecl, lang_cplusplus);
309 1.1 mrg
310 1.1 mrg }
311 1.1 mrg
312 1.1 mrg /* This is a helper function for
313 1.1 mrg vtv_compute_class_hierarchy_transitive_closure. It adds a
314 1.1 mrg vtv_graph_node to the WORKLIST, which is a linked list of
315 1.1 mrg seen-but-not-yet-processed nodes. INSERTED is a bitmap, one bit
316 1.1 mrg per node, to help make sure that we don't insert a node into the
317 1.1 mrg worklist more than once. Each node represents a class somewhere in
318 1.1 mrg our class hierarchy information. Every node in the graph gets added
319 1.1 mrg to the worklist exactly once and removed from the worklist exactly
320 1.1 mrg once (when all of its children have been processed). */
321 1.1 mrg
322 1.1 mrg static void
323 1.1 mrg add_to_worklist (struct work_node **worklist, struct vtv_graph_node *node,
324 1.1 mrg sbitmap inserted)
325 1.1 mrg {
326 1.1 mrg struct work_node *new_work_node;
327 1.1 mrg
328 1.1 mrg if (bitmap_bit_p (inserted, node->class_uid))
329 1.1 mrg return;
330 1.1 mrg
331 1.1 mrg new_work_node = XNEW (struct work_node);
332 1.1 mrg new_work_node->next = *worklist;
333 1.1 mrg new_work_node->node = node;
334 1.1 mrg *worklist = new_work_node;
335 1.1 mrg
336 1.1 mrg bitmap_set_bit (inserted, node->class_uid);
337 1.1 mrg }
338 1.1 mrg
339 1.1 mrg /* This is a helper function for
340 1.1 mrg vtv_compute_class_hierarchy_transitive_closure. It goes through
341 1.1 mrg the WORKLIST of class hierarchy nodes looking for a "leaf" node,
342 1.1 mrg i.e. a node whose children in the hierarchy have all been
343 1.1 mrg processed. When it finds the next leaf node, it removes it from
344 1.1 mrg the linked list (WORKLIST) and returns the node. */
345 1.1 mrg
346 1.1 mrg static struct vtv_graph_node *
347 1.1 mrg find_and_remove_next_leaf_node (struct work_node **worklist)
348 1.1 mrg {
349 1.1 mrg struct work_node *prev, *cur;
350 1.1 mrg struct vtv_graph_node *ret_val = NULL;
351 1.1 mrg
352 1.1 mrg for (prev = NULL, cur = *worklist; cur; prev = cur, cur = cur->next)
353 1.1 mrg {
354 1.1 mrg if ((cur->node->children).length() == cur->node->num_processed_children)
355 1.1 mrg {
356 1.1 mrg if (prev == NULL)
357 1.1 mrg (*worklist) = cur->next;
358 1.1 mrg else
359 1.1 mrg prev->next = cur->next;
360 1.1 mrg
361 1.1 mrg cur->next = NULL;
362 1.1 mrg ret_val = cur->node;
363 1.1 mrg free (cur);
364 1.1 mrg return ret_val;
365 1.1 mrg }
366 1.1 mrg }
367 1.1 mrg
368 1.1 mrg return NULL;
369 1.1 mrg }
370 1.1 mrg
371 1.1 mrg /* In our class hierarchy graph, each class node contains a bitmap,
372 1.1 mrg with one bit for each class in the hierarchy. The bits are set for
373 1.1 mrg classes that are descendants in the graph of the current node.
374 1.1 mrg Initially the descendants bitmap is only set for immediate
375 1.1 mrg descendants. This function traverses the class hierarchy graph,
376 1.1 mrg bottom up, filling in the transitive closures for the descendants
377 1.1 mrg as we rise up the graph. */
378 1.1 mrg
379 1.1 mrg void
380 1.1 mrg vtv_compute_class_hierarchy_transitive_closure (void)
381 1.1 mrg {
382 1.1 mrg struct work_node *worklist = NULL;
383 1.1 mrg sbitmap inserted = sbitmap_alloc (num_vtable_map_nodes);
384 1.1 mrg unsigned i;
385 1.1 mrg unsigned j;
386 1.1 mrg
387 1.1 mrg /* Note: Every node in the graph gets added to the worklist exactly
388 1.1 mrg once and removed from the worklist exactly once (when all of its
389 1.1 mrg children have been processed). Each node's children edges are
390 1.1 mrg followed exactly once, and each node's parent edges are followed
391 1.1 mrg exactly once. So this algorithm is roughly O(V + 2E), i.e.
392 1.1 mrg O(E + V). */
393 1.1 mrg
394 1.1 mrg /* Set-up: */
395 1.1 mrg /* Find all the "leaf" nodes in the graph, and add them to the worklist. */
396 1.1 mrg bitmap_clear (inserted);
397 1.1 mrg for (j = 0; j < num_vtable_map_nodes; ++j)
398 1.1 mrg {
399 1.1 mrg struct vtbl_map_node *cur = vtbl_map_nodes_vec[j];
400 1.1 mrg if (cur->class_info
401 1.1 mrg && ((cur->class_info->children).length() == 0)
402 1.1 mrg && ! (bitmap_bit_p (inserted, cur->class_info->class_uid)))
403 1.1 mrg add_to_worklist (&worklist, cur->class_info, inserted);
404 1.1 mrg }
405 1.1 mrg
406 1.1 mrg /* Main work: pull next leaf node off work list, process it, add its
407 1.1 mrg parents to the worklist, where a 'leaf' node is one that has no
408 1.1 mrg children, or all of its children have been processed. */
409 1.1 mrg while (worklist)
410 1.1 mrg {
411 1.1 mrg struct vtv_graph_node *temp_node =
412 1.1 mrg find_and_remove_next_leaf_node (&worklist);
413 1.1 mrg
414 1.1 mrg gcc_assert (temp_node != NULL);
415 1.1 mrg temp_node->descendants = sbitmap_alloc (num_vtable_map_nodes);
416 1.1 mrg bitmap_clear (temp_node->descendants);
417 1.1 mrg bitmap_set_bit (temp_node->descendants, temp_node->class_uid);
418 1.1 mrg for (i = 0; i < (temp_node->children).length(); ++i)
419 1.1 mrg bitmap_ior (temp_node->descendants, temp_node->descendants,
420 1.1 mrg temp_node->children[i]->descendants);
421 1.1 mrg for (i = 0; i < (temp_node->parents).length(); ++i)
422 1.1 mrg {
423 1.1 mrg temp_node->parents[i]->num_processed_children =
424 1.1 mrg temp_node->parents[i]->num_processed_children + 1;
425 1.1 mrg if (!bitmap_bit_p (inserted, temp_node->parents[i]->class_uid))
426 1.1 mrg add_to_worklist (&worklist, temp_node->parents[i], inserted);
427 1.1 mrg }
428 1.1 mrg }
429 1.1 mrg }
430 1.1 mrg
431 1.1 mrg /* Keep track of which pairs we have already created __VLTRegisterPair
432 1.1 mrg calls for, to prevent creating duplicate calls within the same
433 1.1 mrg compilation unit. VTABLE_DECL is the var decl for the vtable of
434 1.1 mrg the (descendant) class that we are adding to our class hierarchy
435 1.1 mrg data. VPTR_ADDRESS is an expression for calculating the correct
436 1.1 mrg offset into the vtable (VTABLE_DECL). It is the actual vtable
437 1.1 mrg pointer address that will be stored in our list of valid vtable
438 1.1 mrg pointers for BASE_CLASS. BASE_CLASS is the record_type node for
439 1.1 mrg the base class to whose hiearchy we want to add
440 1.1 mrg VPTR_ADDRESS. (VTABLE_DECL should be the vtable for BASE_CLASS or
441 1.1 mrg one of BASE_CLASS' descendents. */
442 1.1 mrg
443 1.1 mrg static bool
444 1.1 mrg check_and_record_registered_pairs (tree vtable_decl, tree vptr_address,
445 1.1 mrg tree base_class)
446 1.1 mrg {
447 1.1 mrg unsigned offset;
448 1.1 mrg struct vtbl_map_node *base_vtable_map_node;
449 1.1 mrg bool inserted_something = false;
450 1.1 mrg
451 1.1 mrg
452 1.1 mrg if (TREE_CODE (vptr_address) == ADDR_EXPR
453 1.1 mrg && TREE_CODE (TREE_OPERAND (vptr_address, 0)) == MEM_REF)
454 1.1 mrg vptr_address = TREE_OPERAND (vptr_address, 0);
455 1.1 mrg
456 1.1 mrg if (TREE_OPERAND_LENGTH (vptr_address) > 1)
457 1.1 mrg offset = TREE_INT_CST_LOW (TREE_OPERAND (vptr_address, 1));
458 1.1 mrg else
459 1.1 mrg offset = 0;
460 1.1 mrg
461 1.1 mrg base_vtable_map_node = vtbl_map_get_node (TYPE_MAIN_VARIANT (base_class));
462 1.1 mrg
463 1.1 mrg inserted_something = vtbl_map_node_registration_insert
464 1.1 mrg (base_vtable_map_node,
465 1.1 mrg vtable_decl,
466 1.1 mrg offset);
467 1.1 mrg return !inserted_something;
468 1.1 mrg }
469 1.1 mrg
470 1.1 mrg /* Given an IDENTIFIER_NODE, build and return a string literal based on it. */
471 1.1 mrg
472 1.1 mrg static tree
473 1.1 mrg build_string_from_id (tree identifier)
474 1.1 mrg {
475 1.1 mrg int len;
476 1.1 mrg
477 1.1 mrg gcc_assert (TREE_CODE (identifier) == IDENTIFIER_NODE);
478 1.1 mrg
479 1.1 mrg len = IDENTIFIER_LENGTH (identifier);
480 1.1 mrg return build_string_literal (len + 1, IDENTIFIER_POINTER (identifier));
481 1.1 mrg }
482 1.1 mrg
483 1.1 mrg /* A class may contain secondary vtables in it, for various reasons.
484 1.1 mrg This function goes through the decl chain of a class record looking
485 1.1 mrg for any fields that point to secondary vtables, and adding calls to
486 1.1 mrg __VLTRegisterPair for the secondary vtable pointers.
487 1.1 mrg
488 1.1 mrg BASE_CLASS_DECL_ARG is an expression for the address of the vtable
489 1.1 mrg map variable for the BASE_CLASS (whose hierarchy we are currently
490 1.1 mrg updating). BASE_CLASS is the record_type node for the base class.
491 1.1 mrg RECORD_TYPE is the record_type node for the descendant class that
492 1.1 mrg we are possibly adding to BASE_CLASS's hierarchy. BODY is the
493 1.1 mrg function body for the constructor init function to which we are
494 1.1 mrg adding our calls to __VLTRegisterPair. */
495 1.1 mrg
496 1.1 mrg static void
497 1.1 mrg register_construction_vtables (tree base_class, tree record_type,
498 1.1 mrg vec<tree> *vtable_ptr_array)
499 1.1 mrg {
500 1.1 mrg tree vtbl_var_decl;
501 1.1 mrg
502 1.1 mrg if (TREE_CODE (record_type) != RECORD_TYPE)
503 1.1 mrg return;
504 1.1 mrg
505 1.1 mrg vtbl_var_decl = CLASSTYPE_VTABLES (record_type);
506 1.1 mrg
507 1.1 mrg if (CLASSTYPE_VBASECLASSES (record_type))
508 1.1 mrg {
509 1.1 mrg tree vtt_decl;
510 1.1 mrg bool already_registered = false;
511 1.1 mrg tree val_vtbl_decl = NULL_TREE;
512 1.1 mrg
513 1.1 mrg vtt_decl = DECL_CHAIN (vtbl_var_decl);
514 1.1 mrg
515 1.1 mrg /* Check to see if we have found a VTT. Add its data if appropriate. */
516 1.1 mrg if (vtt_decl)
517 1.1 mrg {
518 1.1 mrg tree values = DECL_INITIAL (vtt_decl);
519 1.1 mrg if (TREE_ASM_WRITTEN (vtt_decl)
520 1.1 mrg && values != NULL_TREE
521 1.1 mrg && TREE_CODE (values) == CONSTRUCTOR
522 1.1 mrg && TREE_CODE (TREE_TYPE (values)) == ARRAY_TYPE)
523 1.1 mrg {
524 1.1 mrg unsigned HOST_WIDE_INT cnt;
525 1.1 mrg constructor_elt *ce;
526 1.1 mrg
527 1.1 mrg /* Loop through the initialization values for this
528 1.1 mrg vtable to get all the correct vtable pointer
529 1.1 mrg addresses that we need to add to our set of valid
530 1.1 mrg vtable pointers for the current base class. This may
531 1.1 mrg result in adding more than just the element assigned
532 1.1 mrg to the primary vptr of the class, so we may end up
533 1.1 mrg with more vtable pointers than are strictly
534 1.1 mrg necessary. */
535 1.1 mrg
536 1.1 mrg for (cnt = 0;
537 1.1 mrg vec_safe_iterate (CONSTRUCTOR_ELTS (values),
538 1.1 mrg cnt, &ce);
539 1.1 mrg cnt++)
540 1.1 mrg {
541 1.1 mrg tree value = ce->value;
542 1.1 mrg
543 1.1 mrg /* Search for the ADDR_EXPR operand within the value. */
544 1.1 mrg
545 1.1 mrg while (value
546 1.1 mrg && TREE_OPERAND (value, 0)
547 1.1 mrg && TREE_CODE (TREE_OPERAND (value, 0)) == ADDR_EXPR)
548 1.1 mrg value = TREE_OPERAND (value, 0);
549 1.1 mrg
550 1.1 mrg /* The VAR_DECL for the vtable should be the first
551 1.1 mrg argument of the ADDR_EXPR, which is the first
552 1.1 mrg argument of value.*/
553 1.1 mrg
554 1.1 mrg if (TREE_OPERAND (value, 0))
555 1.1 mrg val_vtbl_decl = TREE_OPERAND (value, 0);
556 1.1 mrg
557 1.1 mrg while (!VAR_P (val_vtbl_decl)
558 1.1 mrg && TREE_OPERAND (val_vtbl_decl, 0))
559 1.1 mrg val_vtbl_decl = TREE_OPERAND (val_vtbl_decl, 0);
560 1.1 mrg
561 1.1 mrg gcc_assert (VAR_P (val_vtbl_decl));
562 1.1 mrg
563 1.1 mrg /* Check to see if we already have this vtable pointer in
564 1.1 mrg our valid set for this base class. */
565 1.1 mrg
566 1.1 mrg already_registered = check_and_record_registered_pairs
567 1.1 mrg (val_vtbl_decl,
568 1.1 mrg value,
569 1.1 mrg base_class);
570 1.1 mrg
571 1.1 mrg if (already_registered)
572 1.1 mrg continue;
573 1.1 mrg
574 1.1 mrg /* Add this vtable pointer to our set of valid
575 1.1 mrg pointers for the base class. */
576 1.1 mrg
577 1.1 mrg vtable_ptr_array->safe_push (value);
578 1.1 mrg current_set_size++;
579 1.1 mrg }
580 1.1 mrg }
581 1.1 mrg }
582 1.1 mrg }
583 1.1 mrg }
584 1.1 mrg
585 1.1 mrg /* This function iterates through all the vtables it can find from the
586 1.1 mrg BINFO of a class, to make sure we have found ALL of the vtables
587 1.1 mrg that an object of that class could point to. Generate calls to
588 1.1 mrg __VLTRegisterPair for those vtable pointers that we find.
589 1.1 mrg
590 1.1 mrg BINFO is the tree_binfo node for the BASE_CLASS. BODY is the
591 1.1 mrg function body for the constructor init function to which we are
592 1.1 mrg adding calls to __VLTRegisterPair. ARG1 is an expression for the
593 1.1 mrg address of the vtable map variable (for the BASE_CLASS), that will
594 1.1 mrg point to the updated data set. BASE_CLASS is the record_type node
595 1.1 mrg for the base class whose set of valid vtable pointers we are
596 1.1 mrg updating. STR1 and STR2 are all debugging information, to be passed
597 1.1 mrg as parameters to __VLTRegisterPairDebug. STR1 represents the name
598 1.1 mrg of the vtable map variable to be updated by the call. Similarly,
599 1.1 mrg STR2 represents the name of the class whose vtable pointer is being
600 1.1 mrg added to the hierarchy. */
601 1.1 mrg
602 1.1 mrg static void
603 1.1 mrg register_other_binfo_vtables (tree binfo, tree base_class,
604 1.1 mrg vec<tree> *vtable_ptr_array)
605 1.1 mrg {
606 1.1 mrg unsigned ix;
607 1.1 mrg tree base_binfo;
608 1.1 mrg tree vtable_decl;
609 1.1 mrg bool already_registered;
610 1.1 mrg
611 1.1 mrg if (binfo == NULL_TREE)
612 1.1 mrg return;
613 1.1 mrg
614 1.1 mrg for (ix = 0; BINFO_BASE_ITERATE (binfo, ix, base_binfo); ix++)
615 1.1 mrg {
616 1.1 mrg if ((!BINFO_PRIMARY_P (base_binfo)
617 1.1 mrg || BINFO_VIRTUAL_P (base_binfo))
618 1.1 mrg && (vtable_decl = get_vtbl_decl_for_binfo (base_binfo)))
619 1.1 mrg {
620 1.1 mrg tree vtable_address = build_vtbl_address (base_binfo);
621 1.1 mrg
622 1.1 mrg already_registered = check_and_record_registered_pairs
623 1.1 mrg (vtable_decl,
624 1.1 mrg vtable_address,
625 1.1 mrg base_class);
626 1.1 mrg if (!already_registered)
627 1.1 mrg {
628 1.1 mrg vtable_ptr_array->safe_push (vtable_address);
629 1.1 mrg current_set_size++;
630 1.1 mrg }
631 1.1 mrg }
632 1.1 mrg
633 1.1 mrg register_other_binfo_vtables (base_binfo, base_class, vtable_ptr_array);
634 1.1 mrg }
635 1.1 mrg }
636 1.1 mrg
637 1.1 mrg /* The set of valid vtable pointers for any given class are stored in
638 1.1 mrg a hash table. For reasons of efficiency, that hash table size is
639 1.1 mrg always a power of two. In order to try to prevent re-sizing the
640 1.1 mrg hash tables very often, we pass __VLTRegisterPair an initial guess
641 1.1 mrg as to the number of entries the hashtable will eventually need
642 1.1 mrg (rounded up to the nearest power of two). This function takes the
643 1.1 mrg class information we have collected for a particular class,
644 1.1 mrg CLASS_NODE, and calculates the hash table size guess. */
645 1.1 mrg
646 1.1 mrg static int
647 1.1 mrg guess_num_vtable_pointers (struct vtv_graph_node *class_node)
648 1.1 mrg {
649 1.1 mrg tree vtbl;
650 1.1 mrg int total_num_vtbls = 0;
651 1.1 mrg int num_vtbls_power_of_two = 1;
652 1.1 mrg unsigned i;
653 1.1 mrg
654 1.1 mrg for (i = 0; i < num_vtable_map_nodes; ++i)
655 1.1 mrg if (bitmap_bit_p (class_node->descendants, i))
656 1.1 mrg {
657 1.1 mrg tree class_type = vtbl_map_nodes_vec[i]->class_info->class_type;
658 1.1 mrg for (vtbl = CLASSTYPE_VTABLES (class_type); vtbl;
659 1.1 mrg vtbl = DECL_CHAIN (vtbl))
660 1.1 mrg {
661 1.1 mrg total_num_vtbls++;
662 1.1 mrg if (total_num_vtbls > num_vtbls_power_of_two)
663 1.1 mrg num_vtbls_power_of_two <<= 1;
664 1.1 mrg }
665 1.1 mrg }
666 1.1 mrg return num_vtbls_power_of_two;
667 1.1 mrg }
668 1.1 mrg
669 1.1 mrg /* A simple hash function on strings */
670 1.1 mrg /* Be careful about changing this routine. The values generated will
671 1.1 mrg be stored in the calls to InitSet. So, changing this routine may
672 1.1 mrg cause a binary incompatibility. */
673 1.1 mrg
674 1.1 mrg static uint32_t
675 1.1 mrg vtv_string_hash (const char *in)
676 1.1 mrg {
677 1.1 mrg const char *s = in;
678 1.1 mrg uint32_t h = 0;
679 1.1 mrg
680 1.1 mrg gcc_assert (in != NULL);
681 1.1 mrg for ( ; *s; ++s)
682 1.1 mrg h = 5 * h + *s;
683 1.1 mrg return h;
684 1.1 mrg }
685 1.1 mrg
686 1.1 mrg static char *
687 1.1 mrg get_log_file_name (const char *fname)
688 1.1 mrg {
689 1.1 mrg const char *tmp_dir = concat (dump_dir_name, NULL);
690 1.1 mrg char *full_name;
691 1.1 mrg int dir_len;
692 1.1 mrg int fname_len;
693 1.1 mrg
694 1.1 mrg dir_len = strlen (tmp_dir);
695 1.1 mrg fname_len = strlen (fname);
696 1.1 mrg
697 1.1 mrg full_name = XNEWVEC (char, dir_len + fname_len + 1);
698 1.1 mrg strcpy (full_name, tmp_dir);
699 1.1 mrg strcpy (full_name + dir_len, fname);
700 1.1 mrg
701 1.1 mrg return full_name;
702 1.1 mrg }
703 1.1 mrg
704 1.1 mrg static void
705 1.1 mrg write_out_current_set_data (tree base_class, int set_size)
706 1.1 mrg {
707 1.1 mrg static int class_data_log_fd = -1;
708 1.1 mrg char buffer[1024];
709 1.1 mrg int bytes_written __attribute__ ((unused));
710 1.1 mrg char *file_name = get_log_file_name ("vtv_class_set_sizes.log");
711 1.1 mrg
712 1.1 mrg if (class_data_log_fd == -1)
713 1.1 mrg class_data_log_fd = open (file_name,
714 1.1 mrg O_WRONLY | O_APPEND | O_CREAT, S_IRWXU);
715 1.1 mrg
716 1.1 mrg if (class_data_log_fd == -1)
717 1.1 mrg {
718 1.1 mrg warning_at (UNKNOWN_LOCATION, 0,
719 1.1 mrg "unable to open log file %<vtv_class_set_sizes.log%>: %m");
720 1.1 mrg return;
721 1.1 mrg }
722 1.1 mrg
723 1.1 mrg snprintf (buffer, sizeof (buffer), "%s %d\n",
724 1.1 mrg IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (TYPE_NAME (base_class))),
725 1.1 mrg set_size);
726 1.1 mrg bytes_written = write (class_data_log_fd, buffer, strlen (buffer));
727 1.1 mrg }
728 1.1 mrg
729 1.1 mrg static tree
730 1.1 mrg build_key_buffer_arg (tree base_ptr_var_decl)
731 1.1 mrg {
732 1.1 mrg const int key_type_fixed_size = 8;
733 1.1 mrg uint32_t len1 = IDENTIFIER_LENGTH (DECL_NAME (base_ptr_var_decl));
734 1.1 mrg uint32_t hash_value = vtv_string_hash (IDENTIFIER_POINTER
735 1.1 mrg (DECL_NAME (base_ptr_var_decl)));
736 1.1 mrg void *key_buffer = xmalloc (len1 + key_type_fixed_size);
737 1.1 mrg uint32_t *value_ptr = (uint32_t *) key_buffer;
738 1.1 mrg tree ret_value;
739 1.1 mrg
740 1.1 mrg /* Set the len and hash for the string. */
741 1.1 mrg *value_ptr = len1;
742 1.1 mrg value_ptr++;
743 1.1 mrg *value_ptr = hash_value;
744 1.1 mrg
745 1.1 mrg /* Now copy the string representation of the vtbl map name... */
746 1.1 mrg memcpy ((char *) key_buffer + key_type_fixed_size,
747 1.1 mrg IDENTIFIER_POINTER (DECL_NAME (base_ptr_var_decl)),
748 1.1 mrg len1);
749 1.1 mrg
750 1.1 mrg /* ... and build a string literal from it. This will make a copy
751 1.1 mrg so the key_bufffer is not needed anymore after this. */
752 1.1 mrg ret_value = build_string_literal (len1 + key_type_fixed_size,
753 1.1 mrg (char *) key_buffer);
754 1.1 mrg free (key_buffer);
755 1.1 mrg return ret_value;
756 1.1 mrg }
757 1.1 mrg
758 1.1 mrg static void
759 1.1 mrg insert_call_to_register_set (tree class_name,
760 1.1 mrg vec<tree> *vtbl_ptr_array, tree body, tree arg1,
761 1.1 mrg tree arg2, tree size_hint_arg)
762 1.1 mrg {
763 1.1 mrg tree call_expr;
764 1.1 mrg int num_args = vtbl_ptr_array->length();
765 1.1 mrg char *array_arg_name = ACONCAT (("__vptr_array_",
766 1.1 mrg IDENTIFIER_POINTER (class_name), NULL));
767 1.1 mrg tree array_arg_type = build_array_type_nelts (build_pointer_type
768 1.1 mrg (build_pointer_type
769 1.1 mrg (void_type_node)),
770 1.1 mrg num_args);
771 1.1 mrg tree array_arg = build_decl (UNKNOWN_LOCATION, VAR_DECL,
772 1.1 mrg get_identifier (array_arg_name),
773 1.1 mrg array_arg_type);
774 1.1 mrg int k;
775 1.1 mrg
776 1.1 mrg vec<constructor_elt, va_gc> *array_elements;
777 1.1 mrg vec_alloc (array_elements, num_args);
778 1.1 mrg
779 1.1 mrg tree initial = NULL_TREE;
780 1.1 mrg tree arg3 = NULL_TREE;
781 1.1 mrg
782 1.1 mrg TREE_PUBLIC (array_arg) = 0;
783 1.1 mrg DECL_EXTERNAL (array_arg) = 0;
784 1.1 mrg TREE_STATIC (array_arg) = 1;
785 1.1 mrg DECL_ARTIFICIAL (array_arg) = 0;
786 1.1 mrg TREE_READONLY (array_arg) = 1;
787 1.1 mrg DECL_IGNORED_P (array_arg) = 0;
788 1.1 mrg DECL_PRESERVE_P (array_arg) = 0;
789 1.1 mrg DECL_VISIBILITY (array_arg) = VISIBILITY_HIDDEN;
790 1.1 mrg
791 1.1 mrg for (k = 0; k < num_args; ++k)
792 1.1 mrg {
793 1.1 mrg CONSTRUCTOR_APPEND_ELT (array_elements, NULL_TREE, (*vtbl_ptr_array)[k]);
794 1.1 mrg }
795 1.1 mrg
796 1.1 mrg initial = build_constructor (TREE_TYPE (array_arg), array_elements);
797 1.1 mrg
798 1.1 mrg TREE_CONSTANT (initial) = 1;
799 1.1 mrg TREE_STATIC (initial) = 1;
800 1.1 mrg DECL_INITIAL (array_arg) = initial;
801 1.1 mrg relayout_decl (array_arg);
802 1.1 mrg varpool_node::finalize_decl (array_arg);
803 1.1 mrg
804 1.1 mrg arg3 = build1 (ADDR_EXPR, TYPE_POINTER_TO (TREE_TYPE (array_arg)), array_arg);
805 1.1 mrg
806 1.1 mrg TREE_TYPE (arg3) = build_pointer_type (TREE_TYPE (array_arg));
807 1.1 mrg
808 1.1 mrg call_expr = build_call_expr (vlt_register_set_fndecl, 5, arg1,
809 1.1 mrg arg2, /* set_symbol_key */
810 1.1 mrg size_hint_arg, build_int_cst (size_type_node,
811 1.1 mrg num_args),
812 1.1 mrg arg3);
813 1.1 mrg append_to_statement_list (call_expr, &body);
814 1.1 mrg num_calls_to_regset++;
815 1.1 mrg }
816 1.1 mrg
817 1.1 mrg static void
818 1.1 mrg insert_call_to_register_pair (vec<tree> *vtbl_ptr_array, tree arg1,
819 1.1 mrg tree arg2, tree size_hint_arg, tree str1,
820 1.1 mrg tree str2, tree body)
821 1.1 mrg {
822 1.1 mrg tree call_expr;
823 1.1 mrg int num_args = vtbl_ptr_array->length();
824 1.1 mrg tree vtable_address = NULL_TREE;
825 1.1 mrg
826 1.1 mrg if (num_args == 0)
827 1.1 mrg vtable_address = build_int_cst (build_pointer_type (void_type_node), 0);
828 1.1 mrg else
829 1.1 mrg vtable_address = (*vtbl_ptr_array)[0];
830 1.1 mrg
831 1.1 mrg if (flag_vtv_debug)
832 1.1 mrg call_expr = build_call_expr (vlt_register_pairs_fndecl, 6, arg1, arg2,
833 1.1 mrg size_hint_arg, vtable_address, str1, str2);
834 1.1 mrg else
835 1.1 mrg call_expr = build_call_expr (vlt_register_pairs_fndecl, 4, arg1, arg2,
836 1.1 mrg size_hint_arg, vtable_address);
837 1.1 mrg
838 1.1 mrg append_to_statement_list (call_expr, &body);
839 1.1 mrg num_calls_to_regpair++;
840 1.1 mrg }
841 1.1 mrg
842 1.1 mrg static void
843 1.1 mrg output_set_info (tree record_type, vec<tree> vtbl_ptr_array)
844 1.1 mrg {
845 1.1 mrg static int vtv_debug_log_fd = -1;
846 1.1 mrg char buffer[1024];
847 1.1 mrg int bytes_written __attribute__ ((unused));
848 1.1 mrg int array_len = vtbl_ptr_array.length();
849 1.1 mrg const char *class_name =
850 1.1 mrg IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (TYPE_NAME (record_type)));
851 1.1 mrg char *file_name = get_log_file_name ("vtv_set_ptr_data.log");
852 1.1 mrg
853 1.1 mrg if (vtv_debug_log_fd == -1)
854 1.1 mrg vtv_debug_log_fd = open (file_name,
855 1.1 mrg O_WRONLY | O_APPEND | O_CREAT, S_IRWXU);
856 1.1 mrg if (vtv_debug_log_fd == -1)
857 1.1 mrg {
858 1.1 mrg warning_at (UNKNOWN_LOCATION, 0,
859 1.1 mrg "unable to open log file %<vtv_set_ptr_data.log%>: %m");
860 1.1 mrg return;
861 1.1 mrg }
862 1.1 mrg
863 1.1 mrg for (int i = 0; i < array_len; ++i)
864 1.1 mrg {
865 1.1 mrg const char *vptr_name = "unknown";
866 1.1 mrg int vptr_offset = 0;
867 1.1 mrg
868 1.1 mrg if (TREE_CODE (vtbl_ptr_array[i]) == POINTER_PLUS_EXPR)
869 1.1 mrg {
870 1.1 mrg tree arg0 = TREE_OPERAND (vtbl_ptr_array[i], 0);
871 1.1 mrg tree arg1 = TREE_OPERAND (vtbl_ptr_array[i], 1);
872 1.1 mrg
873 1.1 mrg if (TREE_CODE (arg0) == ADDR_EXPR)
874 1.1 mrg arg0 = TREE_OPERAND (arg0, 0);
875 1.1 mrg
876 1.1 mrg if (VAR_P (arg0))
877 1.1 mrg vptr_name = IDENTIFIER_POINTER (DECL_NAME (arg0));
878 1.1 mrg
879 1.1 mrg if (TREE_CODE (arg1) == INTEGER_CST)
880 1.1 mrg vptr_offset = TREE_INT_CST_LOW (arg1);
881 1.1 mrg }
882 1.1 mrg
883 1.1 mrg snprintf (buffer, sizeof (buffer), "%s %s %s + %d\n",
884 1.1 mrg main_input_filename, class_name, vptr_name, vptr_offset);
885 1.1 mrg bytes_written = write (vtv_debug_log_fd, buffer, strlen(buffer));
886 1.1 mrg }
887 1.1 mrg
888 1.1 mrg }
889 1.1 mrg
890 1.1 mrg /* This function goes through our internal class hierarchy & vtable
891 1.1 mrg pointer data structure and outputs calls to __VLTRegisterPair for
892 1.1 mrg every class-vptr pair (for those classes whose vtable would be
893 1.1 mrg output in the current compilation unit). These calls get put into
894 1.1 mrg our constructor initialization function. BODY is the function
895 1.1 mrg body, so far, of our constructor initialization function, to which we
896 1.1 mrg add the calls. */
897 1.1 mrg
898 1.1 mrg static bool
899 1.1 mrg register_all_pairs (tree body)
900 1.1 mrg {
901 1.1 mrg bool registered_at_least_one = false;
902 1.1 mrg vec<tree> *vtbl_ptr_array = NULL;
903 1.1 mrg unsigned j;
904 1.1 mrg
905 1.1 mrg for (j = 0; j < num_vtable_map_nodes; ++j)
906 1.1 mrg {
907 1.1 mrg struct vtbl_map_node *current = vtbl_map_nodes_vec[j];
908 1.1 mrg unsigned i = 0;
909 1.1 mrg tree base_class = current->class_info->class_type;
910 1.1 mrg tree base_ptr_var_decl = current->vtbl_map_decl;
911 1.1 mrg tree arg1;
912 1.1 mrg tree arg2;
913 1.1 mrg tree new_type;
914 1.1 mrg tree str1 = NULL_TREE;
915 1.1 mrg tree str2 = NULL_TREE;
916 1.1 mrg size_t size_hint;
917 1.1 mrg tree size_hint_arg;
918 1.1 mrg
919 1.1 mrg gcc_assert (current->class_info != NULL);
920 1.1 mrg
921 1.1 mrg
922 1.1 mrg if (flag_vtv_debug)
923 1.1 mrg str1 = build_string_from_id (DECL_NAME (base_ptr_var_decl));
924 1.1 mrg
925 1.1 mrg new_type = build_pointer_type (TREE_TYPE (base_ptr_var_decl));
926 1.1 mrg arg1 = build1 (ADDR_EXPR, new_type, base_ptr_var_decl);
927 1.1 mrg
928 1.1 mrg /* We need a fresh vector for each iteration. */
929 1.1 mrg if (vtbl_ptr_array)
930 1.1 mrg vec_free (vtbl_ptr_array);
931 1.1 mrg
932 1.1 mrg vec_alloc (vtbl_ptr_array, 10);
933 1.1 mrg
934 1.1 mrg for (i = 0; i < num_vtable_map_nodes; ++i)
935 1.1 mrg if (bitmap_bit_p (current->class_info->descendants, i))
936 1.1 mrg {
937 1.1 mrg struct vtbl_map_node *vtbl_class_node = vtbl_map_nodes_vec[i];
938 1.1 mrg tree class_type = vtbl_class_node->class_info->class_type;
939 1.1 mrg
940 1.1 mrg if (class_type
941 1.1 mrg && (TREE_CODE (class_type) == RECORD_TYPE))
942 1.1 mrg {
943 1.1 mrg bool already_registered;
944 1.1 mrg
945 1.1 mrg tree binfo = TYPE_BINFO (class_type);
946 1.1 mrg tree vtable_decl;
947 1.1 mrg bool vtable_should_be_output = false;
948 1.1 mrg
949 1.1 mrg vtable_decl = CLASSTYPE_VTABLES (class_type);
950 1.1 mrg
951 1.1 mrg /* Handle main vtable for this class. */
952 1.1 mrg
953 1.1 mrg if (vtable_decl)
954 1.1 mrg {
955 1.1 mrg vtable_should_be_output = TREE_ASM_WRITTEN (vtable_decl);
956 1.1 mrg str2 = build_string_from_id (DECL_NAME (vtable_decl));
957 1.1 mrg }
958 1.1 mrg
959 1.1 mrg if (vtable_decl && vtable_should_be_output)
960 1.1 mrg {
961 1.1 mrg tree vtable_address = build_vtbl_address (binfo);
962 1.1 mrg
963 1.1 mrg already_registered = check_and_record_registered_pairs
964 1.1 mrg (vtable_decl,
965 1.1 mrg vtable_address,
966 1.1 mrg base_class);
967 1.1 mrg
968 1.1 mrg
969 1.1 mrg if (!already_registered)
970 1.1 mrg {
971 1.1 mrg vtbl_ptr_array->safe_push (vtable_address);
972 1.1 mrg
973 1.1 mrg /* Find and handle any 'extra' vtables associated
974 1.1 mrg with this class, via virtual inheritance. */
975 1.1 mrg register_construction_vtables (base_class, class_type,
976 1.1 mrg vtbl_ptr_array);
977 1.1 mrg
978 1.1 mrg /* Find and handle any 'extra' vtables associated
979 1.1 mrg with this class, via multiple inheritance. */
980 1.1 mrg register_other_binfo_vtables (binfo, base_class,
981 1.1 mrg vtbl_ptr_array);
982 1.1 mrg }
983 1.1 mrg }
984 1.1 mrg }
985 1.1 mrg }
986 1.1 mrg current_set_size = vtbl_ptr_array->length();
987 1.1 mrg
988 1.1 mrg /* Sometimes we need to initialize the set symbol even if we are
989 1.1 mrg not adding any vtable pointers to the set in the current
990 1.1 mrg compilation unit. In that case, we need to initialize the
991 1.1 mrg set to our best guess as to what the eventual size of the set
992 1.1 mrg hash table will be (to prevent having to re-size the hash
993 1.1 mrg table later). */
994 1.1 mrg
995 1.1 mrg size_hint = guess_num_vtable_pointers (current->class_info);
996 1.1 mrg
997 1.1 mrg /* If we have added vtable pointers to the set in this
998 1.1 mrg compilation unit, adjust the size hint for the set's hash
999 1.1 mrg table appropriately. */
1000 1.1 mrg if (vtbl_ptr_array->length() > 0)
1001 1.1 mrg {
1002 1.1 mrg unsigned len = vtbl_ptr_array->length();
1003 1.1 mrg while ((size_t) len > size_hint)
1004 1.1 mrg size_hint <<= 1;
1005 1.1 mrg }
1006 1.1 mrg size_hint_arg = build_int_cst (size_type_node, size_hint);
1007 1.1 mrg
1008 1.1 mrg /* Get the key-buffer argument. */
1009 1.1 mrg arg2 = build_key_buffer_arg (base_ptr_var_decl);
1010 1.1 mrg
1011 1.1 mrg if (str2 == NULL_TREE)
1012 1.1 mrg str2 = build_string_literal (strlen ("unknown") + 1,
1013 1.1 mrg "unknown");
1014 1.1 mrg
1015 1.1 mrg if (flag_vtv_debug)
1016 1.1 mrg output_set_info (current->class_info->class_type,
1017 1.1 mrg *vtbl_ptr_array);
1018 1.1 mrg
1019 1.1 mrg if (vtbl_ptr_array->length() > 1)
1020 1.1 mrg {
1021 1.1 mrg insert_call_to_register_set (current->class_name,
1022 1.1 mrg vtbl_ptr_array, body, arg1, arg2,
1023 1.1 mrg size_hint_arg);
1024 1.1 mrg registered_at_least_one = true;
1025 1.1 mrg }
1026 1.1 mrg else
1027 1.1 mrg {
1028 1.1 mrg
1029 1.1 mrg if (vtbl_ptr_array->length() > 0
1030 1.1 mrg || (current->is_used
1031 1.1 mrg || (current->registered->size() > 0)))
1032 1.1 mrg {
1033 1.1 mrg insert_call_to_register_pair (vtbl_ptr_array,
1034 1.1 mrg arg1, arg2, size_hint_arg, str1,
1035 1.1 mrg str2, body);
1036 1.1 mrg registered_at_least_one = true;
1037 1.1 mrg }
1038 1.1 mrg }
1039 1.1 mrg
1040 1.1 mrg if (flag_vtv_counts && current_set_size > 0)
1041 1.1 mrg write_out_current_set_data (base_class, current_set_size);
1042 1.1 mrg
1043 1.1 mrg }
1044 1.1 mrg
1045 1.1 mrg return registered_at_least_one;
1046 1.1 mrg }
1047 1.1 mrg
1048 1.1 mrg /* Given a tree containing a class type (CLASS_TYPE), this function
1049 1.1 mrg finds and returns the class hierarchy node for that class in our
1050 1.1 mrg data structure. */
1051 1.1 mrg
1052 1.1 mrg static struct vtv_graph_node *
1053 1.1 mrg find_graph_node (tree class_type)
1054 1.1 mrg {
1055 1.1 mrg struct vtbl_map_node *vtbl_node;
1056 1.1 mrg
1057 1.1 mrg vtbl_node = vtbl_map_get_node (TYPE_MAIN_VARIANT (class_type));
1058 1.1 mrg if (vtbl_node)
1059 1.1 mrg return vtbl_node->class_info;
1060 1.1 mrg
1061 1.1 mrg return NULL;
1062 1.1 mrg }
1063 1.1 mrg
1064 1.1 mrg /* Add base class/derived class pair to our internal class hierarchy
1065 1.1 mrg data structure. BASE_NODE is our vtv_graph_node that corresponds
1066 1.1 mrg to a base class. DERIVED_NODE is our vtv_graph_node that
1067 1.1 mrg corresponds to a class that is a descendant of the base class
1068 1.1 mrg (possibly the base class itself). */
1069 1.1 mrg
1070 1.1 mrg static void
1071 1.1 mrg add_hierarchy_pair (struct vtv_graph_node *base_node,
1072 1.1 mrg struct vtv_graph_node *derived_node)
1073 1.1 mrg {
1074 1.1 mrg (base_node->children).safe_push (derived_node);
1075 1.1 mrg (derived_node->parents).safe_push (base_node);
1076 1.1 mrg }
1077 1.1 mrg
1078 1.1 mrg /* This functions adds a new base class/derived class relationship to
1079 1.1 mrg our class hierarchy data structure. Both parameters are trees
1080 1.1 mrg representing the class types, i.e. RECORD_TYPE trees.
1081 1.1 mrg DERIVED_CLASS can be the same as BASE_CLASS. */
1082 1.1 mrg
1083 1.1 mrg static void
1084 1.1 mrg update_class_hierarchy_information (tree base_class,
1085 1.1 mrg tree derived_class)
1086 1.1 mrg {
1087 1.1 mrg struct vtv_graph_node *base_node = find_graph_node (base_class);
1088 1.1 mrg struct vtv_graph_node *derived_node = find_graph_node (derived_class);
1089 1.1 mrg
1090 1.1 mrg add_hierarchy_pair (base_node, derived_node);
1091 1.1 mrg }
1092 1.1 mrg
1093 1.1 mrg
1094 1.1 mrg static void
1095 1.1 mrg write_out_vtv_count_data (void)
1096 1.1 mrg {
1097 1.1 mrg static int vtv_count_log_fd = -1;
1098 1.1 mrg char buffer[1024];
1099 1.1 mrg int unused_vtbl_map_vars = 0;
1100 1.1 mrg int bytes_written __attribute__ ((unused));
1101 1.1 mrg char *file_name = get_log_file_name ("vtv_count_data.log");
1102 1.1 mrg
1103 1.1 mrg if (vtv_count_log_fd == -1)
1104 1.1 mrg vtv_count_log_fd = open (file_name,
1105 1.1 mrg O_WRONLY | O_APPEND | O_CREAT, S_IRWXU);
1106 1.1 mrg if (vtv_count_log_fd == -1)
1107 1.1 mrg {
1108 1.1 mrg warning_at (UNKNOWN_LOCATION, 0,
1109 1.1 mrg "unable to open log file %<vtv_count_data.log%>: %m");
1110 1.1 mrg return;
1111 1.1 mrg }
1112 1.1 mrg
1113 1.1 mrg for (unsigned i = 0; i < num_vtable_map_nodes; ++i)
1114 1.1 mrg {
1115 1.1 mrg struct vtbl_map_node *current = vtbl_map_nodes_vec[i];
1116 1.1 mrg if (!current->is_used
1117 1.1 mrg && current->registered->size() == 0)
1118 1.1 mrg unused_vtbl_map_vars++;
1119 1.1 mrg }
1120 1.1 mrg
1121 1.1 mrg snprintf (buffer, sizeof (buffer), "%s %d %d %d %d %d\n",
1122 1.1 mrg main_input_filename, total_num_virtual_calls,
1123 1.1 mrg total_num_verified_vcalls, num_calls_to_regset,
1124 1.1 mrg num_calls_to_regpair, unused_vtbl_map_vars);
1125 1.1 mrg
1126 1.1 mrg bytes_written = write (vtv_count_log_fd, buffer, strlen (buffer));
1127 1.1 mrg }
1128 1.1 mrg
1129 1.1 mrg /* This function calls register_all_pairs, which actually generates
1130 1.1 mrg all the calls to __VLTRegisterPair (in the verification constructor
1131 1.1 mrg init function). It also generates the calls to
1132 1.1 mrg __VLTChangePermission, if the verification constructor init
1133 1.1 mrg function is going into the preinit array. INIT_ROUTINE_BODY is
1134 1.1 mrg the body of our constructior initialization function, to which we
1135 1.1 mrg add our function calls.*/
1136 1.1 mrg
1137 1.1 mrg bool
1138 1.1 mrg vtv_register_class_hierarchy_information (tree init_routine_body)
1139 1.1 mrg {
1140 1.1 mrg bool registered_something = false;
1141 1.1 mrg
1142 1.1 mrg init_functions ();
1143 1.1 mrg
1144 1.1 mrg if (num_vtable_map_nodes == 0)
1145 1.1 mrg return false;
1146 1.1 mrg
1147 1.1 mrg /* Add class hierarchy pairs to the vtable map data structure. */
1148 1.1 mrg registered_something = register_all_pairs (init_routine_body);
1149 1.1 mrg
1150 1.1 mrg if (flag_vtv_counts)
1151 1.1 mrg write_out_vtv_count_data ();
1152 1.1 mrg
1153 1.1 mrg return registered_something;
1154 1.1 mrg }
1155 1.1 mrg
1156 1.1 mrg
1157 1.1 mrg /* Generate the special constructor function that calls
1158 1.1 mrg __VLTChangePermission and __VLTRegisterPairs, and give it a very
1159 1.1 mrg high initialization priority. */
1160 1.1 mrg
1161 1.1 mrg void
1162 1.1 mrg vtv_generate_init_routine (void)
1163 1.1 mrg {
1164 1.1 mrg tree init_routine_body;
1165 1.1 mrg bool vtable_classes_found = false;
1166 1.1 mrg
1167 1.1 mrg push_lang_context (lang_name_c);
1168 1.1 mrg
1169 1.1 mrg /* The priority for this init function (constructor) is carefully
1170 1.1 mrg chosen so that it will happen after the calls to unprotect the
1171 1.1 mrg memory used for vtable verification and before the memory is
1172 1.1 mrg protected again. */
1173 1.1 mrg init_routine_body = vtv_start_verification_constructor_init_function ();
1174 1.1 mrg
1175 1.1 mrg vtable_classes_found =
1176 1.1 mrg vtv_register_class_hierarchy_information (init_routine_body);
1177 1.1 mrg
1178 1.1 mrg if (vtable_classes_found)
1179 1.1 mrg {
1180 1.1 mrg tree vtv_fndecl =
1181 1.1 mrg vtv_finish_verification_constructor_init_function (init_routine_body);
1182 1.1 mrg TREE_STATIC (vtv_fndecl) = 1;
1183 1.1 mrg TREE_USED (vtv_fndecl) = 1;
1184 1.1 mrg DECL_PRESERVE_P (vtv_fndecl) = 1;
1185 1.1 mrg /* We are running too late to generate any meaningful debug information
1186 1.1 mrg for this routine. */
1187 1.1 mrg DECL_IGNORED_P (vtv_fndecl) = 1;
1188 1.1 mrg if (flag_vtable_verify == VTV_PREINIT_PRIORITY && !TARGET_PECOFF)
1189 1.1 mrg DECL_STATIC_CONSTRUCTOR (vtv_fndecl) = 0;
1190 1.1 mrg
1191 1.1 mrg gimplify_function_tree (vtv_fndecl);
1192 1.1 mrg cgraph_node::add_new_function (vtv_fndecl, false);
1193 1.1 mrg
1194 1.1 mrg if (flag_vtable_verify == VTV_PREINIT_PRIORITY && !TARGET_PECOFF)
1195 1.1 mrg assemble_vtv_preinit_initializer (vtv_fndecl);
1196 1.1 mrg
1197 1.1 mrg }
1198 1.1 mrg pop_lang_context ();
1199 1.1 mrg }
1200 1.1 mrg
1201 1.1 mrg /* This funtion takes a tree containing a class type (BASE_TYPE), and
1202 1.1 mrg it either finds the existing vtbl_map_node for that class in our
1203 1.1 mrg data structure, or it creates a new node and adds it to the data
1204 1.1 mrg structure if there is not one for the class already. As part of
1205 1.1 mrg this process it also creates the global vtable map variable for the
1206 1.1 mrg class. */
1207 1.1 mrg
1208 1.1 mrg struct vtbl_map_node *
1209 1.1 mrg vtable_find_or_create_map_decl (tree base_type)
1210 1.1 mrg {
1211 1.1 mrg char *var_name = NULL;
1212 1.1 mrg struct vtbl_map_node *vtable_map_node = NULL;
1213 1.1 mrg
1214 1.1 mrg /* Verify the type has an associated vtable. */
1215 1.1 mrg if (!TYPE_BINFO (base_type) || !BINFO_VTABLE (TYPE_BINFO (base_type)))
1216 1.1 mrg return NULL;
1217 1.1 mrg
1218 1.1 mrg /* Create map lookup symbol for base class */
1219 1.1 mrg var_name = get_mangled_vtable_map_var_name (base_type);
1220 1.1 mrg
1221 1.1 mrg /* We've already created the variable; just look it. */
1222 1.1 mrg vtable_map_node = vtbl_map_get_node (TYPE_MAIN_VARIANT (base_type));
1223 1.1 mrg
1224 1.1 mrg if (!vtable_map_node || (vtable_map_node->vtbl_map_decl == NULL_TREE))
1225 1.1 mrg {
1226 1.1 mrg /* If we haven't already created the *__vtable_map global
1227 1.1 mrg variable for this class, do so now, and add it to the
1228 1.1 mrg varpool, to make sure it gets saved and written out. */
1229 1.1 mrg
1230 1.1 mrg tree var_decl = NULL;
1231 1.1 mrg tree var_type = build_pointer_type (void_type_node);
1232 1.1 mrg tree initial_value = integer_zero_node;
1233 1.1 mrg
1234 1.1 mrg var_decl = build_decl (UNKNOWN_LOCATION, VAR_DECL,
1235 1.1 mrg get_identifier (var_name), var_type);
1236 1.1 mrg
1237 1.1 mrg DECL_EXTERNAL (var_decl) = 0;
1238 1.1 mrg TREE_STATIC (var_decl) = 1;
1239 1.1 mrg DECL_VISIBILITY (var_decl) = VISIBILITY_HIDDEN;
1240 1.1 mrg SET_DECL_ASSEMBLER_NAME (var_decl, get_identifier (var_name));
1241 1.1 mrg DECL_ARTIFICIAL (var_decl) = 1;
1242 1.1 mrg /* We cannot mark this variable as read-only because we want to be
1243 1.1 mrg able to write to it at runtime. */
1244 1.1 mrg TREE_READONLY (var_decl) = 0;
1245 1.1 mrg DECL_IGNORED_P (var_decl) = 1;
1246 1.1 mrg DECL_PRESERVE_P (var_decl) = 1;
1247 1.1 mrg
1248 1.1 mrg /* Put these mmap variables in thr .vtable_map_vars section, so
1249 1.1 mrg we can find and protect them. */
1250 1.1 mrg
1251 1.1 mrg set_decl_section_name (var_decl, ".vtable_map_vars");
1252 1.1 mrg symtab_node::get (var_decl)->implicit_section = true;
1253 1.1 mrg DECL_INITIAL (var_decl) = initial_value;
1254 1.1 mrg
1255 1.1 mrg comdat_linkage (var_decl);
1256 1.1 mrg
1257 1.1 mrg varpool_node::finalize_decl (var_decl);
1258 1.1 mrg if (!vtable_map_node)
1259 1.1 mrg vtable_map_node =
1260 1.1 mrg find_or_create_vtbl_map_node (TYPE_MAIN_VARIANT (base_type));
1261 1.1 mrg if (vtable_map_node->vtbl_map_decl == NULL_TREE)
1262 1.1 mrg vtable_map_node->vtbl_map_decl = var_decl;
1263 1.1 mrg }
1264 1.1 mrg
1265 1.1 mrg gcc_assert (vtable_map_node);
1266 1.1 mrg return vtable_map_node;
1267 1.1 mrg }
1268 1.1 mrg
1269 1.1 mrg /* This function is used to build up our class hierarchy data for a
1270 1.1 mrg particular class. TYPE is the record_type tree node for the
1271 1.1 mrg class. */
1272 1.1 mrg
1273 1.1 mrg static void
1274 1.1 mrg vtv_insert_single_class_info (tree type)
1275 1.1 mrg {
1276 1.1 mrg if (flag_vtable_verify)
1277 1.1 mrg {
1278 1.1 mrg tree binfo = TYPE_BINFO (type);
1279 1.1 mrg tree base_binfo;
1280 1.1 mrg struct vtbl_map_node *own_map;
1281 1.1 mrg int i;
1282 1.1 mrg
1283 1.1 mrg /* First make sure to create the map for this record type. */
1284 1.1 mrg own_map = vtable_find_or_create_map_decl (type);
1285 1.1 mrg if (own_map == NULL)
1286 1.1 mrg return;
1287 1.1 mrg
1288 1.1 mrg /* Go through the list of all base classes for the current
1289 1.1 mrg (derived) type, make sure the *__vtable_map global variable
1290 1.1 mrg for the base class exists, and add the base class/derived
1291 1.1 mrg class pair to the class hierarchy information we are
1292 1.1 mrg accumulating (for vtable pointer verification). */
1293 1.1 mrg for (i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
1294 1.1 mrg {
1295 1.1 mrg tree tree_val = BINFO_TYPE (base_binfo);
1296 1.1 mrg struct vtbl_map_node *vtable_map_node = NULL;
1297 1.1 mrg
1298 1.1 mrg vtable_map_node = vtable_find_or_create_map_decl (tree_val);
1299 1.1 mrg
1300 1.1 mrg if (vtable_map_node != NULL)
1301 1.1 mrg update_class_hierarchy_information (tree_val, type);
1302 1.1 mrg }
1303 1.1 mrg }
1304 1.1 mrg }
1305 1.1 mrg
1306 1.1 mrg /* This function adds classes we are interested in to a list of
1307 1.1 mrg classes. RECORD is the record_type node for the class we are
1308 1.1 mrg adding to the list. */
1309 1.1 mrg
1310 1.1 mrg void
1311 1.1 mrg vtv_save_class_info (tree record)
1312 1.1 mrg {
1313 1.1 mrg if (!flag_vtable_verify || TREE_CODE (record) == UNION_TYPE)
1314 1.1 mrg return;
1315 1.1 mrg
1316 1.1 mrg if (!vlt_saved_class_info)
1317 1.1 mrg vec_alloc (vlt_saved_class_info, 10);
1318 1.1 mrg
1319 1.1 mrg gcc_assert (TREE_CODE (record) == RECORD_TYPE);
1320 1.1 mrg
1321 1.1 mrg vec_safe_push (vlt_saved_class_info, record);
1322 1.1 mrg }
1323 1.1 mrg
1324 1.1 mrg
1325 1.1 mrg /* This function goes through the list of classes we saved and calls
1326 1.1 mrg vtv_insert_single_class_info on each one, to build up our class
1327 1.1 mrg hierarchy data structure. */
1328 1.1 mrg
1329 1.1 mrg void
1330 1.1 mrg vtv_recover_class_info (void)
1331 1.1 mrg {
1332 1.1 mrg tree current_class;
1333 1.1 mrg unsigned i;
1334 1.1 mrg
1335 1.1 mrg if (vlt_saved_class_info)
1336 1.1 mrg {
1337 1.1 mrg for (i = 0; i < vlt_saved_class_info->length(); ++i)
1338 1.1 mrg {
1339 1.1 mrg current_class = (*vlt_saved_class_info)[i];
1340 1.1 mrg gcc_assert (TREE_CODE (current_class) == RECORD_TYPE);
1341 1.1 mrg vtv_insert_single_class_info (current_class);
1342 1.1 mrg }
1343 1.1 mrg }
1344 1.1 mrg }
1345 1.1 mrg
1346 1.1 mrg #include "gt-cp-vtable-class-hierarchy.h"
1347