+/* The arguments to place_section. */
+
+struct place_section_arg
+{
+ struct section_offsets *offsets;
+ CORE_ADDR lowest;
+};
+
+/* Find a unique offset to use for loadable section SECT if
+ the user did not provide an offset. */
+
+static void
+place_section (bfd *abfd, asection *sect, void *obj)
+{
+ struct place_section_arg *arg = obj;
+ CORE_ADDR *offsets = arg->offsets->offsets, start_addr;
+ int done;
+ ULONGEST align = ((ULONGEST) 1) << bfd_get_section_alignment (abfd, sect);
+
+ /* We are only interested in allocated sections. */
+ if ((bfd_get_section_flags (abfd, sect) & SEC_ALLOC) == 0)
+ return;
+
+ /* If the user specified an offset, honor it. */
+ if (offsets[sect->index] != 0)
+ return;
+
+ /* Otherwise, let's try to find a place for the section. */
+ start_addr = (arg->lowest + align - 1) & -align;
+
+ do {
+ asection *cur_sec;
+
+ done = 1;
+
+ for (cur_sec = abfd->sections; cur_sec != NULL; cur_sec = cur_sec->next)
+ {
+ int indx = cur_sec->index;
+
+ /* We don't need to compare against ourself. */
+ if (cur_sec == sect)
+ continue;
+
+ /* We can only conflict with allocated sections. */
+ if ((bfd_get_section_flags (abfd, cur_sec) & SEC_ALLOC) == 0)
+ continue;
+
+ /* If the section offset is 0, either the section has not been placed
+ yet, or it was the lowest section placed (in which case LOWEST
+ will be past its end). */
+ if (offsets[indx] == 0)
+ continue;
+
+ /* If this section would overlap us, then we must move up. */
+ if (start_addr + bfd_get_section_size (sect) > offsets[indx]
+ && start_addr < offsets[indx] + bfd_get_section_size (cur_sec))
+ {
+ start_addr = offsets[indx] + bfd_get_section_size (cur_sec);
+ start_addr = (start_addr + align - 1) & -align;
+ done = 0;
+ break;
+ }
+
+ /* Otherwise, we appear to be OK. So far. */
+ }
+ }
+ while (!done);
+
+ offsets[sect->index] = start_addr;
+ arg->lowest = start_addr + bfd_get_section_size (sect);
+}
+
+/* Store struct section_addr_info as prepared (made relative and with SECTINDEX
+ filled-in) by addr_info_make_relative into SECTION_OFFSETS of NUM_SECTIONS
+ entries. */
+
+void
+relative_addr_info_to_section_offsets (struct section_offsets *section_offsets,
+ int num_sections,
+ struct section_addr_info *addrs)
+{
+ int i;
+
+ memset (section_offsets, 0, SIZEOF_N_SECTION_OFFSETS (num_sections));
+
+ /* Now calculate offsets for section that were specified by the caller. */
+ for (i = 0; i < addrs->num_sections && addrs->other[i].name; i++)
+ {
+ struct other_sections *osp;
+
+ osp = &addrs->other[i];
+ if (osp->addr == 0)
+ continue;
+
+ /* Record all sections in offsets */
+ /* The section_offsets in the objfile are here filled in using
+ the BFD index. */
+ section_offsets->offsets[osp->sectindex] = osp->addr;
+ }
+}
+
+/* qsort comparator for addrs_section_sort. Sort entries in ascending order by
+ their (name, sectindex) pair. sectindex makes the sort by name stable. */
+
+static int
+addrs_section_compar (const void *ap, const void *bp)
+{
+ const struct other_sections *a = *((struct other_sections **) ap);
+ const struct other_sections *b = *((struct other_sections **) bp);
+ int retval, a_idx, b_idx;
+
+ retval = strcmp (a->name, b->name);
+ if (retval)
+ return retval;
+
+ /* SECTINDEX is undefined iff ADDR is zero. */
+ a_idx = a->addr == 0 ? 0 : a->sectindex;
+ b_idx = b->addr == 0 ? 0 : b->sectindex;
+ return a_idx - b_idx;
+}
+
+/* Provide sorted array of pointers to sections of ADDRS. The array is
+ terminated by NULL. Caller is responsible to call xfree for it. */
+
+static struct other_sections **
+addrs_section_sort (struct section_addr_info *addrs)
+{
+ struct other_sections **array;
+ int i;
+
+ /* `+ 1' for the NULL terminator. */
+ array = xmalloc (sizeof (*array) * (addrs->num_sections + 1));
+ for (i = 0; i < addrs->num_sections && addrs->other[i].name; i++)
+ array[i] = &addrs->other[i];
+ array[i] = NULL;
+
+ qsort (array, i, sizeof (*array), addrs_section_compar);
+
+ return array;
+}
+
+/* Relativize absolute addresses in ADDRS into offsets based on ABFD. Fill-in
+ also SECTINDEXes specific to ABFD there. This function can be used to
+ rebase ADDRS to start referencing different BFD than before. */
+
+void
+addr_info_make_relative (struct section_addr_info *addrs, bfd *abfd)
+{
+ asection *lower_sect;
+ CORE_ADDR lower_offset;
+ int i;
+ struct cleanup *my_cleanup;
+ struct section_addr_info *abfd_addrs;
+ struct other_sections **addrs_sorted, **abfd_addrs_sorted;
+ struct other_sections **addrs_to_abfd_addrs;
+
+ /* Find lowest loadable section to be used as starting point for
+ continguous sections. */
+ lower_sect = NULL;
+ bfd_map_over_sections (abfd, find_lowest_section, &lower_sect);
+ if (lower_sect == NULL)
+ {
+ warning (_("no loadable sections found in added symbol-file %s"),
+ bfd_get_filename (abfd));
+ lower_offset = 0;
+ }
+ else
+ lower_offset = bfd_section_vma (bfd_get_filename (abfd), lower_sect);
+
+ /* Create ADDRS_TO_ABFD_ADDRS array to map the sections in ADDRS to sections
+ in ABFD. Section names are not unique - there can be multiple sections of
+ the same name. Also the sections of the same name do not have to be
+ adjacent to each other. Some sections may be present only in one of the
+ files. Even sections present in both files do not have to be in the same
+ order.
+
+ Use stable sort by name for the sections in both files. Then linearly
+ scan both lists matching as most of the entries as possible. */
+
+ addrs_sorted = addrs_section_sort (addrs);
+ my_cleanup = make_cleanup (xfree, addrs_sorted);
+
+ abfd_addrs = build_section_addr_info_from_bfd (abfd);
+ make_cleanup_free_section_addr_info (abfd_addrs);
+ abfd_addrs_sorted = addrs_section_sort (abfd_addrs);
+ make_cleanup (xfree, abfd_addrs_sorted);
+
+ /* Now create ADDRS_TO_ABFD_ADDRS from ADDRS_SORTED and ABFD_ADDRS_SORTED. */
+
+ addrs_to_abfd_addrs = xzalloc (sizeof (*addrs_to_abfd_addrs)
+ * addrs->num_sections);
+ make_cleanup (xfree, addrs_to_abfd_addrs);
+
+ while (*addrs_sorted)
+ {
+ const char *sect_name = (*addrs_sorted)->name;
+
+ while (*abfd_addrs_sorted
+ && strcmp ((*abfd_addrs_sorted)->name, sect_name) < 0)
+ abfd_addrs_sorted++;
+
+ if (*abfd_addrs_sorted
+ && strcmp ((*abfd_addrs_sorted)->name, sect_name) == 0)
+ {
+ int index_in_addrs;
+
+ /* Make the found item directly addressable from ADDRS. */
+ index_in_addrs = *addrs_sorted - addrs->other;
+ gdb_assert (addrs_to_abfd_addrs[index_in_addrs] == NULL);
+ addrs_to_abfd_addrs[index_in_addrs] = *abfd_addrs_sorted;
+
+ /* Never use the same ABFD entry twice. */
+ abfd_addrs_sorted++;
+ }
+
+ addrs_sorted++;
+ }
+
+ /* Calculate offsets for the loadable sections.
+ FIXME! Sections must be in order of increasing loadable section
+ so that contiguous sections can use the lower-offset!!!
+
+ Adjust offsets if the segments are not contiguous.
+ If the section is contiguous, its offset should be set to
+ the offset of the highest loadable section lower than it
+ (the loadable section directly below it in memory).
+ this_offset = lower_offset = lower_addr - lower_orig_addr */
+
+ for (i = 0; i < addrs->num_sections && addrs->other[i].name; i++)
+ {
+ const char *sect_name = addrs->other[i].name;
+ struct other_sections *sect = addrs_to_abfd_addrs[i];
+
+ if (sect)
+ {
+ /* This is the index used by BFD. */
+ addrs->other[i].sectindex = sect->sectindex;
+
+ if (addrs->other[i].addr != 0)
+ {
+ addrs->other[i].addr -= sect->addr;
+ lower_offset = addrs->other[i].addr;
+ }
+ else
+ addrs->other[i].addr = lower_offset;
+ }
+ else
+ {
+ /* This section does not exist in ABFD, which is normally
+ unexpected and we want to issue a warning.
+
+ However, the ELF prelinker does create a few sections which are
+ marked in the main executable as loadable (they are loaded in
+ memory from the DYNAMIC segment) and yet are not present in
+ separate debug info files. This is fine, and should not cause
+ a warning. Shared libraries contain just the section
+ ".gnu.liblist" but it is not marked as loadable there. There is
+ no other way to identify them than by their name as the sections
+ created by prelink have no special flags. */
+
+ if (!(strcmp (sect_name, ".gnu.liblist") == 0
+ || strcmp (sect_name, ".gnu.conflict") == 0
+ || strcmp (sect_name, ".dynbss") == 0
+ || strcmp (sect_name, ".sdynbss") == 0))
+ warning (_("section %s not found in %s"), sect_name,
+ bfd_get_filename (abfd));
+
+ addrs->other[i].addr = 0;
+
+ /* SECTINDEX is invalid if ADDR is zero. */
+ }
+ }
+
+ do_cleanups (my_cleanup);
+}