1 /* Look up a symbol in the loaded objects.
2 Copyright (C) 1995,96,97,98,99,2000,2001 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, write to the Free
17 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18 02111-1307 USA. */
19
20 #include <alloca.h>
21 #include <libintl.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <ldsodefs.h>
26 #include "dl-hash.h"
27 #include <machine/dl-machine.h>
28 #include <bits/libc-lock.h>
29
30 #include <assert.h>
31
32 #define VERSTAG(tag) (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX (tag))
33
34 /* We need this string more than once. */
35 static const char undefined_msg[] = "undefined symbol: ";
36
37
38 struct sym_val
39 {
40 const ElfW(Sym) *s;
41 struct link_map *m;
42 };
43
44
45 #define make_string(string, rest...) \
46 ({ \
47 const char *all[] = { string, ## rest }; \
48 size_t len, cnt; \
49 char *result, *cp; \
50 \
51 len = 1; \
52 for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt) \
53 len += strlen (all[cnt]); \
54 \
55 cp = result = alloca (len); \
56 for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt) \
57 { \
58 cp = strcpy (cp, all[cnt]); \
59 cp += strlen(all[cnt]); \
60 } \
61 \
62 result; \
63 })
64
65 /* Statistics function. */
66 unsigned long int _dl_num_relocations;
67
68
69 /* We have two different situations when looking up a simple: with or
70 without versioning. gcc is not able to optimize a single function
71 definition serving for both purposes so we define two functions. */
72 #define VERSIONED 0
73 #include "do-lookup.h"
74
75 #define VERSIONED 1
76 #include "do-lookup.h"
77
78
79 /* Add extra dependency on MAP to UNDEF_MAP. */
80 static int
81 internal_function
add_dependency(struct link_map * undef_map,struct link_map * map)82 add_dependency (struct link_map *undef_map, struct link_map *map)
83 {
84 struct link_map **list;
85 struct link_map *runp;
86 unsigned int act;
87 unsigned int i;
88 int result = 0;
89
90 /* Avoid self-references. */
91 if (undef_map == map)
92 return 0;
93
94 /* Make sure nobody can unload the object while we are at it. */
95 #ifdef HAVE_DD_LOCK
96 __lock_acquire(_dl_load_lock);
97 #endif
98
99
100 /* Determine whether UNDEF_MAP already has a reference to MAP. First
101 look in the normal dependencies. */
102 if (undef_map->l_searchlist.r_list != NULL)
103 {
104 list = undef_map->l_initfini;
105
106 for (i = 0; list[i] != NULL; ++i)
107 if (list[i] == map)
108 goto out;
109 }
110
111 /* No normal dependency. See whether we already had to add it
112 to the special list of dynamic dependencies. */
113 list = undef_map->l_reldeps;
114 act = undef_map->l_reldepsact;
115
116 for (i = 0; i < act; ++i)
117 if (list[i] == map)
118 goto out;
119
120 /* The object is not yet in the dependency list. Before we add
121 it make sure just one more time the object we are about to
122 reference is still available. There is a brief period in
123 which the object could have been removed since we found the
124 definition. */
125 runp = _dl_loaded;
126 while (runp != NULL && runp != map)
127 runp = runp->l_next;
128
129 if (runp != NULL)
130 {
131 /* The object is still available. Add the reference now. */
132 if (__builtin_expect (act >= undef_map->l_reldepsmax, 0))
133 {
134 /* Allocate more memory for the dependency list. Since this
135 can never happen during the startup phase we can use
136 `realloc'. */
137 void *newp;
138
139 undef_map->l_reldepsmax += 5;
140 newp = realloc (undef_map->l_reldeps,
141 undef_map->l_reldepsmax
142 * sizeof (struct link_map *));
143
144 if (__builtin_expect (newp != NULL, 1))
145 undef_map->l_reldeps = (struct link_map **) newp;
146 else
147 /* Correct the addition. */
148 undef_map->l_reldepsmax -= 5;
149 }
150
151 /* If we didn't manage to allocate memory for the list this is
152 no fatal mistake. We simply increment the use counter of the
153 referenced object and don't record the dependencies. This
154 means this increment can never be reverted and the object
155 will never be unloaded. This is semantically the correct
156 behaviour. */
157 if (__builtin_expect (act < undef_map->l_reldepsmax, 1))
158 undef_map->l_reldeps[undef_map->l_reldepsact++] = map;
159
160 if (map->l_searchlist.r_list != NULL)
161 /* And increment the counter in the referenced object. */
162 ++map->l_opencount;
163 else
164 /* We have to bump the counts for all dependencies since so far
165 this object was only a normal or transitive dependency.
166 Now it might be closed with _dl_close() directly. */
167 for (list = map->l_initfini; *list != NULL; ++list)
168 ++(*list)->l_opencount;
169
170 /* Display information if we are debugging. */
171 if (__builtin_expect (_dl_debug_mask & DL_DEBUG_FILES, 0))
172 _dl_debug_printf ("\
173 \nfile=%s; needed by %s (relocation dependency)\n\n",
174 map->l_name[0] ? map->l_name : _dl_argv[0],
175 undef_map->l_name[0]
176 ? undef_map->l_name : _dl_argv[0]);
177 }
178 else
179 /* Whoa, that was bad luck. We have to search again. */
180 result = -1;
181
182 out:
183 /* Release the lock. */
184 #ifdef HAVE_DD_LOCK
185 __lock_release(_dl_load_lock);
186 #endif
187
188
189 return result;
190 }
191
192 static int
193 internal_function
194 _dl_do_lookup (const char *undef_name, unsigned long int hash,
195 const ElfW(Sym) *ref, struct sym_val *result,
196 struct r_scope_elem *scope, size_t i,
197 struct link_map *skip, int type_class);
198 static int
199 internal_function
200 _dl_do_lookup_versioned (const char *undef_name, unsigned long int hash,
201 const ElfW(Sym) *ref, struct sym_val *result,
202 struct r_scope_elem *scope, size_t i,
203 const struct r_found_version *const version,
204 struct link_map *skip, int type_class);
205
206
207 /* Search loaded objects' symbol tables for a definition of the symbol
208 UNDEF_NAME. */
209
210 lookup_t
211 internal_function
_dl_lookup_symbol(const char * undef_name,struct link_map * undef_map,const ElfW (Sym)** ref,struct r_scope_elem * symbol_scope[],int type_class,int explicit)212 _dl_lookup_symbol (const char *undef_name, struct link_map *undef_map,
213 const ElfW(Sym) **ref, struct r_scope_elem *symbol_scope[],
214 int type_class, int explicit)
215 {
216 unsigned long int hash = _dl_elf_hash (undef_name);
217 struct sym_val current_value = { NULL, NULL };
218 struct r_scope_elem **scope;
219 int protected;
220
221 ++_dl_num_relocations;
222
223 /* Search the relevant loaded objects for a definition. */
224 for (scope = symbol_scope; *scope; ++scope)
225 if (do_lookup (undef_name, hash, *ref, ¤t_value, *scope, 0, NULL,
226 type_class))
227 {
228 /* We have to check whether this would bind UNDEF_MAP to an object
229 in the global scope which was dynamically loaded. In this case
230 we have to prevent the latter from being unloaded unless the
231 UNDEF_MAP object is also unloaded. */
232 if (__builtin_expect (current_value.m->l_type == lt_loaded, 0)
233 /* Don't do this for explicit lookups as opposed to implicit
234 runtime lookups. */
235 && ! explicit
236 /* Add UNDEF_MAP to the dependencies. */
237 && add_dependency (undef_map, current_value.m) < 0)
238 /* Something went wrong. Perhaps the object we tried to reference
239 was just removed. Try finding another definition. */
240 return _dl_lookup_symbol (undef_name, undef_map, ref, symbol_scope,
241 type_class, 0);
242
243 break;
244 }
245
246 if (__builtin_expect (current_value.s == NULL, 0))
247 {
248 const char *reference_name = undef_map ? undef_map->l_name : NULL;
249
250 if (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK)
251 /* We could find no value for a strong reference. */
252 /* XXX We cannot translate the messages. */
253 _dl_signal_cerror (0, (reference_name && reference_name[0]
254 ? reference_name
255 : (_dl_argv[0] ?: "<main program>")),
256 N_("relocation error"),
257 make_string (undefined_msg, undef_name));
258 *ref = NULL;
259 return 0;
260 }
261
262 protected = *ref && ELFW(ST_VISIBILITY) ((*ref)->st_other) == STV_PROTECTED;
263
264 if (__builtin_expect (_dl_debug_mask & DL_DEBUG_BINDINGS, 0))
265 {
266 const char *reference_name = undef_map ? undef_map->l_name : NULL;
267
268 _dl_debug_printf ("binding file %s to %s: %s symbol `%s'\n",
269 (reference_name && reference_name[0]
270 ? reference_name : (_dl_argv[0] ?: "<main program>")),
271 current_value.m->l_name[0]
272 ? current_value.m->l_name : _dl_argv[0],
273 protected ? "protected" : "normal", undef_name);
274 }
275
276 if (__builtin_expect (protected == 0, 1))
277 {
278 *ref = current_value.s;
279 return LOOKUP_VALUE (current_value.m);
280 }
281 else
282 {
283 /* It is very tricky. We need to figure out what value to
284 return for the protected symbol */
285 struct sym_val protected_value = { NULL, NULL };
286
287 for (scope = symbol_scope; *scope; ++scope)
288 if (_dl_do_lookup (undef_name, hash, *ref, &protected_value, *scope,
289 0, NULL, ELF_RTYPE_CLASS_PLT))
290 break;
291
292 if (protected_value.s == NULL || protected_value.m == undef_map)
293 {
294 *ref = current_value.s;
295 return LOOKUP_VALUE (current_value.m);
296 }
297
298 return LOOKUP_VALUE (undef_map);
299 }
300 }
301
302
303 /* This function is nearly the same as `_dl_lookup_symbol' but it
304 skips in the first list all objects until SKIP_MAP is found. I.e.,
305 it only considers objects which were loaded after the described
306 object. If there are more search lists the object described by
307 SKIP_MAP is only skipped. */
308 lookup_t
309 internal_function
_dl_lookup_symbol_skip(const char * undef_name,struct link_map * undef_map,const ElfW (Sym)** ref,struct r_scope_elem * symbol_scope[],struct link_map * skip_map)310 _dl_lookup_symbol_skip (const char *undef_name,
311 struct link_map *undef_map, const ElfW(Sym) **ref,
312 struct r_scope_elem *symbol_scope[],
313 struct link_map *skip_map)
314 {
315 const char *reference_name = undef_map ? undef_map->l_name : NULL;
316 const unsigned long int hash = _dl_elf_hash (undef_name);
317 struct sym_val current_value = { NULL, NULL };
318 struct r_scope_elem **scope;
319 size_t i;
320 int protected;
321
322 ++_dl_num_relocations;
323
324 /* Search the relevant loaded objects for a definition. */
325 scope = symbol_scope;
326 for (i = 0; (*scope)->r_list[i] != skip_map; ++i)
327 assert (i < (*scope)->r_nlist);
328
329 if (! _dl_do_lookup (undef_name, hash, *ref, ¤t_value, *scope, i,
330 skip_map, 0))
331 while (*++scope)
332 if (_dl_do_lookup (undef_name, hash, *ref, ¤t_value, *scope, 0,
333 skip_map, 0))
334 break;
335
336 if (__builtin_expect (current_value.s == NULL, 0))
337 {
338 *ref = NULL;
339 return 0;
340 }
341
342 protected = *ref && ELFW(ST_VISIBILITY) ((*ref)->st_other) == STV_PROTECTED;
343
344 if (__builtin_expect (_dl_debug_mask & DL_DEBUG_BINDINGS, 0))
345 _dl_debug_printf ("binding file %s to %s: %s symbol `%s'\n",
346 (reference_name && reference_name[0]
347 ? reference_name : (_dl_argv[0] ?: "<main program>")),
348 current_value.m->l_name[0]
349 ? current_value.m->l_name : _dl_argv[0],
350 protected ? "protected" : "normal", undef_name);
351
352 if (__builtin_expect (protected == 0, 1))
353 {
354 *ref = current_value.s;
355 return LOOKUP_VALUE (current_value.m);
356 }
357 else
358 {
359 /* It is very tricky. We need to figure out what value to
360 return for the protected symbol. */
361 struct sym_val protected_value = { NULL, NULL };
362
363 if (i >= (*scope)->r_nlist
364 || !_dl_do_lookup (undef_name, hash, *ref, &protected_value, *scope,
365 i, skip_map, ELF_RTYPE_CLASS_PLT))
366 while (*++scope)
367 if (_dl_do_lookup (undef_name, hash, *ref, &protected_value, *scope,
368 0, skip_map, ELF_RTYPE_CLASS_PLT))
369 break;
370
371 if (protected_value.s == NULL || protected_value.m == undef_map)
372 {
373 *ref = current_value.s;
374 return LOOKUP_VALUE (current_value.m);
375 }
376
377 return LOOKUP_VALUE (undef_map);
378 }
379 }
380
381
382 /* This function works like _dl_lookup_symbol but it takes an
383 additional arguement with the version number of the requested
384 symbol.
385
386 XXX We'll see whether we need this separate function. */
387 lookup_t
388 internal_function
_dl_lookup_versioned_symbol(const char * undef_name,struct link_map * undef_map,const ElfW (Sym)** ref,struct r_scope_elem * symbol_scope[],const struct r_found_version * version,int type_class,int explicit)389 _dl_lookup_versioned_symbol (const char *undef_name,
390 struct link_map *undef_map, const ElfW(Sym) **ref,
391 struct r_scope_elem *symbol_scope[],
392 const struct r_found_version *version,
393 int type_class, int explicit)
394 {
395 unsigned long int hash = _dl_elf_hash (undef_name);
396 struct sym_val current_value = { NULL, NULL };
397 struct r_scope_elem **scope;
398 int protected;
399
400 ++_dl_num_relocations;
401
402 /* Search the relevant loaded objects for a definition. */
403 for (scope = symbol_scope; *scope; ++scope)
404 {
405 int res = do_lookup_versioned (undef_name, hash, *ref, ¤t_value,
406 *scope, 0, version, NULL, type_class);
407 if (res > 0)
408 {
409 /* We have to check whether this would bind UNDEF_MAP to an object
410 in the global scope which was dynamically loaded. In this case
411 we have to prevent the latter from being unloaded unless the
412 UNDEF_MAP object is also unloaded. */
413 if (__builtin_expect (current_value.m->l_type == lt_loaded, 0)
414 /* Don't do this for explicit lookups as opposed to implicit
415 runtime lookups. */
416 && ! explicit
417 /* Add UNDEF_MAP to the dependencies. */
418 && add_dependency (undef_map, current_value.m) < 0)
419 /* Something went wrong. Perhaps the object we tried to reference
420 was just removed. Try finding another definition. */
421 return _dl_lookup_versioned_symbol (undef_name, undef_map, ref,
422 symbol_scope, version,
423 type_class, 0);
424
425 break;
426 }
427
428 if (__builtin_expect (res, 0) < 0)
429 {
430 /* Oh, oh. The file named in the relocation entry does not
431 contain the needed symbol. */
432 const char *reference_name = undef_map ? undef_map->l_name : NULL;
433
434 /* XXX We cannot translate the message. */
435 _dl_signal_cerror (0, (reference_name && reference_name[0]
436 ? reference_name
437 : (_dl_argv[0] ?: "<main program>")),
438 N_("relocation error"),
439 make_string ("symbol ", undef_name, ", version ",
440 version->name,
441 " not defined in file ",
442 version->filename,
443 " with link time reference",
444 res == -2
445 ? " (no version symbols)" : ""));
446 *ref = NULL;
447 return 0;
448 }
449 }
450
451 if (__builtin_expect (current_value.s == NULL, 0))
452 {
453 if (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK)
454 {
455 /* We could find no value for a strong reference. */
456 const char *reference_name = undef_map ? undef_map->l_name : NULL;
457
458 /* XXX We cannot translate the message. */
459 _dl_signal_cerror (0, (reference_name && reference_name[0]
460 ? reference_name
461 : (_dl_argv[0] ?: "<main program>")), NULL,
462 make_string (undefined_msg, undef_name,
463 ", version ",
464 version->name ?: NULL));
465 }
466 *ref = NULL;
467 return 0;
468 }
469
470 protected = *ref && ELFW(ST_VISIBILITY) ((*ref)->st_other) == STV_PROTECTED;
471
472 if (__builtin_expect (_dl_debug_mask & DL_DEBUG_BINDINGS, 0))
473 {
474 const char *reference_name = undef_map ? undef_map->l_name : NULL;
475
476 _dl_debug_printf ("binding file %s to %s: %s symbol `%s' [%s]\n",
477 (reference_name && reference_name[0]
478 ? reference_name : (_dl_argv[0] ?: "<main program>")),
479 current_value.m->l_name[0]
480 ? current_value.m->l_name : _dl_argv[0],
481 protected ? "protected" : "normal",
482 undef_name, version->name);
483 }
484
485 if (__builtin_expect (protected == 0, 1))
486 {
487 *ref = current_value.s;
488 return LOOKUP_VALUE (current_value.m);
489 }
490 else
491 {
492 /* It is very tricky. We need to figure out what value to
493 return for the protected symbol */
494 struct sym_val protected_value = { NULL, NULL };
495
496 for (scope = symbol_scope; *scope; ++scope)
497 if (_dl_do_lookup_versioned (undef_name, hash, *ref, &protected_value,
498 *scope, 0, version, NULL,
499 ELF_RTYPE_CLASS_PLT))
500 break;
501
502 if (protected_value.s == NULL || protected_value.m == undef_map)
503 {
504 *ref = current_value.s;
505 return LOOKUP_VALUE (current_value.m);
506 }
507
508 return LOOKUP_VALUE (undef_map);
509 }
510 }
511
512
513 /* Similar to _dl_lookup_symbol_skip but takes an additional argument
514 with the version we are looking for. */
515 lookup_t
516 internal_function
_dl_lookup_versioned_symbol_skip(const char * undef_name,struct link_map * undef_map,const ElfW (Sym)** ref,struct r_scope_elem * symbol_scope[],const struct r_found_version * version,struct link_map * skip_map)517 _dl_lookup_versioned_symbol_skip (const char *undef_name,
518 struct link_map *undef_map,
519 const ElfW(Sym) **ref,
520 struct r_scope_elem *symbol_scope[],
521 const struct r_found_version *version,
522 struct link_map *skip_map)
523 {
524 const char *reference_name = undef_map ? undef_map->l_name : NULL;
525 const unsigned long int hash = _dl_elf_hash (undef_name);
526 struct sym_val current_value = { NULL, NULL };
527 struct r_scope_elem **scope;
528 size_t i;
529 int protected;
530
531 ++_dl_num_relocations;
532
533 /* Search the relevant loaded objects for a definition. */
534 scope = symbol_scope;
535 for (i = 0; (*scope)->r_list[i] != skip_map; ++i)
536 assert (i < (*scope)->r_nlist);
537
538 if (! _dl_do_lookup_versioned (undef_name, hash, *ref, ¤t_value,
539 *scope, i, version, skip_map, 0))
540 while (*++scope)
541 if (_dl_do_lookup_versioned (undef_name, hash, *ref, ¤t_value,
542 *scope, 0, version, skip_map, 0))
543 break;
544
545 if (__builtin_expect (current_value.s == NULL, 0))
546 {
547 if (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK)
548 {
549 /* We could find no value for a strong reference. */
550 const size_t len = strlen (undef_name);
551 char buf[sizeof undefined_msg + len];
552 char *tmp;
553 tmp = memcpy (buf, undefined_msg, sizeof undefined_msg - 1);
554 tmp += (sizeof undefined_msg - 1);
555
556 memcpy (tmp, undef_name, len + 1);
557
558 /* XXX We cannot translate the messages. */
559 _dl_signal_cerror (0, (reference_name && reference_name[0]
560 ? reference_name
561 : (_dl_argv[0] ?: "<main program>")),
562 NULL, buf);
563 }
564 *ref = NULL;
565 return 0;
566 }
567
568 protected = *ref && ELFW(ST_VISIBILITY) ((*ref)->st_other) == STV_PROTECTED;
569
570 if (__builtin_expect (_dl_debug_mask & DL_DEBUG_BINDINGS, 0))
571 _dl_debug_printf ("binding file %s to %s: %s symbol `%s' [%s]\n",
572 (reference_name && reference_name[0]
573 ? reference_name : (_dl_argv[0] ?: "<main program>")),
574 current_value.m->l_name[0]
575 ? current_value.m->l_name : _dl_argv[0],
576 protected ? "protected" : "normal",
577 undef_name, version->name);
578
579 if (__builtin_expect (protected == 0, 1))
580 {
581 *ref = current_value.s;
582 return LOOKUP_VALUE (current_value.m);
583 }
584 else
585 {
586 /* It is very tricky. We need to figure out what value to
587 return for the protected symbol */
588 struct sym_val protected_value = { NULL, NULL };
589
590 if (i >= (*scope)->r_nlist
591 || !_dl_do_lookup_versioned (undef_name, hash, *ref,
592 &protected_value, *scope, i, version,
593 skip_map, ELF_RTYPE_CLASS_PLT))
594 while (*++scope)
595 if (_dl_do_lookup_versioned (undef_name, hash, *ref,
596 &protected_value, *scope, 0, version,
597 skip_map, ELF_RTYPE_CLASS_PLT))
598 break;
599
600 if (protected_value.s == NULL || protected_value.m == undef_map)
601 {
602 *ref = current_value.s;
603 return LOOKUP_VALUE (current_value.m);
604 }
605
606 return LOOKUP_VALUE (undef_map);
607 }
608 }
609
610
611 /* Cache the location of MAP's hash table. */
612
613 void
614 internal_function
_dl_setup_hash(struct link_map * map)615 _dl_setup_hash (struct link_map *map)
616 {
617 Elf_Symndx *hash;
618 Elf_Symndx nchain;
619
620 if (!map->l_info[DT_HASH])
621 return;
622 hash = (void *)(map->l_addr + map->l_info[DT_HASH]->d_un.d_ptr);
623
624 map->l_nbuckets = *hash++;
625 nchain = *hash++;
626 map->l_buckets = hash;
627 hash += map->l_nbuckets;
628 map->l_chain = hash;
629 }
630
631 /* These are here so that we only inline do_lookup{,_versioned} in the common
632 case, not everywhere. */
633 static int
634 internal_function
_dl_do_lookup(const char * undef_name,unsigned long int hash,const ElfW (Sym)* ref,struct sym_val * result,struct r_scope_elem * scope,size_t i,struct link_map * skip,int type_class)635 _dl_do_lookup (const char *undef_name, unsigned long int hash,
636 const ElfW(Sym) *ref, struct sym_val *result,
637 struct r_scope_elem *scope, size_t i,
638 struct link_map *skip, int type_class)
639 {
640 return do_lookup (undef_name, hash, ref, result, scope, i, skip,
641 type_class);
642 }
643
644 static int
645 internal_function
_dl_do_lookup_versioned(const char * undef_name,unsigned long int hash,const ElfW (Sym)* ref,struct sym_val * result,struct r_scope_elem * scope,size_t i,const struct r_found_version * const version,struct link_map * skip,int type_class)646 _dl_do_lookup_versioned (const char *undef_name, unsigned long int hash,
647 const ElfW(Sym) *ref, struct sym_val *result,
648 struct r_scope_elem *scope, size_t i,
649 const struct r_found_version *const version,
650 struct link_map *skip, int type_class)
651 {
652 return do_lookup_versioned (undef_name, hash, ref, result, scope, i,
653 version, skip, type_class);
654 }
655