+ if (sub->xvec != info->output_bfd->xvec)
+ continue;
+
+ if ((sub->flags & DYNAMIC) != 0
+ && !info->static_link)
+ continue;
+
+ if (! _bfd_coff_get_external_symbols (sub))
+ goto error_return;
+
+ symcount = obj_raw_syment_count (sub);
+ debug_index = bfd_zalloc (sub, symcount * sizeof (long));
+ if (debug_index == NULL)
+ goto error_return;
+ xcoff_data (sub)->debug_indices = debug_index;
+
+ if (info->strip == strip_all
+ || info->strip == strip_debugger
+ || info->discard == discard_all)
+ /* We're stripping all debugging information, so there's no need
+ to read SUB's .debug section. */
+ subdeb = NULL;
+ else
+ {
+ /* Grab the contents of SUB's .debug section, if any. */
+ subdeb = bfd_get_section_by_name (sub, ".debug");
+ if (subdeb != NULL && subdeb->size > 0)
+ {
+ /* We use malloc and copy the names into the debug
+ stringtab, rather than bfd_alloc, because I expect
+ that, when linking many files together, many of the
+ strings will be the same. Storing the strings in the
+ hash table should save space in this case. */
+ if (!bfd_malloc_and_get_section (sub, subdeb, &debug_contents))
+ goto error_return;
+ }
+ }
+
+ csectpp = xcoff_data (sub)->csects;
+ lineno_counts = xcoff_data (sub)->lineno_counts;
+ sym_hash = obj_xcoff_sym_hashes (sub);
+ symesz = bfd_coff_symesz (sub);
+ esym = (bfd_byte *) obj_coff_external_syms (sub);
+ esymend = esym + symcount * symesz;
+
+ while (esym < esymend)
+ {
+ struct internal_syment sym;
+ union internal_auxent aux;
+ asection *csect;
+ const char *name;
+ int keep_p;
+
+ bfd_coff_swap_sym_in (sub, esym, &sym);
+
+ /* Read in the csect information, if any. */
+ if (CSECT_SYM_P (sym.n_sclass))
+ {
+ BFD_ASSERT (sym.n_numaux > 0);
+ bfd_coff_swap_aux_in (sub, esym + symesz * sym.n_numaux,
+ sym.n_type, sym.n_sclass,
+ sym.n_numaux - 1, sym.n_numaux, &aux);
+ }
+
+ /* If this symbol's name is stored in the debug section,
+ get a pointer to it. */
+ if (debug_contents != NULL
+ && sym._n._n_n._n_zeroes == 0
+ && bfd_coff_symname_in_debug (sub, &sym))
+ name = (const char *) debug_contents + sym._n._n_n._n_offset;
+ else
+ name = NULL;
+
+ /* Decide whether to copy this symbol to the output file. */
+ csect = *csectpp;
+ keep_p = xcoff_keep_symbol_p (info, sub, &sym, &aux,
+ *sym_hash, csect, name);
+ if (keep_p < 0)
+ goto error_return;
+
+ if (!keep_p)
+ /* Use a debug_index of -2 to record that a symbol should
+ be stripped. */
+ *debug_index = -2;
+ else
+ {
+ /* See whether we should store the symbol name in the
+ output .debug section. */
+ if (name != NULL)
+ {
+ bfd_size_type indx;
+
+ indx = _bfd_stringtab_add (debug_strtab, name, true, true);
+ if (indx == (bfd_size_type) -1)
+ goto error_return;
+ *debug_index = indx;
+ }
+ else
+ *debug_index = -1;
+ if (*sym_hash != 0)
+ (*sym_hash)->flags |= XCOFF_ALLOCATED;
+ if (*lineno_counts > 0)
+ csect->output_section->lineno_count += *lineno_counts;
+ }
+
+ esym += (sym.n_numaux + 1) * symesz;
+ csectpp += sym.n_numaux + 1;
+ sym_hash += sym.n_numaux + 1;
+ lineno_counts += sym.n_numaux + 1;
+ debug_index += sym.n_numaux + 1;
+ }
+
+ if (debug_contents)
+ {
+ free (debug_contents);
+ debug_contents = NULL;
+
+ /* Clear the size of subdeb, so that it is not included directly
+ in the output file. */
+ subdeb->size = 0;
+ }
+
+ if (! info->keep_memory)
+ {
+ if (! _bfd_coff_free_symbols (sub))
+ goto error_return;
+ }
+ }
+
+ if (info->strip != strip_all
+ && xcoff_hash_table (info)->debug_section != NULL)
+ xcoff_hash_table (info)->debug_section->size =
+ _bfd_stringtab_size (debug_strtab);
+
+ return true;
+
+ error_return:
+ free (debug_contents);
+ return false;
+}
+
+bool
+bfd_xcoff_link_generate_rtinit (bfd *abfd,
+ const char *init,
+ const char *fini,
+ bool rtld)
+{
+ struct bfd_in_memory *bim;
+
+ bim = bfd_malloc ((bfd_size_type) sizeof (* bim));
+ if (bim == NULL)
+ return false;
+
+ bim->size = 0;
+ bim->buffer = 0;
+
+ abfd->link.next = 0;
+ abfd->format = bfd_object;
+ abfd->iostream = (void *) bim;
+ abfd->flags = BFD_IN_MEMORY;
+ abfd->iovec = &_bfd_memory_iovec;
+ abfd->direction = write_direction;
+ abfd->origin = 0;
+ abfd->where = 0;
+
+ if (! bfd_xcoff_generate_rtinit (abfd, init, fini, rtld))
+ return false;
+
+ /* need to reset to unknown or it will not be read back in correctly */
+ abfd->format = bfd_unknown;
+ abfd->direction = read_direction;
+ abfd->where = 0;
+
+ return true;
+}
+\f
+
+/* Linker stubs.
+ The stubs will be gathered in stub csects named "@FIX'number'".
+ A new csect will be created by xcoff_stub_get_csect_in_range,
+ everytime a relocation cannot reach its target and its section
+ is too far from the others stub csects.
+ The stubs will simply be code generated inside these stub
+ csects. In order to simplify the symbol table, only the symbols
+ for the stub csects are written.
+
+ As the code is dependent of the architecture, it's defined
+ in the backend.
+
+ xcoff_stub_indirect_call:
+ Used when a 24 bit branch cannot reach its destination and that
+ this destination isn't a global linkage symbol.
+
+ xcoff_stub_shared_call:
+ As above but when it's a global linkage symbol.
+ The main difference being that it doesn't branch to the global
+ linkage symbol which will then call the shared library. It
+ directly call it saving the TOC.
+
+ TODO: -bbigtoc option should be able to be implemented using
+ this stubs. */
+
+/* Get the name of a csect which will contain stubs.
+ It has the same pattern as AIX linker: @FIX"number". */
+static char *
+xcoff_stub_csect_name (unsigned int n)
+{
+ char buf[8];
+ size_t len;
+ char *csect_name;
+
+ /* For now, allow "only" 1000000 stub csects. */
+ if (n >= 1000000)
+ {
+ BFD_FAIL();
+ return NULL;
+ }
+
+ sprintf (buf, "%d", n);
+ len = 4 + strlen (buf) + 1;
+
+ csect_name = bfd_malloc (len);
+ if (csect_name == NULL)
+ return NULL;
+ sprintf (csect_name, "@FIX%d", n);
+
+ return csect_name;
+}
+
+/* Return a stub section which can be reach with a single branch
+ from SECTION. CREATE means that creating a csect is allowed. */
+static struct xcoff_link_hash_entry *
+xcoff_stub_get_csect_in_range (asection *section,
+ struct bfd_link_info *info,
+ bool create)
+{
+ struct xcoff_link_hash_table *htab = xcoff_hash_table (info);
+ struct xcoff_link_hash_entry *csect_entry;
+ struct bfd_link_hash_entry *bh = NULL;
+ asection *csect;
+ unsigned int it;
+ char *csect_name;
+
+ /* Search for a csect in range. */
+ for (csect = htab->params->stub_bfd->sections, it = 0;
+ csect != NULL;
+ csect = csect->next, it++)
+ {
+ /* A csect is in range if everything instructions in SECTION
+ can branch to every stubs in the stub csect. This can
+ be simplify by saying that the first entry of each sections
+ (ie the vma of this section) can reach the last entry of the
+ stub csect (ie the vma of the csect + its size).
+ However, as the stub csect might be growing its size isn't
+ fixed. Thus, the last entry of SECTION might not be able
+ to reach the first entry of the stub csect anymore.
+ If this case happens, the following condition will be
+ false during the next pass of bfd_xcoff_size_stubs and
+ another csect will be used.
+ This means we might create more stubs than needed. */
+ bfd_vma csect_vma, section_vma;
+ bfd_vma csect_last_vma, section_last_vma;
+
+ csect_vma = (csect->output_section->vma
+ + csect->output_offset);
+ csect_last_vma = (csect->output_section->vma
+ + csect->output_offset
+ + csect->size);
+ section_vma = (section->output_section->vma
+ + section->output_offset);
+ section_last_vma = (section->output_section->vma
+ + section->output_offset
+ + section->size);
+
+ if (csect_last_vma - section_vma + (1 << 25) < 2 * (1 << 25)
+ && section_last_vma - csect_vma + (1 << 25) < 2 * (1 << 25))
+ break;
+ }
+
+ if (!create && csect == NULL)
+ return NULL;
+
+ csect_name = xcoff_stub_csect_name (it);
+ if (!csect_name)
+ return NULL;
+
+ /* A stub csect already exists, get its entry. */
+ if (csect != NULL)
+ {
+ csect_entry = xcoff_link_hash_lookup (htab, csect_name, false, false, true);
+ free(csect_name);
+ return csect_entry;
+ }
+
+ /* Create the csect and its symbol. */
+ csect = (*htab->params->add_stub_section) (".pr", section);
+ if (!csect)
+ {
+ free(csect_name);
+ return NULL;
+ }
+
+ csect->alignment_power = 2;
+ csect->gc_mark = 1;
+ csect->reloc_count = 0;
+
+ /* We need to associate a VMA to this new csect. Otherwise,
+ our "in range" algorithm won't find it for the next stub.
+ And as we will be adding this stub section just after the
+ SECTION, we know its address. */
+ csect->output_offset = BFD_ALIGN (section->output_offset + section->size,
+ 4);
+
+ if (!_bfd_generic_link_add_one_symbol (info, htab->params->stub_bfd,
+ csect_name, BSF_GLOBAL, csect, 0,
+ NULL, true, true, &bh))
+ {
+ free(csect_name);
+ return NULL;
+ }
+
+ csect_entry = (struct xcoff_link_hash_entry *)bh;
+ csect_entry->smclas = XMC_PR;
+ csect_entry->flags = XCOFF_MARK | XCOFF_DEF_REGULAR;
+
+ free(csect_name);
+ return csect_entry;
+}
+
+
+/* Build a name for an entry in the stub hash table. */
+static char *
+xcoff_stub_name (const struct xcoff_link_hash_entry *h,
+ const struct xcoff_link_hash_entry *hcsect)
+{
+ char *stub_name;
+ size_t len;
+
+ if (h)
+ {
+ /* The name of a stub is based on its stub csect and the
+ symbol it wants to reach. It looks like: ".@FIX0.tramp.f".
+ When the stub targets a function, the last dot of ".tramp."
+ is removed to avoid having two dot. */
+ len = (1 + 6
+ + strlen (hcsect->root.root.string)
+ + strlen (h->root.root.string)
+ + 1);
+ if (h->root.root.string[0] != '.')
+ len++;
+
+ stub_name = bfd_malloc (len);
+ if (stub_name == NULL)
+ return stub_name;
+
+ if (h->root.root.string[0] == '.')
+ sprintf (stub_name, ".%s.tramp%s",
+ hcsect->root.root.string,
+ h->root.root.string);
+ else
+ sprintf (stub_name, ".%s.tramp.%s",
+ hcsect->root.root.string,
+ h->root.root.string);
+ }
+ else
+ {
+ BFD_FAIL();
+ return NULL;
+ }
+
+ return stub_name;
+}
+
+/* Look up an entry in the stub hash. */
+struct xcoff_stub_hash_entry *
+bfd_xcoff_get_stub_entry (asection *section,
+ struct xcoff_link_hash_entry *h,
+ struct bfd_link_info *info)
+{
+ struct xcoff_link_hash_table *htab = xcoff_hash_table (info);
+ struct xcoff_link_hash_entry *hcsect;
+ struct xcoff_stub_hash_entry *hstub;
+ char *stub_name;
+
+ hcsect = xcoff_stub_get_csect_in_range (section, info, false);
+ if (!hcsect)
+ return NULL;
+
+ stub_name = xcoff_stub_name (h, hcsect);
+ if (stub_name == NULL)
+ return NULL;
+
+ hstub = xcoff_stub_hash_lookup (&htab->stub_hash_table,
+ stub_name, false, false);
+
+ free (stub_name);
+ return hstub;
+}
+
+/* Check if the symbol targeted by IREL is reachable.
+ Return the type of stub needed otherwise. */
+enum xcoff_stub_type
+bfd_xcoff_type_of_stub (asection *sec,
+ const struct internal_reloc *irel,
+ bfd_vma destination,
+ struct xcoff_link_hash_entry *h)
+{
+ bfd_vma location, offset, max_offset;
+
+ switch (irel->r_type)
+ {
+ default:
+ return xcoff_stub_none;
+
+ case R_BR:
+ case R_RBR:
+ location = (sec->output_section->vma
+ + sec->output_offset
+ + irel->r_vaddr
+ - sec->vma);
+
+ max_offset = 1 << 25 ;
+
+ offset = destination - location;
+
+ if (offset + max_offset < 2 * max_offset)
+ return xcoff_stub_none;
+
+ /* A stub is needed. Now, check that we can make one. */
+ if (h != NULL
+ && h->descriptor != NULL)
+ {
+ /* Not sure how to handle this case. For now, skip it. */
+ if (bfd_is_abs_section (h->root.u.def.section))
+ return xcoff_stub_none;
+
+ if (h->smclas == XMC_GL)
+ return xcoff_stub_shared_call;
+ else
+ return xcoff_stub_indirect_call;
+ }
+ break;
+ }
+
+ return xcoff_stub_none;
+}
+
+/* Add a new stub entry to the stub hash. Not all fields of the new
+ stub entry are initialised. */
+static struct xcoff_stub_hash_entry *
+xcoff_add_stub (const char *stub_name,
+ struct xcoff_link_hash_entry *hstub_csect,
+ struct xcoff_link_hash_entry *htarget,
+ struct bfd_link_info *info,
+ enum xcoff_stub_type stub_type)
+{
+ struct xcoff_link_hash_table *htab = xcoff_hash_table (info);
+ struct xcoff_stub_hash_entry *hstub;
+ bfd_vma stub_offset;
+ asection *stub_csect;
+
+ stub_csect = hstub_csect->root.u.def.section;
+ stub_offset = stub_csect->size;
+
+ /* Update the relocation counter and the size of
+ the containing csect. The size is needed for
+ the algorithm in xcoff_stub_get_csect_in_range. */
+ switch (stub_type)
+ {
+ default:
+ BFD_FAIL ();
+ return NULL;
+
+ case xcoff_stub_indirect_call:
+ stub_csect->reloc_count++;
+ stub_csect->size += bfd_xcoff_stub_indirect_call_size (info->output_bfd);
+ break;
+
+ case xcoff_stub_shared_call:
+ stub_csect->reloc_count++;
+ stub_csect->size += bfd_xcoff_stub_shared_call_size (info->output_bfd);
+ break;
+ }
+
+ /* Create the stub entry. */
+ hstub = xcoff_stub_hash_lookup (&htab->stub_hash_table, stub_name,
+ true, true);
+ if (hstub == NULL)
+ return NULL;
+
+ hstub->htarget = htarget;
+ hstub->stub_offset = stub_offset;
+
+ /* For indirect call or shared call, the relocations are against
+ the target descriptor. Its toc entry will be used. */
+ if (stub_type == xcoff_stub_indirect_call
+ || stub_type == xcoff_stub_shared_call)
+ {
+ struct xcoff_link_hash_entry *hds = htarget->descriptor;
+ asection *hds_section = hds->root.u.def.section;
+
+ hstub->htarget = hds;
+
+ /* If the symbol haven't been marked, its section might have
+ its size and its relocation count been deleted by xcoff_sweep.
+ Restore it. */
+ if ((hds->flags & XCOFF_MARK) == 0)
+ {
+ if (hds_section->size == 0
+ && hds_section->reloc_count == 0
+ && hds_section->rawsize != 0)
+ {
+ hds_section->size = hds_section->rawsize;
+ /* Always two relocations for a XMC_DS symbol. */
+ hds_section->reloc_count = 2;
+ }
+
+ /* Mark the section and the symbol. */
+ if (!xcoff_mark (info, hds_section))
+ return NULL;
+ }
+
+ /* Add a TOC entry for the descriptor if non exists. */
+ if (hds->toc_section == NULL)
+ {
+ int byte_size;
+
+ if (bfd_xcoff_is_xcoff64 (info->output_bfd))
+ byte_size = 8;
+ else if (bfd_xcoff_is_xcoff32 (info->output_bfd))
+ byte_size = 4;
+ else
+ return NULL;
+
+ /* Allocate room in the fallback TOC section. */
+ hds->toc_section = xcoff_hash_table (info)->toc_section;
+ hds->u.toc_offset = hds->toc_section->size;
+ hds->toc_section->size += byte_size;
+ if (!xcoff_mark (info, hds->toc_section))
+ return NULL;
+
+ /* Update relocation counters for a static and dynamic
+ R_TOC relocation. */
+ ++hds->toc_section->reloc_count;
+ ++htab->ldinfo.ldrel_count;
+
+ /* Set the index to -2 to force this symbol to
+ get written out. */
+ hds->indx = -2;
+ hds->flags |= XCOFF_SET_TOC;
+ }
+ }
+
+ return hstub;
+}
+
+static bool
+xcoff_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
+{
+ struct xcoff_stub_hash_entry *hstub
+ = (struct xcoff_stub_hash_entry *) gen_entry;
+
+ bfd *stub_bfd;
+ bfd *output_bfd;
+ struct bfd_link_info *info;
+ bfd_byte *loc;
+ bfd_byte *p;
+ unsigned int i;
+
+ info = (struct bfd_link_info *) in_arg;
+ stub_bfd = xcoff_hash_table (info)->params->stub_bfd;
+ output_bfd = info->output_bfd;
+
+ /* Fail if the target section could not be assigned to an output
+ section. The user should fix his linker script. */
+ if (hstub->target_section != NULL
+ && hstub->target_section->output_section == NULL
+ && info->non_contiguous_regions)
+ info->callbacks->einfo (_("%F%P: Could not assign '%pA' to an output section. "
+ "Retry without --enable-non-contiguous-regions.\n"),
+ hstub->target_section);
+
+ loc = (hstub->hcsect->root.u.def.section->contents
+ + hstub->stub_offset);
+ p = loc;
+
+ switch (hstub->stub_type)
+ {
+ case xcoff_stub_indirect_call:
+ BFD_ASSERT (hstub->htarget->toc_section != NULL);
+ /* The first instruction in the stub code needs to be
+ cooked to hold the correct offset in the toc. It will
+ be filled by xcoff_stub_create_relocations. */
+ for (i = 0; i < bfd_xcoff_stub_indirect_call_size(output_bfd) / 4; i++)
+ bfd_put_32 (stub_bfd,
+ (bfd_vma) bfd_xcoff_stub_indirect_call_code(output_bfd, i),
+ &p[4 * i]);
+ break;
+
+ case xcoff_stub_shared_call:
+ BFD_ASSERT (hstub->htarget->toc_section != NULL);
+ /* The first instruction in the glink code needs to be
+ cooked to hold the correct offset in the toc. It will
+ be filled by xcoff_stub_create_relocations. */
+ for (i = 0; i < bfd_xcoff_stub_shared_call_size(output_bfd) / 4; i++)
+ bfd_put_32 (stub_bfd,
+ (bfd_vma) bfd_xcoff_stub_shared_call_code(output_bfd, i),
+ &p[4 * i]);
+
+ break;
+
+ default:
+ BFD_FAIL ();
+ return false;
+ }
+ return true;
+}
+
+/* Check relocations and adds stubs if needed. */
+
+bool
+bfd_xcoff_size_stubs (struct bfd_link_info *info)
+{
+ struct xcoff_link_hash_table *htab = xcoff_hash_table (info);
+ struct xcoff_loader_info *ldinfo = &(htab->ldinfo);
+
+ while (1)
+ {
+ bfd *input_bfd;
+ bool stub_changed = false;
+
+ for (input_bfd = info->input_bfds;
+ input_bfd != NULL;
+ input_bfd = input_bfd->link.next)
+ {
+ asection *section;
+ bfd_size_type symcount;
+ bfd_size_type symesz;
+ bfd_byte *esyms;
+
+ if (bfd_get_flavour (input_bfd) != bfd_target_xcoff_flavour)
+ continue;
+
+ symcount = obj_raw_syment_count (input_bfd);
+ if (!symcount)
+ continue;
+ symesz = bfd_coff_symesz (input_bfd);
+ esyms = (bfd_byte *) obj_coff_external_syms (input_bfd);
+
+ /* Walk over each section attached to the input bfd. */
+ for (section = input_bfd->sections;
+ section != NULL;
+ section = section->next)
+ {
+ struct internal_reloc *internal_relocs;
+ struct internal_reloc *irel, *irelend;
+
+ /* If there aren't any relocs, then there's nothing more
+ to do. */
+ if ((section->flags & SEC_RELOC) == 0
+ || section->reloc_count == 0)
+ continue;
+
+ /* If this section is a link-once section that will be
+ discarded, then don't create any stubs. */
+ if (section->output_section == NULL
+ || section->output_section->owner != info->output_bfd)
+ continue;
+
+ /* This section have been garbage-collected. */
+ if (section->gc_mark == 0)
+ continue;
+
+ /* Read in the relocs. */
+ internal_relocs = (xcoff_read_internal_relocs
+ (input_bfd, section, true, NULL,
+ false, NULL));
+ if (internal_relocs == NULL)
+ goto error_ret;
+
+ irel = internal_relocs;
+ irelend = irel + section->reloc_count;
+ for (; irel < irelend; irel++)
+ {
+ enum xcoff_stub_type stub_type;
+ struct xcoff_link_hash_entry *hsym = NULL;
+ struct xcoff_link_hash_entry *hstub_csect = NULL;
+ struct xcoff_stub_hash_entry *hstub = NULL;
+ asection *sym_sec;
+ bfd_vma sym_value;
+ bfd_vma destination;
+ char *stub_name;
+
+ if (irel->r_symndx == -1)
+ continue;
+
+ switch (irel->r_type)
+ {
+ default:
+ continue;
+
+ case R_BR:
+ case R_RBR:
+ break;
+ }
+
+ /* Retrieve targeted symbol address */
+ hsym = obj_xcoff_sym_hashes (input_bfd)[irel->r_symndx];
+ if (hsym == NULL)
+ {
+ struct internal_syment sym;
+ if ((long unsigned int)irel->r_symndx > symcount)
+ {
+ BFD_FAIL();
+ goto error_ret;
+ }
+
+ bfd_coff_swap_sym_in (input_bfd,
+ (void *) esyms + irel->r_symndx * symesz,
+ (void *) &sym);