diff --git a/gcc/common.opt b/gcc/common.opt
index b49ac46..a73d176 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1277,6 +1277,10 @@ ffunction-sections
 Common Report Var(flag_function_sections)
 Place each function into its own section
 
+fdwarf-sections
+Common Report Var(flag_dwarf_sections) Init(0)
+Place dwarf info for named functions into its own section when using DWARF v4 debuginfo
+
 fgcse
 Common Report Var(flag_gcse) Optimization
 Perform global common subexpression elimination
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index f9781f4..7e44c7c 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -392,6 +392,7 @@ Objective-C and Objective-C++ Dialects}.
 -fearly-inlining -fipa-sra -fexpensive-optimizations -ffat-lto-objects @gol
 -ffast-math -ffinite-math-only -ffloat-store -fexcess-precision=@var{style} @gol
 -fforward-propagate -ffp-contract=@var{style} -ffunction-sections @gol
+-fdwarf-sections @gol
 -fgcse -fgcse-after-reload -fgcse-las -fgcse-lm -fgraphite-identity @gol
 -fgcse-sm -fhoist-adjacent-loads -fif-conversion @gol
 -fif-conversion2 -findirect-inlining @gol
@@ -9990,6 +9991,11 @@ You cannot use @command{gprof} on all systems if you
 specify this option, and you may have problems with debugging if
 you specify both this option and @option{-g}.
 
+@item -fdwarf-sections
+@opindex fdwarf-sections
+Place dwarf info for named functions into its own section when using DWARF
+v4 debuginfo.
+
 @item -fbranch-target-load-optimize
 @opindex fbranch-target-load-optimize
 Perform branch target register load optimization before prologue / epilogue
diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c
index 6c8e51f..6b519b8 100644
--- a/gcc/dwarf2out.c
+++ b/gcc/dwarf2out.c
@@ -244,6 +244,35 @@ struct indirect_string_hasher : ggc_hasher<indirect_string_node *>
 
 static GTY (()) hash_table<indirect_string_hasher> *debug_str_hash;
 
+/* A set of local types referenced from outside their function.  */
+
+static GTY (()) hash_set<dw_die_ref> *local_type_template_args;
+
+static void
+switch_to_section_for_imported_decl (tree, const char *prefix);
+
+struct GTY((for_user)) imported_unit_node {
+  tree decl;
+  dw_die_ref die;
+};
+
+struct imported_unit_hasher : ggc_hasher<imported_unit_node *>
+{
+  typedef dw_die_ref compare_type;
+
+  static hashval_t hash (imported_unit_node *x)
+    {
+      return htab_hash_pointer (x->die);
+    }
+   static bool equal (imported_unit_node *x1, dw_die_ref x2)
+     {
+       return x1->die == x2;
+     }
+};
+
+static GTY (()) hash_table<imported_unit_hasher> *imported_unit_hash;
+static tree lookup_imported_unit_key (dw_die_ref);
+
 /* With split_debug_info, both the comp_dir and dwo_name go in the
    main object file, rather than the dwo, similar to the force_direct
    parameter elsewhere but with additional complications:
@@ -290,6 +319,8 @@ static char *stripattributes (const char *);
 static void output_call_frame_info (int);
 static void dwarf2out_note_section_used (void);
 
+static bool should_fde_move_to_imported_unit (dw_fde_ref);
+
 /* Personality decl of current unit.  Used only when assembler does not support
    personality CFI.  */
 static GTY(()) rtx current_unit_personality;
@@ -3153,6 +3184,7 @@ static int is_symbol_die (dw_die_ref);
 static inline bool is_template_instantiation (dw_die_ref);
 static void assign_symbol_names (dw_die_ref);
 static void break_out_includes (dw_die_ref);
+static int is_abstract_die (dw_die_ref);
 static int is_declaration_die (dw_die_ref);
 static int should_move_die_to_comdat (dw_die_ref);
 static dw_die_ref clone_as_declaration (dw_die_ref);
@@ -3165,6 +3197,8 @@ static dw_die_ref generate_skeleton (dw_die_ref);
 static dw_die_ref remove_child_or_replace_with_skeleton (dw_die_ref,
                                                          dw_die_ref,
                                                          dw_die_ref);
+static bool can_use_low_high_pc ();
+static void break_out_functions (dw_die_ref);
 static void break_out_comdat_types (dw_die_ref);
 static void copy_decls_for_unworthy_types (dw_die_ref);
 
@@ -3173,7 +3207,6 @@ static void output_location_lists (dw_die_ref);
 static int constant_size (unsigned HOST_WIDE_INT);
 static unsigned long size_of_die (dw_die_ref);
 static void calc_die_sizes (dw_die_ref);
-static void calc_base_type_die_sizes (void);
 static void mark_dies (dw_die_ref);
 static void unmark_dies (dw_die_ref);
 static void unmark_all_dies (dw_die_ref);
@@ -3194,7 +3227,8 @@ static void add_enumerator_pubname (const char *, dw_die_ref);
 static void add_pubname_string (const char *, dw_die_ref);
 static void add_pubtype (tree, dw_die_ref);
 static void output_pubnames (vec<pubname_entry, va_gc> *);
