Blame SOURCES/gcc8-pch-tweaks.patch

f5f9ee
commit fe7c3ecff1f9c0520090a77fa824d8c5d9dbec12
f5f9ee
Author: Jakub Jelinek <jakub@redhat.com>
f5f9ee
Date:   Fri Dec 3 11:03:30 2021 +0100
f5f9ee
f5f9ee
    pch: Add support for PCH for relocatable executables [PR71934]
f5f9ee
    
f5f9ee
    So, if we want to make PCH work for PIEs, I'd say we can:
f5f9ee
    1) add a new GTY option, say callback, which would act like
f5f9ee
       skip for non-PCH and for PCH would make us skip it but
f5f9ee
       remember for address bias translation
f5f9ee
    2) drop the skip for tree_translation_unit_decl::language
f5f9ee
    3) change get_unnamed_section to have const char * as
f5f9ee
       last argument instead of const void *, change
f5f9ee
       unnamed_section::data also to const char * and update
f5f9ee
       everything related to that
f5f9ee
    4) maybe add a host hook whether it is ok to support binaries
f5f9ee
       changing addresses (the only thing I'm worried is if
f5f9ee
       some host that uses function descriptors allocates them
f5f9ee
       dynamically instead of having them somewhere in the
f5f9ee
       executable)
f5f9ee
    5) maybe add a gengtype warning if it sees in GTY tracked
f5f9ee
       structure a function pointer without that new callback
f5f9ee
       option
f5f9ee
    
f5f9ee
    Here is 1), 2), 3) implemented.
f5f9ee
    
f5f9ee
    Note, on stdc++.h.gch/O2g.gch there are just those 10 relocations without
f5f9ee
    the second patch, with it a few more, but nothing huge.  And for non-PIEs
f5f9ee
    there isn't really any extra work on the load side except freading two scalar
f5f9ee
    values and fseek.
f5f9ee
    
f5f9ee
    2021-12-03  Jakub Jelinek  <jakub@redhat.com>
f5f9ee
    
f5f9ee
            PR pch/71934
f5f9ee
    gcc/
f5f9ee
            * ggc.h (gt_pch_note_callback): Declare.
f5f9ee
            * gengtype.h (enum typekind): Add TYPE_CALLBACK.
f5f9ee
            (callback_type): Declare.
f5f9ee
            * gengtype.c (dbgprint_count_type_at): Handle TYPE_CALLBACK.
f5f9ee
            (callback_type): New variable.
f5f9ee
            (process_gc_options): Add CALLBACK argument, handle callback
f5f9ee
            option.
f5f9ee
            (set_gc_used_type): Adjust process_gc_options caller, if callback,
f5f9ee
            set type to &callback_type.
f5f9ee
            (output_mangled_typename): Handle TYPE_CALLBACK.
f5f9ee
            (walk_type): Likewise.  Handle callback option.
f5f9ee
            (write_types_process_field): Handle TYPE_CALLBACK.
f5f9ee
            (write_types_local_user_process_field): Likewise.
f5f9ee
            (write_types_local_process_field): Likewise.
f5f9ee
            (write_root): Likewise.
f5f9ee
            (dump_typekind): Likewise.
f5f9ee
            (dump_type): Likewise.
f5f9ee
            * gengtype-state.c (type_lineloc): Handle TYPE_CALLBACK.
f5f9ee
            (state_writer::write_state_callback_type): New method.
f5f9ee
            (state_writer::write_state_type): Handle TYPE_CALLBACK.
f5f9ee
            (read_state_callback_type): New function.
f5f9ee
            (read_state_type): Handle TYPE_CALLBACK.
f5f9ee
            * ggc-common.c (callback_vec): New variable.
f5f9ee
            (gt_pch_note_callback): New function.
f5f9ee
            (gt_pch_save): Stream out gt_pch_save function address and relocation
f5f9ee
            table.
f5f9ee
            (gt_pch_restore): Stream in saved gt_pch_save function address and
f5f9ee
            relocation table and apply relocations if needed.
f5f9ee
            * doc/gty.texi (callback): Document new GTY option.
f5f9ee
            * varasm.c (get_unnamed_section): Change callback argument's type and
f5f9ee
            last argument's type from const void * to const char *.
f5f9ee
            (output_section_asm_op): Change argument's type from const void *
f5f9ee
            to const char *, remove unnecessary cast.
f5f9ee
            * tree-core.h (struct tree_translation_unit_decl): Drop GTY((skip))
f5f9ee
            from language member.
f5f9ee
            * output.h (unnamed_section_callback): Change argument type from
f5f9ee
            const void * to const char *.
f5f9ee
            (struct unnamed_section): Use GTY((callback)) instead of GTY((skip))
f5f9ee
            for callback member.  Change data member type from const void *
f5f9ee
            to const char *.
f5f9ee
            (struct noswitch_section): Use GTY((callback)) instead of GTY((skip))
f5f9ee
            for callback member.
f5f9ee
            (get_unnamed_section): Change callback argument's type and
f5f9ee
            last argument's type from const void * to const char *.
f5f9ee
            (output_section_asm_op): Change argument's type from const void *
f5f9ee
            to const char *.
f5f9ee
            * config/avr/avr.c (avr_output_progmem_section_asm_op): Likewise.
f5f9ee
            Remove unneeded cast.
f5f9ee
            * config/darwin.c (output_objc_section_asm_op): Change argument's type
f5f9ee
            from const void * to const char *.
f5f9ee
            * config/pa/pa.c (som_output_text_section_asm_op): Likewise.
f5f9ee
            (som_output_comdat_data_section_asm_op): Likewise.
f5f9ee
            * config/rs6000/rs6000.c (rs6000_elf_output_toc_section_asm_op):
f5f9ee
            Likewise.
f5f9ee
            (rs6000_xcoff_output_readonly_section_asm_op): Likewise.  Instead
f5f9ee
            of dereferencing directive hardcode variable names and decide based on
f5f9ee
            whether directive is NULL or not.
f5f9ee
            (rs6000_xcoff_output_readwrite_section_asm_op): Change argument's type
f5f9ee
            from const void * to const char *.
f5f9ee
            (rs6000_xcoff_output_tls_section_asm_op): Likewise.  Instead
f5f9ee
            of dereferencing directive hardcode variable names and decide based on
f5f9ee
            whether directive is NULL or not.
f5f9ee
            (rs6000_xcoff_output_toc_section_asm_op): Change argument's type
