• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 %{
2 /* Parser for linker scripts.
3    Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006 Red Hat, Inc.
4    This file is part of Red Hat elfutils.
5    Written by Ulrich Drepper <drepper@redhat.com>, 2001.
6 
7    Red Hat elfutils is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by the
9    Free Software Foundation; version 2 of the License.
10 
11    Red Hat elfutils is distributed in the hope that it will be useful, but
12    WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License along
17    with Red Hat elfutils; if not, write to the Free Software Foundation,
18    Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
19 
20    Red Hat elfutils is an included package of the Open Invention Network.
21    An included package of the Open Invention Network is a package for which
22    Open Invention Network licensees cross-license their patents.  No patent
23    license is granted, either expressly or impliedly, by designation as an
24    included package.  Should you wish to participate in the Open Invention
25    Network licensing program, please visit www.openinventionnetwork.com
26    <http://www.openinventionnetwork.com>.  */
27 
28 #ifdef HAVE_CONFIG_H
29 # include <config.h>
30 #endif
31 
32 #include <assert.h>
33 #include <error.h>
34 #include <libintl.h>
35 #include <stdbool.h>
36 #include <stdint.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 
41 #include <system.h>
42 #include <ld.h>
43 
44 /* The error handler.  */
45 static void yyerror (const char *s);
46 
47 /* Some helper functions we need to construct the data structures
48    describing information from the file.  */
49 static struct expression *new_expr (int tag);
50 static struct input_section_name *new_input_section_name (const char *name,
51 							  bool sort_flag);
52 static struct input_rule *new_input_rule (int tag);
53 static struct output_rule *new_output_rule (int tag);
54 static struct assignment *new_assignment (const char *variable,
55 					  struct expression *expression,
56 					  bool provide_flag);
57 static void new_segment (int mode, struct output_rule *output_rule);
58 static struct filename_list *new_filename_listelem (const char *string);
59 static void add_inputfiles (struct filename_list *fnames);
60 static struct id_list *new_id_listelem (const char *str);
61  static struct filename_list *mark_as_needed (struct filename_list *listp);
62 static struct version *new_version (struct id_list *local,
63 				    struct id_list *global);
64 static struct version *merge_versions (struct version *one,
65 				       struct version *two);
66 static void add_versions (struct version *versions);
67 
68 extern int yylex (void);
69 %}
70 
71 %union {
72   uintmax_t num;
73   enum expression_tag op;
74   char *str;
75   struct expression *expr;
76   struct input_section_name *sectionname;
77   struct filemask_section_name *filemask_section_name;
78   struct input_rule *input_rule;
79   struct output_rule *output_rule;
80   struct assignment *assignment;
81   struct filename_list *filename_list;
82   struct version *version;
83   struct id_list *id_list;
84 }
85 
86 %token kADD_OP
87 %token kALIGN
88 %token kAS_NEEDED
89 %token kENTRY
90 %token kEXCLUDE_FILE
91 %token <str> kFILENAME
92 %token kGLOBAL
93 %token kGROUP
94 %token <str> kID
95 %token kINPUT
96 %token kINTERP
97 %token kKEEP
98 %token kLOCAL
99 %token <num> kMODE
100 %token kMUL_OP
101 %token <num> kNUM
102 %token kOUTPUT_FORMAT
103 %token kPAGESIZE
104 %token kPROVIDE
105 %token kSEARCH_DIR
106 %token kSEGMENT
107 %token kSIZEOF_HEADERS
108 %token kSORT
109 %token kVERSION
110 %token kVERSION_SCRIPT
111 
112 %left '|'
113 %left '&'
114 %left ADD_OP
115 %left MUL_OP '*'
116 
117 %type <op> kADD_OP
118 %type <op> kMUL_OP
119 %type <str> filename_id
120 %type <str> filename_id_star
121 %type <str> exclude_opt
122 %type <expr> expr
123 %type <sectionname> sort_opt_name
124 %type <filemask_section_name> sectionname
125 %type <input_rule> inputsection
126 %type <input_rule> inputsections
127 %type <output_rule> outputsection
128 %type <output_rule> outputsections
129 %type <assignment> assignment
130 %type <filename_list> filename_id_list
131 %type <filename_list> filename_id_listelem
132 %type <version> versionlist
133 %type <version> version
134 %type <version> version_stmt_list
135 %type <version> version_stmt
136 %type <id_list> filename_id_star_list
137 
138 %expect 16
139 
140 %%
141 
142 script_or_version:
143 		  file
144 		| kVERSION_SCRIPT versionlist
145 		    { add_versions ($2); }
146 		;
147 
148 file:		  file content
149 		| content
150 		;
151 
152 content:	  kENTRY '(' kID ')' ';'
153 		    {
154 		      if (likely (ld_state.entry == NULL))
155 			ld_state.entry = $3;
156 		    }
157 		| kSEARCH_DIR '(' filename_id ')' ';'
158 		    {
159 		      ld_new_searchdir ($3);
160 		    }
161 		| kPAGESIZE '(' kNUM ')' ';'
162 		    {
163 		      if (likely (ld_state.pagesize == 0))
164 			ld_state.pagesize = $3;
165 		    }
166 		| kINTERP '(' filename_id ')' ';'
167 		    {
168 		      if (likely (ld_state.interp == NULL)
169 			  && ld_state.file_type != dso_file_type)
170 			ld_state.interp = $3;
171 		    }
172 		| kSEGMENT kMODE '{' outputsections '}'
173 		    {
174 		      new_segment ($2, $4);
175 		    }
176 		| kSEGMENT error '{' outputsections '}'
177 		    {
178 		      fputs_unlocked (gettext ("mode for segment invalid\n"),
179 				      stderr);
180 		      new_segment (0, $4);
181 		    }
182 		| kGROUP '(' filename_id_list ')'
183 		    {
184 		      /* First little optimization.  If there is only one
185 			 file in the group don't do anything.  */
186 		      if ($3 != $3->next)
187 			{
188 			  $3->next->group_start = 1;
189 			  $3->group_end = 1;
190 			}
191 		      add_inputfiles ($3);
192 		    }
193 		| kINPUT '(' filename_id_list ')'
194 		    { add_inputfiles ($3); }
195 		| kAS_NEEDED '(' filename_id_list ')'
196 		    { add_inputfiles (mark_as_needed ($3)); }
197 		| kVERSION '{' versionlist '}'
198 		    { add_versions ($3); }
199 		| kOUTPUT_FORMAT '(' filename_id ')'
200 		    { /* XXX TODO */ }
201 		;
202 
203 outputsections:	  outputsections outputsection
204 		    {
205 		      $2->next = $1->next;
206 		      $$ = $1->next = $2;
207 		    }
208 		| outputsection
209 		    { $$ = $1; }
210 		;
211 
212 outputsection:	  assignment ';'
213 		    {
214 		      $$ = new_output_rule (output_assignment);
215 		      $$->val.assignment = $1;
216 		    }
217 		| kID '{' inputsections '}'
218 		    {
219 		      $$ = new_output_rule (output_section);
220 		      $$->val.section.name = $1;
221 		      $$->val.section.input = $3->next;
222 		      if (ld_state.strip == strip_debug
223 			  && ebl_debugscn_p (ld_state.ebl, $1))
224 			$$->val.section.ignored = true;
225 		      else
226 			$$->val.section.ignored = false;
227 		      $3->next = NULL;
228 		    }
229 		| kID ';'
230 		    {
231 		      /* This is a short cut for "ID { *(ID) }".  */
232 		      $$ = new_output_rule (output_section);
233 		      $$->val.section.name = $1;
234 		      $$->val.section.input = new_input_rule (input_section);
235 		      $$->val.section.input->next = NULL;
236 		      $$->val.section.input->val.section =
237 			(struct filemask_section_name *)
238 			  obstack_alloc (&ld_state.smem,
239 					 sizeof (struct filemask_section_name));
240 		      $$->val.section.input->val.section->filemask = NULL;
241 		      $$->val.section.input->val.section->excludemask = NULL;
242 		      $$->val.section.input->val.section->section_name =
243 			new_input_section_name ($1, false);
244 		      $$->val.section.input->val.section->keep_flag = false;
245 		      if (ld_state.strip == strip_debug
246 			  && ebl_debugscn_p (ld_state.ebl, $1))
247 			$$->val.section.ignored = true;
248 		      else
249 			$$->val.section.ignored = false;
250 		    }
251 		;
252 
253 assignment:	  kID '=' expr
254 		    { $$ = new_assignment ($1, $3, false); }
255 		| kPROVIDE '(' kID '=' expr ')'
256 		    { $$ = new_assignment ($3, $5, true); }
257 		;
258 
259 inputsections:	  inputsections inputsection
260 		    {
261 		      $2->next = $1->next;
262 		      $$ = $1->next = $2;
263 		    }
264 		| inputsection
265 		    { $$ = $1; }
266 		;
267 
268 inputsection:	  sectionname
269 		    {
270 		      $$ = new_input_rule (input_section);
271 		      $$->val.section = $1;
272 		    }
273 		| kKEEP '(' sectionname ')'
274 		    {
275 		      $3->keep_flag = true;
276 
277 		      $$ = new_input_rule (input_section);
278 		      $$->val.section = $3;
279 		    }
280 		| assignment ';'
281 		    {
282 		      $$ = new_input_rule (input_assignment);
283 		      $$->val.assignment = $1;
284 		    }
285 		;
286 
287 sectionname:	  filename_id_star '(' exclude_opt sort_opt_name ')'
288 		    {
289 		      $$ = (struct filemask_section_name *)
290 			obstack_alloc (&ld_state.smem, sizeof (*$$));
291 		      $$->filemask = $1;
292 		      $$->excludemask = $3;
293 		      $$->section_name = $4;
294 		      $$->keep_flag = false;
295 		    }
296 		;
297 
298 sort_opt_name:	  kID
299 		    { $$ = new_input_section_name ($1, false); }
300 		| kSORT '(' kID ')'
301 		    { $$ = new_input_section_name ($3, true); }
302 		;
303 
304 exclude_opt:	  kEXCLUDE_FILE '(' filename_id ')'
305 		    { $$ = $3; }
306 		|
307 		    { $$ = NULL; }
308 		;
309 
310 expr:		  kALIGN '(' expr ')'
311 		    {
312 		      $$ = new_expr (exp_align);
313 		      $$->val.child = $3;
314 		    }
315 		| '(' expr ')'
316 		    { $$ = $2; }
317 		| expr '*' expr
318 		    {
319 		      $$ = new_expr (exp_mult);
320 		      $$->val.binary.left = $1;
321 		      $$->val.binary.right = $3;
322 		    }
323 		| expr kMUL_OP expr
324 		    {
325 		      $$ = new_expr ($2);
326 		      $$->val.binary.left = $1;
327 		      $$->val.binary.right = $3;
328 		    }
329 		| expr kADD_OP expr
330 		    {
331 		      $$ = new_expr ($2);
332 		      $$->val.binary.left = $1;
333 		      $$->val.binary.right = $3;
334 		    }
335 		| expr '&' expr
336 		    {
337 		      $$ = new_expr (exp_and);
338 		      $$->val.binary.left = $1;
339 		      $$->val.binary.right = $3;
340 		    }
341 		| expr '|' expr
342 		    {
343 		      $$ = new_expr (exp_or);
344 		      $$->val.binary.left = $1;
345 		      $$->val.binary.right = $3;
346 		    }
347 		| kNUM
348 		    {
349 		      $$ = new_expr (exp_num);
350 		      $$->val.num = $1;
351 		    }
352 		| kID
353 		    {
354 		      $$ = new_expr (exp_id);
355 		      $$->val.str = $1;
356 		    }
357 		| kSIZEOF_HEADERS
358 		    { $$ = new_expr (exp_sizeof_headers); }
359 		| kPAGESIZE
360 		    { $$ = new_expr (exp_pagesize); }
361 		;
362 
363 filename_id_list: filename_id_list comma_opt filename_id_listelem
364 		    {
365 		      $3->next = $1->next;
366 		      $$ = $1->next = $3;
367 		    }
368 		| filename_id_listelem
369 		    { $$ = $1; }
370 		;
371 
372 comma_opt:	  ','
373 		|
374 		;
375 
376 filename_id_listelem: kGROUP '(' filename_id_list ')'
377 		    {
378 		      /* First little optimization.  If there is only one
379 			 file in the group don't do anything.  */
380 		      if ($3 != $3->next)
381 			{
382 			  $3->next->group_start = 1;
383 			  $3->group_end = 1;
384 			}
385 		      $$ = $3;
386 		    }
387 		| kAS_NEEDED '(' filename_id_list ')'
388 		    { $$ = mark_as_needed ($3); }
389 		| filename_id
390 		    { $$ = new_filename_listelem ($1); }
391 		;
392 
393 
394 versionlist:	  versionlist version
395 		    {
396 		      $2->next = $1->next;
397 		      $$ = $1->next = $2;
398 		    }
399 		| version
400 		    { $$ = $1; }
401 		;
402 
403 version:	  '{' version_stmt_list '}' ';'
404 		    {
405 		      $2->versionname = "";
406 		      $2->parentname = NULL;
407 		      $$ = $2;
408 		    }
409 		| filename_id '{' version_stmt_list '}' ';'
410 		    {
411 		      $3->versionname = $1;
412 		      $3->parentname = NULL;
413 		      $$ = $3;
414 		    }
415 		| filename_id '{' version_stmt_list '}' filename_id ';'
416 		    {
417 		      $3->versionname = $1;
418 		      $3->parentname = $5;
419 		      $$ = $3;
420 		    }
421 		;
422 
423 version_stmt_list:
424 		  version_stmt_list version_stmt
425 		    { $$ = merge_versions ($1, $2); }
426 		| version_stmt
427 		    { $$ = $1; }
428 		;
429 
430 version_stmt:	  kGLOBAL filename_id_star_list
431 		    { $$ = new_version (NULL, $2); }
432 		| kLOCAL filename_id_star_list
433 		    { $$ = new_version ($2, NULL); }
434 		;
435 
436 filename_id_star_list:
437 		  filename_id_star_list filename_id_star ';'
438 		    {
439 		      struct id_list *newp = new_id_listelem ($2);
440 		      newp->next = $1->next;
441 		      $$ = $1->next = newp;
442 		    }
443 		| filename_id_star ';'
444 		    { $$ = new_id_listelem ($1); }
445 		;
446 
447 filename_id:	  kFILENAME
448 		    { $$ = $1; }
449 		| kID
450 		    { $$ = $1; }
451 		;
452 
453 filename_id_star: filename_id
454 		    { $$ = $1; }
455 		| '*'
456 		    { $$ = NULL; }
457 		;
458 
459 %%
460 
461 static void
462 yyerror (const char *s)
463 {
464   error (0, 0, (ld_scan_version_script
465 		? gettext ("while reading version script '%s': %s at line %d")
466 		: gettext ("while reading linker script '%s': %s at line %d")),
467 	 ldin_fname, gettext (s), ldlineno);
468 }
469 
470 
471 static struct expression *
new_expr(int tag)472 new_expr (int tag)
473 {
474   struct expression *newp = (struct expression *)
475     obstack_alloc (&ld_state.smem, sizeof (*newp));
476 
477   newp->tag = tag;
478   return newp;
479 }
480 
481 
482 static struct input_section_name *
new_input_section_name(const char * name,bool sort_flag)483 new_input_section_name (const char *name, bool sort_flag)
484 {
485   struct input_section_name *newp = (struct input_section_name *)
486     obstack_alloc (&ld_state.smem, sizeof (*newp));
487 
488   newp->name = name;
489   newp->sort_flag = sort_flag;
490   return newp;
491 }
492 
493 
494 static struct input_rule *
new_input_rule(int tag)495 new_input_rule (int tag)
496 {
497   struct input_rule *newp = (struct input_rule *)
498     obstack_alloc (&ld_state.smem, sizeof (*newp));
499 
500   newp->tag = tag;
501   newp->next = newp;
502   return newp;
503 }
504 
505 
506 static struct output_rule *
new_output_rule(int tag)507 new_output_rule (int tag)
508 {
509   struct output_rule *newp = (struct output_rule *)
510     memset (obstack_alloc (&ld_state.smem, sizeof (*newp)),
511 	    '\0', sizeof (*newp));
512 
513   newp->tag = tag;
514   newp->next = newp;
515   return newp;
516 }
517 
518 
519 static struct assignment *
new_assignment(const char * variable,struct expression * expression,bool provide_flag)520 new_assignment (const char *variable, struct expression *expression,
521 		bool provide_flag)
522 {
523   struct assignment *newp = (struct assignment *)
524     obstack_alloc (&ld_state.smem, sizeof (*newp));
525 
526   newp->variable = variable;
527   newp->expression = expression;
528   newp->sym = NULL;
529   newp->provide_flag = provide_flag;
530 
531   /* Insert the symbol into a hash table.  We will later have to matc*/
532   return newp;
533 }
534 
535 
536 static void
new_segment(int mode,struct output_rule * output_rule)537 new_segment (int mode, struct output_rule *output_rule)
538 {
539   struct output_segment *newp;
540 
541   newp
542     = (struct output_segment *) obstack_alloc (&ld_state.smem, sizeof (*newp));
543   newp->mode = mode;
544   newp->next = newp;
545 
546   newp->output_rules = output_rule->next;
547   output_rule->next = NULL;
548 
549   /* Enqueue the output segment description.  */
550   if (ld_state.output_segments == NULL)
551     ld_state.output_segments = newp;
552   else
553     {
554       newp->next = ld_state.output_segments->next;
555       ld_state.output_segments = ld_state.output_segments->next = newp;
556     }
557 
558   /* If the output file should be stripped of all symbol set the flag
559      in the structures of all output sections.  */
560   if (mode == 0 && ld_state.strip == strip_all)
561     {
562       struct output_rule *runp;
563 
564       for (runp = newp->output_rules; runp != NULL; runp = runp->next)
565 	if (runp->tag == output_section)
566 	  runp->val.section.ignored = true;
567     }
568 }
569 
570 
571 static struct filename_list *
new_filename_listelem(const char * string)572 new_filename_listelem (const char *string)
573 {
574   struct filename_list *newp;
575 
576   /* We use calloc and not the obstack since this object can be freed soon.  */
577   newp = (struct filename_list *) xcalloc (1, sizeof (*newp));
578   newp->name = string;
579   newp->next = newp;
580   return newp;
581 }
582 
583 
584 static struct filename_list *
mark_as_needed(struct filename_list * listp)585 mark_as_needed (struct filename_list *listp)
586 {
587   struct filename_list *runp = listp;
588   do
589     {
590       runp->as_needed = true;
591       runp = runp->next;
592     }
593   while (runp != listp);
594 
595   return listp;
596 }
597 
598 
599 static void
add_inputfiles(struct filename_list * fnames)600 add_inputfiles (struct filename_list *fnames)
601 {
602   assert (fnames != NULL);
603 
604   if (ld_state.srcfiles == NULL)
605     ld_state.srcfiles = fnames;
606   else
607     {
608       struct filename_list *first = ld_state.srcfiles->next;
609 
610       ld_state.srcfiles->next = fnames->next;
611       fnames->next = first;
612       ld_state.srcfiles->next = fnames;
613     }
614 }
615 
616 
617 static _Bool
special_char_p(const char * str)618 special_char_p (const char *str)
619 {
620   while (*str != '\0')
621     {
622       if (__builtin_expect (*str == '*', 0)
623 	  || __builtin_expect (*str == '?', 0)
624 	  || __builtin_expect (*str == '[', 0))
625 	return true;
626 
627       ++str;
628     }
629 
630   return false;
631 }
632 
633 
634 static struct id_list *
new_id_listelem(const char * str)635 new_id_listelem (const char *str)
636 {
637   struct id_list *newp;
638 
639   newp = (struct id_list *) obstack_alloc (&ld_state.smem, sizeof (*newp));
640   if (str == NULL)
641     newp->u.id_type = id_all;
642   else if (__builtin_expect (special_char_p (str), false))
643     newp->u.id_type = id_wild;
644   else
645     newp->u.id_type = id_str;
646   newp->id = str;
647   newp->next = newp;
648 
649   return newp;
650 }
651 
652 
653 static struct version *
new_version(struct id_list * local,struct id_list * global)654 new_version (struct id_list *local, struct id_list *global)
655 {
656   struct version *newp;
657 
658   newp = (struct version *) obstack_alloc (&ld_state.smem, sizeof (*newp));
659   newp->next = newp;
660   newp->local_names = local;
661   newp->global_names = global;
662   newp->versionname = NULL;
663   newp->parentname = NULL;
664 
665   return newp;
666 }
667 
668 
669 static struct version *
merge_versions(struct version * one,struct version * two)670 merge_versions (struct version *one, struct version *two)
671 {
672   assert (two->local_names == NULL || two->global_names == NULL);
673 
674   if (two->local_names != NULL)
675     {
676       if (one->local_names == NULL)
677 	one->local_names = two->local_names;
678       else
679 	{
680 	  two->local_names->next = one->local_names->next;
681 	  one->local_names = one->local_names->next = two->local_names;
682 	}
683     }
684   else
685     {
686       if (one->global_names == NULL)
687 	one->global_names = two->global_names;
688       else
689 	{
690 	  two->global_names->next = one->global_names->next;
691 	  one->global_names = one->global_names->next = two->global_names;
692 	}
693     }
694 
695   return one;
696 }
697 
698 
699 static void
add_id_list(const char * versionname,struct id_list * runp,_Bool local)700 add_id_list (const char *versionname, struct id_list *runp, _Bool local)
701 {
702   struct id_list *lastp = runp;
703 
704   if (runp == NULL)
705     /* Nothing to do.  */
706     return;
707 
708   /* Convert into a simple single-linked list.  */
709   runp = runp->next;
710   assert (runp != NULL);
711   lastp->next = NULL;
712 
713   do
714     if (runp->u.id_type == id_str)
715       {
716 	struct id_list *curp;
717 	struct id_list *defp;
718 	unsigned long int hval = elf_hash (runp->id);
719 
720 	curp = runp;
721 	runp = runp->next;
722 
723 	defp = ld_version_str_tab_find (&ld_state.version_str_tab, hval, curp);
724 	if (defp != NULL)
725 	  {
726 	    /* There is already a version definition for this symbol.  */
727 	    while (strcmp (defp->u.s.versionname, versionname) != 0)
728 	      {
729 		if (defp->next == NULL)
730 		  {
731 		    /* No version like this so far.  */
732 		    defp->next = curp;
733 		    curp->u.s.local = local;
734 		    curp->u.s.versionname = versionname;
735 		    curp->next = NULL;
736 		    defp = NULL;
737 		    break;
738 		  }
739 
740 		defp = defp->next;
741 	      }
742 
743 	    if (defp != NULL && defp->u.s.local != local)
744 	      error (EXIT_FAILURE, 0, versionname[0] == '\0'
745 		     ? gettext ("\
746 symbol '%s' in declared both local and global for unnamed version")
747 		     : gettext ("\
748 symbol '%s' in declared both local and global for version '%s'"),
749 		     runp->id, versionname);
750 	  }
751 	else
752 	  {
753 	    /* This is the first version definition for this symbol.  */
754 	    ld_version_str_tab_insert (&ld_state.version_str_tab, hval, curp);
755 
756 	    curp->u.s.local = local;
757 	    curp->u.s.versionname = versionname;
758 	    curp->next = NULL;
759 	  }
760       }
761     else if (runp->u.id_type == id_all)
762       {
763 	if (local)
764 	  {
765 	    if (ld_state.default_bind_global)
766 	      error (EXIT_FAILURE, 0,
767 		     gettext ("default visibility set as local and global"));
768 	    ld_state.default_bind_local = true;
769 	  }
770 	else
771 	  {
772 	    if (ld_state.default_bind_local)
773 	      error (EXIT_FAILURE, 0,
774 		     gettext ("default visibility set as local and global"));
775 	    ld_state.default_bind_global = true;
776 	  }
777 
778 	runp = runp->next;
779       }
780     else
781       {
782 	assert (runp->u.id_type == id_wild);
783 	/* XXX TBI */
784 	abort ();
785       }
786   while (runp != NULL);
787 }
788 
789 
790 static void
add_versions(struct version * versions)791 add_versions (struct version *versions)
792 {
793   struct version *lastp = versions;
794 
795   if (versions == NULL)
796     return;
797 
798   /* Convert into a simple single-linked list.  */
799   versions = versions->next;
800   assert (versions != NULL);
801   lastp->next = NULL;
802 
803   do
804     {
805       struct version *oldp;
806 
807       add_id_list (versions->versionname, versions->local_names, true);
808       add_id_list (versions->versionname, versions->global_names, false);
809 
810       oldp = versions;
811       versions = versions->next;
812     }
813   while (versions != NULL);
814 }
815