+/* Elf_Internal_Shdr->contents is an array of these for SHT_GROUP
+ sections. The first element is the flags, the rest are section
+ pointers. */
+
+typedef union elf_internal_group {
+ Elf_Internal_Shdr *shdr;
+ unsigned int flags;
+} Elf_Internal_Group;
+
+/* Set next_in_group list pointer, and group name for NEWSECT. */
+
+static boolean
+setup_group (abfd, hdr, newsect)
+ bfd *abfd;
+ Elf_Internal_Shdr *hdr;
+ asection *newsect;
+{
+ unsigned int num_group = elf_tdata (abfd)->num_group;
+
+ /* If num_group is zero, read in all SHT_GROUP sections. The count
+ is set to -1 if there are no SHT_GROUP sections. */
+ if (num_group == 0)
+ {
+ unsigned int i, shnum;
+
+ /* First count the number of groups. If we have a SHT_GROUP
+ section with just a flag word (ie. sh_size is 4), ignore it. */
+ shnum = elf_elfheader (abfd)->e_shnum;
+ num_group = 0;
+ for (i = 0; i < shnum; i++)
+ {
+ Elf_Internal_Shdr *shdr = elf_elfsections (abfd)[i];
+ if (shdr->sh_type == SHT_GROUP && shdr->sh_size >= 8)
+ num_group += 1;
+ }
+
+ if (num_group == 0)
+ num_group = (unsigned) -1;
+ elf_tdata (abfd)->num_group = num_group;
+
+ if (num_group > 0)
+ {
+ /* We keep a list of elf section headers for group sections,
+ so we can find them quickly. */
+ bfd_size_type amt = num_group * sizeof (Elf_Internal_Shdr *);
+ elf_tdata (abfd)->group_sect_ptr = bfd_alloc (abfd, amt);
+ if (elf_tdata (abfd)->group_sect_ptr == NULL)
+ return false;
+
+ num_group = 0;
+ for (i = 0; i < shnum; i++)
+ {
+ Elf_Internal_Shdr *shdr = elf_elfsections (abfd)[i];
+ if (shdr->sh_type == SHT_GROUP && shdr->sh_size >= 8)
+ {
+ unsigned char *src;
+ Elf_Internal_Group *dest;
+
+ /* Add to list of sections. */
+ elf_tdata (abfd)->group_sect_ptr[num_group] = shdr;
+ num_group += 1;
+
+ /* Read the raw contents. */
+ BFD_ASSERT (sizeof (*dest) >= 4);
+ amt = shdr->sh_size * sizeof (*dest) / 4;
+ shdr->contents = bfd_alloc (abfd, amt);
+ if (shdr->contents == NULL
+ || bfd_seek (abfd, shdr->sh_offset, SEEK_SET) != 0
+ || (bfd_bread (shdr->contents, shdr->sh_size, abfd)
+ != shdr->sh_size))
+ return false;
+
+ /* Translate raw contents, a flag word followed by an
+ array of elf section indices all in target byte order,
+ to the flag word followed by an array of elf section
+ pointers. */
+ src = shdr->contents + shdr->sh_size;
+ dest = (Elf_Internal_Group *) (shdr->contents + amt);
+ while (1)
+ {
+ unsigned int idx;
+
+ src -= 4;
+ --dest;
+ idx = H_GET_32 (abfd, src);
+ if (src == shdr->contents)
+ {
+ dest->flags = idx;
+ break;
+ }
+ if (idx >= shnum)
+ {
+ ((*_bfd_error_handler)
+ (_("%s: invalid SHT_GROUP entry"),
+ bfd_archive_filename (abfd)));
+ idx = 0;
+ }
+ dest->shdr = elf_elfsections (abfd)[idx];
+ }
+ }
+ }
+ }
+ }
+
+ if (num_group != (unsigned) -1)
+ {
+ unsigned int i;
+
+ for (i = 0; i < num_group; i++)
+ {
+ Elf_Internal_Shdr *shdr = elf_tdata (abfd)->group_sect_ptr[i];
+ Elf_Internal_Group *idx = (Elf_Internal_Group *) shdr->contents;
+ unsigned int n_elt = shdr->sh_size / 4;
+
+ /* Look through this group's sections to see if current
+ section is a member. */
+ while (--n_elt != 0)
+ if ((++idx)->shdr == hdr)
+ {
+ asection *s = NULL;
+
+ /* We are a member of this group. Go looking through
+ other members to see if any others are linked via
+ next_in_group. */
+ idx = (Elf_Internal_Group *) shdr->contents;
+ n_elt = shdr->sh_size / 4;
+ while (--n_elt != 0)
+ if ((s = (++idx)->shdr->bfd_section) != NULL
+ && elf_next_in_group (s) != NULL)
+ break;
+ if (n_elt != 0)
+ {
+ /* Snarf the group name from other member, and
+ insert current section in circular list. */
+ elf_group_name (newsect) = elf_group_name (s);
+ elf_next_in_group (newsect) = elf_next_in_group (s);
+ elf_next_in_group (s) = newsect;
+ }
+ else
+ {
+ struct elf_backend_data *bed;
+ file_ptr pos;
+ unsigned char ename[4];
+ unsigned long iname;
+ const char *gname;
+
+ /* Humbug. Get the name from the group signature
+ symbol. Why isn't the signature just a string?
+ Fortunately, the name index is at the same
+ place in the external symbol for both 32 and 64
+ bit ELF. */
+ bed = get_elf_backend_data (abfd);
+ pos = elf_tdata (abfd)->symtab_hdr.sh_offset;
+ pos += shdr->sh_info * bed->s->sizeof_sym;
+ if (bfd_seek (abfd, pos, SEEK_SET) != 0
+ || bfd_bread (ename, (bfd_size_type) 4, abfd) != 4)
+ return false;
+ iname = H_GET_32 (abfd, ename);
+ gname = elf_string_from_elf_strtab (abfd, iname);
+ elf_group_name (newsect) = gname;
+
+ /* Start a circular list with one element. */
+ elf_next_in_group (newsect) = newsect;
+ }
+ if (shdr->bfd_section != NULL)
+ elf_next_in_group (shdr->bfd_section) = newsect;
+ i = num_group - 1;
+ break;
+ }
+ }
+ }
+
+ if (elf_group_name (newsect) == NULL)
+ {
+ (*_bfd_error_handler) (_("%s: no group info for section %s"),
+ bfd_archive_filename (abfd), newsect->name);
+ }
+ return true;
+}
+