f5f9ee
            from const void * to const char *.
f5f9ee
            (rs6000_xcoff_asm_init_sections): Adjust get_unnamed_section callers.
f5f9ee
    gcc/c-family/
f5f9ee
            * c-pch.c (struct c_pch_validity): Remove pch_init member.
f5f9ee
            (pch_init): Don't initialize v.pch_init.
f5f9ee
            (c_common_valid_pch): Don't warn and punt if .text addresses change.
f5f9ee
    libcpp/
f5f9ee
            * include/line-map.h (class line_maps): Add GTY((callback)) to
f5f9ee
            reallocator and round_alloc_size members.
f5f9ee
f5f9ee
commit 4dc6d19222581c77a174d44d97507d234fb7e39b
f5f9ee
Author: Jakub Jelinek <jakub@redhat.com>
f5f9ee
Date:   Mon Dec 6 11:18:58 2021 +0100
f5f9ee
f5f9ee
    avr: Fix AVR build [PR71934]
f5f9ee
    
f5f9ee
    On Mon, Dec 06, 2021 at 11:00:30AM +0100, Martin Liška wrote:
f5f9ee
    > Jakub, I think the patch broke avr-linux target:
f5f9ee
    >
f5f9ee
    > g++  -fno-PIE -c   -g   -DIN_GCC  -DCROSS_DIRECTORY_STRUCTURE   -fno-exceptions -fno-rtti -fasynchronous-unwind-tables -W -Wall -Wno-narrowing -Wwrite-strings -Wcast-qual -Wno-erro
f5f9ee
    > /home/marxin/Programming/gcc/gcc/config/avr/avr.c: In function ‘void avr_output_data_section_asm_op(const void*)’:
f5f9ee
    > /home/marxin/Programming/gcc/gcc/config/avr/avr.c:10097:26: error: invalid conversion from ‘const void*’ to ‘const char*’ [-fpermissive]
f5f9ee
    
f5f9ee
    This patch fixes that.
f5f9ee
    
f5f9ee
    2021-12-06  Jakub Jelinek  <jakub@redhat.com>
f5f9ee
    
f5f9ee
            PR pch/71934
f5f9ee
            * config/avr/avr.c (avr_output_data_section_asm_op,
f5f9ee
            avr_output_bss_section_asm_op): Change argument type from const void *
f5f9ee
            to const char *.
f5f9ee
f5f9ee
diff --git a/gcc/c-family/c-pch.c b/gcc/c-family/c-pch.c
f5f9ee
index 5da60423354..2cafa1387bb 100644
f5f9ee
--- a/gcc/c-family/c-pch.c
f5f9ee
+++ b/gcc/c-family/c-pch.c
f5f9ee
@@ -58,7 +58,6 @@ struct c_pch_validity
f5f9ee
 {
f5f9ee
   unsigned char debug_info_type;
f5f9ee
   signed char match[MATCH_SIZE];
f5f9ee
-  void (*pch_init) (void);
f5f9ee
   size_t target_data_length;
f5f9ee
 };
f5f9ee
 
f5f9ee
@@ -123,7 +122,6 @@ pch_init (void)
f5f9ee
 	gcc_assert (v.match[i] == *pch_matching[i].flag_var);
f5f9ee
       }
f5f9ee
   }
f5f9ee
-  v.pch_init = &pch_init;
f5f9ee
   target_validity = targetm.get_pch_validity (&v.target_data_length);
f5f9ee
 
f5f9ee
   if (fwrite (partial_pch, IDENT_LENGTH, 1, f) != 1
f5f9ee
@@ -287,20 +285,6 @@ c_common_valid_pch (cpp_reader *pfile, c
f5f9ee
 	}
f5f9ee
   }
f5f9ee
 
f5f9ee
-  /* If the text segment was not loaded at the same address as it was
f5f9ee
-     when the PCH file was created, function pointers loaded from the
f5f9ee
-     PCH will not be valid.  We could in theory remap all the function
f5f9ee
-     pointers, but no support for that exists at present.
f5f9ee
-     Since we have the same executable, it should only be necessary to
f5f9ee
-     check one function.  */
f5f9ee
-  if (v.pch_init != &pch_init)
f5f9ee
-    {
f5f9ee
-      if (cpp_get_options (pfile)->warn_invalid_pch)
f5f9ee
-	cpp_error (pfile, CPP_DL_WARNING,
f5f9ee
-		   "%s: had text segment at different address", name);
f5f9ee
-      return 2;
f5f9ee
-    }
f5f9ee
-
f5f9ee
   /* Check the target-specific validity data.  */
