+/* This handles the .symver pseudo-op, which is used to specify a
+ symbol version. The syntax is ``.symver NAME,SYMVERNAME''.
+ SYMVERNAME may contain ELF_VER_CHR ('@') characters. This
+ pseudo-op causes the assembler to emit a symbol named SYMVERNAME
+ with the same value as the symbol NAME. */
+
+static void
+obj_elf_symver (ignore)
+ int ignore ATTRIBUTE_UNUSED;
+{
+ char *name;
+ char c;
+ symbolS *sym;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+
+ sym = symbol_find_or_make (name);
+
+ *input_line_pointer = c;
+
+ if (symbol_get_obj (sym)->versioned_name != NULL)
+ {
+ as_bad (_("multiple .symver directives for symbol `%s'"),
+ S_GET_NAME (sym));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("expected comma after name in .symver"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ ++input_line_pointer;
+ name = input_line_pointer;
+ while (1)
+ {
+ c = get_symbol_end ();
+ if (c != ELF_VER_CHR)
+ break;
+ *input_line_pointer++ = c;
+ }
+
+ symbol_get_obj (sym)->versioned_name = xstrdup (name);
+
+ *input_line_pointer = c;
+
+ if (strchr (symbol_get_obj (sym)->versioned_name, ELF_VER_CHR) == NULL)
+ {
+ as_bad (_("missing version name in `%s' for symbol `%s'"),
+ symbol_get_obj (sym)->versioned_name, S_GET_NAME (sym));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* This handles the .vtable_inherit pseudo-op, which is used to indicate
+ to the linker the hierarchy in which a particular table resides. The
+ syntax is ".vtable_inherit CHILDNAME, PARENTNAME". */
+
+static void
+obj_elf_vtable_inherit (ignore)
+ int ignore ATTRIBUTE_UNUSED;
+{
+ char *cname, *pname;
+ symbolS *csym, *psym;
+ char c, bad = 0;
+
+ if (*input_line_pointer == '#')
+ ++input_line_pointer;
+
+ cname = input_line_pointer;
+ c = get_symbol_end ();
+ csym = symbol_find (cname);
+
+ /* GCFIXME: should check that we don't have two .vtable_inherits for
+ the same child symbol. Also, we can currently only do this if the
+ child symbol is already exists and is placed in a fragment. */
+
+ if (csym == NULL || symbol_get_frag (csym) == NULL)
+ {
+ as_bad ("expected `%s' to have already been set for .vtable_inherit",
+ cname);
+ bad = 1;
+ }
+
+ *input_line_pointer = c;
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ {
+ as_bad ("expected comma after name in .vtable_inherit");
+ ignore_rest_of_line ();
+ return;
+ }
+
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer == '#')
+ ++input_line_pointer;
+
+ if (input_line_pointer[0] == '0'
+ && (input_line_pointer[1] == '\0'
+ || isspace ((unsigned char) input_line_pointer[1])))
+ {
+ psym = section_symbol (absolute_section);
+ ++input_line_pointer;
+ }
+ else
+ {
+ pname = input_line_pointer;
+ c = get_symbol_end ();
+ psym = symbol_find_or_make (pname);
+ *input_line_pointer = c;
+ }
+
+ demand_empty_rest_of_line ();
+
+ if (bad)
+ return;
+
+ assert (symbol_get_value_expression (csym)->X_op == O_constant);
+ fix_new (symbol_get_frag (csym),
+ symbol_get_value_expression (csym)->X_add_number, 0, psym, 0, 0,
+ BFD_RELOC_VTABLE_INHERIT);
+}
+
+/* This handles the .vtable_entry pseudo-op, which is used to indicate
+ to the linker that a vtable slot was used. The syntax is
+ ".vtable_entry tablename, offset". */
+
+static void
+obj_elf_vtable_entry (ignore)
+ int ignore ATTRIBUTE_UNUSED;
+{
+ char *name;
+ symbolS *sym;
+ offsetT offset;
+ char c;
+
+ if (*input_line_pointer == '#')
+ ++input_line_pointer;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ sym = symbol_find_or_make (name);
+ *input_line_pointer = c;
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ {
+ as_bad ("expected comma after name in .vtable_entry");
+ ignore_rest_of_line ();
+ return;
+ }
+
+ ++input_line_pointer;
+ if (*input_line_pointer == '#')
+ ++input_line_pointer;
+
+ offset = get_absolute_expression ();
+
+ fix_new (frag_now, frag_now_fix (), 0, sym, offset, 0,
+ BFD_RELOC_VTABLE_ENTRY);
+
+ demand_empty_rest_of_line ();
+}
+
+void
+elf_obj_read_begin_hook ()