/* ELF object file format
- Copyright (C) 1992-2020 Free Software Foundation, Inc.
+ Copyright (C) 1992-2023 Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
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},
/* 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", dwarf2_directive_file, 0 },
{ "loc", dwarf2_directive_loc, 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},
#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)
{
asymbol *bsym;
+ symbolS *sym = symbol_new (s, absolute_section, &zero_address_frag, 0);
+ size_t name_length = strlen (s);
- if (!appfile
- || symbol_rootP == NULL
- || (bsym = symbol_get_bfdsym (symbol_rootP)) == NULL
- || (bsym->flags & BSF_FILE) == 0)
+ if (name_length > strlen (S_GET_NAME (sym)))
{
- symbolS *sym;
- size_t name_length;
-
- sym = symbol_new (s, absolute_section, 0, NULL);
- symbol_set_frag (sym, &zero_address_frag);
-
- 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);
+ 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
- && ((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);
- }
+ 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)
{
struct elf_section_match *match = (struct elf_section_match *) inf;
const char *gname = match->group_name;
const char *group_name = elf_group_name (sec);
- unsigned int info = elf_section_data (sec)->this_hdr.sh_info;
-
- return (info == match->info
+ 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)));
+ && 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
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,
+ 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)
{
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))
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->info;
+ elf_section_data (sec)->this_hdr.sh_info = match_p->sh_info;
/* Prevent SEC_HAS_CONTENTS from being inadvertently set. */
if (type == SHT_NOBITS)
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;
/* 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, bfd_vma *gnu_attr)
+ 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 '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;
- }
- /* Fall through. */
default:
{
const char *bad_msg = _("unrecognized .section attribute:"
- " want a,e,w,x,M,S,G,T or number");
+ " 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. */
const char *
return NULL;
}
- name = xmemdup0 (input_line_pointer, end - input_line_pointer);
+ 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 = XNEWVEC (char, 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)
{
int linkonce;
subsegT new_subsection = -1;
struct elf_section_match match;
+ unsigned long linked_to_section_index = -1UL;
if (flag_mri)
{
md_flush_pending_output ();
#endif
- previous_section = now_seg;
- previous_subsection = now_subseg;
+ obj_elf_section_change_hook ();
s_mri_sect (&mri_type);
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;
}
- type = obj_elf_section_type (beg, strlen (beg), TRUE);
+ type = obj_elf_section_type (beg, strlen (beg), true);
}
else if (c == '@' || c == '%')
{
(void) restore_line_pointer (c);
type = obj_elf_section_type (beg,
input_line_pointer - beg,
- TRUE);
+ 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;
{
++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)
{
- match.group_name = xstrdup (now_group);
+ match.group_name = now_group;
linkonce = (now_seg->flags & SEC_LINK_ONCE) != 0;
}
}
if (ISDIGIT (* input_line_pointer))
{
char *t = input_line_pointer;
- match.info = strtoul (input_line_pointer,
+ match.sh_info = strtoul (input_line_pointer,
&input_line_pointer, 0);
- if (match.info == (unsigned int) -1)
+ if (match.sh_info == (unsigned int) -1)
{
as_warn (_("unsupported mbind section info: %s"), t);
- match.info = 0;
+ 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 (strncmp (input_line_pointer, "unique", 6) == 0)
+ if (startswith (input_line_pointer, "unique"))
{
input_line_pointer += 6;
SKIP_WHITESPACE ();
if (ISDIGIT (* input_line_pointer))
{
bfd_vma id;
- bfd_boolean overflow;
+ bool overflow;
char *t = input_line_pointer;
if (sizeof (bfd_vma) <= sizeof (unsigned long))
{
input_line_pointer = save;
}
}
+#ifdef TC_SPARC
else
{
do
while (*input_line_pointer++ == ',');
--input_line_pointer;
}
+#endif
}
-done:
+ done:
demand_empty_rest_of_line ();
- obj_elf_change_section (name, type, attr, entsize, &match, linkonce,
- push);
-
- if ((gnu_attr & SHF_GNU_MBIND) != 0)
+ if ((gnu_attr & (SHF_GNU_MBIND | SHF_GNU_RETAIN)) != 0)
{
- struct elf_backend_data *bed;
+ const struct elf_backend_data *bed;
+ bool mbind_p = (gnu_attr & SHF_GNU_MBIND) != 0;
- if ((attr & SHF_ALLOC) == 0)
+ if (mbind_p && (attr & SHF_ALLOC) == 0)
as_bad (_("SHF_ALLOC isn't set for GNU_MBIND section: %s"), name);
- bed = (struct elf_backend_data *) get_elf_backend_data (stdoutput);
- if (bed->elf_osabi == ELFOSABI_NONE)
- bed->elf_osabi = ELFOSABI_GNU;
- else if (bed->elf_osabi != ELFOSABI_GNU
- && bed->elf_osabi != ELFOSABI_FREEBSD)
- as_bad (_("GNU_MBIND section is supported only by GNU "
- "and FreeBSD targets"));
- elf_tdata (stdoutput)->has_gnu_osabi |= elf_gnu_osabi_mbind;
+ 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 ? */
}
- elf_section_flags (now_seg) |= gnu_attr;
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 (S_IS_COMMON (sym))
{
as_bad (_("`%s' can't be versioned to common symbol '%s'"),
- name, S_GET_NAME (sym));
+ name, sym_name);
ignore_rest_of_line ();
return;
}
- if (symbol_get_obj (sym)->versioned_name == NULL)
+ p = strchr (name, ELF_VER_CHR);
+ if (p == NULL)
{
- symbol_get_obj (sym)->versioned_name = xstrdup (name);
+ as_bad (_("missing version name in `%s' for symbol `%s'"),
+ name, sym_name);
+ ignore_rest_of_line ();
+ return;
+ }
- (void) restore_line_pointer (c);
+ 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;
+ }
- if (strchr (symbol_get_obj (sym)->versioned_name,
- ELF_VER_CHR) == NULL)
+ (void) restore_line_pointer (c);
+
+ if (*input_line_pointer == ',')
+ {
+ char *save = input_line_pointer;
+
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ if (startswith (input_line_pointer, "local"))
{
- 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;
+ input_line_pointer += 5;
+ sy_obj->visibility = visibility_local;
}
- }
- else
- {
- if (strcmp (symbol_get_obj (sym)->versioned_name, name))
+ else if (startswith (input_line_pointer, "hidden"))
{
- 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 += 6;
+ sy_obj->visibility = visibility_hidden;
}
-
- (void) restore_line_pointer (c);
+ 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 ();
/* 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 = xstrndup (s, i);
+ name = xmemdup0 (s, i);
#ifndef CONVERT_SYMBOLIC_ATTRIBUTE
#define CONVERT_SYMBOLIC_ATTRIBUTE(a) -1
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 = XNEW (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))));
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
{
|| strcmp (type_name, "10") == 0
|| strcmp (type_name, "STT_GNU_IFUNC") == 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_NONE)
- bed->elf_osabi = ELFOSABI_GNU;
- else if (bed->elf_osabi != ELFOSABI_GNU
- && bed->elf_osabi != ELFOSABI_FREEBSD)
+ bed = get_elf_backend_data (stdoutput);
+ 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_NONE)
- bed->elf_osabi = ELFOSABI_GNU;
- else if (bed->elf_osabi != ELFOSABI_GNU)
+ 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;
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;
void
obj_elf_init_stab_section (segT seg)
{
- const char *file;
+ char *file;
char *p;
char *stabstr_name;
unsigned int stroff;
p = frag_more (12);
/* Zero it out. */
memset (p, 0, 12);
- file = as_where (NULL);
+ 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);
+ 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;
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)
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)
- {
- 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
+ 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)
{
- 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. */
- if (S_IS_COMMON (symp))
- {
- as_bad (_("`%s' can't be versioned to common symbol '%s'"),
- sy_obj->versioned_name, S_GET_NAME (symp));
- *puntp = TRUE;
- return;
- }
+ 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;
+ }
}
}
}
}
+/* 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 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];
/* Add index to hash. */
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. */
/* Go find section groups. */
groups.num_group = 0;
groups.head = NULL;
- groups.indexes = hash_new ();
- bfd_map_over_sections (stdoutput, build_group_lists, &groups);
+ 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
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);
+ /* Mark the group signature symbol as used so that it will be
+ included in the symbol table. */
+ symbol_mark_used_in_reloc (sy);
}
}
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 there was .weak foo, but foo was neither defined nor
- used anywhere, remove it. */
+ if (sy_obj->versioned_name)
+ {
+ char *p = strchr (sy_obj->versioned_name->name,
+ ELF_VER_CHR);
- 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 (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);
+ }
+
+ 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);
+ }
}
}
frag_wane (frag_now);
}
- /* Cleanup hash. */
- hash_traverse (groups.indexes, free_section_idx);
- hash_die (groups.indexes);
-
#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 ()));
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
};