f5f9ee
   {
f5f9ee
     void *this_file_data = xmalloc (v.target_data_length);
f5f9ee
diff --git a/gcc/config/avr/avr.c b/gcc/config/avr/avr.c
f5f9ee
index 200701a583c..6ba038881d6 100644
f5f9ee
--- a/gcc/config/avr/avr.c
f5f9ee
+++ b/gcc/config/avr/avr.c
f5f9ee
@@ -10114,10 +10114,9 @@ avr_output_bss_section_asm_op (const void *data)
f5f9ee
 /* Unnamed section callback for progmem*.data sections.  */
f5f9ee
 
f5f9ee
 static void
f5f9ee
-avr_output_progmem_section_asm_op (const void *data)
f5f9ee
+avr_output_progmem_section_asm_op (const char *data)
f5f9ee
 {
f5f9ee
-  fprintf (asm_out_file, "\t.section\t%s,\"a\",@progbits\n",
f5f9ee
-           (const char*) data);
f5f9ee
+  fprintf (asm_out_file, "\t.section\t%s,\"a\",@progbits\n", data);
f5f9ee
 }
f5f9ee
 
f5f9ee
 
f5f9ee
diff --git a/gcc/config/darwin.c b/gcc/config/darwin.c
f5f9ee
index c5ba7927ce1..8ad5b26c980 100644
f5f9ee
--- a/gcc/config/darwin.c
f5f9ee
+++ b/gcc/config/darwin.c
f5f9ee
@@ -134,7 +134,7 @@ int emit_aligned_common = false;
f5f9ee
    DIRECTIVE is as for output_section_asm_op.  */
f5f9ee
 
f5f9ee
 static void
f5f9ee
-output_objc_section_asm_op (const void *directive)
f5f9ee
+output_objc_section_asm_op (const char *directive)
f5f9ee
 {
f5f9ee
   static bool been_here = false;
f5f9ee
 
f5f9ee
diff --git a/gcc/config/pa/pa.c b/gcc/config/pa/pa.c
f5f9ee
index f22d25a4066..2b10ef34061 100644
f5f9ee
--- a/gcc/config/pa/pa.c
f5f9ee
+++ b/gcc/config/pa/pa.c
f5f9ee
@@ -10009,7 +10009,7 @@ pa_arg_partial_bytes (cumulative_args_t cum_v, const function_arg_info &arg)
f5f9ee
    to the default text subspace.  */
f5f9ee
 
f5f9ee
 static void
f5f9ee
-som_output_text_section_asm_op (const void *data ATTRIBUTE_UNUSED)
f5f9ee
+som_output_text_section_asm_op (const char *data ATTRIBUTE_UNUSED)
f5f9ee
 {
f5f9ee
   gcc_assert (TARGET_SOM);
f5f9ee
   if (TARGET_GAS)
f5f9ee
@@ -10053,7 +10053,7 @@ som_output_text_section_asm_op (const void *data ATTRIBUTE_UNUSED)
f5f9ee
    sections.  This function is only used with SOM.  */
f5f9ee
 
f5f9ee
 static void
f5f9ee
-som_output_comdat_data_section_asm_op (const void *data)
f5f9ee
+som_output_comdat_data_section_asm_op (const char *data)
f5f9ee
 {
f5f9ee
   in_section = NULL;
f5f9ee
   output_section_asm_op (data);
f5f9ee
diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c
f5f9ee
index 945157b1c1a..34089743759 100644
f5f9ee
--- a/gcc/config/rs6000/rs6000.c
f5f9ee
+++ b/gcc/config/rs6000/rs6000.c
f5f9ee
@@ -20599,7 +20599,7 @@ rs6000_ms_bitfield_layout_p (const_tree record_type)
f5f9ee
 /* A get_unnamed_section callback, used for switching to toc_section.  */
f5f9ee
 
f5f9ee
 static void
f5f9ee
-rs6000_elf_output_toc_section_asm_op (const void *data ATTRIBUTE_UNUSED)
f5f9ee
+rs6000_elf_output_toc_section_asm_op (const char *data ATTRIBUTE_UNUSED)
f5f9ee
 {
f5f9ee
   if ((DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2)
f5f9ee
       && TARGET_MINIMAL_TOC)
f5f9ee
@@ -21303,35 +21303,39 @@ rs6000_xcoff_asm_globalize_label (FILE *stream, const char *name)
f5f9ee
    points to the section string variable.  */
f5f9ee
 
f5f9ee
 static void
f5f9ee
-rs6000_xcoff_output_readonly_section_asm_op (const void *directive)
f5f9ee
+rs6000_xcoff_output_readonly_section_asm_op (const char *directive)
f5f9ee
 {
f5f9ee
   fprintf (asm_out_file, "\t.csect %s[RO],%s\n",
f5f9ee
-	   *(const char *const *) directive,
f5f9ee
+	   directive
f5f9ee
+	   ? xcoff_private_rodata_section_name
f5f9ee
+	   : xcoff_read_only_section_name,
f5f9ee
 	   XCOFF_CSECT_DEFAULT_ALIGNMENT_STR);
f5f9ee
 }
f5f9ee
 
f5f9ee
 /* Likewise for read-write sections.  */
f5f9ee
 
f5f9ee
 static void
f5f9ee
-rs6000_xcoff_output_readwrite_section_asm_op (const void *directive)
f5f9ee
+rs6000_xcoff_output_readwrite_section_asm_op (const char *)
f5f9ee
 {
f5f9ee
   fprintf (asm_out_file, "\t.csect %s[RW],%s\n",
f5f9ee
-	   *(const char *const *) directive,
f5f9ee
+	   xcoff_private_data_section_name,
f5f9ee
 	   XCOFF_CSECT_DEFAULT_ALIGNMENT_STR);
f5f9ee
 }
f5f9ee
 
f5f9ee
 static void
f5f9ee
-rs6000_xcoff_output_tls_section_asm_op (const void *directive)
f5f9ee
+rs6000_xcoff_output_tls_section_asm_op (const char *directive)
f5f9ee
 {
f5f9ee
   fprintf (asm_out_file, "\t.csect %s[TL],%s\n",
f5f9ee
-	   *(const char *const *) directive,
f5f9ee
+	   directive
f5f9ee
+	   ? xcoff_private_data_section_name
f5f9ee
+	   : xcoff_tls_data_section_name,
f5f9ee
 	   XCOFF_CSECT_DEFAULT_ALIGNMENT_STR);
f5f9ee
 }
f5f9ee
 
f5f9ee
 /* A get_unnamed_section callback, used for switching to toc_section.  */
f5f9ee
 
f5f9ee
 static void
f5f9ee
-rs6000_xcoff_output_toc_section_asm_op (const void *data ATTRIBUTE_UNUSED)
f5f9ee
+rs6000_xcoff_output_toc_section_asm_op (const char *data ATTRIBUTE_UNUSED)
f5f9ee
 {
f5f9ee
   if (TARGET_MINIMAL_TOC)
f5f9ee
     {
f5f9ee
@@ -21358,26 +21362,26 @@ rs6000_xcoff_asm_init_sections (void)
f5f9ee
 {
f5f9ee
   read_only_data_section
f5f9ee
     = get_unnamed_section (0, rs6000_xcoff_output_readonly_section_asm_op,
f5f9ee
-			   &xcoff_read_only_section_name);
f5f9ee
+			   NULL);
f5f9ee
 
f5f9ee
   private_data_section
f5f9ee
     = get_unnamed_section (SECTION_WRITE,
f5f9ee
 			   rs6000_xcoff_output_readwrite_section_asm_op,
f5f9ee
-			   &xcoff_private_data_section_name);
f5f9ee
+			   NULL);
f5f9ee
 
f5f9ee
   read_only_private_data_section
f5f9ee
     = get_unnamed_section (0, rs6000_xcoff_output_readonly_section_asm_op,
f5f9ee
-			   &xcoff_private_rodata_section_name);
f5f9ee
+			   "");
f5f9ee
 
f5f9ee
   tls_data_section
f5f9ee
     = get_unnamed_section (SECTION_TLS,
f5f9ee
 			   rs6000_xcoff_output_tls_section_asm_op,
f5f9ee
-			   &xcoff_tls_data_section_name);
f5f9ee
+			   NULL);
f5f9ee
 
f5f9ee
   tls_private_data_section
f5f9ee
     = get_unnamed_section (SECTION_TLS,
f5f9ee
 			   rs6000_xcoff_output_tls_section_asm_op,
f5f9ee
-			   &xcoff_private_data_section_name);
f5f9ee
+			   "");
f5f9ee
 
f5f9ee
   toc_section
f5f9ee
     = get_unnamed_section (0, rs6000_xcoff_output_toc_section_asm_op, NULL);
f5f9ee
diff --git a/gcc/doc/gty.texi b/gcc/doc/gty.texi
f5f9ee
index b996ff2c44e..ca2c8404894 100644
f5f9ee
--- a/gcc/doc/gty.texi
f5f9ee
+++ b/gcc/doc/gty.texi
f5f9ee
@@ -205,6 +205,15 @@ If @code{skip} is applied to a field, the type machinery will ignore it.
f5f9ee
 This is somewhat dangerous; the only safe use is in a union when one
f5f9ee
 field really isn't ever used.
f5f9ee
 
f5f9ee
+@findex callback
f5f9ee
+@item callback
f5f9ee
+
f5f9ee
+@code{callback} should be applied to fields with pointer to function type
f5f9ee
+and causes the field to be ignored similarly to @code{skip}, except when
f5f9ee
+writing PCH and the field is non-NULL it will remember the field's address
f5f9ee
+for relocation purposes if the process writing PCH has different load base
f5f9ee
+from a process reading PCH.
f5f9ee
+
f5f9ee
 @findex for_user
f5f9ee
 @item for_user
f5f9ee
 
f5f9ee
diff --git a/gcc/gengtype-state.c b/gcc/gengtype-state.c
f5f9ee
index ac9d536963f..36a96e84574 100644
f5f9ee
--- a/gcc/gengtype-state.c
f5f9ee
+++ b/gcc/gengtype-state.c
f5f9ee
@@ -57,6 +57,7 @@ type_lineloc (const_type_p ty)
f5f9ee
     case TYPE_STRING:
f5f9ee
     case TYPE_POINTER:
f5f9ee
     case TYPE_ARRAY:
f5f9ee
+    case TYPE_CALLBACK:
f5f9ee
       return NULL;
f5f9ee
     default:
f5f9ee
       gcc_unreachable ();
f5f9ee
@@ -171,6 +172,7 @@ private:
f5f9ee
   void write_state_version (const char *version);
f5f9ee
   void write_state_scalar_type (type_p current);
f5f9ee
   void write_state_string_type (type_p current);
f5f9ee
+  void write_state_callback_type (type_p current);
f5f9ee
   void write_state_undefined_type (type_p current);
f5f9ee
   void write_state_struct_union_type (type_p current, const char *kindstr);
f5f9ee
   void write_state_struct_type (type_p current);
f5f9ee
@@ -898,6 +900,20 @@ state_writer::write_state_string_type (type_p current)
f5f9ee
     fatal ("Unexpected type in write_state_string_type");
f5f9ee
 }
f5f9ee
 
f5f9ee
+/* Write the callback type.  There is only one such thing! */
f5f9ee
+void
f5f9ee
+state_writer::write_state_callback_type (type_p current)
f5f9ee
+{
f5f9ee
+  if (current == &callback_type)
f5f9ee
+    {
f5f9ee
+      write_any_indent (0);
f5f9ee
+      fprintf (state_file, "callback ");
f5f9ee
+      write_state_common_type_content (current);
f5f9ee
+    }
f5f9ee
+  else
f5f9ee
+    fatal ("Unexpected type in write_state_callback_type");
f5f9ee
+}
f5f9ee
+
f5f9ee
 /* Write an undefined type.  */
f5f9ee
 void
f5f9ee
 state_writer::write_state_undefined_type (type_p current)
f5f9ee
@@ -1143,6 +1159,9 @@ state_writer::write_state_type (type_p current)
f5f9ee
 	case TYPE_STRING:
f5f9ee
 	  write_state_string_type (current);
f5f9ee
 	  break;
f5f9ee
+	case TYPE_CALLBACK:
f5f9ee
+	  write_state_callback_type (current);
f5f9ee
+	  break;
f5f9ee
 	}
f5f9ee
     }
