/* ELF object file format
- Copyright (C) 1992-2016 Free Software Foundation, Inc.
+ Copyright (C) 1992-2023 Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
#include "safe-ctype.h"
#include "subsegs.h"
#include "obstack.h"
-#include "struc-symbol.h"
#include "dwarf2dbg.h"
#ifndef ECOFF_DEBUGGING
#ifdef NEED_ECOFF_DEBUG
#include "ecoff.h"
+#include "bfd/ecoff-bfd.h"
#endif
#ifdef TC_ALPHA
#include "elf/ppc.h"
#endif
-#ifdef TC_I370
-#include "elf/i370.h"
-#endif
-
#ifdef TC_I386
#include "elf/x86-64.h"
#endif
#include "elf/nios2.h"
#endif
+#ifdef TC_PRU
+#include "elf/pru.h"
+#endif
+
static void obj_elf_line (int);
static void obj_elf_size (int);
static void obj_elf_type (int);
static void obj_elf_tls_common (int);
static void obj_elf_lcomm (int);
static void obj_elf_struct (int);
+static void obj_elf_attach_to_group (int);
static const pseudo_typeS elf_pseudo_table[] =
{
+ {"attach_to_group", obj_elf_attach_to_group, 0},
{"comm", obj_elf_common, 0},
{"common", obj_elf_common, 1},
{"ident", obj_elf_ident, 0},
{"subsection", obj_elf_subsection, 0},
/* These are GNU extensions to aid in garbage collecting C++ vtables. */
- {"vtable_inherit", (void (*) (int)) &obj_elf_vtable_inherit, 0},
- {"vtable_entry", (void (*) (int)) &obj_elf_vtable_entry, 0},
+ {"vtable_inherit", obj_elf_vtable_inherit, 0},
+ {"vtable_entry", obj_elf_vtable_entry, 0},
/* A GNU extension for object attributes. */
{"gnu_attribute", obj_elf_gnu_attribute, 0},
- /* These are used for dwarf. */
- {"2byte", cons, 2},
- {"4byte", cons, 4},
- {"8byte", cons, 8},
/* These are used for dwarf2. */
- { "file", (void (*) (int)) dwarf2_directive_file, 0 },
+ { "file", dwarf2_directive_file, 0 },
{ "loc", dwarf2_directive_loc, 0 },
{ "loc_mark_labels", dwarf2_directive_loc_mark_labels, 0 },
{"offset", obj_elf_struct, 0},
{"struct", obj_elf_struct, 0},
{"text", obj_elf_text, 0},
+ {"bss", obj_elf_bss, 0},
{"tls_common", obj_elf_tls_common, 0},
{ "etype", ecoff_directive_type, 0 },
/* ECOFF specific debugging information. */
+ { "aent", ecoff_directive_ent, 1 },
{ "begin", ecoff_directive_begin, 0 },
{ "bend", ecoff_directive_bend, 0 },
{ "end", ecoff_directive_end, 0 },
#undef NO_RELOC
#include "aout/aout64.h"
-/* This is called when the assembler starts. */
-
asection *elf_com_section_ptr;
-void
-elf_begin (void)
-{
- asection *s;
-
- /* Add symbols for the known sections to the symbol table. */
- s = bfd_get_section_by_name (stdoutput, TEXT_SECTION_NAME);
- symbol_table_insert (section_symbol (s));
- s = bfd_get_section_by_name (stdoutput, DATA_SECTION_NAME);
- symbol_table_insert (section_symbol (s));
- s = bfd_get_section_by_name (stdoutput, BSS_SECTION_NAME);
- symbol_table_insert (section_symbol (s));
- elf_com_section_ptr = bfd_com_section_ptr;
-}
-
void
elf_pop_insert (void)
{
}
void
-elf_file_symbol (const char *s, int appfile)
+elf_file_symbol (const char *s)
{
- if (!appfile
- || symbol_rootP == NULL
- || symbol_rootP->bsym == NULL
- || (symbol_rootP->bsym->flags & BSF_FILE) == 0)
- {
- symbolS *sym;
- size_t name_length;
-
- sym = symbol_new (s, absolute_section, 0, NULL);
- symbol_set_frag (sym, &zero_address_frag);
+ asymbol *bsym;
+ symbolS *sym = symbol_new (s, absolute_section, &zero_address_frag, 0);
+ size_t name_length = strlen (s);
- name_length = strlen (s);
- if (name_length > strlen (S_GET_NAME (sym)))
- {
- obstack_grow (¬es, s, name_length + 1);
- S_SET_NAME (sym, (const char *) obstack_finish (¬es));
- }
- else
- strcpy ((char *) S_GET_NAME (sym), s);
+ if (name_length > strlen (S_GET_NAME (sym)))
+ {
+ obstack_grow (¬es, s, name_length + 1);
+ S_SET_NAME (sym, (const char *) obstack_finish (¬es));
+ }
+ else
+ strcpy ((char *) S_GET_NAME (sym), s);
- symbol_get_bfdsym (sym)->flags |= BSF_FILE;
+ symbol_get_bfdsym (sym)->flags |= BSF_FILE;
- if (symbol_rootP != sym
- && (symbol_rootP->bsym == NULL
- || !(symbol_rootP->bsym->flags & BSF_FILE)))
- {
- symbol_remove (sym, &symbol_rootP, &symbol_lastP);
- symbol_insert (sym, symbol_rootP, &symbol_rootP, &symbol_lastP);
- }
+ if (symbol_rootP != sym
+ && ((bsym = symbol_get_bfdsym (symbol_rootP)) == NULL
+ || (bsym->flags & BSF_FILE) == 0))
+ {
+ symbol_remove (sym, &symbol_rootP, &symbol_lastP);
+ symbol_insert (sym, symbol_rootP, &symbol_rootP, &symbol_lastP);
+ }
#ifdef DEBUG
- verify_symbol_chain (symbol_rootP, symbol_lastP);
+ verify_symbol_chain (symbol_rootP, symbol_lastP);
#endif
- }
#ifdef NEED_ECOFF_DEBUG
- ecoff_new_file (s, appfile);
+ ecoff_new_file (s);
#endif
}
if (*input_line_pointer == '.')
input_line_pointer++;
/* Some say data, some say bss. */
- if (strncmp (input_line_pointer, "bss\"", 4) == 0)
+ if (startswith (input_line_pointer, "bss\""))
input_line_pointer += 4;
- else if (strncmp (input_line_pointer, "data\"", 5) == 0)
+ else if (startswith (input_line_pointer, "data\""))
input_line_pointer += 5;
else
{
symbolP = get_sym_from_input_line_and_check ();
bfdsym = symbol_get_bfdsym (symbolP);
- elfsym = elf_symbol_from (bfd_asymbol_bfd (bfdsym), bfdsym);
+ elfsym = elf_symbol_from (bfdsym);
gas_assert (elfsym);
static struct section_stack *section_stack;
-static bfd_boolean
-get_section (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)
+/* ELF section flags for unique sections. */
+#define SEC_ASSEMBLER_SHF_MASK SHF_GNU_RETAIN
+
+/* Return TRUE iff SEC matches the section info INF. */
+
+static bool
+get_section_by_match (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)
{
- const char *gname = (const char *) inf;
+ struct elf_section_match *match = (struct elf_section_match *) inf;
+ const char *gname = match->group_name;
const char *group_name = elf_group_name (sec);
-
- return (group_name == gname
- || (group_name != NULL
- && gname != NULL
- && strcmp (group_name, gname) == 0));
+ const char *linked_to_symbol_name
+ = sec->map_head.linked_to_symbol_name;
+ unsigned int sh_info = elf_section_data (sec)->this_hdr.sh_info;
+ bfd_vma sh_flags = (elf_section_data (sec)->this_hdr.sh_flags
+ & SEC_ASSEMBLER_SHF_MASK);
+
+ return (sh_info == match->sh_info
+ && sh_flags == match->sh_flags
+ && ((bfd_section_flags (sec) & SEC_ASSEMBLER_SECTION_ID)
+ == (match->flags & SEC_ASSEMBLER_SECTION_ID))
+ && sec->section_id == match->section_id
+ && (group_name == gname
+ || (group_name != NULL
+ && gname != NULL
+ && strcmp (group_name, gname) == 0))
+ && (linked_to_symbol_name == match->linked_to_symbol_name
+ || (linked_to_symbol_name != NULL
+ && match->linked_to_symbol_name != NULL
+ && strcmp (linked_to_symbol_name,
+ match->linked_to_symbol_name) == 0)));
}
/* Handle the .section pseudo-op. This code supports two different
unsigned int type,
bfd_vma attr,
int entsize,
- const char *group_name,
+ struct elf_section_match *match_p,
int linkonce,
int push)
{
const struct elf_backend_data *bed;
const struct bfd_elf_special_section *ssect;
+ if (match_p == NULL)
+ {
+ static struct elf_section_match unused_match;
+ match_p = &unused_match;
+ }
+
#ifdef md_flush_pending_output
md_flush_pending_output ();
#endif
if (push)
{
struct section_stack *elt;
- elt = (struct section_stack *) xmalloc (sizeof (struct section_stack));
+ elt = XNEW (struct section_stack);
elt->next = section_stack;
elt->seg = now_seg;
elt->prev_seg = previous_section;
elt->prev_subseg = previous_subsection;
section_stack = elt;
}
- previous_section = now_seg;
- previous_subsection = now_subseg;
- old_sec = bfd_get_section_by_name_if (stdoutput, name, get_section,
- (void *) group_name);
+ obj_elf_section_change_hook ();
+
+ old_sec = bfd_get_section_by_name_if (stdoutput, name, get_section_by_match,
+ (void *) match_p);
if (old_sec)
{
sec = old_sec;
if (ssect != NULL)
{
- bfd_boolean override = FALSE;
+ bool override = false;
if (type == SHT_NULL)
type = ssect->type;
}
}
- if (old_sec == NULL && ((attr & ~(SHF_MASKOS | SHF_MASKPROC))
+ if (old_sec == NULL && ((attr & ~(SHF_LINK_ORDER
+ | SHF_MASKOS
+ | SHF_MASKPROC))
& ~ssect->attr) != 0)
{
+ /* Strip SHF_GNU_RETAIN. */
+ bfd_vma generic_attr = attr;
+ if (elf_tdata (stdoutput)->has_gnu_osabi)
+ generic_attr &= ~SHF_GNU_RETAIN;
+
/* As a GNU extension, we permit a .note section to be
allocatable. If the linker sees an allocatable .note
section, it will create a PT_NOTE segment in the output
file. We also allow "x" for .note.GNU-stack. */
if (ssect->type == SHT_NOTE
- && (attr == SHF_ALLOC || attr == SHF_EXECINSTR))
+ && (generic_attr == SHF_ALLOC
+ || generic_attr == SHF_EXECINSTR))
;
/* Allow different SHF_MERGE and SHF_STRINGS if we have
something like .rodata.str. */
else if (ssect->suffix_length == -2
&& name[ssect->prefix_length] == '.'
- && (attr
+ && (generic_attr
& ~ssect->attr
& ~SHF_MERGE
& ~SHF_STRINGS) == 0)
;
/* .interp, .strtab and .symtab can have SHF_ALLOC. */
- else if (attr == SHF_ALLOC
+ else if (generic_attr == SHF_ALLOC
&& (strcmp (name, ".interp") == 0
|| strcmp (name, ".strtab") == 0
|| strcmp (name, ".symtab") == 0))
- override = TRUE;
+ override = true;
/* .note.GNU-stack can have SHF_EXECINSTR. */
- else if (attr == SHF_EXECINSTR
+ else if (generic_attr == SHF_EXECINSTR
&& strcmp (name, ".note.GNU-stack") == 0)
- override = TRUE;
+ override = true;
#ifdef TC_ALPHA
/* A section on Alpha may have SHF_ALPHA_GPREL. */
- else if ((attr & ~ssect->attr) == SHF_ALPHA_GPREL)
- override = TRUE;
+ else if ((generic_attr & ~ssect->attr) == SHF_ALPHA_GPREL)
+ override = true;
#endif
#ifdef TC_RX
- else if (attr == (SHF_EXECINSTR | SHF_WRITE | SHF_ALLOC)
+ else if (generic_attr == (SHF_EXECINSTR | SHF_WRITE | SHF_ALLOC)
&& (ssect->type == SHT_INIT_ARRAY
|| ssect->type == SHT_FINI_ARRAY
|| ssect->type == SHT_PREINIT_ARRAY))
#endif
else
{
- if (group_name == NULL)
+ if (match_p->group_name == NULL)
as_warn (_("setting incorrect section attributes for %s"),
name);
- override = TRUE;
+ override = true;
}
}
if (linkonce)
flags |= SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD;
+ /* PR 28054: Set the SEC_ELF_OCTETS flag for debugging sections.
+ Based on the code in bfd/elf.c:_bfd_elf_make_section_from_shdr().
+
+ FIXME: We do not set the SEC_DEBUGGING flag because that causes
+ problems for the FT32 and MSP430 targets. Investigate and fix. */
+ if ((flags & SEC_ALLOC) == 0 && name [0] == '.')
+ {
+ if ( startswith (name, ".debug")
+ || startswith (name, ".zdebug")
+ || startswith (name, ".gnu.debuglto_.debug_")
+ || startswith (name, ".gnu.linkonce.wi.")
+ || startswith (name, GNU_BUILD_ATTRS_SECTION_NAME)
+ || startswith (name, ".note.gnu"))
+ flags |= SEC_ELF_OCTETS;
+ }
+
if (old_sec == NULL)
{
symbolS *secsym;
type = bfd_elf_get_default_section_type (flags);
elf_section_type (sec) = type;
elf_section_flags (sec) = attr;
+ elf_section_data (sec)->this_hdr.sh_info = match_p->sh_info;
/* Prevent SEC_HAS_CONTENTS from being inadvertently set. */
if (type == SHT_NOBITS)
seg_info (sec)->bss = 1;
- bfd_set_section_flags (stdoutput, sec, flags);
+ /* Set the section ID and flags. */
+ sec->section_id = match_p->section_id;
+ flags |= match_p->flags;
+
+ /* Set the linked-to symbol name. */
+ sec->map_head.linked_to_symbol_name
+ = match_p->linked_to_symbol_name;
+
+ bfd_set_section_flags (sec, flags);
if (flags & SEC_MERGE)
sec->entsize = entsize;
- elf_group_name (sec) = group_name;
+ elf_group_name (sec) = match_p->group_name;
/* Add a symbol for this section to the symbol table. */
secsym = symbol_find (name);
if (secsym != NULL)
- symbol_set_bfdsym (secsym, sec->symbol);
+ {
+ /* We could be repurposing an undefined symbol here: make sure we
+ reset sy_value to look like other section symbols in order to avoid
+ trying to incorrectly resolve this section symbol later on. */
+ static const expressionS exp = { .X_op = O_constant };
+ symbol_set_value_expression (secsym, &exp);
+ symbol_set_bfdsym (secsym, sec->symbol);
+ }
else
symbol_table_insert (section_symbol (sec));
}
{
if (type != SHT_NULL
&& (unsigned) type != elf_section_type (old_sec))
- as_warn (_("ignoring changed section type for %s"), name);
+ {
+ if (ssect != NULL)
+ /* This is a special section with known type. User
+ assembly might get the section type wrong; Even high
+ profile projects like glibc have done so in the past.
+ So don't error in this case. */
+ as_warn (_("ignoring changed section type for %s"), name);
+ else
+ /* Do error when assembly isn't self-consistent. */
+ as_bad (_("changed section type for %s"), name);
+ }
if (attr != 0)
{
| SEC_EXCLUDE | SEC_SORT_ENTRIES | SEC_MERGE | SEC_STRINGS
| SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD
| SEC_THREAD_LOCAL)))
- as_warn (_("ignoring changed section attributes for %s"), name);
+ {
+ if (ssect != NULL)
+ as_warn (_("ignoring changed section attributes for %s"), name);
+ else
+ as_bad (_("changed section attributes for %s"), name);
+ }
else
/* FIXME: Maybe we should consider removing a previously set
- processor or application specific attribute as suspicious ? */
+ processor or application specific attribute as suspicious? */
elf_section_flags (sec) = attr;
if ((flags & SEC_MERGE) && old_sec->entsize != (unsigned) entsize)
- as_warn (_("ignoring changed section entity size for %s"), name);
+ as_bad (_("changed section entity size for %s"), name);
}
}
}
static bfd_vma
-obj_elf_parse_section_letters (char *str, size_t len, bfd_boolean *is_clone)
+obj_elf_parse_section_letters (char *str, size_t len,
+ bool *is_clone, bfd_vma *gnu_attr)
{
bfd_vma attr = 0;
- *is_clone = FALSE;
+ *is_clone = false;
while (len > 0)
{
{
case 'a':
attr |= SHF_ALLOC;
+ /* Compatibility. */
+ if (len > 1 && str[1] == 'm')
+ {
+ attr |= SHF_MERGE;
+ str++, len--;
+ if (len > 1 && str[1] == 's')
+ {
+ attr |= SHF_STRINGS;
+ str++, len--;
+ }
+ }
break;
case 'e':
attr |= SHF_EXCLUDE;
break;
+ case 'o':
+ attr |= SHF_LINK_ORDER;
+ break;
case 'w':
attr |= SHF_WRITE;
break;
case 'T':
attr |= SHF_TLS;
break;
+ case 'd':
+ *gnu_attr |= SHF_GNU_MBIND;
+ break;
+ case 'R':
+ *gnu_attr |= SHF_GNU_RETAIN;
+ break;
case '?':
- *is_clone = TRUE;
+ *is_clone = true;
break;
- /* Compatibility. */
- case 'm':
- if (*(str - 1) == 'a')
- {
- attr |= SHF_MERGE;
- if (len > 1 && str[1] == 's')
- {
- attr |= SHF_STRINGS;
- str++, len--;
- }
- break;
- }
default:
{
- char *bad_msg = _("unrecognized .section attribute: want a,e,w,x,M,S,G,T or number");
+ const char *bad_msg = _("unrecognized .section attribute:"
+ " want a,e,o,w,x,M,S,G,T or number");
#ifdef md_elf_section_letter
bfd_vma md_attr = md_elf_section_letter (*str, &bad_msg);
if (md_attr != (bfd_vma) -1)
if (ISDIGIT (*str))
{
char * end;
+ struct elf_backend_data *bed;
+ bfd_vma numeric_flags = strtoul (str, &end, 0);
+
+ attr |= numeric_flags;
+
+ bed = (struct elf_backend_data *)
+ get_elf_backend_data (stdoutput);
+
+ if (bed->elf_osabi == ELFOSABI_NONE
+ || bed->elf_osabi == ELFOSABI_STANDALONE
+ || bed->elf_osabi == ELFOSABI_GNU
+ || bed->elf_osabi == ELFOSABI_FREEBSD)
+ {
+ /* Add flags in the SHF_MASKOS range to gnu_attr for
+ OSABIs that support those flags.
+ Also adding the flags for ELFOSABI_{NONE,STANDALONE}
+ allows them to be validated later in obj_elf_section.
+ We can't just always set these bits in gnu_attr for
+ all OSABIs, since Binutils does not recognize all
+ SHF_MASKOS bits for non-GNU OSABIs. It's therefore
+ possible that numeric flags are being used to set bits
+ in the SHF_MASKOS range for those targets, and we
+ don't want assembly to fail in those situations. */
+ *gnu_attr |= (numeric_flags & SHF_MASKOS);
+ }
- attr |= strtoul (str, & end, 0);
/* Update str and len, allowing for the fact that
we will execute str++ and len-- below. */
end --;
}
static int
-obj_elf_section_type (char *str, size_t len, bfd_boolean warn)
+obj_elf_section_type (char *str, size_t len, bool warn)
{
- if (len == 8 && strncmp (str, "progbits", 8) == 0)
+ if (len == 8 && startswith (str, "progbits"))
return SHT_PROGBITS;
- if (len == 6 && strncmp (str, "nobits", 6) == 0)
+ if (len == 6 && startswith (str, "nobits"))
return SHT_NOBITS;
- if (len == 4 && strncmp (str, "note", 4) == 0)
+ if (len == 4 && startswith (str, "note"))
return SHT_NOTE;
- if (len == 10 && strncmp (str, "init_array", 10) == 0)
+ if (len == 10 && startswith (str, "init_array"))
return SHT_INIT_ARRAY;
- if (len == 10 && strncmp (str, "fini_array", 10) == 0)
+ if (len == 10 && startswith (str, "fini_array"))
return SHT_FINI_ARRAY;
- if (len == 13 && strncmp (str, "preinit_array", 13) == 0)
+ if (len == 13 && startswith (str, "preinit_array"))
return SHT_PREINIT_ARRAY;
#ifdef md_elf_section_type
return 0;
}
+#ifdef TC_SPARC
static bfd_vma
obj_elf_section_word (char *str, size_t len, int *type)
{
int ret;
- if (len == 5 && strncmp (str, "write", 5) == 0)
+ if (len == 5 && startswith (str, "write"))
return SHF_WRITE;
- if (len == 5 && strncmp (str, "alloc", 5) == 0)
+ if (len == 5 && startswith (str, "alloc"))
return SHF_ALLOC;
- if (len == 9 && strncmp (str, "execinstr", 9) == 0)
+ if (len == 9 && startswith (str, "execinstr"))
return SHF_EXECINSTR;
- if (len == 7 && strncmp (str, "exclude", 7) == 0)
+ if (len == 7 && startswith (str, "exclude"))
return SHF_EXCLUDE;
- if (len == 3 && strncmp (str, "tls", 3) == 0)
+ if (len == 3 && startswith (str, "tls"))
return SHF_TLS;
#ifdef md_elf_section_word
}
#endif
- ret = obj_elf_section_type (str, len, FALSE);
+ ret = obj_elf_section_type (str, len, false);
if (ret != 0)
*type = ret;
else
return 0;
}
+#endif
/* Get name of section. */
-char *
+const char *
obj_elf_section_name (void)
{
char *name;
return NULL;
}
- name = (char *) xmalloc (end - input_line_pointer + 1);
- memcpy (name, input_line_pointer, end - input_line_pointer);
- name[end - input_line_pointer] = '\0';
+ obstack_grow0 (¬es, input_line_pointer, end - input_line_pointer);
+ name = obstack_base (¬es);
while (flag_sectname_subst)
{
char *subst = strchr (name, '%');
if (subst && subst[1] == 'S')
{
- int oldlen = strlen (name);
- int substlen = strlen (now_seg->name);
- int newlen = oldlen - 2 + substlen;
- char *newname = (char *) xmalloc (newlen + 1);
- int headlen = subst - name;
- memcpy (newname, name, headlen);
- strcpy (newname + headlen, now_seg->name);
- strcat (newname + headlen, subst + 2);
- xfree (name);
- name = newname;
+ size_t head = subst - name;
+ size_t tail = strlen (subst + 2) + 1;
+ size_t slen = strlen (now_seg->name);
+
+ if (slen > 2)
+ {
+ obstack_blank (¬es, slen - 2);
+ name = obstack_base (¬es);
+ }
+ memmove (name + head + slen, name + head + 2, tail);
+ memcpy (name + head, now_seg->name, slen);
}
else
break;
}
+ obstack_finish (¬es);
+
#ifdef tc_canonicalize_section_name
name = tc_canonicalize_section_name (name);
#endif
return name;
}
+static void
+obj_elf_attach_to_group (int dummy ATTRIBUTE_UNUSED)
+{
+ const char * gname = obj_elf_section_name ();
+
+ if (gname == NULL)
+ {
+ as_warn (_("group name not parseable"));
+ return;
+ }
+
+ if (elf_group_name (now_seg))
+ {
+ if (strcmp (elf_group_name (now_seg), gname) != 0)
+ as_warn (_("section %s already has a group (%s)"),
+ bfd_section_name (now_seg), elf_group_name (now_seg));
+ return;
+ }
+
+ elf_group_name (now_seg) = gname;
+ elf_section_flags (now_seg) |= SHF_GROUP;
+}
+
void
obj_elf_section (int push)
{
- char *name, *group_name, *beg;
+ const char *name;
+ char *beg;
int type, dummy;
bfd_vma attr;
+ bfd_vma gnu_attr;
int entsize;
int linkonce;
subsegT new_subsection = -1;
+ struct elf_section_match match;
+ unsigned long linked_to_section_index = -1UL;
-#ifndef TC_I370
if (flag_mri)
{
char mri_type;
md_flush_pending_output ();
#endif
- previous_section = now_seg;
- previous_subsection = now_subseg;
+ obj_elf_section_change_hook ();
s_mri_sect (&mri_type);
return;
}
-#endif /* ! defined (TC_I370) */
name = obj_elf_section_name ();
if (name == NULL)
return;
+
+ memset (&match, 0, sizeof (match));
+
+ symbolS * sym;
+ if ((sym = symbol_find (name)) != NULL
+ && ! symbol_section_p (sym)
+ && S_IS_DEFINED (sym)
+ && ! S_IS_VOLATILE (sym)
+ && ! S_CAN_BE_REDEFINED (sym))
+ {
+ as_bad (_("section name '%s' already defined as another symbol"), name);
+ ignore_rest_of_line ();
+ return;
+ }
type = SHT_NULL;
attr = 0;
- group_name = NULL;
+ gnu_attr = 0;
entsize = 0;
linkonce = 0;
if (*input_line_pointer == '"')
{
- bfd_boolean is_clone;
+ bool is_clone;
beg = demand_copy_C_string (&dummy);
if (beg == NULL)
ignore_rest_of_line ();
return;
}
- attr |= obj_elf_parse_section_letters (beg, strlen (beg), &is_clone);
+ attr |= obj_elf_parse_section_letters (beg, strlen (beg),
+ &is_clone, &gnu_attr);
SKIP_WHITESPACE ();
if (*input_line_pointer == ',')
ignore_rest_of_line ();
return;
}
- type = obj_elf_section_type (beg, strlen (beg), TRUE);
+ type = obj_elf_section_type (beg, strlen (beg), true);
}
else if (c == '@' || c == '%')
{
++input_line_pointer;
if (ISDIGIT (* input_line_pointer))
- {
- type = strtoul (input_line_pointer, & input_line_pointer, 0);
- }
+ type = strtoul (input_line_pointer, &input_line_pointer, 0);
else
{
c = get_symbol_name (& beg);
(void) restore_line_pointer (c);
- type = obj_elf_section_type (beg, input_line_pointer - beg, TRUE);
+ type = obj_elf_section_type (beg,
+ input_line_pointer - beg,
+ true);
}
}
else
attr &= ~SHF_MERGE;
}
+ if ((attr & SHF_LINK_ORDER) != 0 && *input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ /* Check for a numeric section index, rather than a symbol name. */
+ if (ISDIGIT (* input_line_pointer))
+ {
+ linked_to_section_index = strtoul (input_line_pointer, & input_line_pointer, 0);
+ }
+ else
+ {
+ char c;
+ unsigned int length;
+
+ c = get_symbol_name (& beg);
+ (void) restore_line_pointer (c);
+ length = input_line_pointer - beg;
+ if (length)
+ match.linked_to_symbol_name = xmemdup0 (beg, length);
+ }
+ }
+
if ((attr & SHF_GROUP) != 0 && is_clone)
{
as_warn (_("? section flag ignored with G present"));
- is_clone = FALSE;
+ is_clone = false;
}
+
if ((attr & SHF_GROUP) != 0 && *input_line_pointer == ',')
{
++input_line_pointer;
- group_name = obj_elf_section_name ();
- if (group_name == NULL)
+ match.group_name = obj_elf_section_name ();
+ if (match.group_name == NULL)
attr &= ~SHF_GROUP;
else if (*input_line_pointer == ',')
{
++input_line_pointer;
SKIP_WHITESPACE ();
- if (strncmp (input_line_pointer, "comdat", 6) == 0)
+ if (startswith (input_line_pointer, "comdat"))
{
input_line_pointer += 6;
linkonce = 1;
}
}
- else if (strncmp (name, ".gnu.linkonce", 13) == 0)
+ else if (startswith (name, ".gnu.linkonce"))
linkonce = 1;
}
else if ((attr & SHF_GROUP) != 0)
const char *now_group = elf_group_name (now_seg);
if (now_group != NULL)
{
- group_name = xstrdup (now_group);
+ match.group_name = now_group;
linkonce = (now_seg->flags & SEC_LINK_ONCE) != 0;
}
}
+
+ if ((gnu_attr & SHF_GNU_MBIND) != 0 && *input_line_pointer == ',')
+ {
+ char *save = input_line_pointer;
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ if (ISDIGIT (* input_line_pointer))
+ {
+ char *t = input_line_pointer;
+ match.sh_info = strtoul (input_line_pointer,
+ &input_line_pointer, 0);
+ if (match.sh_info == (unsigned int) -1)
+ {
+ as_warn (_("unsupported mbind section info: %s"), t);
+ match.sh_info = 0;
+ }
+ }
+ else
+ input_line_pointer = save;
+ }
+
+ if ((gnu_attr & SHF_GNU_RETAIN) != 0)
+ match.sh_flags |= SHF_GNU_RETAIN;
+
+ if (*input_line_pointer == ',')
+ {
+ char *save = input_line_pointer;
+
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ if (startswith (input_line_pointer, "unique"))
+ {
+ input_line_pointer += 6;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ if (ISDIGIT (* input_line_pointer))
+ {
+ bfd_vma id;
+ bool overflow;
+ char *t = input_line_pointer;
+ if (sizeof (bfd_vma) <= sizeof (unsigned long))
+ {
+ errno = 0;
+ id = strtoul (input_line_pointer,
+ &input_line_pointer, 0);
+ overflow = (id == (unsigned long) -1
+ && errno == ERANGE);
+ }
+ else
+ {
+ id = bfd_scan_vma
+ (input_line_pointer,
+ (const char **) &input_line_pointer, 0);
+ overflow = id == ~(bfd_vma) 0;
+ }
+ if (overflow || id > (unsigned int) -1)
+ {
+ char *linefeed, saved_char = 0;
+ if ((linefeed = strchr (t, '\n')) != NULL)
+ {
+ saved_char = *linefeed;
+ *linefeed = '\0';
+ }
+ as_bad (_("unsupported section id: %s"), t);
+ if (saved_char)
+ *linefeed = saved_char;
+ }
+ else
+ {
+ match.section_id = id;
+ match.flags |= SEC_ASSEMBLER_SECTION_ID;
+ }
+ }
+ }
+ }
+ else
+ input_line_pointer = save;
+ }
}
+#ifdef TC_SPARC
else
{
do
c = get_symbol_name (& beg);
(void) restore_line_pointer (c);
- attr |= obj_elf_section_word (beg, input_line_pointer - beg, & type);
+ attr |= obj_elf_section_word (beg, input_line_pointer - beg,
+ &type);
SKIP_WHITESPACE ();
}
while (*input_line_pointer++ == ',');
--input_line_pointer;
}
+#endif
}
-done:
+ done:
demand_empty_rest_of_line ();
- obj_elf_change_section (name, type, attr, entsize, group_name, linkonce, push);
+ if ((gnu_attr & (SHF_GNU_MBIND | SHF_GNU_RETAIN)) != 0)
+ {
+ const struct elf_backend_data *bed;
+ bool mbind_p = (gnu_attr & SHF_GNU_MBIND) != 0;
+
+ if (mbind_p && (attr & SHF_ALLOC) == 0)
+ as_bad (_("SHF_ALLOC isn't set for GNU_MBIND section: %s"), name);
+
+ bed = get_elf_backend_data (stdoutput);
+
+ if (bed->elf_osabi != ELFOSABI_GNU
+ && bed->elf_osabi != ELFOSABI_FREEBSD
+ && bed->elf_osabi != ELFOSABI_NONE)
+ as_bad (_("%s section is supported only by GNU and FreeBSD targets"),
+ mbind_p ? "GNU_MBIND" : "GNU_RETAIN");
+ else
+ {
+ if (mbind_p)
+ elf_tdata (stdoutput)->has_gnu_osabi |= elf_gnu_osabi_mbind;
+ if ((gnu_attr & SHF_GNU_RETAIN) != 0)
+ elf_tdata (stdoutput)->has_gnu_osabi |= elf_gnu_osabi_retain;
+
+ attr |= gnu_attr;
+ }
+ }
+
+ obj_elf_change_section (name, type, attr, entsize, &match, linkonce,
+ push);
+
+ if (linked_to_section_index != -1UL)
+ {
+ elf_section_flags (now_seg) |= SHF_LINK_ORDER;
+ elf_section_data (now_seg)->this_hdr.sh_link = linked_to_section_index;
+ /* FIXME: Should we perform some sanity checking on the section index ? */
+ }
if (push && new_subsection != -1)
subseg_set (now_seg, new_subsection);
}
+/* Change to the .bss section. */
+
+void
+obj_elf_bss (int i ATTRIBUTE_UNUSED)
+{
+ int temp;
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ obj_elf_section_change_hook ();
+
+ temp = get_absolute_expression ();
+ subseg_set (bss_section, (subsegT) temp);
+ demand_empty_rest_of_line ();
+
+#ifdef md_elf_section_change_hook
+ md_elf_section_change_hook ();
+#endif
+}
+
/* Change to the .data section. */
void
md_flush_pending_output ();
#endif
- previous_section = now_seg;
- previous_subsection = now_subseg;
+ obj_elf_section_change_hook ();
+
s_data (i);
#ifdef md_elf_section_change_hook
md_flush_pending_output ();
#endif
- previous_section = now_seg;
- previous_subsection = now_subseg;
+ obj_elf_section_change_hook ();
+
s_text (i);
#ifdef md_elf_section_change_hook
md_flush_pending_output ();
#endif
- previous_section = now_seg;
- previous_subsection = now_subseg;
+ obj_elf_section_change_hook ();
+
s_struct (i);
#ifdef md_elf_section_change_hook
md_flush_pending_output ();
#endif
- previous_section = now_seg;
- previous_subsection = now_subseg;
+ obj_elf_section_change_hook ();
temp = get_absolute_expression ();
subseg_set (now_seg, (subsegT) temp);
new_section = previous_section;
new_subsection = previous_subsection;
- previous_section = now_seg;
- previous_subsection = now_subseg;
+ obj_elf_section_change_hook ();
+
subseg_set (new_section, new_subsection);
#ifdef md_elf_section_change_hook
demand_empty_rest_of_line ();
}
+static struct elf_versioned_name_list *
+obj_elf_find_and_add_versioned_name (const char *version_name,
+ const char *sym_name,
+ const char *ver,
+ struct elf_obj_sy *sy_obj)
+{
+ struct elf_versioned_name_list *versioned_name;
+ const char *p;
+
+ for (p = ver + 1; *p == ELF_VER_CHR; p++)
+ ;
+
+ /* NB: Since some tests in ld/testsuite/ld-elfvers have no version
+ names, we have to disable this. */
+ if (0 && *p == '\0')
+ {
+ as_bad (_("missing version name in `%s' for symbol `%s'"),
+ version_name, sym_name);
+ return NULL;
+ }
+
+ versioned_name = sy_obj->versioned_name;
+
+ switch (p - ver)
+ {
+ case 1:
+ case 2:
+ break;
+ case 3:
+ if (sy_obj->rename)
+ {
+ if (strcmp (versioned_name->name, version_name) == 0)
+ return versioned_name;
+ else
+ {
+ as_bad (_("only one version name with `@@@' is allowed "
+ "for symbol `%s'"), sym_name);
+ return NULL;
+ }
+ }
+ sy_obj->rename = true;
+ break;
+ default:
+ as_bad (_("invalid version name '%s' for symbol `%s'"),
+ version_name, sym_name);
+ return NULL;
+ }
+
+ for (;
+ versioned_name != NULL;
+ versioned_name = versioned_name->next)
+ if (strcmp (versioned_name->name, version_name) == 0)
+ return versioned_name;
+
+ /* Add this versioned name to the head of the list, */
+ versioned_name = (struct elf_versioned_name_list *)
+ xmalloc (sizeof (*versioned_name));
+ versioned_name->name = xstrdup (version_name);
+ versioned_name->next = sy_obj->versioned_name;
+ sy_obj->versioned_name = versioned_name;
+
+ return versioned_name;
+}
+
/* 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
obj_elf_symver (int ignore ATTRIBUTE_UNUSED)
{
char *name;
+ const char *sym_name;
char c;
char old_lexat;
symbolS *sym;
+ struct elf_obj_sy *sy_obj;
+ char *p;
sym = get_sym_from_input_line_and_check ();
lex_type[(unsigned char) '@'] |= LEX_NAME;
c = get_symbol_name (& name);
lex_type[(unsigned char) '@'] = old_lexat;
+ sym_name = S_GET_NAME (sym);
- if (symbol_get_obj (sym)->versioned_name == NULL)
+ if (S_IS_COMMON (sym))
{
- symbol_get_obj (sym)->versioned_name = xstrdup (name);
+ as_bad (_("`%s' can't be versioned to common symbol '%s'"),
+ name, sym_name);
+ ignore_rest_of_line ();
+ return;
+ }
- (void) restore_line_pointer (c);
+ p = strchr (name, ELF_VER_CHR);
+ if (p == NULL)
+ {
+ as_bad (_("missing version name in `%s' for symbol `%s'"),
+ name, sym_name);
+ ignore_rest_of_line ();
+ return;
+ }
- 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;
- }
+ sy_obj = symbol_get_obj (sym);
+ if (obj_elf_find_and_add_versioned_name (name, sym_name,
+ p, sy_obj) == NULL)
+ {
+ sy_obj->bad_version = true;
+ ignore_rest_of_line ();
+ return;
}
- else
+
+ (void) restore_line_pointer (c);
+
+ if (*input_line_pointer == ',')
{
- if (strcmp (symbol_get_obj (sym)->versioned_name, name))
+ char *save = input_line_pointer;
+
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ if (startswith (input_line_pointer, "local"))
{
- as_bad (_("multiple versions [`%s'|`%s'] for symbol `%s'"),
- name, symbol_get_obj (sym)->versioned_name,
- S_GET_NAME (sym));
- ignore_rest_of_line ();
- return;
+ input_line_pointer += 5;
+ sy_obj->visibility = visibility_local;
}
-
- (void) restore_line_pointer (c);
+ else if (startswith (input_line_pointer, "hidden"))
+ {
+ input_line_pointer += 6;
+ sy_obj->visibility = visibility_hidden;
+ }
+ else if (startswith (input_line_pointer, "remove"))
+ {
+ input_line_pointer += 6;
+ sy_obj->visibility = visibility_remove;
+ }
+ else
+ input_line_pointer = save;
}
demand_empty_rest_of_line ();
syntax is ".vtable_inherit CHILDNAME, PARENTNAME". */
struct fix *
-obj_elf_vtable_inherit (int ignore ATTRIBUTE_UNUSED)
+obj_elf_get_vtable_inherit (void)
{
char *cname, *pname;
symbolS *csym, *psym;
0, psym, 0, 0, BFD_RELOC_VTABLE_INHERIT);
}
+/* This is a version of obj_elf_get_vtable_inherit() that is
+ suitable for use in struct _pseudo_type tables. */
+
+void
+obj_elf_vtable_inherit (int ignore ATTRIBUTE_UNUSED)
+{
+ (void) obj_elf_get_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". */
struct fix *
-obj_elf_vtable_entry (int ignore ATTRIBUTE_UNUSED)
+obj_elf_get_vtable_entry (void)
{
symbolS *sym;
offsetT offset;
BFD_RELOC_VTABLE_ENTRY);
}
+/* This is a version of obj_elf_get_vtable_entry() that is
+ suitable for use in struct _pseudo_type tables. */
+
+void
+obj_elf_vtable_entry (int ignore ATTRIBUTE_UNUSED)
+{
+ (void) obj_elf_get_vtable_entry ();
+}
+
#define skip_whitespace(str) do { if (*(str) == ' ') ++(str); } while (0)
static inline int
/* Return true if we have seen an explicit specification of attribute TAG
for vendor VENDOR. */
-bfd_boolean
+bool
obj_elf_seen_attribute (int vendor, unsigned int tag)
{
unsigned int base;
for (rai = recorded_attributes; rai; rai = rai->next)
if (rai->vendor == vendor && rai->base == base)
return (rai->mask & mask) != 0;
- return FALSE;
+ return false;
}
/* Parse an attribute directive for VENDOR.
if (i == 0)
goto bad;
- name = (char *) alloca (i + 1);
- memcpy (name, s, i);
- name[i] = '\0';
+ name = xmemdup0 (s, i);
#ifndef CONVERT_SYMBOLIC_ATTRIBUTE
#define CONVERT_SYMBOLIC_ATTRIBUTE(a) -1
{
as_bad (_("Attribute name not recognised: %s"), name);
ignore_rest_of_line ();
+ free (name);
return 0;
}
+ free (name);
}
type = _bfd_elf_obj_attrs_arg_type (stdoutput, vendor, tag);
demand_empty_rest_of_line ();
return tag;
-bad_string:
+ bad_string:
as_bad (_("bad string constant"));
ignore_rest_of_line ();
return 0;
-bad:
+ bad:
as_bad (_("expected <tag> , <value>"));
ignore_rest_of_line ();
return 0;
#endif
}
-/* When setting one symbol equal to another, by default we probably
- want them to have the same "size", whatever it means in the current
- context. */
+/* Deduplicate size expressions. We might get into trouble with
+ multiple freeing or use after free if we leave them pointing to the
+ same expressionS. */
+
+void
+elf_obj_symbol_clone_hook (symbolS *newsym, symbolS *orgsym ATTRIBUTE_UNUSED)
+{
+ struct elf_obj_sy *newelf = symbol_get_obj (newsym);
+ if (newelf->size)
+ {
+ expressionS *exp = XNEW (expressionS);
+ *exp = *newelf->size;
+ newelf->size = exp;
+ }
+}
void
elf_copy_symbol_attributes (symbolS *dest, symbolS *src)
{
struct elf_obj_sy *srcelf = symbol_get_obj (src);
struct elf_obj_sy *destelf = symbol_get_obj (dest);
- if (srcelf->size)
- {
- if (destelf->size == NULL)
- destelf->size = (expressionS *) xmalloc (sizeof (expressionS));
- *destelf->size = *srcelf->size;
- }
- else
+ /* If size is unset, copy size from src. Because we don't track whether
+ .size has been used, we can't differentiate .size dest, 0 from the case
+ where dest's size is unset. */
+ if (!destelf->size && S_GET_SIZE (dest) == 0)
{
- if (destelf->size != NULL)
- free (destelf->size);
- destelf->size = NULL;
+ if (srcelf->size)
+ {
+ destelf->size = XNEW (expressionS);
+ *destelf->size = *srcelf->size;
+ }
+ S_SET_SIZE (dest, S_GET_SIZE (src));
}
- S_SET_SIZE (dest, S_GET_SIZE (src));
/* Don't copy visibility. */
S_SET_OTHER (dest, (ELF_ST_VISIBILITY (S_GET_OTHER (dest))
| (S_GET_OTHER (src) & ~ELF_ST_VISIBILITY (-1))));
/* Create the .note section. */
note_secp = subseg_new (".note", 0);
- bfd_set_section_flags (stdoutput,
- note_secp,
- SEC_HAS_CONTENTS | SEC_READONLY);
+ bfd_set_section_flags (note_secp, SEC_HAS_CONTENTS | SEC_READONLY);
+ record_alignment (note_secp, 2);
/* Process the version string. */
len = strlen (name) + 1;
if (exp.X_op == O_constant)
{
S_SET_SIZE (sym, exp.X_add_number);
- if (symbol_get_obj (sym)->size)
- {
- xfree (symbol_get_obj (sym)->size);
- symbol_get_obj (sym)->size = NULL;
- }
+ xfree (symbol_get_obj (sym)->size);
+ symbol_get_obj (sym)->size = NULL;
}
else
{
- symbol_get_obj (sym)->size =
- (expressionS *) xmalloc (sizeof (expressionS));
+ symbol_get_obj (sym)->size = XNEW (expressionS);
*symbol_get_obj (sym)->size = exp;
}
demand_empty_rest_of_line ();
const struct elf_backend_data *bed;
bed = get_elf_backend_data (stdoutput);
- if (!(bed->elf_osabi == ELFOSABI_GNU
- || bed->elf_osabi == ELFOSABI_FREEBSD
- /* GNU is still using the default value 0. */
- || bed->elf_osabi == ELFOSABI_NONE))
- as_bad (_("symbol type \"%s\" is supported only by GNU and FreeBSD targets"),
- type_name);
+ if (bed->elf_osabi != ELFOSABI_NONE
+ && bed->elf_osabi != ELFOSABI_GNU
+ && bed->elf_osabi != ELFOSABI_FREEBSD)
+ as_bad (_("symbol type \"%s\" is supported only by GNU "
+ "and FreeBSD targets"), type_name);
+ /* MIPS targets do not support IFUNCS. */
+ else if (bed->target_id == MIPS_ELF_DATA)
+ as_bad (_("symbol type \"%s\" is not supported by "
+ "MIPS targets"), type_name);
+ elf_tdata (stdoutput)->has_gnu_osabi |= elf_gnu_osabi_ifunc;
type = BSF_FUNCTION | BSF_GNU_INDIRECT_FUNCTION;
}
else if (strcmp (type_name, "gnu_unique_object") == 0)
{
- struct elf_backend_data *bed;
+ const struct elf_backend_data *bed;
- bed = (struct elf_backend_data *) get_elf_backend_data (stdoutput);
- if (!(bed->elf_osabi == ELFOSABI_GNU
- /* GNU is still using the default value 0. */
- || bed->elf_osabi == ELFOSABI_NONE))
+ bed = get_elf_backend_data (stdoutput);
+ if (bed->elf_osabi != ELFOSABI_NONE
+ && bed->elf_osabi != ELFOSABI_GNU)
as_bad (_("symbol type \"%s\" is supported only by GNU targets"),
type_name);
+ elf_tdata (stdoutput)->has_gnu_osabi |= elf_gnu_osabi_unique;
type = BSF_OBJECT | BSF_GNU_UNIQUE;
- /* PR 10549: Always set OSABI field to GNU for objects containing unique symbols. */
- bed->elf_osabi = ELFOSABI_GNU;
}
#ifdef md_elf_symbol_type
else if ((type = md_elf_symbol_type (type_name, sym, elfsym)) != -1)
if (*input_line_pointer == '"')
++input_line_pointer;
- elfsym->symbol.flags |= type;
+#ifdef md_elf_symbol_type_change
+ if (!md_elf_symbol_type_change (sym, elfsym, type))
+#endif
+ {
+ flagword mask = BSF_FUNCTION | BSF_OBJECT;
+
+ if (type != BSF_FUNCTION)
+ mask |= BSF_GNU_INDIRECT_FUNCTION;
+ if (type != BSF_OBJECT)
+ {
+ mask |= BSF_GNU_UNIQUE | BSF_THREAD_LOCAL;
+
+ if (S_IS_COMMON (sym))
+ {
+ as_bad (_("cannot change type of common symbol '%s'"),
+ S_GET_NAME (sym));
+ mask = type = 0;
+ }
+ }
+
+ /* Don't warn when changing to STT_NOTYPE. */
+ if (type)
+ {
+ flagword new = (elfsym->symbol.flags & ~mask) | type;
+
+ if (new != (elfsym->symbol.flags | type))
+ as_warn (_("symbol '%s' already has its type set"), S_GET_NAME (sym));
+ elfsym->symbol.flags = new;
+ }
+ else
+ elfsym->symbol.flags &= ~mask;
+ }
demand_empty_rest_of_line ();
}
+static segT comment_section;
+
static void
obj_elf_ident (int ignore ATTRIBUTE_UNUSED)
{
- static segT comment_section;
segT old_section = now_seg;
int old_subsection = now_subseg;
{
char *p;
comment_section = subseg_new (".comment", 0);
- bfd_set_section_flags (stdoutput, comment_section,
- SEC_READONLY | SEC_HAS_CONTENTS
- | SEC_MERGE | SEC_STRINGS);
+ bfd_set_section_flags (comment_section, (SEC_READONLY | SEC_HAS_CONTENTS
+ | SEC_MERGE | SEC_STRINGS));
comment_section->entsize = 1;
#ifdef md_elf_section_change_hook
md_elf_section_change_hook ();
/* Force the section to align to a longword boundary. Without this,
UnixWare ar crashes. */
- bfd_set_section_alignment (stdoutput, seg, 2);
+ bfd_set_section_alignment (seg, 2);
/* Make space for this first symbol. */
p = frag_more (12);
/* Zero it out. */
memset (p, 0, 12);
- as_where (&file, NULL);
- stabstr_name = (char *) xmalloc (strlen (segment_name (seg)) + 4);
- strcpy (stabstr_name, segment_name (seg));
- strcat (stabstr_name, "str");
- stroff = get_stab_string_offset (file, stabstr_name);
+ file = remap_debug_filename (as_where (NULL));
+ stabstr_name = concat (segment_name (seg), "str", (char *) NULL);
+ stroff = get_stab_string_offset (file, stabstr_name, true);
know (stroff == 1 || (stroff == 0 && file[0] == '\0'));
md_number_to_chars (p, stroff, 4);
seg_info (seg)->stabu.p = p;
+ free (file);
}
#endif
char *p;
int strsz, nsyms;
- if (strncmp (".stab", sec->name, 5))
+ if (!startswith (sec->name, ".stab"))
return;
if (!strcmp ("str", sec->name + strlen (sec->name) - 3))
return;
- name = (char *) alloca (strlen (sec->name) + 4);
- strcpy (name, sec->name);
- strcat (name, "str");
+ name = concat (sec->name, "str", NULL);
strsec = bfd_get_section_by_name (abfd, name);
if (strsec)
- strsz = bfd_section_size (abfd, strsec);
+ strsz = bfd_section_size (strsec);
else
strsz = 0;
- nsyms = bfd_section_size (abfd, sec) / 12 - 1;
+ nsyms = bfd_section_size (sec) / 12 - 1;
p = seg_info (sec)->stabu.p;
gas_assert (p != 0);
bfd_h_put_16 (abfd, nsyms, p + 6);
bfd_h_put_32 (abfd, strsz, p + 8);
+ free (name);
}
#ifdef NEED_ECOFF_DEBUG
supposed to *EXT to the external symbol information, and return
whether the symbol should be used at all. */
-static bfd_boolean
+static bool
elf_get_extr (asymbol *sym, EXTR *ext)
{
if (sym->udata.p == NULL)
- return FALSE;
+ return false;
*ext = *(EXTR *) sym->udata.p;
- return TRUE;
+ return true;
}
/* This function is called by bfd_ecoff_debug_externals. It has
{
struct elf_obj_sy *sy_obj;
expressionS *size;
+ struct elf_versioned_name_list *versioned_name;
#ifdef NEED_ECOFF_DEBUG
if (ECOFF_DEBUGGING)
S_SET_SIZE (symp, size->X_add_number);
else
{
- if (flag_size_check == size_check_error)
+ if (!flag_allow_nonconst_size)
as_bad (_(".size expression for %s "
"does not evaluate to a constant"), S_GET_NAME (symp));
else
sy_obj->size = NULL;
}
- if (sy_obj->versioned_name != NULL)
+ versioned_name = sy_obj->versioned_name;
+ if (versioned_name)
{
- char *p;
-
- p = strchr (sy_obj->versioned_name, ELF_VER_CHR);
- if (p == NULL)
- /* We will have already reported an error about a missing version. */
- *puntp = TRUE;
-
/* This symbol was given a new name with the .symver directive.
-
If this is an external reference, just rename the symbol to
include the version string. This will make the relocs be
- against the correct versioned symbol.
-
- If this is a definition, add an alias. FIXME: Using an alias
- will permit the debugging information to refer to the right
- symbol. However, it's not clear whether it is the best
- approach. */
-
- else if (! S_IS_DEFINED (symp))
+ against the correct versioned symbol. */
+
+ /* We will have already reported an version error. */
+ if (sy_obj->bad_version)
+ *puntp = true;
+ /* elf_frob_file_before_adjust only allows one version symbol for
+ renamed symbol. */
+ else if (sy_obj->rename)
+ S_SET_NAME (symp, versioned_name->name);
+ else if (S_IS_COMMON (symp))
{
- /* Verify that the name isn't using the @@ syntax--this is
- reserved for definitions of the default version to link
- against. */
- if (p[1] == ELF_VER_CHR)
- {
- as_bad (_("invalid attempt to declare external version name"
- " as default in symbol `%s'"),
- sy_obj->versioned_name);
- *puntp = TRUE;
- }
- S_SET_NAME (symp, sy_obj->versioned_name);
+ as_bad (_("`%s' can't be versioned to common symbol '%s'"),
+ versioned_name->name, S_GET_NAME (symp));
+ *puntp = true;
}
else
{
- if (p[1] == ELF_VER_CHR && p[2] == ELF_VER_CHR)
+ asymbol *bfdsym;
+ elf_symbol_type *elfsym;
+
+ /* This is a definition. Add an alias for each version.
+ FIXME: Using an alias will permit the debugging information
+ to refer to the right symbol. However, it's not clear
+ whether it is the best approach. */
+
+ /* FIXME: Creating a new symbol here is risky. We're
+ in the final loop over the symbol table. We can
+ get away with it only because the symbol goes to
+ the end of the list, where the loop will still see
+ it. It would probably be better to do this in
+ obj_frob_file_before_adjust. */
+ for (; versioned_name != NULL;
+ versioned_name = versioned_name->next)
{
- size_t l;
-
- /* The @@@ syntax is a special case. It renames the
- symbol name to versioned_name with one `@' removed. */
- l = strlen (&p[3]) + 1;
- memmove (&p[2], &p[3], l);
- S_SET_NAME (symp, sy_obj->versioned_name);
- }
- else
- {
- symbolS *symp2;
-
- /* FIXME: Creating a new symbol here is risky. We're
- in the final loop over the symbol table. We can
- get away with it only because the symbol goes to
- the end of the list, where the loop will still see
- it. It would probably be better to do this in
- obj_frob_file_before_adjust. */
-
- symp2 = symbol_find_or_make (sy_obj->versioned_name);
-
- /* Now we act as though we saw symp2 = sym. */
+ symbolS *symp2 = symbol_find_or_make (versioned_name->name);
S_SET_SEGMENT (symp2, S_GET_SEGMENT (symp));
because we are in the middle of the final loop. */
S_SET_VALUE (symp2,
(S_GET_VALUE (symp)
- - symbol_get_frag (symp)->fr_address));
+ - (symbol_get_frag (symp)->fr_address
+ / OCTETS_PER_BYTE)));
symbol_set_frag (symp2, symbol_get_frag (symp));
if (S_IS_EXTERNAL (symp))
S_SET_EXTERNAL (symp2);
}
+
+ switch (sy_obj->visibility)
+ {
+ case visibility_unchanged:
+ break;
+ case visibility_hidden:
+ bfdsym = symbol_get_bfdsym (symp);
+ elfsym = elf_symbol_from (bfdsym);
+ elfsym->internal_elf_sym.st_other &= ~3;
+ elfsym->internal_elf_sym.st_other |= STV_HIDDEN;
+ break;
+ case visibility_remove:
+ /* Don't remove the symbol if it is used in relocation.
+ Instead, mark it as to be removed and issue an error
+ if the symbol has more than one versioned name. */
+ if (symbol_used_in_reloc_p (symp))
+ {
+ if (sy_obj->versioned_name->next != NULL)
+ as_bad (_("symbol '%s' with multiple versions cannot be used in relocation"),
+ S_GET_NAME (symp));
+ symbol_mark_removed (symp);
+ }
+ else
+ symbol_remove (symp, &symbol_rootP, &symbol_lastP);
+ break;
+ case visibility_local:
+ S_CLEAR_EXTERNAL (symp);
+ break;
+ }
}
}
as_bad (_("symbol `%s' can not be both weak and common"),
S_GET_NAME (symp));
}
+}
-#ifdef TC_MIPS
- /* The Irix 5 and 6 assemblers set the type of any common symbol and
- any undefined non-function symbol to STT_OBJECT. We try to be
- compatible, since newer Irix 5 and 6 linkers care. However, we
- only set undefined symbols to be STT_OBJECT if we are on Irix,
- because that is the only time gcc will generate the necessary
- .global directives to mark functions. */
-
- if (S_IS_COMMON (symp))
- symbol_get_bfdsym (symp)->flags |= BSF_OBJECT;
-
- if (strstr (TARGET_OS, "irix") != NULL
- && ! S_IS_DEFINED (symp)
- && (symbol_get_bfdsym (symp)->flags & BSF_FUNCTION) == 0)
- symbol_get_bfdsym (symp)->flags |= BSF_OBJECT;
-#endif
+/* Fix up SYMPP which has been marked to be removed by .symver. */
+
+void
+elf_fixup_removed_symbol (symbolS **sympp)
+{
+ symbolS *symp = *sympp;
+ struct elf_obj_sy *sy_obj = symbol_get_obj (symp);
+
+ /* Replace the removed symbol with the versioned symbol. */
+ symp = symbol_find (sy_obj->versioned_name->name);
+ *sympp = symp;
}
struct group_list
{
asection **head; /* Section lists. */
- unsigned int *elt_count; /* Number of sections in each list. */
unsigned int num_group; /* Number of lists. */
- struct hash_control *indexes; /* Maps group name to index in head array. */
+ htab_t indexes; /* Maps group name to index in head array. */
};
+static struct group_list groups;
+
/* Called via bfd_map_over_sections. If SEC is a member of a group,
add it to a list of sections belonging to the group. INF is a
pointer to a struct group_list, which is where we store the head of
- each list. */
+ each list. If its link_to_symbol_name isn't NULL, set up its
+ linked-to section. */
static void
-build_group_lists (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)
+build_additional_section_info (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec, void *inf)
{
struct group_list *list = (struct group_list *) inf;
const char *group_name = elf_group_name (sec);
unsigned int *elem_idx;
unsigned int *idx_ptr;
+ if (sec->map_head.linked_to_symbol_name)
+ {
+ symbolS *linked_to_sym;
+ linked_to_sym = symbol_find (sec->map_head.linked_to_symbol_name);
+ if (!linked_to_sym || !S_IS_DEFINED (linked_to_sym))
+ as_bad (_("undefined linked-to symbol `%s' on section `%s'"),
+ sec->map_head.linked_to_symbol_name,
+ bfd_section_name (sec));
+ else
+ elf_linked_to_section (sec) = S_GET_SEGMENT (linked_to_sym);
+ }
+
if (group_name == NULL)
return;
/* If this group already has a list, add the section to the head of
the list. */
- elem_idx = (unsigned int *) hash_find (list->indexes, group_name);
+ elem_idx = (unsigned int *) str_hash_find (list->indexes, group_name);
if (elem_idx != NULL)
{
elf_next_in_group (sec) = list->head[*elem_idx];
list->head[*elem_idx] = sec;
- list->elt_count[*elem_idx] += 1;
return;
}
if ((i & 127) == 0)
{
unsigned int newsize = i + 128;
- list->head = (asection **) xrealloc (list->head,
- newsize * sizeof (*list->head));
- list->elt_count = (unsigned int *)
- xrealloc (list->elt_count, newsize * sizeof (*list->elt_count));
+ list->head = XRESIZEVEC (asection *, list->head, newsize);
}
list->head[i] = sec;
- list->elt_count[i] = 1;
list->num_group += 1;
/* Add index to hash. */
- idx_ptr = (unsigned int *) xmalloc (sizeof (unsigned int));
+ idx_ptr = XNEW (unsigned int);
*idx_ptr = i;
- hash_insert (list->indexes, group_name, idx_ptr);
+ str_hash_insert (list->indexes, group_name, idx_ptr, 0);
}
-static void free_section_idx (const char *key ATTRIBUTE_UNUSED, void *val)
+static void
+free_section_idx (void *ent)
{
- free ((unsigned int *) val);
+ string_tuple_t *tuple = ent;
+ free ((char *) tuple->value);
}
+/* Create symbols for group signature. */
+
void
elf_adjust_symtab (void)
{
- struct group_list list;
unsigned int i;
/* Go find section groups. */
- list.num_group = 0;
- list.head = NULL;
- list.elt_count = NULL;
- list.indexes = hash_new ();
- bfd_map_over_sections (stdoutput, build_group_lists, &list);
+ groups.num_group = 0;
+ groups.head = NULL;
+ groups.indexes = htab_create_alloc (16, hash_string_tuple, eq_string_tuple,
+ free_section_idx, notes_calloc, NULL);
+ bfd_map_over_sections (stdoutput, build_additional_section_info,
+ &groups);
/* Make the SHT_GROUP sections that describe each section group. We
can't set up the section contents here yet, because elf section
indices have yet to be calculated. elf.c:set_group_contents does
the rest of the work. */
- for (i = 0; i < list.num_group; i++)
+ for (i = 0; i < groups.num_group; i++)
{
- const char *group_name = elf_group_name (list.head[i]);
+ const char *group_name = elf_group_name (groups.head[i]);
const char *sec_name;
asection *s;
flagword flags;
struct symbol *sy;
- bfd_size_type size;
flags = SEC_READONLY | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_GROUP;
- for (s = list.head[i]; s != NULL; s = elf_next_in_group (s))
+ for (s = groups.head[i]; s != NULL; s = elf_next_in_group (s))
if ((s->flags ^ flags) & SEC_LINK_ONCE)
{
flags |= SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD;
- if (s != list.head[i])
+ if (s != groups.head[i])
{
as_warn (_("assuming all members of group `%s' are COMDAT"),
group_name);
sec_name = ".group";
s = subseg_force_new (sec_name, 0);
if (s == NULL
- || !bfd_set_section_flags (stdoutput, s, flags)
- || !bfd_set_section_alignment (stdoutput, s, 2))
+ || !bfd_set_section_flags (s, flags)
+ || !bfd_set_section_alignment (s, 2))
{
as_fatal (_("can't create group: %s"),
bfd_errmsg (bfd_get_error ()));
elf_section_type (s) = SHT_GROUP;
/* Pass a pointer to the first section in this group. */
- elf_next_in_group (s) = list.head[i];
+ elf_next_in_group (s) = groups.head[i];
+ elf_sec_group (groups.head[i]) = s;
/* Make sure that the signature symbol for the group has the
name of the group. */
sy = symbol_find_exact (group_name);
- if (!sy
- || (sy != symbol_lastP
- && (sy->sy_next == NULL
- || sy->sy_next->sy_previous != sy)))
+ if (!sy || !symbol_on_chain (sy, symbol_rootP, symbol_lastP))
{
/* Create the symbol now. */
- sy = symbol_new (group_name, now_seg, (valueT) 0, frag_now);
+ sy = symbol_new (group_name, now_seg, frag_now, 0);
#ifdef TE_SOLARIS
/* Before Solaris 11 build 154, Sun ld rejects local group
signature symbols, so make them weak hidden instead. */
symbol_table_insert (sy);
}
elf_group_id (s) = symbol_get_bfdsym (sy);
-
- size = 4 * (list.elt_count[i] + 1);
- bfd_set_section_size (stdoutput, s, size);
- s->contents = (unsigned char *) frag_more (size);
- frag_now->fr_fix = frag_now_fix_octets ();
- frag_wane (frag_now);
+ /* Mark the group signature symbol as used so that it will be
+ included in the symbol table. */
+ symbol_mark_used_in_reloc (sy);
}
-
- /* Cleanup hash. */
- hash_traverse (list.indexes, free_section_idx);
- hash_die (list.indexes);
}
void
symbolS *symp;
for (symp = symbol_rootP; symp; symp = symbol_next (symp))
- if (!S_IS_DEFINED (symp))
- {
- if (symbol_get_obj (symp)->versioned_name)
- {
- char *p;
-
- /* The @@@ syntax is a special case. If the symbol is
- not defined, 2 `@'s will be removed from the
- versioned_name. */
-
- p = strchr (symbol_get_obj (symp)->versioned_name,
- ELF_VER_CHR);
- if (p != NULL && p[1] == ELF_VER_CHR && p[2] == ELF_VER_CHR)
- {
- size_t l = strlen (&p[3]) + 1;
- memmove (&p[1], &p[3], l);
- }
- if (symbol_used_p (symp) == 0
- && symbol_used_in_reloc_p (symp) == 0)
- symbol_remove (symp, &symbol_rootP, &symbol_lastP);
- }
+ {
+ struct elf_obj_sy *sy_obj = symbol_get_obj (symp);
+ int is_defined = !!S_IS_DEFINED (symp);
+
+ if (sy_obj->versioned_name)
+ {
+ char *p = strchr (sy_obj->versioned_name->name,
+ ELF_VER_CHR);
- /* If there was .weak foo, but foo was neither defined nor
- used anywhere, remove it. */
+ if (sy_obj->rename)
+ {
+ /* The @@@ syntax is a special case. If the symbol is
+ not defined, 2 `@'s will be removed from the
+ versioned_name. Otherwise, 1 `@' will be removed. */
+ size_t l = strlen (&p[3]) + 1;
+ memmove (&p[1 + is_defined], &p[3], l);
+ }
- else if (S_IS_WEAK (symp)
- && symbol_used_p (symp) == 0
- && symbol_used_in_reloc_p (symp) == 0)
- symbol_remove (symp, &symbol_rootP, &symbol_lastP);
- }
+ if (!is_defined)
+ {
+ /* Verify that the name isn't using the @@ syntax--this
+ is reserved for definitions of the default version
+ to link against. */
+ if (!sy_obj->rename && p[1] == ELF_VER_CHR)
+ {
+ as_bad (_("invalid attempt to declare external "
+ "version name as default in symbol `%s'"),
+ sy_obj->versioned_name->name);
+ return;
+ }
+
+ /* Only one version symbol is allowed for undefined
+ symbol. */
+ if (sy_obj->versioned_name->next)
+ {
+ as_bad (_("multiple versions [`%s'|`%s'] for "
+ "symbol `%s'"),
+ sy_obj->versioned_name->name,
+ sy_obj->versioned_name->next->name,
+ S_GET_NAME (symp));
+ return;
+ }
+
+ sy_obj->rename = true;
+ }
+ }
+
+ /* If there was .symver or .weak, but symbol was neither
+ defined nor used anywhere, remove it. */
+ if (!is_defined
+ && (sy_obj->versioned_name || S_IS_WEAK (symp))
+ && symbol_used_p (symp) == 0
+ && symbol_used_in_reloc_p (symp) == 0)
+ symbol_remove (symp, &symbol_rootP, &symbol_lastP);
+ }
}
}
void
elf_frob_file_after_relocs (void)
{
+ unsigned int i;
+
+ /* Set SHT_GROUP section size. */
+ for (i = 0; i < groups.num_group; i++)
+ {
+ asection *s, *head, *group;
+ bfd_size_type size;
+
+ head = groups.head[i];
+ size = 4;
+ for (s = head; s != NULL; s = elf_next_in_group (s))
+ size += (s->flags & SEC_RELOC) != 0 ? 8 : 4;
+
+ group = elf_sec_group (head);
+ subseg_set (group, 0);
+ bfd_set_section_size (group, size);
+ group->contents = (unsigned char *) frag_more (size);
+ frag_now->fr_fix = frag_now_fix_octets ();
+ frag_wane (frag_now);
+ }
+
#ifdef NEED_ECOFF_DEBUG
if (ECOFF_DEBUGGING)
/* Generate the ECOFF debugging information. */
ecoff_build_debug (&debug.symbolic_header, &buf, debug_swap);
/* Set up the pointers in debug. */
+ debug.alloc_syments = true;
#define SET(ptr, offset, type) \
debug.ptr = (type) (buf + debug.symbolic_header.offset)
/* Set up the external symbols. */
debug.ssext = debug.ssext_end = NULL;
debug.external_ext = debug.external_ext_end = NULL;
- if (! bfd_ecoff_debug_externals (stdoutput, &debug, debug_swap, TRUE,
+ if (! bfd_ecoff_debug_externals (stdoutput, &debug, debug_swap, true,
elf_get_extr, elf_set_index))
as_fatal (_("failed to set up debugging information: %s"),
bfd_errmsg (bfd_get_error ()));
to force the ELF backend to allocate a file position, and then
write out the data. FIXME: Is this really the best way to do
this? */
- bfd_set_section_size
- (stdoutput, sec, bfd_ecoff_debug_size (stdoutput, &debug, debug_swap));
+ bfd_set_section_size (sec, bfd_ecoff_debug_size (stdoutput, &debug,
+ debug_swap));
/* Pass BUF to bfd_set_section_contents because this will
eventually become a call to fwrite, and ISO C prohibits
#endif /* NEED_ECOFF_DEBUG */
}
-#ifdef SCO_ELF
-
-/* Heavily plagiarized from obj_elf_version. The idea is to emit the
- SCO specific identifier in the .notes section to satisfy the SCO
- linker.
-
- This looks more complicated than it really is. As opposed to the
- "obvious" solution, this should handle the cross dev cases
- correctly. (i.e, hosting on a 64 bit big endian processor, but
- generating SCO Elf code) Efficiency isn't a concern, as there
- should be exactly one of these sections per object module.
-
- SCO OpenServer 5 identifies it's ELF modules with a standard ELF
- .note section.
-
- int_32 namesz = 4 ; Name size
- int_32 descsz = 12 ; Descriptive information
- int_32 type = 1 ;
- char name[4] = "SCO" ; Originator name ALWAYS SCO + NULL
- int_32 version = (major ver # << 16) | version of tools ;
- int_32 source = (tool_id << 16 ) | 1 ;
- int_32 info = 0 ; These are set by the SCO tools, but we
- don't know enough about the source
- environment to set them. SCO ld currently
- ignores them, and recommends we set them
- to zero. */
-
-#define SCO_MAJOR_VERSION 0x1
-#define SCO_MINOR_VERSION 0x1
-
-void
-sco_id (void)
-{
-
- char *name;
- unsigned int c;
- char ch;
- char *p;
- asection *seg = now_seg;
- subsegT subseg = now_subseg;
- Elf_Internal_Note i_note;
- Elf_External_Note e_note;
- asection *note_secp = NULL;
- int i, len;
-
- /* create the .note section */
-
- note_secp = subseg_new (".note", 0);
- bfd_set_section_flags (stdoutput,
- note_secp,
- SEC_HAS_CONTENTS | SEC_READONLY);
-
- /* process the version string */
-
- i_note.namesz = 4;
- i_note.descsz = 12; /* 12 descriptive bytes */
- i_note.type = NT_VERSION; /* Contains a version string */
-
- p = frag_more (sizeof (i_note.namesz));
- md_number_to_chars (p, i_note.namesz, 4);
-
- p = frag_more (sizeof (i_note.descsz));
- md_number_to_chars (p, i_note.descsz, 4);
-
- p = frag_more (sizeof (i_note.type));
- md_number_to_chars (p, i_note.type, 4);
-
- p = frag_more (4);
- strcpy (p, "SCO");
-
- /* Note: this is the version number of the ELF we're representing */
- p = frag_more (4);
- md_number_to_chars (p, (SCO_MAJOR_VERSION << 16) | (SCO_MINOR_VERSION), 4);
-
- /* Here, we pick a magic number for ourselves (yes, I "registered"
- it with SCO. The bottom bit shows that we are compat with the
- SCO ABI. */
- p = frag_more (4);
- md_number_to_chars (p, 0x4c520000 | 0x0001, 4);
-
- /* If we knew (or cared) what the source language options were, we'd
- fill them in here. SCO has given us permission to ignore these
- and just set them to zero. */
- p = frag_more (4);
- md_number_to_chars (p, 0x0000, 4);
-
- frag_align (2, 0, 0);
-
- /* We probably can't restore the current segment, for there likely
- isn't one yet... */
- if (seg && subseg)
- subseg_set (seg, subseg);
-
-}
-
-#endif /* SCO_ELF */
-
static void
elf_generate_asm_lineno (void)
{
obj_elf_init_stab_section (seg);
}
+/* This is called when the assembler starts. */
+
+void
+elf_begin (void)
+{
+ asection *s;
+
+ /* Add symbols for the known sections to the symbol table. */
+ s = bfd_get_section_by_name (stdoutput, TEXT_SECTION_NAME);
+ symbol_table_insert (section_symbol (s));
+ s = bfd_get_section_by_name (stdoutput, DATA_SECTION_NAME);
+ symbol_table_insert (section_symbol (s));
+ s = bfd_get_section_by_name (stdoutput, BSS_SECTION_NAME);
+ symbol_table_insert (section_symbol (s));
+ elf_com_section_ptr = bfd_com_section_ptr;
+ previous_section = NULL;
+ previous_subsection = 0;
+ comment_section = NULL;
+ memset (&groups, 0, sizeof (groups));
+}
+
+void
+elf_end (void)
+{
+ while (section_stack)
+ {
+ struct section_stack *top = section_stack;
+ section_stack = top->next;
+ free (top);
+ }
+ while (recorded_attributes)
+ {
+ struct recorded_attribute_info *rai = recorded_attributes;
+ recorded_attributes = rai->next;
+ free (rai);
+ }
+ if (groups.indexes)
+ {
+ htab_delete (groups.indexes);
+ free (groups.head);
+ }
+}
+
const struct format_ops elf_format_ops =
{
bfd_target_elf_flavour,
0, /* dfl_leading_underscore */
1, /* emit_section_symbols */
elf_begin,
+ elf_end,
elf_file_symbol,
elf_frob_symbol,
elf_frob_file,
#endif
elf_obj_read_begin_hook,
elf_obj_symbol_new_hook,
- 0,
+ elf_obj_symbol_clone_hook,
elf_adjust_symtab
};