/* POWER/PowerPC XCOFF linker support.
- Copyright (C) 1995-2021 Free Software Foundation, Inc.
+ Copyright (C) 1995-2022 Free Software Foundation, Inc.
Written by Ian Lance Taylor <ian@cygnus.com>, Cygnus Support.
This file is part of BFD, the Binary File Descriptor library.
#undef STRING_SIZE_SIZE
#define STRING_SIZE_SIZE 4
-/* We reuse the SEC_ROM flag as a mark flag for garbage collection.
- This flag will only be used on input sections. */
-
-#define SEC_MARK (SEC_ROM)
-
/* The list of import files. */
struct xcoff_import_file
_bfd_xcoff_bfd_link_hash_table_create (bfd *abfd)
{
struct xcoff_link_hash_table *ret;
+ bool isxcoff64 = false;
size_t amt = sizeof (* ret);
ret = bfd_zmalloc (amt);
return NULL;
}
- ret->debug_strtab = _bfd_xcoff_stringtab_init ();
+ isxcoff64 = bfd_coff_debug_string_prefix_length (abfd) == 4;
+
+ ret->debug_strtab = _bfd_xcoff_stringtab_init (isxcoff64);
ret->archive_info = htab_create (37, xcoff_archive_info_hash,
xcoff_archive_info_eq, NULL);
if (!ret->debug_strtab || !ret->archive_info)
|| h->root.type == bfd_link_hash_undefweak))
return true;
- /* If H is currently undefined, LDSYM defines it. */
+ /* If H is currently undefined, LDSYM defines it.
+ However, if H has a hidden visibility, LDSYM must not
+ define it. */
if ((h->flags & XCOFF_DEF_DYNAMIC) == 0
&& (h->root.type == bfd_link_hash_undefined
- || h->root.type == bfd_link_hash_undefweak))
+ || h->root.type == bfd_link_hash_undefweak)
+ && (h->visibility != SYM_V_HIDDEN
+ && h->visibility != SYM_V_INTERNAL))
return true;
return false;
return min;
}
+/* Return true if the symbol has to be added to the linker hash
+ table. */
+static bool
+xcoff_link_add_symbols_to_hash_table (struct internal_syment sym,
+ union internal_auxent aux)
+{
+ /* External symbols must be added. */
+ if (EXTERN_SYM_P (sym.n_sclass))
+ return true;
+
+ /* Hidden TLS symbols must be added to verify TLS relocations
+ in xcoff_reloc_type_tls. */
+ if (sym.n_sclass == C_HIDEXT
+ && ((aux.x_csect.x_smclas == XMC_TL
+ || aux.x_csect.x_smclas == XMC_UL)))
+ return true;
+
+ return false;
+}
+
/* Add all the symbols from an object file to the hash table.
XCOFF is a weird format. A normal XCOFF .o files will have three
bfd_byte *linenos;
} *reloc_info = NULL;
bfd_size_type amt;
+ unsigned short visibility;
keep_syms = obj_coff_keep_syms (abfd);
}
}
+ /* Record visibility. */
+ visibility = sym.n_type & SYM_V_MASK;
+
/* Pick up the csect auxiliary information. */
if (sym.n_numaux == 0)
{
32 bit has a csect length of 4 for TOC
64 bit has a csect length of 8 for TOC
+ An exception is made for TOC entries with a R_TLSML
+ relocation. This relocation is made for the loader.
+ We must check that the referenced symbol is the TOC entry
+ itself.
+
The conditions to get past the if-check are not that bad.
They are what is used to create the TOC csects in the first
place. */
64 bit R_POS r_size is 63 */
if (relindx < enclosing->reloc_count
&& rel->r_vaddr == (bfd_vma) sym.n_value
- && rel->r_type == R_POS
+ && (rel->r_type == R_POS ||
+ rel->r_type == R_TLSML)
&& ((bfd_xcoff_is_xcoff32 (abfd)
&& rel->r_size == 31)
|| (bfd_xcoff_is_xcoff64 (abfd)
set_toc = h;
}
}
+ else if (rel->r_type == R_TLSML)
+ {
+ csect_index = ((esym
+ - (bfd_byte *) obj_coff_external_syms (abfd))
+ / symesz);
+ if (((unsigned long) rel->r_symndx) != csect_index)
+ {
+ _bfd_error_handler
+ /* xgettext:c-format */
+ (_("%pB: TOC entry `%s' has a R_TLSML"
+ "relocation not targeting itself"),
+ abfd, name);
+ bfd_set_error (bfd_error_bad_value);
+ goto error_return;
+ }
+ }
}
}
if (first_csect == NULL)
first_csect = csect;
- /* If this symbol is external, we treat it as starting at the
- beginning of the newly created section. */
- if (EXTERN_SYM_P (sym.n_sclass))
+ /* If this symbol must be added to the linker hash table,
+ we treat it as starting at the beginning of the newly
+ created section. */
+ if (xcoff_link_add_symbols_to_hash_table (sym, aux))
{
section = csect;
value = 0;
if (first_csect == NULL)
first_csect = csect;
- if (EXTERN_SYM_P (sym.n_sclass))
+ if (xcoff_link_add_symbols_to_hash_table (sym, aux))
{
csect->flags |= SEC_IS_COMMON;
csect->size = 0;
/* Now we have enough information to add the symbol to the
linker hash table. */
- if (EXTERN_SYM_P (sym.n_sclass))
+ if (xcoff_link_add_symbols_to_hash_table (sym, aux))
{
bool copy, ok;
flagword flags;
/* Try not to give this error too many times. */
(*sym_hash)->flags &= ~XCOFF_MULTIPLY_DEFINED;
}
+
+
+ /* If the symbol is hidden or internal, completely undo
+ any dynamic link state. */
+ if ((*sym_hash)->flags & XCOFF_DEF_DYNAMIC
+ && (visibility == SYM_V_HIDDEN
+ || visibility == SYM_V_INTERNAL))
+ (*sym_hash)->flags &= ~XCOFF_DEF_DYNAMIC;
+ else
+ {
+ /* Keep the most constraining visibility. */
+ unsigned short hvis = (*sym_hash)->visibility;
+ if (visibility && ( !hvis || visibility < hvis))
+ (*sym_hash)->visibility = visibility;
+ }
+
}
/* _bfd_generic_link_add_one_symbol may call the linker to
struct internal_syment sym;
bfd_coff_swap_sym_in (abfd, (void *) esym, (void *) &sym);
+ esym += (sym.n_numaux + 1) * symesz;
if (EXTERN_SYM_P (sym.n_sclass) && sym.n_scnum != N_UNDEF)
{
return true;
}
}
-
- esym += (sym.n_numaux + 1) * symesz;
}
/* We do not need this object file. */
if (h->root.root.string[0] == '.')
return false;
+ /* Don't export hidden or internal symbols. */
+ if (h->visibility == SYM_V_HIDDEN
+ || h->visibility == SYM_V_INTERNAL)
+ return false;
+
/* We don't export a symbol which is being defined by an object
included from an archive which contains a shared object. The
rationale is that if an archive contains both an unshared and
static bool
xcoff_need_ldrel_p (struct bfd_link_info *info, struct internal_reloc *rel,
- struct xcoff_link_hash_entry *h)
+ struct xcoff_link_hash_entry *h, asection *ssec)
{
if (!xcoff_hash_table (info)->loader_section)
return false;
&& bfd_is_abs_section (sec->output_section)))
return false;
}
+
+ /* Absolute relocations from read-only sections are forbidden
+ by AIX loader. However, they can appear in their section's
+ relocations. */
+ if (ssec != NULL
+ && (ssec->output_section->flags & SEC_READONLY) != 0)
+ return false;
+
return true;
case R_TLS:
hsec = h->root.u.def.section;
if (! bfd_is_abs_section (hsec)
- && (hsec->flags & SEC_MARK) == 0)
+ && hsec->gc_mark == 0)
{
if (! xcoff_mark (info, hsec))
return false;
}
if (h->toc_section != NULL
- && (h->toc_section->flags & SEC_MARK) == 0)
+ && h->toc_section->gc_mark == 0)
{
if (! xcoff_mark (info, h->toc_section))
return false;
xcoff_mark (struct bfd_link_info *info, asection *sec)
{
if (bfd_is_const_section (sec)
- || (sec->flags & SEC_MARK) != 0)
+ || sec->gc_mark != 0)
+ return true;
+
+ sec->gc_mark = 1;
+
+ if (sec->owner->xvec != info->output_bfd->xvec)
+ return true;
+
+ if (coff_section_data (sec->owner, sec) == NULL)
return true;
- sec->flags |= SEC_MARK;
- if (sec->owner->xvec == info->output_bfd->xvec
- && coff_section_data (sec->owner, sec) != NULL
- && xcoff_section_data (sec->owner, sec) != NULL)
+ if (xcoff_section_data (sec->owner, sec) != NULL)
{
struct xcoff_link_hash_entry **syms;
- struct internal_reloc *rel, *relend;
asection **csects;
unsigned long i, first, last;
if (!xcoff_mark_symbol (info, syms[i]))
return false;
}
+ }
- /* Look through the section relocs. */
- if ((sec->flags & SEC_RELOC) != 0
- && sec->reloc_count > 0)
+ /* Look through the section relocs. */
+ if ((sec->flags & SEC_RELOC) != 0
+ && sec->reloc_count > 0)
+ {
+ struct internal_reloc *rel, *relend;
+
+ rel = xcoff_read_internal_relocs (sec->owner, sec, true,
+ NULL, false, NULL);
+ if (rel == NULL)
+ return false;
+ relend = rel + sec->reloc_count;
+ for (; rel < relend; rel++)
{
- rel = xcoff_read_internal_relocs (sec->owner, sec, true,
- NULL, false, NULL);
- if (rel == NULL)
- return false;
- relend = rel + sec->reloc_count;
- for (; rel < relend; rel++)
- {
- struct xcoff_link_hash_entry *h;
+ struct xcoff_link_hash_entry *h;
- if ((unsigned int) rel->r_symndx
- > obj_raw_syment_count (sec->owner))
- continue;
+ if ((unsigned int) rel->r_symndx
+ > obj_raw_syment_count (sec->owner))
+ continue;
- h = obj_xcoff_sym_hashes (sec->owner)[rel->r_symndx];
- if (h != NULL)
- {
- if ((h->flags & XCOFF_MARK) == 0)
- {
- if (!xcoff_mark_symbol (info, h))
- return false;
- }
- }
- else
+ h = obj_xcoff_sym_hashes (sec->owner)[rel->r_symndx];
+ if (h != NULL)
+ {
+ if ((h->flags & XCOFF_MARK) == 0)
{
- asection *rsec;
-
- rsec = xcoff_data (sec->owner)->csects[rel->r_symndx];
- if (rsec != NULL
- && (rsec->flags & SEC_MARK) == 0)
- {
- if (!xcoff_mark (info, rsec))
- return false;
- }
+ if (!xcoff_mark_symbol (info, h))
+ return false;
}
+ }
+ else
+ {
+ asection *rsec;
- /* See if this reloc needs to be copied into the .loader
- section. */
- if (xcoff_need_ldrel_p (info, rel, h))
+ rsec = xcoff_data (sec->owner)->csects[rel->r_symndx];
+ if (rsec != NULL
+ && rsec->gc_mark == 0)
{
- ++xcoff_hash_table (info)->ldrel_count;
- if (h != NULL)
- h->flags |= XCOFF_LDREL;
+ if (!xcoff_mark (info, rsec))
+ return false;
}
}
- if (! info->keep_memory
- && coff_section_data (sec->owner, sec) != NULL
- && ! coff_section_data (sec->owner, sec)->keep_relocs)
+ /* See if this reloc needs to be copied into the .loader
+ section. */
+ if ((sec->flags & SEC_DEBUGGING) == 0
+ && xcoff_need_ldrel_p (info, rel, h, sec))
{
- free (coff_section_data (sec->owner, sec)->relocs);
- coff_section_data (sec->owner, sec)->relocs = NULL;
+ ++xcoff_hash_table (info)->ldrel_count;
+ if (h != NULL)
+ h->flags |= XCOFF_LDREL;
}
}
+
+ if (! info->keep_memory
+ && coff_section_data (sec->owner, sec) != NULL
+ && ! coff_section_data (sec->owner, sec)->keep_relocs)
+ {
+ free (coff_section_data (sec->owner, sec)->relocs);
+ coff_section_data (sec->owner, sec)->relocs = NULL;
+ }
}
return true;
for (sub = info->input_bfds; sub != NULL; sub = sub->link.next)
{
asection *o;
+ bool some_kept = false;
+ /* As says below keep all sections from non-XCOFF
+ input files. */
+ if (sub->xvec != info->output_bfd->xvec)
+ some_kept = true;
+ else
+ {
+ /* See whether any section is already marked. */
+ for (o = sub->sections; o != NULL; o = o->next)
+ if (o->gc_mark)
+ some_kept = true;
+ }
+
+ /* If no section in this file will be kept, then we can
+ toss out debug sections. */
+ if (!some_kept)
+ {
+ for (o = sub->sections; o != NULL; o = o->next)
+ {
+ o->size = 0;
+ o->reloc_count = 0;
+ }
+ continue;
+ }
+
+ /* Keep all sections from non-XCOFF input files. Keep
+ special sections. Keep .debug sections for the
+ moment. */
for (o = sub->sections; o != NULL; o = o->next)
{
- if ((o->flags & SEC_MARK) == 0)
+ if (o->gc_mark == 1)
+ continue;
+
+ if (sub->xvec != info->output_bfd->xvec
+ || o == xcoff_hash_table (info)->debug_section
+ || o == xcoff_hash_table (info)->loader_section
+ || o == xcoff_hash_table (info)->linkage_section
+ || o == xcoff_hash_table (info)->descriptor_section
+ || (bfd_section_flags (o) & SEC_DEBUGGING)
+ || strcmp (o->name, ".debug") == 0)
+ xcoff_mark (info, o);
+ else
{
- /* Keep all sections from non-XCOFF input files. Keep
- special sections. Keep .debug sections for the
- moment. */
- if (sub->xvec != info->output_bfd->xvec
- || o == xcoff_hash_table (info)->debug_section
- || o == xcoff_hash_table (info)->loader_section
- || o == xcoff_hash_table (info)->linkage_section
- || o == xcoff_hash_table (info)->descriptor_section
- || (bfd_section_flags (o) & SEC_DEBUGGING)
- || strcmp (o->name, ".debug") == 0)
- o->flags |= SEC_MARK;
- else
- {
- o->size = 0;
- o->reloc_count = 0;
- }
+ o->size = 0;
+ o->reloc_count = 0;
}
}
}
if (bfd_get_flavour (output_bfd) != bfd_target_xcoff_flavour)
return true;
+ /* As AIX linker, symbols exported with hidden visibility are
+ silently ignored. */
+ if (h->visibility == SYM_V_HIDDEN)
+ return true;
+
+ if (h->visibility == SYM_V_INTERNAL)
+ {
+ _bfd_error_handler (_("%pB: cannot export internal symbol `%s`."),
+ output_bfd, h->root.root.string);
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
h->flags |= XCOFF_EXPORT;
/* FIXME: I'm not at all sure what syscall is supposed to mean, so
return true;
}
-/* Add a symbol to the .loader symbols, if necessary. */
-
/* INPUT_BFD has an external symbol associated with hash table entry H
and csect CSECT. Return true if INPUT_BFD defines H. */
if (xcoff_hash_table (info)->gc
&& !bfd_is_abs_section (csect)
&& !bfd_is_und_section (csect)
- && (csect->flags & SEC_MARK) == 0)
+ && csect->gc_mark == 0)
return 0;
/* An XCOFF linker always removes C_STAT symbols. */
(a) one of the input files did or (b) we end up
creating TOC references as part of the link process. */
if (o != xcoff_hash_table (info)->toc_section
- && (o->flags & SEC_MARK) == 0)
+ && o->gc_mark == 0)
{
if (! xcoff_mark (info, o))
goto error_return;
if (sec != NULL
&& gc
- && (sec->flags & SEC_MARK) == 0)
+ && sec->gc_mark == 0)
sec = NULL;
special_sections[i] = sec;
}
}
- if (info->strip != strip_all)
+ if (info->strip != strip_all
+ && xcoff_hash_table (info)->debug_section != NULL)
xcoff_hash_table (info)->debug_section->size =
_bfd_stringtab_size (debug_strtab);
- (*csectpp)->vma);
}
+ /* Update visibility. */
+ if (*sym_hash)
+ {
+ isym.n_type &= ~SYM_V_MASK;
+ isym.n_type |= (*sym_hash)->visibility;
+ }
+
/* Output the symbol. */
bfd_coff_swap_sym_out (output_bfd, (void *) &isym, (void *) outsym);
/* This is the file name (or some comment put in by
the compiler). If it is long, we must put it in
the string table. */
- if (aux.x_file.x_n.x_zeroes == 0
- && aux.x_file.x_n.x_offset != 0)
+ if (aux.x_file.x_n.x_n.x_zeroes == 0
+ && aux.x_file.x_n.x_n.x_offset != 0)
{
const char *filename;
bfd_size_type indx;
- BFD_ASSERT (aux.x_file.x_n.x_offset
+ BFD_ASSERT (aux.x_file.x_n.x_n.x_offset
>= STRING_SIZE_SIZE);
if (strings == NULL)
{
if (strings == NULL)
return false;
}
- if ((bfd_size_type) aux.x_file.x_n.x_offset >= obj_coff_strings_len (input_bfd))
+ if ((bfd_size_type) aux.x_file.x_n.x_n.x_offset >= obj_coff_strings_len (input_bfd))
filename = _("<corrupt>");
else
- filename = strings + aux.x_file.x_n.x_offset;
+ filename = strings + aux.x_file.x_n.x_n.x_offset;
indx = _bfd_stringtab_add (flinfo->strtab, filename,
hash, copy);
if (indx == (bfd_size_type) -1)
return false;
- aux.x_file.x_n.x_offset = STRING_SIZE_SIZE + indx;
+ aux.x_file.x_n.x_n.x_offset = STRING_SIZE_SIZE + indx;
}
}
else if (CSECT_SYM_P (isymp->n_sclass)
}
if ((o->flags & SEC_DEBUGGING) == 0
- && xcoff_need_ldrel_p (flinfo->info, irel, h))
+ && xcoff_need_ldrel_p (flinfo->info, irel, h, o))
{
asection *sec;
input_bfd != NULL;
input_bfd = input_bfd->link.next)
for (sec = input_bfd->sections; sec != NULL; sec = sec->next)
- if ((sec->flags & SEC_MARK) != 0 && xcoff_toc_section_p (sec))
+ if (sec->gc_mark != 0 && xcoff_toc_section_p (sec))
{
start = sec->output_section->vma + sec->output_offset;
if (toc_start > start)
input_bfd != NULL;
input_bfd = input_bfd->link.next)
for (sec = input_bfd->sections; sec != NULL; sec = sec->next)
- if ((sec->flags & SEC_MARK) != 0 && xcoff_toc_section_p (sec))
+ if (sec->gc_mark != 0 && xcoff_toc_section_p (sec))
{
start = sec->output_section->vma + sec->output_offset;
if (start < best_address