+
+/* The mark phase of garbage collection. For a given section, mark
+ it and any sections in this section's group, and all the sections
+ which define symbols to which it refers. */
+
+static bfd_boolean
+_bfd_coff_gc_mark (struct bfd_link_info *info,
+ asection *sec,
+ coff_gc_mark_hook_fn gc_mark_hook)
+{
+ bfd_boolean ret = TRUE;
+
+ sec->gc_mark = 1;
+
+ /* Look through the section relocs. */
+ if ((sec->flags & SEC_RELOC) != 0
+ && sec->reloc_count > 0)
+ {
+ struct coff_reloc_cookie cookie;
+
+ if (!init_reloc_cookie_for_section (&cookie, info, sec))
+ ret = FALSE;
+ else
+ {
+ for (; cookie.rel < cookie.relend; cookie.rel++)
+ {
+ if (!_bfd_coff_gc_mark_reloc (info, sec, gc_mark_hook, &cookie))
+ {
+ ret = FALSE;
+ break;
+ }
+ }
+ fini_reloc_cookie_for_section (&cookie, sec);
+ }
+ }
+
+ return ret;
+}
+
+static bfd_boolean
+_bfd_coff_gc_mark_extra_sections (struct bfd_link_info *info,
+ coff_gc_mark_hook_fn mark_hook ATTRIBUTE_UNUSED)
+{
+ bfd *ibfd;
+
+ for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
+ {
+ asection *isec;
+ bfd_boolean some_kept;
+
+ if (bfd_get_flavour (ibfd) != bfd_target_coff_flavour)
+ continue;
+
+ /* Ensure all linker created sections are kept, and see whether
+ any other section is already marked. */
+ some_kept = FALSE;
+ for (isec = ibfd->sections; isec != NULL; isec = isec->next)
+ {
+ if ((isec->flags & SEC_LINKER_CREATED) != 0)
+ isec->gc_mark = 1;
+ else if (isec->gc_mark)
+ some_kept = TRUE;
+ }
+
+ /* If no section in this file will be kept, then we can
+ toss out debug sections. */
+ if (!some_kept)
+ continue;
+
+ /* Keep debug and special sections like .comment when they are
+ not part of a group, or when we have single-member groups. */
+ for (isec = ibfd->sections; isec != NULL; isec = isec->next)
+ if ((isec->flags & SEC_DEBUGGING) != 0
+ || (isec->flags & (SEC_ALLOC | SEC_LOAD | SEC_RELOC)) == 0)
+ isec->gc_mark = 1;
+ }
+ return TRUE;
+}
+
+/* Sweep symbols in swept sections. Called via coff_link_hash_traverse. */
+
+static bfd_boolean
+coff_gc_sweep_symbol (struct coff_link_hash_entry *h,
+ void *data ATTRIBUTE_UNUSED)
+{
+ if (h->root.type == bfd_link_hash_warning)
+ h = (struct coff_link_hash_entry *) h->root.u.i.link;
+
+ if ((h->root.type == bfd_link_hash_defined
+ || h->root.type == bfd_link_hash_defweak)
+ && !h->root.u.def.section->gc_mark
+ && !(h->root.u.def.section->owner->flags & DYNAMIC))
+ {
+ /* Do our best to hide the symbol. */
+ h->root.u.def.section = bfd_und_section_ptr;
+ h->symbol_class = C_HIDDEN;
+ }
+
+ return TRUE;
+}
+
+/* The sweep phase of garbage collection. Remove all garbage sections. */
+
+typedef bfd_boolean (*gc_sweep_hook_fn)
+ (bfd *, struct bfd_link_info *, asection *, const struct internal_reloc *);
+
+static bfd_boolean
+coff_gc_sweep (bfd *abfd ATTRIBUTE_UNUSED, struct bfd_link_info *info)
+{
+ bfd *sub;
+
+ for (sub = info->input_bfds; sub != NULL; sub = sub->link.next)
+ {
+ asection *o;
+
+ if (bfd_get_flavour (sub) != bfd_target_coff_flavour)
+ continue;
+
+ for (o = sub->sections; o != NULL; o = o->next)
+ {
+ /* Keep debug and special sections. */
+ if ((o->flags & (SEC_DEBUGGING | SEC_LINKER_CREATED)) != 0
+ || (o->flags & (SEC_ALLOC | SEC_LOAD | SEC_RELOC)) == 0)
+ o->gc_mark = 1;
+ else if (CONST_STRNEQ (o->name, ".idata")
+ || CONST_STRNEQ (o->name, ".pdata")
+ || CONST_STRNEQ (o->name, ".xdata")
+ || CONST_STRNEQ (o->name, ".rsrc"))
+ o->gc_mark = 1;
+
+ if (o->gc_mark)
+ continue;
+
+ /* Skip sweeping sections already excluded. */
+ if (o->flags & SEC_EXCLUDE)
+ continue;
+
+ /* Since this is early in the link process, it is simple
+ to remove a section from the output. */
+ o->flags |= SEC_EXCLUDE;
+
+ if (info->print_gc_sections && o->size != 0)
+ /* xgettext: c-format */
+ _bfd_error_handler (_("removing unused section '%pA' in file '%pB'"),
+ o, sub);
+
+#if 0
+ /* But we also have to update some of the relocation
+ info we collected before. */
+ if (gc_sweep_hook
+ && (o->flags & SEC_RELOC) != 0
+ && o->reloc_count > 0
+ && !bfd_is_abs_section (o->output_section))
+ {
+ struct internal_reloc *internal_relocs;
+ bfd_boolean r;
+
+ internal_relocs
+ = _bfd_coff_link_read_relocs (o->owner, o, NULL, NULL,
+ info->keep_memory);
+ if (internal_relocs == NULL)
+ return FALSE;
+
+ r = (*gc_sweep_hook) (o->owner, info, o, internal_relocs);
+
+ if (coff_section_data (o)->relocs != internal_relocs)
+ free (internal_relocs);
+
+ if (!r)
+ return FALSE;
+ }
+#endif
+ }
+ }
+
+ /* Remove the symbols that were in the swept sections from the dynamic
+ symbol table. */
+ coff_link_hash_traverse (coff_hash_table (info), coff_gc_sweep_symbol,
+ NULL);
+
+ return TRUE;
+}
+
+/* Keep all sections containing symbols undefined on the command-line,
+ and the section containing the entry symbol. */
+
+static void
+_bfd_coff_gc_keep (struct bfd_link_info *info)
+{
+ struct bfd_sym_chain *sym;
+
+ for (sym = info->gc_sym_list; sym != NULL; sym = sym->next)
+ {
+ struct coff_link_hash_entry *h;
+
+ h = coff_link_hash_lookup (coff_hash_table (info), sym->name,
+ FALSE, FALSE, FALSE);
+
+ if (h != NULL
+ && (h->root.type == bfd_link_hash_defined
+ || h->root.type == bfd_link_hash_defweak)
+ && !bfd_is_abs_section (h->root.u.def.section))
+ h->root.u.def.section->flags |= SEC_KEEP;
+ }
+}
+
+/* Do mark and sweep of unused sections. */
+
+bfd_boolean
+bfd_coff_gc_sections (bfd *abfd ATTRIBUTE_UNUSED, struct bfd_link_info *info)
+{
+ bfd *sub;
+
+ /* FIXME: Should we implement this? */
+#if 0
+ const bfd_coff_backend_data *bed = coff_backend_info (abfd);
+
+ if (!bed->can_gc_sections
+ || !is_coff_hash_table (info->hash))
+ {
+ _bfd_error_handler(_("warning: gc-sections option ignored"));
+ return TRUE;
+ }
+#endif
+
+ _bfd_coff_gc_keep (info);
+
+ /* Grovel through relocs to find out who stays ... */
+ for (sub = info->input_bfds; sub != NULL; sub = sub->link.next)
+ {
+ asection *o;
+
+ if (bfd_get_flavour (sub) != bfd_target_coff_flavour)
+ continue;
+
+ for (o = sub->sections; o != NULL; o = o->next)
+ {
+ if (((o->flags & (SEC_EXCLUDE | SEC_KEEP)) == SEC_KEEP
+ || CONST_STRNEQ (o->name, ".vectors")
+ || CONST_STRNEQ (o->name, ".ctors")
+ || CONST_STRNEQ (o->name, ".dtors"))
+ && !o->gc_mark)
+ {
+ if (!_bfd_coff_gc_mark (info, o, _bfd_coff_gc_mark_hook))
+ return FALSE;
+ }
+ }
+ }
+
+ /* Allow the backend to mark additional target specific sections. */
+ _bfd_coff_gc_mark_extra_sections (info, _bfd_coff_gc_mark_hook);
+
+ /* ... and mark SEC_EXCLUDE for those that go. */
+ return coff_gc_sweep (abfd, info);
+}