f5f9ee
 
f5f9ee
@@ -1477,6 +1496,14 @@ read_state_string_type (type_p *type)
f5f9ee
   read_state_common_type_content (*type);
f5f9ee
 }
f5f9ee
 
f5f9ee
+/* Read the callback_type.  */
f5f9ee
+static void
f5f9ee
+read_state_callback_type (type_p *type)
f5f9ee
+{
f5f9ee
+  *type = &callback_type;
f5f9ee
+  read_state_common_type_content (*type);
f5f9ee
+}
f5f9ee
+
f5f9ee
 
f5f9ee
 /* Read a lang_bitmap representing a set of GCC front-end languages.  */
f5f9ee
 static void
f5f9ee
@@ -1834,6 +1861,11 @@ read_state_type (type_p *current)
f5f9ee
 	      next_state_tokens (1);
f5f9ee
 	      read_state_string_type (current);
f5f9ee
 	    }
f5f9ee
+	  else if (state_token_is_name (t0, "callback"))
f5f9ee
+	    {
f5f9ee
+	      next_state_tokens (1);
f5f9ee
+	      read_state_callback_type (current);
f5f9ee
+	    }
f5f9ee
 	  else if (state_token_is_name (t0, "undefined"))
f5f9ee
 	    {
f5f9ee
 	      *current = XCNEW (struct type);
f5f9ee
diff --git a/gcc/gengtype.c b/gcc/gengtype.c
f5f9ee
index a77cfd92bfa..b9daaa43689 100644
f5f9ee
--- a/gcc/gengtype.c
f5f9ee
+++ b/gcc/gengtype.c
f5f9ee
@@ -172,6 +172,7 @@ dbgprint_count_type_at (const char *fil, int lin, const char *msg, type_p t)
f5f9ee
   int nb_struct = 0, nb_union = 0, nb_array = 0, nb_pointer = 0;
f5f9ee
   int nb_lang_struct = 0;
f5f9ee
   int nb_user_struct = 0, nb_undefined = 0;
f5f9ee
+  int nb_callback = 0;
f5f9ee
   type_p p = NULL;
f5f9ee
   for (p = t; p; p = p->next)
f5f9ee
     {
f5f9ee
@@ -202,6 +203,9 @@ dbgprint_count_type_at (const char *fil, int lin, const char *msg, type_p t)
f5f9ee
 	case TYPE_ARRAY:
f5f9ee
 	  nb_array++;
f5f9ee
 	  break;
f5f9ee
+	case TYPE_CALLBACK:
f5f9ee
+	  nb_callback++;
f5f9ee
+	  break;
f5f9ee
 	case TYPE_LANG_STRUCT:
f5f9ee
 	  nb_lang_struct++;
f5f9ee
 	  break;
f5f9ee
@@ -217,6 +221,8 @@ dbgprint_count_type_at (const char *fil, int lin, const char *msg, type_p t)
f5f9ee
     fprintf (stderr, "@@%%@@ %d structs, %d unions\n", nb_struct, nb_union);
f5f9ee
   if (nb_pointer > 0 || nb_array > 0)
f5f9ee
     fprintf (stderr, "@@%%@@ %d pointers, %d arrays\n", nb_pointer, nb_array);
f5f9ee
+  if (nb_callback > 0)
f5f9ee
+    fprintf (stderr, "@@%%@@ %d callbacks\n", nb_callback);
f5f9ee
   if (nb_lang_struct > 0)
f5f9ee
     fprintf (stderr, "@@%%@@ %d lang_structs\n", nb_lang_struct);
f5f9ee
   if (nb_user_struct > 0)
f5f9ee
@@ -495,6 +501,10 @@ struct type scalar_char = {
f5f9ee
   TYPE_SCALAR, 0, 0, 0, GC_USED, {0}
f5f9ee
 };
f5f9ee
 
f5f9ee
+struct type callback_type = {
f5f9ee
+  TYPE_CALLBACK, 0, 0, 0, GC_USED, {0}
f5f9ee
+};
f5f9ee
+
f5f9ee
 /* Lists of various things.  */
f5f9ee
 
f5f9ee
 pair_p typedefs = NULL;
f5f9ee
@@ -1464,7 +1474,7 @@ static void set_gc_used (pair_p);
f5f9ee
 
f5f9ee
 static void
f5f9ee
 process_gc_options (options_p opt, enum gc_used_enum level, int *maybe_undef,
f5f9ee
-		    int *length, int *skip, type_p *nested_ptr)
f5f9ee
+		    int *length, int *skip, int *callback, type_p *nested_ptr)
f5f9ee
 {
f5f9ee
   options_p o;
f5f9ee
   for (o = opt; o; o = o->next)
f5f9ee
@@ -1478,6 +1488,8 @@ process_gc_options (options_p opt, enum gc_used_enum level, int *maybe_undef,
f5f9ee
       *length = 1;
f5f9ee
     else if (strcmp (o->name, "skip") == 0)
f5f9ee
       *skip = 1;
f5f9ee
+    else if (strcmp (o->name, "callback") == 0)
f5f9ee
+      *callback = 1;
f5f9ee
     else if (strcmp (o->name, "nested_ptr") == 0
f5f9ee
 	     && o->kind == OPTION_NESTED)
f5f9ee
       *nested_ptr = ((const struct nested_ptr_data *) o->info.nested)->type;
f5f9ee
@@ -1526,7 +1538,7 @@ set_gc_used_type (type_p t, enum gc_used_enum level,
f5f9ee
 	type_p dummy2;
f5f9ee
 	bool allow_undefined_field_types = (t->kind == TYPE_USER_STRUCT);
f5f9ee
 
f5f9ee
-	process_gc_options (t->u.s.opt, level, &dummy, &dummy, &dummy,
f5f9ee
+	process_gc_options (t->u.s.opt, level, &dummy, &dummy, &dummy, &dummy,
f5f9ee
 			    &dummy2);
f5f9ee
 
f5f9ee
 	if (t->u.s.base_class)
f5f9ee
@@ -1542,9 +1554,10 @@ set_gc_used_type (type_p t, enum gc_used_enum level,
f5f9ee
 	    int maybe_undef = 0;
f5f9ee
 	    int length = 0;
f5f9ee
 	    int skip = 0;
f5f9ee
+	    int callback = 0;
f5f9ee
 	    type_p nested_ptr = NULL;
f5f9ee
 	    process_gc_options (f->opt, level, &maybe_undef, &length, &skip,
f5f9ee
-				&nested_ptr);
f5f9ee
+				&callback, &nested_ptr);
f5f9ee
 
f5f9ee
 	    if (nested_ptr && f->type->kind == TYPE_POINTER)
f5f9ee
 	      set_gc_used_type (nested_ptr, GC_POINTED_TO);
f5f9ee
@@ -1554,6 +1567,8 @@ set_gc_used_type (type_p t, enum gc_used_enum level,
f5f9ee
 	      set_gc_used_type (f->type->u.p, GC_MAYBE_POINTED_TO);
f5f9ee
 	    else if (skip)
f5f9ee
 	      ;			/* target type is not used through this field */
f5f9ee
+	    else if (callback)
f5f9ee
+	      f->type = &callback_type;
f5f9ee
 	    else
f5f9ee
 	      set_gc_used_type (f->type, GC_USED, allow_undefined_field_types);
f5f9ee
 	  }
f5f9ee
@@ -2519,6 +2534,7 @@ output_mangled_typename (outf_p of, const_type_p t)
f5f9ee
       {
f5f9ee
       case TYPE_NONE:
f5f9ee
       case TYPE_UNDEFINED:
f5f9ee
+      case TYPE_CALLBACK:
f5f9ee
 	gcc_unreachable ();
f5f9ee
 	break;
f5f9ee
       case TYPE_POINTER:
f5f9ee
@@ -2719,6 +2735,8 @@ walk_type (type_p t, struct walk_type_data *d)
f5f9ee
       ;
f5f9ee
     else if (strcmp (oo->name, "for_user") == 0)
f5f9ee
       ;
f5f9ee
+    else if (strcmp (oo->name, "callback") == 0)
f5f9ee
+      ;
f5f9ee
     else
f5f9ee
       error_at_line (d->line, "unknown option `%s'\n", oo->name);
f5f9ee
 
f5f9ee
@@ -2744,6 +2762,7 @@ walk_type (type_p t, struct walk_type_data *d)
f5f9ee
     {
f5f9ee
     case TYPE_SCALAR:
f5f9ee
     case TYPE_STRING:
f5f9ee
+    case TYPE_CALLBACK:
f5f9ee
       d->process_field (t, d);
f5f9ee
       break;
f5f9ee
 
f5f9ee
@@ -3275,6 +3294,7 @@ write_types_process_field (type_p f, const struct walk_type_data *d)
f5f9ee
       break;
f5f9ee
 
f5f9ee
     case TYPE_SCALAR:
f5f9ee
+    case TYPE_CALLBACK:
f5f9ee
       break;
f5f9ee
 
f5f9ee
     case TYPE_ARRAY:
f5f9ee
@@ -3820,6 +3840,7 @@ write_types_local_user_process_field (type_p f, const struct walk_type_data *d)
f5f9ee
       break;
f5f9ee
 
f5f9ee
     case TYPE_SCALAR:
f5f9ee
+    case TYPE_CALLBACK:
f5f9ee
       break;
f5f9ee
 
f5f9ee
     case TYPE_ARRAY:
f5f9ee
@@ -3906,6 +3927,13 @@ write_types_local_process_field (type_p f, const struct walk_type_data *d)
f5f9ee
     case TYPE_SCALAR:
f5f9ee
       break;
f5f9ee
 
f5f9ee
+    case TYPE_CALLBACK:
f5f9ee
+      oprintf (d->of, "%*sif ((void *)(%s) == this_obj)\n", d->indent, "",
f5f9ee
+	       d->prev_val[3]);
f5f9ee
+      oprintf (d->of, "%*s  gt_pch_note_callback (&(%s), this_obj);\n",
f5f9ee
+	       d->indent, "", d->val);
f5f9ee
+      break;
f5f9ee
+
f5f9ee
     case TYPE_ARRAY:
f5f9ee
     case TYPE_NONE:
f5f9ee
     case TYPE_UNDEFINED:
f5f9ee
@@ -4434,6 +4462,7 @@ write_root (outf_p f, pair_p v, type_p type, const char *name, int has_length,
f5f9ee
     case TYPE_UNDEFINED:
f5f9ee
     case TYPE_UNION:
f5f9ee
     case TYPE_LANG_STRUCT:
f5f9ee
+    case TYPE_CALLBACK:
f5f9ee
       error_at_line (line, "global `%s' is unimplemented type", name);
f5f9ee
     }
f5f9ee
 }
f5f9ee
@@ -4728,6 +4757,9 @@ dump_typekind (int indent, enum typekind kind)
f5f9ee
     case TYPE_ARRAY:
f5f9ee
       printf ("TYPE_ARRAY");
f5f9ee
       break;
f5f9ee
+    case TYPE_CALLBACK:
f5f9ee
+      printf ("TYPE_CALLBACK");
f5f9ee
+      break;
f5f9ee
     case TYPE_LANG_STRUCT:
f5f9ee
       printf ("TYPE_LANG_STRUCT");
f5f9ee
       break;
f5f9ee
@@ -4894,6 +4926,7 @@ dump_type (int indent, type_p t)
f5f9ee
 	      t->u.scalar_is_char ? "true" : "false");
f5f9ee
       break;
f5f9ee
     case TYPE_STRING:
f5f9ee
+    case TYPE_CALLBACK:
f5f9ee
       break;
f5f9ee
     case TYPE_STRUCT:
f5f9ee
     case TYPE_UNION:
f5f9ee
diff --git a/gcc/gengtype.h b/gcc/gengtype.h
f5f9ee
index 8a7a54957ea..8fa7064ca85 100644
f5f9ee
--- a/gcc/gengtype.h
f5f9ee
+++ b/gcc/gengtype.h
f5f9ee
@@ -154,6 +154,9 @@ enum typekind {
f5f9ee
   TYPE_UNION,           /* Type for GTY-ed discriminated unions.  */
f5f9ee
   TYPE_POINTER,         /* Pointer type to GTY-ed type.  */
f5f9ee
   TYPE_ARRAY,           /* Array of GTY-ed types.  */
f5f9ee
+  TYPE_CALLBACK,	/* A function pointer that needs relocation if
f5f9ee
+			   the executable has been loaded at a different
f5f9ee
+			   address.  */
f5f9ee
   TYPE_LANG_STRUCT,     /* GCC front-end language specific structs.
f5f9ee
                            Various languages may have homonymous but
f5f9ee
                            different structs.  */
f5f9ee
@@ -331,6 +334,9 @@ extern struct type string_type;
f5f9ee
 extern struct type scalar_nonchar;
f5f9ee
 extern struct type scalar_char;
f5f9ee
 
f5f9ee
+/* The one and only TYPE_CALLBACK.  */
f5f9ee
+extern struct type callback_type;
f5f9ee
+
f5f9ee
 /* Test if a type is a union, either a plain one or a language
f5f9ee
    specific one.  */
f5f9ee
 #define UNION_P(x)					\
f5f9ee
diff --git a/gcc/ggc-common.c b/gcc/ggc-common.c
f5f9ee
index b6abed1d9a2..7c998e95473 100644
f5f9ee
--- a/gcc/ggc-common.c
f5f9ee
+++ b/gcc/ggc-common.c
f5f9ee
@@ -256,6 +256,7 @@ saving_hasher::equal (const ptr_data *p1
f5f9ee
 }
f5f9ee
 
f5f9ee
 static hash_table<saving_hasher> *saving_htab;
f5f9ee
+static vec<void *> callback_vec;
f5f9ee
 
f5f9ee
 /* Register an object in the hash table.  */
f5f9ee
 
f5f9ee
@@ -288,6 +289,23 @@ gt_pch_note_object (void *obj, void *not
f5f9ee
   return 1;
f5f9ee
 }
f5f9ee
 
f5f9ee
+/* Register address of a callback pointer.  */
f5f9ee
+void
f5f9ee
+gt_pch_note_callback (void *obj, void *base)
f5f9ee
+{
f5f9ee
+  void *ptr;
f5f9ee
+  memcpy (&ptr, obj, sizeof (void *));
f5f9ee
+  if (ptr != NULL)
f5f9ee
+    {
f5f9ee
+      struct ptr_data *data
f5f9ee
+	= (struct ptr_data *)
f5f9ee
+	  saving_htab->find_with_hash (base, POINTER_HASH (base));
f5f9ee
+      gcc_assert (data);
f5f9ee
+      callback_vec.safe_push ((char *) data->new_addr
f5f9ee
+			      + ((char *) obj - (char *) base));
f5f9ee
+    }
f5f9ee
+}
f5f9ee
+
f5f9ee
 /* Register an object in the hash table.  */
f5f9ee
 
f5f9ee
 void
f5f9ee
@@ -582,10 +600,20 @@ gt_pch_save (FILE *f)
f5f9ee
   ggc_pch_finish (state.d, state.f);
f5f9ee
   gt_pch_fixup_stringpool ();
f5f9ee
 
f5f9ee
+  unsigned num_callbacks = callback_vec.length ();
f5f9ee
+  void (*pch_save) (FILE *) = &gt_pch_save;
f5f9ee
+  if (fwrite (&pch_save, sizeof (pch_save), 1, f) != 1
f5f9ee
+      || fwrite (&num_callbacks, sizeof (num_callbacks), 1, f) != 1
f5f9ee
+      || (num_callbacks
f5f9ee
+	  && fwrite (callback_vec.address (), sizeof (void *), num_callbacks,
f5f9ee
+		     f) != num_callbacks))
f5f9ee
+    fatal_error (input_location, "cannot write PCH file: %m");
f5f9ee
+
f5f9ee
   XDELETE (state.ptrs);
f5f9ee
   XDELETE (this_object);
f5f9ee
   delete saving_htab;
f5f9ee
   saving_htab = NULL;
f5f9ee
+  callback_vec.release ();
f5f9ee
 }
f5f9ee
 
f5f9ee
 /* Read the state of the compiler back in from F.  */
f5f9ee
@@ -639,6 +667,30 @@ gt_pch_restore (FILE *f)
f5f9ee
   ggc_pch_read (f, mmi.preferred_base);
f5f9ee
 
f5f9ee
   gt_pch_restore_stringpool ();
f5f9ee
+
f5f9ee
+  void (*pch_save) (FILE *);
f5f9ee
+  unsigned num_callbacks;
f5f9ee
+  if (fread (&pch_save, sizeof (pch_save), 1, f) != 1
f5f9ee
+      || fread (&num_callbacks, sizeof (num_callbacks), 1, f) != 1)
f5f9ee
+    fatal_error (input_location, "cannot read PCH file: %m");
f5f9ee
+  if (pch_save != &gt_pch_save)
f5f9ee
+    {
f5f9ee
+      uintptr_t bias = (uintptr_t) &gt_pch_save - (uintptr_t) pch_save;
f5f9ee
+      void **ptrs = XNEWVEC (void *, num_callbacks);
f5f9ee
+      unsigned i;
f5f9ee
+
f5f9ee
+      if (fread (ptrs, sizeof (void *), num_callbacks, f) != num_callbacks)
f5f9ee
+	fatal_error (input_location, "cannot read PCH file: %m");
f5f9ee
+      for (i = 0; i < num_callbacks; ++i)
f5f9ee
+	{
f5f9ee
+	  memcpy (&pch_save, ptrs[i], sizeof (pch_save));
f5f9ee
+	  pch_save = (void (*) (FILE *)) ((uintptr_t) pch_save + bias);
f5f9ee
+	  memcpy (ptrs[i], &pch_save, sizeof (pch_save));
f5f9ee
+	}
f5f9ee
+      XDELETE (ptrs);
f5f9ee
+    }
f5f9ee
+  else if (fseek (f, num_callbacks * sizeof (void *), SEEK_CUR) != 0)
f5f9ee
+    fatal_error (input_location, "cannot read PCH file: %m");
f5f9ee
 }
f5f9ee
 
f5f9ee
 /* Default version of HOST_HOOKS_GT_PCH_GET_ADDRESS when mmap is not present.
f5f9ee
diff --git a/gcc/ggc.h b/gcc/ggc.h
f5f9ee
index 5e921d957fd..c005f7e0412 100644
f5f9ee
--- a/gcc/ggc.h
f5f9ee
+++ b/gcc/ggc.h
f5f9ee
@@ -46,6 +46,10 @@ typedef void (*gt_handle_reorder) (void *, void *, gt_pointer_operator,
f5f9ee
 /* Used by the gt_pch_n_* routines.  Register an object in the hash table.  */
f5f9ee
 extern int gt_pch_note_object (void *, void *, gt_note_pointers);
f5f9ee
 
f5f9ee
+/* Used by the gt_pch_p_* routines.  Register address of a callback
f5f9ee
+   pointer.  */
f5f9ee
+extern void gt_pch_note_callback (void *, void *);
f5f9ee
+
f5f9ee
 /* Used by the gt_pch_n_* routines.  Register that an object has a reorder
f5f9ee
    function.  */
f5f9ee
 extern void gt_pch_note_reorder (void *, void *, gt_handle_reorder);
f5f9ee
diff --git a/gcc/output.h b/gcc/output.h
f5f9ee
index 8f6f15308f4..4a23795bf7e 100644
f5f9ee
--- a/gcc/output.h
f5f9ee
+++ b/gcc/output.h
f5f9ee
@@ -456,7 +456,7 @@ struct GTY(()) named_section {
f5f9ee
 
f5f9ee
 /* A callback that writes the assembly code for switching to an unnamed
f5f9ee
    section.  The argument provides callback-specific data.  */
f5f9ee
-typedef void (*unnamed_section_callback) (const void *);
f5f9ee
+typedef void (*unnamed_section_callback) (const char *);
f5f9ee
 
f5f9ee
 /* Information about a SECTION_UNNAMED section.  */
f5f9ee
 struct GTY(()) unnamed_section {
f5f9ee
@@ -464,8 +464,8 @@ struct GTY(()) unnamed_section {
f5f9ee
 
f5f9ee
   /* The callback used to switch to the section, and the data that
f5f9ee
      should be passed to the callback.  */
f5f9ee
-  unnamed_section_callback GTY ((skip)) callback;
f5f9ee
-  const void *GTY ((skip)) data;
f5f9ee
+  unnamed_section_callback GTY ((callback)) callback;
f5f9ee
+  const char *data;
f5f9ee
 
f5f9ee
   /* The next entry in the chain of unnamed sections.  */
f5f9ee
   section *next;
f5f9ee
@@ -489,7 +489,7 @@ struct GTY(()) noswitch_section {
f5f9ee
   struct section_common common;
f5f9ee
 
f5f9ee
   /* The callback used to assemble decls in this section.  */
f5f9ee
-  noswitch_section_callback GTY ((skip)) callback;
f5f9ee
+  noswitch_section_callback GTY ((callback)) callback;
f5f9ee
 };
f5f9ee
 
f5f9ee
 /* Information about a section, which may be named or unnamed.  */
f5f9ee
@@ -524,8 +524,8 @@ extern GTY(()) section *bss_noswitch_sec
f5f9ee
 extern GTY(()) section *in_section;
f5f9ee
 extern GTY(()) bool in_cold_section_p;
f5f9ee
 
f5f9ee
-extern section *get_unnamed_section (unsigned int, void (*) (const void *),
f5f9ee
-				     const void *);
f5f9ee
+extern section *get_unnamed_section (unsigned int, void (*) (const char *),
f5f9ee
+				     const char *);
f5f9ee
 extern section *get_section (const char *, unsigned int, tree);
f5f9ee
 extern section *get_named_section (tree, const char *, int);
f5f9ee
 extern section *get_variable_section (tree, bool);
f5f9ee
@@ -546,7 +546,7 @@ extern section *get_cdtor_priority_secti
f5f9ee
 
f5f9ee
 extern bool unlikely_text_section_p (section *);
f5f9ee
 extern void switch_to_section (section *);
f5f9ee
-extern void output_section_asm_op (const void *);
f5f9ee
+extern void output_section_asm_op (const char *);
f5f9ee
 
f5f9ee
 extern void record_tm_clone_pair (tree, tree);
f5f9ee
 extern void finish_tm_clone_pairs (void);
f5f9ee
diff --git a/gcc/tree-core.h b/gcc/tree-core.h
f5f9ee
index 8ab119dc9a2..91ae5237d7e 100644
f5f9ee
--- a/gcc/tree-core.h
f5f9ee
+++ b/gcc/tree-core.h
f5f9ee
@@ -1961,7 +1961,7 @@ struct GTY(()) tree_function_decl {
f5f9ee
 struct GTY(()) tree_translation_unit_decl {
f5f9ee
   struct tree_decl_common common;
f5f9ee
   /* Source language of this translation unit.  Used for DWARF output.  */
f5f9ee
-  const char * GTY((skip(""))) language;
f5f9ee
+  const char *language;
f5f9ee
   /* TODO: Non-optimization used to build this translation unit.  */
f5f9ee
   /* TODO: Root of a partial DWARF tree for global types and decls.  */
f5f9ee
 };
f5f9ee
diff --git a/gcc/varasm.c b/gcc/varasm.c
f5f9ee
index 9315e2c6936..aff93ca5de9 100644
f5f9ee
--- a/gcc/varasm.c
f5f9ee
+++ b/gcc/varasm.c
f5f9ee
@@ -250,8 +250,8 @@ object_block_hasher::hash (object_block *old)
f5f9ee
 /* Return a new unnamed section with the given fields.  */
f5f9ee
 
f5f9ee
 section *
f5f9ee
-get_unnamed_section (unsigned int flags, void (*callback) (const void *),
f5f9ee
-		     const void *data)
f5f9ee
+get_unnamed_section (unsigned int flags, void (*callback) (const char *),
f5f9ee
+		     const char *data)
f5f9ee
 {
f5f9ee
   section *sect;
f5f9ee
 
f5f9ee
@@ -7778,9 +7778,9 @@ file_end_indicate_split_stack (void)
f5f9ee
    a get_unnamed_section callback.  */
f5f9ee
 
f5f9ee
 void
f5f9ee
-output_section_asm_op (const void *directive)
f5f9ee
+output_section_asm_op (const char *directive)
f5f9ee
 {
f5f9ee
-  fprintf (asm_out_file, "%s\n", (const char *) directive);
f5f9ee
+  fprintf (asm_out_file, "%s\n", directive);
f5f9ee
 }
f5f9ee
 
f5f9ee
 /* Emit assembly code to switch to section NEW_SECTION.  Do nothing if
f5f9ee
diff --git a/libcpp/include/line-map.h b/libcpp/include/line-map.h
f5f9ee
index 8b5e2f82982..bc40e333579 100644
f5f9ee
--- a/libcpp/include/line-map.h
f5f9ee
+++ b/libcpp/include/line-map.h
f5f9ee
@@ -758,11 +758,11 @@ struct GTY(()) line_maps {
f5f9ee
 
f5f9ee
   /* If non-null, the allocator to use when resizing 'maps'.  If null,
f5f9ee
      xrealloc is used.  */
f5f9ee
-  line_map_realloc reallocator;
f5f9ee
+  line_map_realloc GTY((callback)) reallocator;
f5f9ee
 
f5f9ee
   /* The allocators' function used to know the actual size it
f5f9ee
      allocated, for a certain allocation size requested.  */
f5f9ee
-  line_map_round_alloc_size_func round_alloc_size;
f5f9ee
+  line_map_round_alloc_size_func GTY((callback)) round_alloc_size;
f5f9ee
 
f5f9ee
   struct location_adhoc_data_map location_adhoc_data_map;
f5f9ee
 
f5f9ee
diff --git a/gcc/config/avr/avr.c b/gcc/config/avr/avr.c
f5f9ee
index 6ba038881d6..1c2f7d564e7 100644
f5f9ee
--- a/gcc/config/avr/avr.c
f5f9ee
+++ b/gcc/config/avr/avr.c
f5f9ee
@@ -10089,7 +10089,7 @@ avr_asm_asm_output_aligned_bss (FILE *file, tree decl, const char *name,
f5f9ee
    to track need of __do_copy_data.  */
f5f9ee
 
f5f9ee
 static void
f5f9ee
-avr_output_data_section_asm_op (const void *data)
f5f9ee
+avr_output_data_section_asm_op (const char *data)
f5f9ee
 {
f5f9ee
   avr_need_copy_data_p = true;
f5f9ee
 
f5f9ee
@@ -10102,7 +10102,7 @@ avr_output_data_section_asm_op (const void *data)
f5f9ee
    to track need of __do_clear_bss.  */
f5f9ee
 
f5f9ee
 static void
f5f9ee
-avr_output_bss_section_asm_op (const void *data)
f5f9ee
+avr_output_bss_section_asm_op (const char *data)
f5f9ee
 {
f5f9ee
   avr_need_clear_bss_p = true;
f5f9ee