-static void output_aranges (unsigned long);
+static inline unsigned get_range_idx (dw_attr_ref);
+static void output_aranges ();
 static unsigned int add_ranges_num (int);
 static unsigned int add_ranges (const_tree);
 static void add_ranges_by_labels (dw_die_ref, const char *, const char *,
@@ -3656,11 +3690,10 @@ get_base_type_offset (dw_die_ref ref)
 {
   if (ref->die_offset)
     return ref->die_offset;
-  if (comp_unit_die ()->die_abbrev)
-    {
-      calc_base_type_die_sizes ();
-      gcc_assert (ref->die_offset);
-    }
+  /* If we're calculating DIE offsets, this needs to be set.  It's OK if
+     it's unset during optimize_location_lists.  */
+  if (next_die_offset)
+    gcc_assert (ref->die_offset);
   return ref->die_offset;
 }
 
@@ -6752,6 +6785,12 @@ is_symbol_die (dw_die_ref c)
 {
   return (is_type_die (c)
 	  || is_declaration_die (c)
+      || is_abstract_die (c)
+	  /* DW_TAG_GNU_call_site can refer to subprograms.  */
+	  || c->die_tag == DW_TAG_subprogram
+	  /* DW_TAG_imported_unit can refer to *_unit.  */
+	  || c->die_tag == DW_TAG_compile_unit
+	  || c->die_tag == DW_TAG_partial_unit
 	  || c->die_tag == DW_TAG_namespace
 	  || c->die_tag == DW_TAG_module);
 }
@@ -6837,7 +6876,8 @@ assign_symbol_names (dw_die_ref die)
 {
   dw_die_ref c;
 
-  if (is_symbol_die (die) && !die->comdat_type_p)
+  if (is_symbol_die (die) && !die->comdat_type_p &&
+      !die->die_id.die_symbol)
     {
       if (comdat_symbol_id)
 	{
@@ -6999,6 +7039,8 @@ break_out_includes (dw_die_ref die)
        node = node->next)
     {
       int is_dupl;
+      if (lookup_imported_unit_key (node->die) != NULL)
+       continue;
 
       compute_section_prefix (node->die);
       is_dupl = check_duplicate_cu (node->die, &cu_hash_table,
@@ -7015,6 +7057,100 @@ break_out_includes (dw_die_ref die)
     }
 }
 
+static void
+copy_needed_base_types_loc (dw_die_ref unit, dw_loc_descr_ref loc,
+			    hash_map<dw_die_ref, dw_die_ref> *map)
+{
+  for (; loc; loc = loc->dw_loc_next)
+    {
+      dw_die_ref *op, *slot, copy;
+      switch (loc->dw_loc_opc)
+	{
+	case DW_OP_GNU_convert:
+	case DW_OP_GNU_reinterpret:
+	  if (loc->dw_loc_oprnd1.val_class == dw_val_class_unsigned_const)
+	    continue;
+	  /* else fall through */
+	case DW_OP_GNU_const_type:
+	  op = &loc->dw_loc_oprnd1.v.val_die_ref.die;
+	  break;
+	case DW_OP_GNU_regval_type:
+	case DW_OP_GNU_deref_type:
+	  op = &loc->dw_loc_oprnd2.v.val_die_ref.die;
+	  break;
+
+	  /* DW_OP_GNU_entry_value has a location expression for its
+	     operand, so recurse.  */
+	case DW_OP_GNU_entry_value:
+	  copy_needed_base_types_loc (unit, loc->dw_loc_oprnd1.v.val_loc, map);
+	  continue;
+
+	default:
+	  continue;
+	}
+
+      gcc_assert ((*op)->die_tag == DW_TAG_base_type);
+
+      slot = map->get (*op);
+      if (slot)
+        copy = *slot;
+      else
+	{
+	  /* Insert the base type at the beginning of unit's child list
+	     so that get_base_type_offset works.  */
+	  copy = ggc_cleared_alloc<die_node> ();
+	  copy->die_tag = DW_TAG_base_type;
+	  copy->die_sib = unit->die_child->die_sib;
+	  unit->die_child->die_sib = copy;
+	  add_AT_unsigned (copy, DW_AT_byte_size,
+			   get_AT_unsigned (*op, DW_AT_byte_size));
+	  add_AT_unsigned (copy, DW_AT_encoding,
+			   get_AT_unsigned (*op, DW_AT_encoding));
+	  map->put (*op, copy);
+	}
+      *op = copy;
+    }
+}
+
+static void
+copy_needed_base_types_1 (dw_die_ref unit, dw_die_ref die,
+			hash_map<dw_die_ref, dw_die_ref> *map)
+{
+  dw_die_ref c;
+  dw_attr_ref a;
+  unsigned ix;
+  FOR_EACH_VEC_SAFE_ELT (die->die_attr, ix, a)
+    {
+      switch (AT_class (a))
+	{
+	case dw_val_class_loc:
+	  copy_needed_base_types_loc (unit, AT_loc (a), map);
+	  break;
+
+	case dw_val_class_loc_list:
+	  {
+	    dw_loc_list_ref p = AT_loc_list (a);
+	    for (; p; p = p->dw_loc_next)
+	      copy_needed_base_types_loc (unit, p->expr, map);
+	    break;
+	  }
+
+	default:
+	  break;
+	}
+    }
+
+  FOR_EACH_CHILD (die, c, copy_needed_base_types_1 (unit, c, map));
+}
+
+static void
+copy_needed_base_types (dw_die_ref unit)
+{
+  hash_map<dw_die_ref, dw_die_ref> *map = new hash_map<dw_die_ref, dw_die_ref>;
+  copy_needed_base_types_1 (unit, unit, map);
+  delete map;
+}
+
 /* Return non-zero if this DIE is a declaration.  */
 
 static int
@@ -7030,6 +7166,37 @@ is_declaration_die (dw_die_ref die)
   return 0;
 }
 
+/* Return non-zero if this DIE is part of an abstract inline.  That is, if
+   it's an inline function or a variable, label or parameter of an inline
+   function.  */
+
+static int
+is_abstract_die (dw_die_ref die)
+{
+  switch (die->die_tag)
+    {
+    case DW_TAG_subprogram:
+    case DW_TAG_variable:
+    case DW_TAG_label:
+    case DW_TAG_formal_parameter:
+      break;
+
+    default:
+      return 0;
+    }
+
+  for (; die->die_tag != DW_TAG_subprogram; die = die->die_parent)
+    {
+      if (die->die_tag == DW_TAG_compile_unit
+	  || die->die_tag == DW_TAG_namespace)
+	return 0;
+    }
+
+  if (get_AT (die, DW_AT_inline))
+    return 1;
+  return 0;
+}
+
 /* Return non-zero if this DIE is nested inside a subprogram.  */
 
 static int
@@ -7480,6 +7647,284 @@ remove_child_or_replace_with_skeleton (dw_die_ref unit, dw_die_ref child,
   return skeleton;
 }
 
+
+static void 
+record_imported_unit_key (dw_die_ref die, tree decl)
+{
+  struct imported_unit_node *mp;
+
+  if (imported_unit_hash == NULL)
+    imported_unit_hash = hash_table<imported_unit_hasher>::create_ggc (10);
+
+  imported_unit_node **slot = imported_unit_hash->find_slot_with_hash (die, 
+      htab_hash_pointer (die), INSERT);
+  if (*slot == NULL)
+    {
+      mp = ggc_cleared_alloc<imported_unit_node> ();
+      mp->die = die;
+      mp->decl = decl;
+      *slot = mp;
+    }
+}
+
+static tree
+lookup_imported_unit_key (dw_die_ref die)
+{
+  struct imported_unit_node *mp;
+
+  if (imported_unit_hash == NULL)
+    return NULL;
+
+  mp = imported_unit_hash->find_with_hash (die, htab_hash_pointer (die));
+
+  if (mp)
+    return mp->decl;
+  else
+    return NULL;
+}
+
+static bool
+can_use_low_high_pc ()
+{
+  unsigned fde_idx;
+  dw_fde_ref fde;
+  bool any_non_cu_ranges;
+
+  any_non_cu_ranges = (ranges_table_in_use > 0);
+  if ((any_non_cu_ranges && have_multiple_function_sections)
+      || cold_text_section_used)
+    return false;
+
+  if (!fde_vec)
+    return false;
+
+  /* If we don't need AT_ranges for either of those reasons, are there
+     any extra function sections that belong to the main CU? */
+  FOR_EACH_VEC_ELT (*fde_vec, fde_idx, fde)
+    {
+      if (should_fde_move_to_imported_unit (fde))
+        return false;
+    }
+
+  return true;
+}
+
+static void
+split_out_local_types (dw_die_ref old, dw_die_ref decl, bool decl_is_new)
+{
+  dw_die_ref c, nc;
+  dw_die_ref prev = old->die_child;
+
+  if (prev) do {
+    force_loop:
+      c = prev->die_sib;
+
+      switch (c->die_tag)
+	{
+	case DW_TAG_formal_parameter:
+	case DW_TAG_template_type_param:
+	case DW_TAG_template_value_param:
+	case DW_TAG_GNU_template_template_param:
+	  /* If we're making a new declaration, we need to copy these
+	     over.  */
+	  if (decl_is_new)
+	    {
+	      nc = new_die (c->die_tag, decl, NULL_TREE);
+	      if (c->die_tag == DW_TAG_GNU_template_template_param)
+		add_AT_string (nc, DW_AT_GNU_template_name,
+			       get_AT_string (c, DW_AT_GNU_template_name));
+	      else
+		add_AT_die_ref (nc, DW_AT_type, get_AT_ref (c, DW_AT_type));
+	      if (get_AT_flag (c, DW_AT_artificial))
+		add_AT_flag (nc, DW_AT_artificial, 1);
+	    }
+	  break;
+
+	case DW_TAG_enumeration_type:
+	case DW_TAG_class_type:
+	case DW_TAG_structure_type:
+	  /* Types that are used from outside the function need to move
+	     into the declaration, but we should keep a stub locally for
+	     name lookup.  */
+	  if (local_type_template_args->contains (c))
+	    {
+	      nc = ggc_cleared_alloc<die_node> ();
+	      nc->die_tag = DW_TAG_typedef;
+	      add_AT_die_ref (nc, DW_AT_type, c);
+	      replace_child (c, nc, prev);
+	      add_child_die (decl, c);
+	      c = nc;
+	    }
+	  break;
+
+	case DW_TAG_lexical_block:
+	  /* Recurse into lexical blocks, then leave them in place.  */
+	  split_out_local_types (c, decl, decl_is_new);
+	  break;
+
+	default:
+	  /* Unnamed types that are used from outside the function can
+	     just move.  */
+	  if (is_type_die (c) && local_type_template_args->contains (c))
+	    {
+	      remove_child_with_prev (c, prev);
+	      add_child_die (decl, c);
+	      if (old->die_child == NULL)
+		/* We removed the last child from old.  */
+		return;
+	      else
+		/* We don't want to advance prev.  */
+		goto force_loop;
+	    }
+	  /* Everything else should stay in the COMDAT die.  */
+	  break;
+	}
+
+      prev = c;
+    }
+  while (c != old->die_child);
+}
+
+static dw_die_ref
+split_out_function_skeleton (dw_die_ref old)
+{
+  dw_die_ref decl;
+  dw_die_ref new_ = NULL;
+
+  /* Is there already a declaration DIE we can use?  */
+  decl = get_AT_ref (old, DW_AT_specification);
+  if (!decl)
+    {
+      /* Nope, need to make one and move attributes over.  */
+      dw_attr_ref a;
+      unsigned ix;
+
+      decl = new_ = ggc_cleared_alloc<die_node> ();
+      new_->die_tag = DW_TAG_subprogram;
+
+      add_AT_flag (new_, DW_AT_declaration, 1);
+      add_AT_die_ref (old, DW_AT_specification, new_);
+
+      FOR_EACH_VEC_SAFE_ELT (old->die_attr, ix, a)
+	switch (a->dw_attr)
+	  {
+	  case DW_AT_object_pointer:
+	  case DW_AT_virtuality:
+	  case DW_AT_accessibility:
+	  case DW_AT_explicit:
+	    /* Member function; we should have already had a declaration.  */
+	    gcc_unreachable ();
+	    break;
+
+	  case DW_AT_name:
+	  case DW_AT_type:
+	  case DW_AT_artificial:
+	  case DW_AT_decl_file:
+	  case DW_AT_decl_line:
+	  case DW_AT_external:
+	  case DW_AT_pure:
+	  case DW_AT_calling_convention:
+	  case DW_AT_prototyped:
+	  case DW_AT_elemental:
+	  case DW_AT_recursive:
+	    /* Move these to the declaration.  */
+	    add_dwarf_attr (new_, a);
+	    old->die_attr->ordered_remove(ix);
+	    --ix;
+	    break;
+
+	  default:
+	    /* Leave anything else in the definition.  */
+	    break;
+	  }
+    }
+
+  split_out_local_types (old, decl, decl == new_);
+
+  return new_;
+}
+
+bool
+gather_local_type_fns_r (
+    const dw_die_ref &slot, 
+    hash_set<dw_die_ref> *local_type_fns)
+{
+  dw_die_ref die = slot;
+  for (die = die->die_parent; die; die = die->die_parent)
+    if (die->die_tag == DW_TAG_subprogram)
+      break;
+  if (die && lookup_imported_unit_key (die))
+    local_type_fns->add (die);
+  return true;
+}
+
+static struct hash_set<dw_die_ref> *
+gather_local_type_fns (void)
+{
+  hash_set<dw_die_ref> *local_type_fns;
+  if (local_type_template_args == NULL)
+    return NULL;
+
+  local_type_fns = new hash_set<dw_die_ref>;
+  local_type_template_args->traverse<hash_set<dw_die_ref>*, gather_local_type_fns_r>(
+		 local_type_fns);
+  return local_type_fns;
+}
+
+static void
+break_out_functions (dw_die_ref die)
+{
+  dw_die_ref c, prev;
+  bool found = false;
+  limbo_die_node *node;
+  hash_set<dw_die_ref> *local_type_fns = gather_local_type_fns ();
+
+  prev = die->die_child;
+  if (prev == NULL)
+    return;
+
+  do
+    {
+      tree key;
+      c = prev->die_sib;
+      if (c->die_tag == DW_TAG_subprogram
+          && (key = lookup_imported_unit_key (c)))
+        {
+          dw_die_ref nc;
+          dw_die_ref unit = gen_compile_unit_die (NULL);
+          dw_die_ref imp = new_die (DW_TAG_imported_unit, unit,
+					    NULL_TREE);
+          add_AT_die_ref (imp, DW_AT_import, comp_unit_die ());
+          if (local_type_fns
+              && local_type_fns->contains (c)
+              && (nc = split_out_function_skeleton (c)))
+            replace_child (c, nc, prev);
+          else
+            remove_child_with_prev (c, prev);
+          add_child_die (unit, c);
+
+          record_imported_unit_key (unit, key);
+          found = true;
+        }
+      else
+        prev = c;
+    }
+  while (die->die_child && (c != die->die_child));
+
+  if (!found)
+    return;
+
+  assign_symbol_names (die);
+  for (node = limbo_die_list;
+       node;
+       node = node->next)
+    if (lookup_imported_unit_key (node->die) != NULL)
+      {
+        copy_needed_base_types (node->die);
+        assign_symbol_names (node->die);
+      }
+}
+
 /* Traverse the DIE and set up additional .debug_types sections for each
    type worthy of being placed in a COMDAT section.  */
 
@@ -8164,34 +8609,23 @@ calc_die_sizes (dw_die_ref die)
     next_die_offset += 1;
 }
 
-/* Size just the base type children at the start of the CU.
-   This is needed because build_abbrev needs to size locs
-   and sizing of type based stack ops needs to know die_offset
-   values for the base types.  */
+/* Return the size of the unit starting at DIE, assuming calc_die_sizes has
+   already been run.  */
 
-static void
-calc_base_type_die_sizes (void)
+static unsigned long
+cu_size (dw_die_ref die)
 {
-  unsigned long die_offset = DWARF_COMPILE_UNIT_HEADER_SIZE;
-  unsigned int i;
-  dw_die_ref base_type;
-#if ENABLE_ASSERT_CHECKING
-  dw_die_ref prev = comp_unit_die ()->die_child;
-#endif
-
-  die_offset += size_of_die (comp_unit_die ());
-  for (i = 0; base_types.iterate (i, &base_type); i++)
+  unsigned terminators = 0;
+  /* die_child points to the last child of a DIE, so we can find the last
+     die by chasing down die_child.  */
+  while (die->die_child)
     {
-#if ENABLE_ASSERT_CHECKING
-      gcc_assert (base_type->die_offset == 0
-		  && prev->die_sib == base_type
-		  && base_type->die_child == NULL
-		  && base_type->die_abbrev);
-      prev = base_type;
-#endif
-      base_type->die_offset = die_offset;
-      die_offset += size_of_die (base_type);
+      ++terminators;
+      die = die->die_child;
     }
+  /* Then add the size of the last die and the null bytes to terminate
+     sibling lists.  */
+  return die->die_offset + size_of_die (die) + terminators;
 }
 
 /* Set the marks for a die and its children.  We do this so
@@ -8289,7 +8723,8 @@ size_of_pubnames (vec<pubname_entry, va_gc> *names)
 
   size = DWARF_PUBNAMES_HEADER_SIZE;
   FOR_EACH_VEC_ELT (*names, i, p)
-    if (include_pubname_in_output (names, p))
+    if ((include_pubname_in_output (names, p)) &&
+        (!lookup_imported_unit_key (p->die)))
       size += strlen (p->name) + DWARF_OFFSET_SIZE + 1 + space_for_flags;
 
   size += DWARF_OFFSET_SIZE;
@@ -8317,7 +8752,8 @@ size_of_aranges (void)
 
       FOR_EACH_VEC_ELT (*fde_vec, fde_idx, fde)
 	{
-	  if (DECL_IGNORED_P (fde->decl))
+	  if (DECL_IGNORED_P (fde->decl) ||
+        should_fde_move_to_imported_unit (fde))
 	    continue;
 	  if (!fde->in_std_section)
 	    size += 2 * DWARF2_ADDR_SIZE;
@@ -9107,6 +9543,7 @@ output_comp_unit (dw_die_ref die, int output_if_empty)
 {
   const char *secname, *oldsym;
   char *tmp;
+  tree key;
 
   /* Unless we are outputting main CU, we may throw away empty ones.  */
   if (!output_if_empty && die->die_child == NULL)
@@ -9129,21 +9566,37 @@ output_comp_unit (dw_die_ref die, int output_if_empty)
   next_die_offset = DWARF_COMPILE_UNIT_HEADER_SIZE;
   calc_die_sizes (die);
 
-  oldsym = die->die_id.die_symbol;
-  if (oldsym)
+  key = lookup_imported_unit_key (die);
+  if (key)
     {
-      tmp = XALLOCAVEC (char, strlen (oldsym) + 24);
+      const char *decl_name;
+      switch_to_section_for_imported_decl (key,
+          DEBUG_INFO_SECTION);
+      decl_name = targetm.strip_name_encoding (IDENTIFIER_POINTER 
+          (DECL_ASSEMBLER_NAME(key)));
+
+      ASM_OUTPUT_DEBUG_LABEL (asm_out_file, decl_name, 0);
 
-      sprintf (tmp, ".gnu.linkonce.wi.%s", oldsym);
-      secname = tmp;
-      die->die_id.die_symbol = NULL;
-      switch_to_section (get_section (secname, SECTION_DEBUG, NULL));
+      oldsym = die->die_id.die_symbol;
     }
   else
     {
-      switch_to_section (debug_info_section);
-      ASM_OUTPUT_LABEL (asm_out_file, debug_info_section_label);
-      info_section_emitted = true;
+      oldsym = die->die_id.die_symbol;
+      if (oldsym && (imported_unit_hash && imported_unit_hash->elements() == 0))
+        {
+          tmp = XALLOCAVEC (char, strlen (oldsym) + 24);
+
+          sprintf (tmp, ".gnu.linkonce.wi.%s", oldsym);
+          secname = tmp;
+          die->die_id.die_symbol = NULL;
+          switch_to_section (get_section (secname, SECTION_DEBUG, NULL));
+        }
+      else
+        {
+          switch_to_section (debug_info_section);
+          ASM_OUTPUT_LABEL (asm_out_file, debug_info_section_label);
+          info_section_emitted = true;
+        }
     }
 
   /* Output debugging information.  */
@@ -9516,41 +9969,55 @@ output_pubname (dw_offset die_offset, pubname_entry *entry)
    visible names; or the public types table used to find type definitions.  */
 
 static void
-output_pubnames (vec<pubname_entry, va_gc> *names)
+output_pubnames_header (unsigned length, const char *label,
+                        unsigned long cu_length)
 {
-  unsigned i;
-  unsigned long pubnames_length = size_of_pubnames (names);
-  pubname_ref pub;
-
   if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4)
     dw2_asm_output_data (4, 0xffffffff,
       "Initial length escape value indicating 64-bit DWARF extension");
-  dw2_asm_output_data (DWARF_OFFSET_SIZE, pubnames_length, "Pub Info Length");
+  dw2_asm_output_data (DWARF_OFFSET_SIZE, length, "Pub Info Length");
 
   /* Version number for pubnames/pubtypes is independent of dwarf version.  */
   dw2_asm_output_data (2, 2, "DWARF Version");
 
   if (dwarf_split_debug_info)
-    dw2_asm_output_offset (DWARF_OFFSET_SIZE, debug_skeleton_info_section_label,
+    dw2_asm_output_offset (DWARF_OFFSET_SIZE,
+                           debug_skeleton_info_section_label,
                            debug_skeleton_info_section,
                            "Offset of Compilation Unit Info");
   else
-    dw2_asm_output_offset (DWARF_OFFSET_SIZE, debug_info_section_label,
-                           debug_info_section,
+    dw2_asm_output_offset (DWARF_OFFSET_SIZE, label, debug_info_section,
                            "Offset of Compilation Unit Info");
-  dw2_asm_output_data (DWARF_OFFSET_SIZE, next_die_offset,
+  dw2_asm_output_data (DWARF_OFFSET_SIZE, cu_length,
 		       "Compilation Unit Length");
+}
+
+static void
+output_pubnames (vec<pubname_entry, va_gc> *names)
+{
+  unsigned i;
+  unsigned long pubnames_length = size_of_pubnames (names);
+  pubname_ref pub;
+  limbo_die_node *node;
+
+  if (names == pubname_table)
+    switch_to_section (debug_pubnames_section);
+  else
+    switch_to_section (debug_pubtypes_section);
+
+  output_pubnames_header (pubnames_length, debug_info_section_label,
+             next_die_offset);
 
   FOR_EACH_VEC_ELT (*names, i, pub)
     {
+      /* Skip imported units, as they have their own CUs.  */
+      if (lookup_imported_unit_key (pub->die))
+        continue;
+
       if (include_pubname_in_output (names, pub))
 	{
 	  dw_offset die_offset = pub->die->die_offset;
 
-          /* We shouldn't see pubnames for DIEs outside of the main CU.  */
-          if (names == pubname_table && pub->die->die_tag != DW_TAG_enumerator)
-            gcc_assert (pub->die->die_mark);
-
 	  /* If we're putting types in their own .debug_types sections,
 	     the .debug_pubtypes table will still point to the compile
 	     unit (not the type unit), so we want to use the offset of
@@ -9570,39 +10037,76 @@ output_pubnames (vec<pubname_entry, va_gc> *names)
     }
 
   dw2_asm_output_data (DWARF_OFFSET_SIZE, 0, NULL);
-}
-
-/* Output public names and types tables if necessary.  */
 
-static void
-output_pubtables (void)
-{
-  if (!want_pubnames () || !info_section_emitted)
+  if (names == pubtype_table)
     return;
 
-  switch_to_section (debug_pubnames_section);
-  output_pubnames (pubname_table);
-  /* ??? Only defined by DWARF3, but emitted by Darwin for DWARF2.
-     It shouldn't hurt to emit it always, since pure DWARF2 consumers
-     simply won't look for the section.  */
-  switch_to_section (debug_pubtypes_section);
-  output_pubnames (pubtype_table);
+  /* Now output the pubnames for each CU.  */
+  for (node = limbo_die_list; node; node = node->next)
+    {
+      dw_die_ref cu = node->die;
+      dw_die_ref die;
+      tree key = lookup_imported_unit_key (cu);
+      char *label;
+      const char *name, *decl_name;
+      unsigned long size;
+      bool found = false;
+
+      if (!key)
+        continue;
+
+      die = cu->die_child->die_sib;
+      if (die->die_tag == DW_TAG_imported_unit)
+        die = die->die_sib;
+      gcc_assert (die->die_tag == DW_TAG_subprogram);
+
+      /* Find respective pubname entry.  */
+      FOR_EACH_VEC_ELT (*names, i, pub)
+        {
+          if (die == pub->die)
+            {
+              found = true;
+              break;
+            }
+        }
+
+      gcc_assert (found);
+      name = pub->name;
+      size = (DWARF_PUBNAMES_HEADER_SIZE + strlen(name) + DWARF_OFFSET_SIZE
+        + 1 + DWARF_OFFSET_SIZE);
+
+      decl_name = IDENTIFIER_POINTER (DECL_NAME (key));
+      label = XALLOCAVEC (char, strlen (decl_name) + 10);
+      ASM_GENERATE_INTERNAL_LABEL (label, decl_name, 0);
+
+      switch_to_section_for_imported_decl (key, DEBUG_PUBNAMES_SECTION);
+
+      output_pubnames_header (size, label, cu_size (cu));
+      output_pubname (die->die_offset, pub);
+      dw2_asm_output_data (DWARF_OFFSET_SIZE, 0 , NULL);
+    }
 }
 
+static inline unsigned
+get_range_idx (dw_attr_ref a)
+{
+  gcc_assert (AT_class (a) == dw_val_class_range_list);
+  return a->dw_attr_val.v.val_offset / 2 / DWARF2_ADDR_SIZE;
+}
 
 /* Output the information that goes into the .debug_aranges table.
    Namely, define the beginning and ending address range of the
    text section generated for this compilation unit.  */
 
 static void
-output_aranges (unsigned long aranges_length)
+output_aranges_header (unsigned long length, const char *label)
 {
   unsigned i;
 
   if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4)
     dw2_asm_output_data (4, 0xffffffff,
       "Initial length escape value indicating 64-bit DWARF extension");
-  dw2_asm_output_data (DWARF_OFFSET_SIZE, aranges_length,
+  dw2_asm_output_data (DWARF_OFFSET_SIZE, length,
 		       "Length of Address Ranges Info");
   /* Version number for aranges is still 2, even up to DWARF5.  */
   dw2_asm_output_data (2, 2, "DWARF Version");
@@ -9611,7 +10115,7 @@ output_aranges (unsigned long aranges_length)
                            debug_skeleton_info_section,
                            "Offset of Compilation Unit Info");
   else
-    dw2_asm_output_offset (DWARF_OFFSET_SIZE, debug_info_section_label,
+    dw2_asm_output_offset (DWARF_OFFSET_SIZE, label,
                            debug_info_section,
                            "Offset of Compilation Unit Info");
   dw2_asm_output_data (1, DWARF2_ADDR_SIZE, "Size of Address");
@@ -9627,6 +10131,28 @@ output_aranges (unsigned long aranges_length)
       for (i = 2; i < (unsigned) DWARF_ARANGES_PAD_SIZE; i += 2)
 	dw2_asm_output_data (2, 0, NULL);
     }
+}
+
+static unsigned
+count_ranges (dw_attr_ref a)
+{
+  unsigned idx, count;
+
+  count = 0;
+  for (idx = get_range_idx (a); ranges_table[idx].num != 0; ++idx)
+    ++count;
+
+  return count;
+}
+
+static void
+output_aranges ()
+{
+  unsigned long aranges_length = size_of_aranges ();
+  limbo_die_node *node;
+
+  switch_to_section (debug_aranges_section);
+  output_aranges_header (aranges_length, debug_info_section_label);
 
   /* It is necessary not to output these entries if the sections were
      not used; if the sections were not used, the length will be 0 and
@@ -9654,7 +10180,8 @@ output_aranges (unsigned long aranges_length)
 
       FOR_EACH_VEC_ELT (*fde_vec, fde_idx, fde)
 	{
-	  if (DECL_IGNORED_P (fde->decl))
+	  if (DECL_IGNORED_P (fde->decl) ||
+          should_fde_move_to_imported_unit (fde))
 	    continue;
 	  if (!fde->in_std_section)
 	    {
@@ -9676,6 +10203,72 @@ output_aranges (unsigned long aranges_length)
   /* Output the terminator words.  */
   dw2_asm_output_data (DWARF2_ADDR_SIZE, 0, NULL);
   dw2_asm_output_data (DWARF2_ADDR_SIZE, 0, NULL);
+
+  /* Now output the aranges for function CUs.  */
+  for (node = limbo_die_list; node; node = node->next)
+    {
+      dw_die_ref cu = node->die;
+      dw_attr_ref a;
+      unsigned num_ranges;
+      tree key;
+      char *label;
+      const char *decl_name;
+
+      if ((a = get_AT (cu, DW_AT_ranges)))
+        num_ranges = count_ranges (a);
+      else if ((a = get_AT (cu, DW_AT_low_pc)))
+        num_ranges = 1;
+      else
+	    continue;
+
+      key = lookup_imported_unit_key (cu);
+      if (key)
+        {
+          switch_to_section_for_imported_decl (key,
+              DEBUG_ARANGES_SECTION);
+        }
+
+      aranges_length = (DWARF_ARANGES_HEADER_SIZE
+			+ 2 * num_ranges * DWARF2_ADDR_SIZE
+			+ 2 * DWARF2_ADDR_SIZE);
+
+      decl_name = targetm.strip_name_encoding (IDENTIFIER_POINTER 
+          (DECL_ASSEMBLER_NAME (key)));
+      label = XALLOCAVEC (char, strlen (decl_name) + 10);
+      ASM_GENERATE_INTERNAL_LABEL (label, decl_name , 0);
+      output_aranges_header (aranges_length, label);
+      if (a->dw_attr == DW_AT_low_pc)
+        {
+          const char *start = AT_lbl (a);
+          const char *end = get_AT_hi_pc (cu);
+          dw2_asm_output_addr (DWARF2_ADDR_SIZE, start, "Address");
+          dw2_asm_output_delta (DWARF2_ADDR_SIZE, end, start, "Length");
+        }
+      else
+        {
+          unsigned idx;
+          for (idx = get_range_idx (a); ; ++idx)
+            {
+              int num = ranges_table[idx].num;
+              if (num == 0)
+                break;
+              else if (num < 0)
+                {
+                  int lab_idx = - num - 1;
+                  const char *start = ranges_by_label[lab_idx].begin;
+                  const char *end = ranges_by_label[lab_idx].end;
+                  dw2_asm_output_addr (DWARF2_ADDR_SIZE, start, "Address");
+                  dw2_asm_output_delta (DWARF2_ADDR_SIZE, end, start, "Length");
+                }
+              else
+                /* We shouldn't need to handle block ranges here.  */
+                gcc_unreachable ();
+            }
+        }
+      /* Output the terminator words.  */
+      dw2_asm_output_data (DWARF2_ADDR_SIZE, 0, NULL);
+      dw2_asm_output_data (DWARF2_ADDR_SIZE, 0, NULL);
+    }
 }
 
 /* Add a new entry to .debug_ranges.  Return the offset at which it
@@ -13808,6 +14401,21 @@ secname_for_decl (const_tree decl)
   return secname;
 }
 
+static void
+switch_to_section_for_imported_decl (tree decl, const char *prefix)
+{
+  const char *section_name;
+  char *tmp;
+  section_name = DECL_SECTION_NAME (decl);
+  if (!section_name)
+    section_name = function_section (decl)->named.name;
+  tmp = XALLOCAVEC (char, strlen (section_name) + 
+      strlen (prefix) + 1);
+
+  sprintf (tmp, "%s%s", prefix, section_name);
+  switch_to_section (get_section (tmp, SECTION_DEBUG, NULL));
+}
+
 /* Return true when DECL_BY_REFERENCE is defined and set for DECL.  */
 
 static bool
@@ -18386,6 +18994,25 @@ gen_call_site_die (tree decl, dw_die_ref subr_die,
   return die;
 }
 
+static bool
+should_fde_move_to_imported_unit (dw_fde_ref fde)
+{
+  dw_die_ref die;
+
+  if (!flag_dwarf_sections)
+    return false;
+
+  if (fde->in_std_section ||
+          (fde->dw_fde_second_begin && fde->second_in_std_section))
+    return false;
+
+  if (!(die = lookup_decl_die (fde->decl)))
+    return false;
+
+  return !get_AT (die, DW_AT_abstract_origin) &&
+          !get_AT (die, DW_AT_specification); 
+}
+
 /* Generate a DIE to represent a declared function (either file-scope or
    block-local).  */
 
@@ -18732,6 +19359,10 @@ gen_subprogram_die (tree decl, dw_die_ref context_die)
 	    add_AT_loc (subr_die, DW_AT_frame_base, list->expr);
 	}
 
+      if (should_fde_move_to_imported_unit (fun->fde))
+        if (context_die == comp_unit_die ())
+          record_imported_unit_key (subr_die, decl);
+
       /* Compute a displacement from the "steady-state frame pointer" to
 	 the CFA.  The former is what all stack slots and argument slots
 	 will reference in the rtl; the latter is what we've told the
@@ -24644,6 +25275,9 @@ dwarf2out_finish (const char *filename)
   if (flag_eliminate_unused_debug_types)
     prune_unused_types ();
 
+  if (flag_dwarf_sections)
+    break_out_functions (comp_unit_die ());
+
   /* Generate separate COMDAT sections for type DIEs. */
   if (use_debug_types)
     {
@@ -24699,8 +25333,7 @@ dwarf2out_finish (const char *filename)
 
   /* We can only use the low/high_pc attributes if all of the code was
      in .text.  */
-  if (!have_multiple_function_sections 
-      || (dwarf_version < 3 && dwarf_strict))
+  if (can_use_low_high_pc () || (dwarf_version < 3 && dwarf_strict))
     {
       /* Don't add if the CU has no associated code.  */
       if (text_section_used)
@@ -24709,9 +25342,9 @@ dwarf2out_finish (const char *filename)
     }
   else
     {
+      bool range_list_added = false;
       unsigned fde_idx;
       dw_fde_ref fde;
-      bool range_list_added = false;
 
       if (text_section_used)
         add_ranges_by_labels (main_comp_unit_die, text_section_label,
@@ -24720,6 +25353,8 @@ dwarf2out_finish (const char *filename)
         add_ranges_by_labels (main_comp_unit_die, cold_text_section_label,
                               cold_end_label, &range_list_added, true);
 
+      if (fde_vec)
+        {
       FOR_EACH_VEC_ELT (*fde_vec, fde_idx, fde)
 	{
 	  if (DECL_IGNORED_P (fde->decl))
@@ -24733,6 +25368,7 @@ dwarf2out_finish (const char *filename)
                                   fde->dw_fde_second_end, &range_list_added,
                                   true);
 	}
+        }
 
       if (range_list_added)
 	{
@@ -24749,14 +25385,102 @@ dwarf2out_finish (const char *filename)
 	}
     }
 
+  /* Also add ranges to the CUs for broken out functions.  */
+  for (node = limbo_die_list; node; node = node->next)
+    {
+      dw_die_ref c = node->die->die_child;
+      bool added = false;
+      dw_attr_ref a;
+
+      if (c == NULL)
+	continue;
+      c = c->die_sib;
+      if (c->die_tag == DW_TAG_imported_unit)
+	c = c->die_sib;
+      if (c->die_tag != DW_TAG_subprogram)
+	continue;
+
+      if (c->die_sib && c->die_sib->die_tag == DW_TAG_subprogram)
+	{
+	  /* There are multiple functions in this CU, so we need to add
+	     ranges for all of them.  */
+	  dw_die_ref next = c;
+	  do
+	    {
+	      c = next;
+	      next = c->die_sib;
+	      if (c->die_tag == DW_TAG_subprogram)
+		{
+		  if ((a = get_AT (c, DW_AT_ranges)))
+		    {
+		      /* Copy the ranges from this function.  */
+		      unsigned idx; int num;
+		      for (idx = get_range_idx (a);
+			   (num = ranges_table[idx].num) != 0;
+			   ++idx)
+			{
+			  if (num < 0)
+			    {
+			      int idx2 = - num - 1;
+			      const char *start = ranges_by_label[idx2].begin;
+			      const char *end = ranges_by_label[idx2].end;
+			      add_ranges_by_labels (node->die, start, end,
+						    &added, true);
+			    }
+			  else
+			    /* We shouldn't need to handle block ranges here.  */
+			    gcc_unreachable ();
+			}
+		    }
+		  else
+		    add_ranges_by_labels (node->die, get_AT_low_pc (c),
+					  get_AT_hi_pc (c), &added, true);
+		}
+	    }
+	  while (c != node->die->die_child);
+	  /* Set the base address.  */
+	  add_AT_addr (node->die, DW_AT_low_pc, const0_rtx, true);
+	  add_ranges (NULL);
+	}
+      /* We need to use DW_AT_ranges on the CU if any descendant DIE does
+	 so that we can set up a base address; for now, rather than search
+	 descendants let's just use it if we used DW_AT_ranges anywhere in
+	 this translation unit.
+
+         FIXME better would be to use low/hi_pc if the function does and
+         make any ranges in descendant dies relative to the low_pc.  */
+
+    else if (ranges_table_in_use > 0)
+      {
+        if ((a = get_AT (c, DW_AT_ranges)))
+          add_AT_range_list (node->die, DW_AT_ranges,
+              a->dw_attr_val.v.val_offset, true);
+        else
+          add_ranges_by_labels (node->die, get_AT_low_pc (c),
+              get_AT_hi_pc (c), &added, true);
+        add_AT_addr (node->die, DW_AT_low_pc, const0_rtx, true);
+        add_ranges (NULL);
+      }
+    else
+      {
+        add_AT_lbl_id (node->die, DW_AT_low_pc, get_AT_low_pc (c));
+        add_AT_lbl_id (node->die, DW_AT_high_pc, get_AT_hi_pc (c));
+      }
+    }
   if (debug_info_level >= DINFO_LEVEL_TERSE)
     add_AT_lineptr (main_comp_unit_die, DW_AT_stmt_list,
 		    debug_line_section_label);
 
   if (have_macinfo)
-    add_AT_macptr (comp_unit_die (),
+    {
+      add_AT_macptr (comp_unit_die (),
 		   dwarf_strict ? DW_AT_macro_info : DW_AT_GNU_macros,
 		   macinfo_section_label);
+      for (node = limbo_die_list; node; node = node->next)
+	    add_AT_macptr (node->die,
+		       dwarf_strict ? DW_AT_macro_info : DW_AT_GNU_macros,
+		       macinfo_section_label);
+    }
 
   if (dwarf_split_debug_info)
     {
@@ -24777,7 +25501,11 @@ dwarf2out_finish (const char *filename)
     }
 
   if (have_location_lists)
-    optimize_location_lists (comp_unit_die ());
+    {
+      optimize_location_lists (comp_unit_die ());
+      for (node = limbo_die_list; node; node = node->next)
+	    optimize_location_lists (node->die);
+    }
 
   save_macinfo_strings ();
 
@@ -24875,13 +25603,39 @@ dwarf2out_finish (const char *filename)
   /* Output location list section if necessary.  */
   if (have_location_lists)
     {
+      tree key;
       /* Output the location lists info.  */
       switch_to_section (debug_loc_section);
       ASM_OUTPUT_LABEL (asm_out_file, loc_section_label);
       output_location_lists (comp_unit_die ());
+
+      for (node = limbo_die_list; node; node = node->next)
+	  {
+        if ((key = lookup_imported_unit_key (node->die)))
+          {
+            switch_to_section_for_imported_decl (key, 
+                DEBUG_LOC_SECTION);
+	        output_location_lists (node->die);
+          }
+	  }
+    }
+
+  /* Output public names table if necessary.  */
+  if (!pubname_table->is_empty() && want_pubnames ())
+    {
+      gcc_assert (info_section_emitted);
+      output_pubnames (pubname_table);
     }
 
-  output_pubtables ();
+  /* Output public types table if necessary.  */
+  /* ??? Only defined by DWARF3, but emitted by Darwin for DWARF2.
+     It shouldn't hurt to emit it always, since pure DWARF2 consumers
+     simply won't look for the section.  */
+  if (!pubtype_table->is_empty() && want_pubnames ())
+    {
+      gcc_assert (info_section_emitted);
+      output_pubnames (pubtype_table);
+    }
 
   /* Output the address range information if a CU (.debug_info section)
      was emitted.  We output an empty table even if we had no functions
@@ -24890,10 +25644,7 @@ dwarf2out_finish (const char *filename)
      generate a table that would have contained data.  */
   if (info_section_emitted)
     {
-      unsigned long aranges_length = size_of_aranges ();
-
-      switch_to_section (debug_aranges_section);
-      output_aranges (aranges_length);
+      output_aranges ();
     }
 
   /* Output ranges section if necessary.  */
@@ -25022,6 +25773,7 @@ dwarf2out_c_finalize (void)
   base_types.release ();
   XDELETEVEC (producer_string);
   producer_string = NULL;
+  imported_unit_hash = NULL;
 }
 
 #include "gt-dwarf2out.h"
diff --git a/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-sections-1.c b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-sections-1.c
new file mode 100644
index 0000000..b2d0ee6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-sections-1.c
@@ -0,0 +1,34 @@
+/* { dg-do compile } */
+/* { dg-options "-ffunction-sections -fdwarf-sections -gdwarf-4 -gpubnames" } */
+/* { dg-final { scan-assembler-times "\.section\[\t \]\[^\n\]*debug_info" 4 } } */
+/* { dg-final { scan-assembler-times "\.section\[\t \]\[^\n\]*debug_infomyTextSection" 1 } } */
+/* { dg-final { scan-assembler-times "\.section\[\t \]\[^\n\]*debug_info\.text\.bar_func" 1 } } */
+/* { dg-final { scan-assembler-times "\.section\[\t \]\[^\n\]*debug_info\.text\.main" 1 } } */
+/* { dg-final { scan-assembler-times "\.section\[\t \]\[^\n\]*debug_aranges" 4 } } */
+/* { dg-final { scan-assembler-times "\.section\[\t \]\[^\n\]*debug_arangesmyTextSection" 1 } } */
+/* { dg-final { scan-assembler-times "\.section\[\t \]\[^\n\]*debug_aranges\.text\.bar_func" 1 } } */
+/* { dg-final { scan-assembler-times "\.section\[\t \]\[^\n\]*debug_aranges\.text\.main" 1 } } */
+/* { dg-final { scan-assembler-times "\.section\[\t \]\[^\n\]*debug_pubnames" 4 } } */
+/* { dg-final { scan-assembler-times "\.section\[\t \]\[^\n\]*debug_pubnamesmyTextSection" 1 } } */
+/* { dg-final { scan-assembler-times "\.section\[\t \]\[^\n\]*debug_pubnames\.text\.bar_func" 1 } } */
+/* { dg-final { scan-assembler-times "\.section\[\t \]\[^\n\]*debug_pubnames\.text\.main" 1 } } */
+
+int gvar1, i1;
+
+void foo (int i) __attribute__((section ("myTextSection")));
+void bar () asm ("bar_func");
+
+void foo (int i) {
+  gvar1 += i;
+}
+
+void bar () {
+  gvar1 -= 1;
+  return;
+}
+
+int main () {
+  foo (i1);
+  i1++;
+  return 1;
+}
diff --git a/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-sections-2.c b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-sections-2.c
new file mode 100644
index 0000000..ae918b7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-sections-2.c
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-options "-fdwarf-sections -gdwarf-4 -gpubnames" } */
+/* { dg-final { scan-assembler-times "\.section\[\t \]\[^\n\]*debug_info" 2 } } */
+/* { dg-final { scan-assembler-times "\.section\[\t \]\[^\n\]*debug_info.NonStdSection" 1 } } */
+/* { dg-final { scan-assembler-times "\.section\[\t \]\[^\n\]*debug_aranges" 2 } } */
+/* { dg-final { scan-assembler-times "\.section\[\t \]\[^\n\]*debug_aranges.NonStdSection" 1 } } */
+/* { dg-final { scan-assembler-times "\.section\[\t \]\[^\n\]*debug_pubnames" 2 } } */
+/* { dg-final { scan-assembler-times "\.section\[\t \]\[^\n\]*debug_pubnames.NonStdSection" 1 } } */
+
+int gvar1, i1;
+
+void foo (int i) __attribute__((section (".NonStdSection")));
+void bar () asm ("bar_func");
+
+void foo (int i) {
+  gvar1 += i;
+}
+
+void bar () {
+  gvar1 -= 1;
+  return;
+}
+
+int main () {
+  bar ();
+  i1++;
+  return 1;
+}
diff --git a/gcc/toplev.c b/gcc/toplev.c
index b06eed3..8548912 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -1603,6 +1603,12 @@ process_options (void)
 	}
     }
 
+  if (flag_dwarf_sections && (dwarf_version < 4))
+    {
+      warning (0, "-fdwarf-sections not supported for dwarf version lower than 4");
+      flag_dwarf_sections = 0;
+    }
+
 #ifndef HAVE_prefetch
   if (flag_prefetch_loop_arrays > 0)
     {
