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