This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH,4.6/4.5.1,PR42776] Implement LTO for Windows (PE-COFF) targets.


    Hi list,

  The attached patch generalizes the interface between lto1 and its object
file reader, and provides an object file format-specific reader for COFF
(currently only validated against and enabled for Windows platforms) as an
alternative to the current ELF object file reader.

  The configury provided to make the object format reader selectable will
facilitate an eventual solution to PR43729 (no LTO on Mach-O) and the COFF
reader will be easily extensible (if not already sufficient) for any other
COFF targets that want to enable LTO.

ChangeLog:

	PR lto/42776
	* configure.ac (--enable-lto): Refactor handling so libelf tests
	are only performed inside then-clause of ACX_ELF_TARGET_IFELSE,
	and allow LTO to be explicitly enabled on non-ELF platforms that
	are known to support it inside else-clause.
	* configure: Regenerate.

gcc/ChangeLog

	PR lto/42776
	* configure.ac (gcc_cv_as_section_has_align): Set if installed
	binutils supports extended .section directive needed by LTO, or
	warn if older binutils found.
	(LTO_BINARY_READER): New AC_SUBST'd variable.
	(LTO_USE_LIBELF): Likewise.
	* gcc/config.gcc (lto_binary_reader): New target-specific configure
	variable.
	* gcc/Makefile.in (LTO_BINARY_READER): Import AC_SUBST'd autoconf var.
	(LTO_USE_LIBELF): Likewise.
	* configure: Regenerate.

	* collect2.c (is_elf): Rename from this ...
	(is_elf_or_coff): ... to this, and recognize and allow i386 COFF
	 object files in addition to ELF-formatted ones.
	(scan_prog_file): Caller updated.  Also allow for LTO info marker
	symbol to be prefixed or not by an extra underscore.

	* config/i386/t-cygming (winnt.o): Also depend on LTO_STREAMER_H.
	* config/i386/winnt.c: Also #include lto-streamer.h
	(i386_pe_asm_named_section): Specify 1-byte section alignment for
	LTO named sections.
	(i386_pe_asm_output_aligned_decl_common): Add comment.
	(i386_pe_maybe_record_exported_symbol): Allow decl arg to be NULL.

lto/ChangeLog

	PR lto/42776
	* Make-lang.in (LTO_OBJS): Use LTO_BINARY_READER instead of
	hardcoding 'lto-elf.o'.
	($(LTO_EXE)): Use LTO_USE_LIBELF instead of hardcoding '-lelf'.

	* lto.h (lto_elf_file_open): Rename prototype from this ...
	(lto_obj_file_open): ... to this.
	(lto_elf_file_close): Likewise ...
	(lto_obj_file_close): ... and likewise.
	(lto_elf_build_section_table): Likewise ...
	(lto_obj_build_section_table): ... and likewise.
	(lto_elf_begin_section): Likewise ...
	(lto_obj_begin_section): ... and likewise.
	(lto_elf_append_data): Likewise ...
	(lto_obj_append_data): ... and likewise.
	(lto_elf_end_section): Likewise ...
	(lto_obj_end_section): ... and likewise.
	* lto.c (lto_file_read): Update references to the above.
	(lto_wpa_write_files): Likewise.
	(lto_read_all_file_options): Likewise.
	(read_cgraph_and_symbols): Likewise.
	* gcc/lto/lto-lang.c (LANG_HOOKS_BEGIN_SECTION): Likewise.
	(LANG_HOOKS_APPEND_DATA): Likewise.
	(LANG_HOOKS_END_SECTION): Likewise.
	* lto-elf.c (lto_elf_file_open): Rename from this ...
	(lto_obj_file_open): ... to this, updating any references.
	(lto_elf_file_close): Likewise ...
	(lto_obj_file_close): ... and likewise.
	(lto_elf_build_section_table): Likewise ...
	(lto_obj_build_section_table): ... and likewise.
	(lto_elf_begin_section): Likewise ...
	(lto_obj_begin_section): ... and likewise.
	(lto_elf_append_data): Likewise ...
	(lto_obj_append_data): ... and likewise.
	(lto_elf_end_section): Likewise ...
	(lto_obj_end_section): ... and likewise.
	* lto-coff.h: New file.
	* lto-coff.c: Likewise.

gcc/testsuite/ChangeLog

	PR lto/42776
	* lib/lto.exp (lto_prune_vis_warns): New function.
	(lto-link-and-maybe-run): Call it.

  Bootstrapped on i686-pc-cygwin against r.158195, tests in progress; repeated
previous runs have shown that all lto tests (except for a couple which rely on
ELF symbol aliasing) now pass without regressions.  It's also been tested,
according to contributors on the PR, against MinGW, where it works, and on
Linux, where it doesn't break existing ELF LTO any.  I've also quickly tested
(in a non-bootstrap configuration) that the configury correctly warns when I
roll back my binutils to a version that lacks the necessary support for LTO on
COFF (an extended form of the .section directive that allows specifying
alignments below the default section alignment).

  Assuming no regressions when the tests complete, OK for HEAD, and for 4.5
branch when it reopens after the .0 release?

    cheers,
      DaveK
Index: configure.ac
===================================================================
--- configure.ac	(revision 158195)
+++ configure.ac	(working copy)
@@ -1636,17 +1636,8 @@ AC_ARG_ENABLE(lto,
 enable_lto=$enableval,
 enable_lto=yes; default_enable_lto=yes)
 
-ACX_ELF_TARGET_IFELSE([],
-if test x"$default_enable_lto" = x"yes" ; then
-  enable_lto=no
-else
-  if test x"$enable_lto" = x"yes"; then
-    AC_MSG_ERROR([LTO support requires an ELF target.])
-  fi
-fi
-default_enable_lto=no)
 
-if test x"$enable_lto" = x"yes" ; then
+ACX_ELF_TARGET_IFELSE([if test x"$enable_lto" = x"yes" ; then
   # Make sure that libelf.h and gelf.h are available.
   AC_ARG_WITH(libelf, [  --with-libelf=PATH       Specify prefix directory for the installed libelf package
                           Equivalent to --with-libelf-include=PATH/include
@@ -1762,7 +1753,24 @@ to specify its location.])
   # Flags needed for libelf.
   AC_SUBST(libelflibs)
   AC_SUBST(libelfinc)
-fi
+fi],[if test x"$default_enable_lto" = x"yes" ; then
+    # On non-ELF platforms, LTO must be explicitly enabled.
+    enable_lto=no
+  else
+  # Apart from ELF platforms, only Windows supports LTO so far.  It
+  # would also be nice to check the binutils support, but we don't
+  # have gcc_GAS_CHECK_FEATURE available here.  For now, we'll just
+  # warn during gcc/ subconfigure; unless you're bootstrapping with
+  # -flto it won't be needed until after installation anyway.
+    case $target in
+      *-cygwin*|*-mingw*) ;;
+      *) if test x"$enable_lto" = x"yes"; then
+	AC_MSG_ERROR([LTO support is not enabled for this target.])
+        fi
+      ;;
+    esac
+  fi
+  default_enable_lto=no])
 
 
 # By default, C is the only stage 1 language.
Index: gcc/configure.ac
===================================================================
--- gcc/configure.ac	(revision 158195)
+++ gcc/configure.ac	(working copy)
@@ -3186,6 +3186,19 @@ foo:	nop
 	   rm -f conftest],
 	  [AC_DEFINE(HAVE_GAS_PE_SECREL32_RELOC, 1,
 	    [Define if your assembler and linker support 32-bit section relative relocs via '.secrel32 label'.])])
+	# Test if the assembler supports the extended form of the .section
+	# directive that specifies section alignment.  LTO support uses this,
+	# but normally only after installation, so we warn but don't fail the
+	# configure if LTO is enabled but the assembler does not support it.
+	gcc_GAS_CHECK_FEATURE([.section with alignment], gcc_cv_as_section_has_align,
+	  [2,20,1],-fatal-warnings,[.section lto_test,"dr0"])
+	if test x$gcc_cv_as_section_has_align != xyes; then
+	  case ",$enable_languages," in
+	    *,lto,*)
+	      AC_MSG_WARN([LTO for $target requires binutils >= 2.20.1, but version found appears insufficient; LTO will not work until binutils is upgraded.])
+	      ;;
+	  esac
+	fi
 	;;
     esac
 
@@ -4254,6 +4267,17 @@ changequote([,])dnl
 		    AC_DEFINE(ENABLE_LTO, 1, [Define to enable LTO support.])
 		    enable_lto=yes
 		    AC_SUBST(enable_lto)
+		    # LTO needs to speak the platform's object file format, and has a
+		    # number of implementations of the required binary file access APIs.
+		    # ELF is the most common, and default.  We only link libelf if ELF
+		    # is indeed the selected format.
+		    LTO_BINARY_READER=${lto_binary_reader}
+		    LTO_USE_LIBELF=-lelf
+		    if test "x$lto_binary_reader" != "xlto-elf" ; then
+		      LTO_USE_LIBELF=
+		    fi
+		    AC_SUBST(LTO_BINARY_READER)
+		    AC_SUBST(LTO_USE_LIBELF)
 		    ;;
 		*) ;;
 	esac
Index: gcc/config.gcc
===================================================================
--- gcc/config.gcc	(revision 158195)
+++ gcc/config.gcc	(working copy)
@@ -200,6 +200,8 @@ default_use_cxa_atexit=no
 target_gtfiles=
 need_64bit_hwint=
 need_64bit_isa=
+# Selects the object file format reader/writer used by LTO.
+lto_binary_reader=lto-elf
 
 # Don't carry these over build->host->target.  Please.
 xm_file=
@@ -1339,6 +1341,7 @@ i[34567]86-*-pe | i[34567]86-*-cygwin*)
 		thread_file='posix'
 	fi
 	use_gcc_stdint=wrap
+	lto_binary_reader=lto-coff
 	;;
 i[34567]86-*-mingw* | x86_64-*-mingw*)
 	tm_file="${tm_file} i386/unix.h i386/bsd.h i386/gas.h dbxcoff.h i386/cygming.h i386/mingw32.h"
@@ -1406,6 +1409,7 @@ i[34567]86-*-mingw* | x86_64-*-mingw*)
 	cxx_target_objs="${cxx_target_objs} winnt-cxx.o msformat-c.o"
 	default_use_cxa_atexit=yes
 	use_gcc_stdint=wrap
+	lto_binary_reader=lto-coff
 	case ${enable_threads} in
 	  "" | yes | win32)	  thread_file='win32'
 	  tmake_file="${tmake_file} i386/t-gthr-win32"
Index: gcc/Makefile.in
===================================================================
--- gcc/Makefile.in	(revision 158195)
+++ gcc/Makefile.in	(working copy)
@@ -326,6 +326,10 @@ LIBELFINC = @LIBELFINC@
 # Set to 'yes' if the LTO front end is enabled.
 enable_lto = @enable_lto@
 
+# Set according to LTO object file format.
+LTO_BINARY_READER = @LTO_BINARY_READER@
+LTO_USE_LIBELF = @LTO_USE_LIBELF@
+
 # Compiler needed for plugin support
 PLUGINCC = @CC@
 
Index: gcc/lto/Make-lang.in
===================================================================
--- gcc/lto/Make-lang.in	(revision 158195)
+++ gcc/lto/Make-lang.in	(working copy)
@@ -23,7 +23,7 @@
 # The name of the LTO compiler.
 LTO_EXE = lto1$(exeext)
 # The LTO-specific object files inclued in $(LTO_EXE).
-LTO_OBJS = lto/lto-lang.o lto/lto.o lto/lto-elf.o attribs.o
+LTO_OBJS = lto/lto-lang.o lto/lto.o lto/$(LTO_BINARY_READER).o attribs.o
 LTO_H = lto/lto.h $(HASHTAB_H)
 LINKER_PLUGIN_API_H = $(srcdir)/../include/plugin-api.h
 LTO_TREE_H = lto/lto-tree.h $(LINKER_PLUGIN_API_H)
@@ -73,7 +73,7 @@ lto-warn = $(STRICT_WARN)
 
 $(LTO_EXE): $(LTO_OBJS) $(BACKEND) $(LIBDEPS)
 	$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -o $@ \
-		$(LTO_OBJS) $(BACKEND) $(BACKENDLIBS) $(LIBS) -lelf
+		$(LTO_OBJS) $(BACKEND) $(BACKENDLIBS) $(LIBS) $(LTO_USE_LIBELF)
 
 # Dependencies
 lto/lto-lang.o: lto/lto-lang.c $(CONFIG_H) coretypes.h debug.h \
@@ -88,3 +88,6 @@ lto/lto.o: lto/lto.c $(CONFIG_H) $(SYSTEM_H) coret
 	$(LTO_TAGS_H) $(LTO_STREAMER_H)
 lto/lto-elf.o: lto/lto-elf.c $(CONFIG_H) coretypes.h $(SYSTEM_H) \
 	toplev.h $(LTO_H) $(TM_H) $(LIBIBERTY_H) $(GGC_H) $(LTO_STREAMER_H)
+lto/lto-coff.o: lto/lto-coff.c $(CONFIG_H) coretypes.h $(SYSTEM_H) \
+	toplev.h $(LTO_H) $(TM_H) $(LIBIBERTY_H) $(GGC_H) $(LTO_STREAMER_H) \
+	lto/lto-coff.h
Index: gcc/collect2.c
===================================================================
--- gcc/collect2.c	(revision 158195)
+++ gcc/collect2.c	(working copy)
@@ -2548,19 +2548,21 @@ write_aix_file (FILE *stream, struct id *list)
    be in ELF format.  */
 
 static bool
-is_elf (const char *prog_name)
+is_elf_or_coff (const char *prog_name)
 {
   FILE *f;
   char buf[4];
   static char magic[4] = { 0x7f, 'E', 'L', 'F' };
+  static char coffmag[2] = { 0x4c, 0x01 };
 
-  f = fopen (prog_name, "r");
+  f = fopen (prog_name, "rb");
   if (f == NULL)
     return false;
   if (fread (buf, sizeof (buf), 1, f) != 1)
     buf[0] = 0;
   fclose (f);
-  return memcmp (buf, magic, sizeof (magic)) == 0;
+  return memcmp (buf, magic, sizeof (magic)) == 0
+	|| memcmp (buf, coffmag, sizeof (coffmag)) == 0;
 }
 
 /* Generic version to scan the name list of the loaded program for
@@ -2587,10 +2589,10 @@ scan_prog_file (const char *prog_name, scanpass wh
   if (which_pass == PASS_SECOND)
     return;
 
-  /* LTO objects must be in ELF format.  This check prevents
+  /* LTO objects must be in a known format.  This check prevents
      us from accepting an archive containing LTO objects, which
      gcc cannnot currently handle.  */
-  if (which_pass == PASS_LTOINFO && !is_elf (prog_name))
+  if (which_pass == PASS_LTOINFO && !is_elf_or_coff (prog_name))
     return;
 
   /* If we do not have an `nm', complain.  */
@@ -2670,9 +2672,9 @@ scan_prog_file (const char *prog_name, scanpass wh
           /* Look for the LTO info marker symbol, and add filename to
              the LTO objects list if found.  */
           for (p = buf; (ch = *p) != '\0' && ch != '\n'; p++)
-            if (ch == ' '
-		&& (strncmp (p + 1, "__gnu_lto_v1", 12) == 0)
-		&& ISSPACE (p[13]))
+            if (ch == ' '  && p[1] == '_' && p[2] == '_'
+		&& (strncmp (p + (p[3] == '_' ? 2 : 1), "__gnu_lto_v1", 12) == 0)
+		&& ISSPACE (p[p[3] == '_' ? 14 : 13]))
               {
                 add_lto_object (&lto_objects, prog_name);
 
Index: gcc/lto/lto.h
===================================================================
--- gcc/lto/lto.h	(revision 158195)
+++ gcc/lto/lto.h	(working copy)
@@ -38,13 +38,13 @@ extern const char *resolution_file_name;
 extern void lto_main (int);
 extern void lto_read_all_file_options (void);
 
-/* In lto-elf.c  */
-extern lto_file *lto_elf_file_open (const char *filename, bool writable);
-extern void lto_elf_file_close (lto_file *file);
-extern htab_t lto_elf_build_section_table (lto_file *file);
-extern void lto_elf_begin_section (const char *name);
-extern void lto_elf_append_data (const void *data, size_t len, void *block);
-extern void lto_elf_end_section (void);
+/* In lto-elf.c or lto-coff.c  */
+extern lto_file *lto_obj_file_open (const char *filename, bool writable);
+extern void lto_obj_file_close (lto_file *file);
+extern htab_t lto_obj_build_section_table (lto_file *file);
+extern void lto_obj_begin_section (const char *name);
+extern void lto_obj_append_data (const void *data, size_t len, void *block);
+extern void lto_obj_end_section (void);
 extern lto_file *lto_set_current_out_file (lto_file *file);
 extern lto_file *lto_get_current_out_file (void);
 
Index: gcc/lto/lto.c
===================================================================
--- gcc/lto/lto.c	(revision 158195)
+++ gcc/lto/lto.c	(working copy)
@@ -378,7 +378,7 @@ lto_file_read (lto_file *file, FILE *resolution_fi
 
   file_data = XCNEW (struct lto_file_decl_data);
   file_data->file_name = file->filename;
-  file_data->section_hash_table = lto_elf_build_section_table (file);
+  file_data->section_hash_table = lto_obj_build_section_table (file);
   file_data->renaming_hash_table = lto_create_renaming_table ();
 
   data = lto_get_section_data (file_data, LTO_section_decls, NULL, &len);
@@ -1044,9 +1044,9 @@ lto_wpa_write_files (void)
       if (cgraph_node_set_needs_ltrans_p (set))
 	{
 	  /* Write all the nodes in SET to TEMP_FILENAME.  */
-	  file = lto_elf_file_open (temp_filename, true);
+	  file = lto_obj_file_open (temp_filename, true);
 	  if (!file)
-	    fatal_error ("lto_elf_file_open() failed");
+	    fatal_error ("lto_obj_file_open() failed");
 
 	  lto_set_current_out_file (file);
 	  lto_new_extern_inline_states ();
@@ -1058,7 +1058,7 @@ lto_wpa_write_files (void)
 	  lto_delete_extern_inline_states ();
 
 	  lto_set_current_out_file (NULL);
-	  lto_elf_file_close (file);
+	  lto_obj_file_close (file);
 	}
     }
 
@@ -1775,17 +1775,17 @@ lto_read_all_file_options (void)
   for (i = 0; i < num_in_fnames; i++)
     {
       struct lto_file_decl_data *file_data;
-      lto_file *file = lto_elf_file_open (in_fnames[i], false);
+      lto_file *file = lto_obj_file_open (in_fnames[i], false);
       if (!file)
 	break;
 
       file_data = XCNEW (struct lto_file_decl_data);
       file_data->file_name = file->filename;
-      file_data->section_hash_table = lto_elf_build_section_table (file);
+      file_data->section_hash_table = lto_obj_build_section_table (file);
 
       lto_read_file_options (file_data);
 
-      lto_elf_file_close (file);
+      lto_obj_file_close (file);
       htab_delete (file_data->section_hash_table);
       free (file_data);
     }
@@ -1840,7 +1840,7 @@ read_cgraph_and_symbols (unsigned nfiles, const ch
     {
       struct lto_file_decl_data *file_data = NULL;
 
-      current_lto_file = lto_elf_file_open (fnames[i], false);
+      current_lto_file = lto_obj_file_open (fnames[i], false);
       if (!current_lto_file)
 	break;
 
@@ -1850,7 +1850,7 @@ read_cgraph_and_symbols (unsigned nfiles, const ch
 
       all_file_decl_data[last_file_ix++] = file_data;
 
-      lto_elf_file_close (current_lto_file);
+      lto_obj_file_close (current_lto_file);
       current_lto_file = NULL;
     }
 
Index: gcc/lto/lto-lang.c
===================================================================
--- gcc/lto/lto-lang.c	(revision 158195)
+++ gcc/lto/lto-lang.c	(working copy)
@@ -1158,11 +1158,11 @@ static void lto_init_ts (void)
 #define LANG_HOOKS_FORMAT_ATTRIBUTE_TABLE lto_format_attribute_table
 
 #undef LANG_HOOKS_BEGIN_SECTION
-#define LANG_HOOKS_BEGIN_SECTION lto_elf_begin_section
+#define LANG_HOOKS_BEGIN_SECTION lto_obj_begin_section
 #undef LANG_HOOKS_APPEND_DATA
-#define LANG_HOOKS_APPEND_DATA lto_elf_append_data
+#define LANG_HOOKS_APPEND_DATA lto_obj_append_data
 #undef LANG_HOOKS_END_SECTION
-#define LANG_HOOKS_END_SECTION lto_elf_end_section
+#define LANG_HOOKS_END_SECTION lto_obj_end_section
 
 #undef LANG_HOOKS_INIT_TS
 #define LANG_HOOKS_INIT_TS lto_init_ts
Index: gcc/lto/lto-elf.c
===================================================================
--- gcc/lto/lto-elf.c	(revision 158195)
+++ gcc/lto/lto-elf.c	(working copy)
@@ -179,7 +179,7 @@ eq_name (const void *p1, const void *p2)
    the start and size of each section in the .o file.  */
 
 htab_t
-lto_elf_build_section_table (lto_file *lto_file) 
+lto_obj_build_section_table (lto_file *lto_file) 
 {
   lto_elf_file *elf_file = (lto_elf_file *)lto_file;
   htab_t section_hash_table;
@@ -322,7 +322,7 @@ lto_elf_begin_section_with_type (const char *name,
 /* Begin a new ELF section named NAME in the current output file.  */
 
 void
-lto_elf_begin_section (const char *name)
+lto_obj_begin_section (const char *name)
 {
   lto_elf_begin_section_with_type (name, SHT_PROGBITS);
 }
@@ -333,7 +333,7 @@ void
    been written.  */
 
 void
-lto_elf_append_data (const void *data, size_t len, void *block)
+lto_obj_append_data (const void *data, size_t len, void *block)
 {
   lto_elf_file *file;
   Elf_Data *elf_data;
@@ -370,7 +370,7 @@ void
    and sets the current output file's scn member to NULL.  */
 
 void
-lto_elf_end_section (void)
+lto_obj_end_section (void)
 {
   lto_elf_file *file;
 
@@ -588,7 +588,7 @@ init_ehdr (lto_elf_file *elf_file)
    Returns the opened file.  */
 
 lto_file *
-lto_elf_file_open (const char *filename, bool writable)
+lto_obj_file_open (const char *filename, bool writable)
 {
   lto_elf_file *elf_file;
   lto_file *result = NULL;
@@ -688,7 +688,7 @@ lto_file *
 
  fail:
   if (result)
-    lto_elf_file_close (result);
+    lto_obj_file_close (result);
   return NULL;
 }
 
@@ -698,7 +698,7 @@ lto_file *
    any cached data buffers are freed.  */
 
 void
-lto_elf_file_close (lto_file *file)
+lto_obj_file_close (lto_file *file)
 {
   lto_elf_file *elf_file = (lto_elf_file *) file;
   struct lto_char_ptr_base *cur, *tmp;
@@ -734,7 +734,7 @@ void
       if (gelf_update_ehdr (elf_file->elf, ehdr_p) == 0)
 	fatal_error ("gelf_update_ehdr() failed: %s", elf_errmsg (-1));
       lto_write_stream (elf_file->shstrtab_stream);
-      lto_elf_end_section ();
+      lto_obj_end_section ();
 
       lto_set_current_out_file (old_file);
       free (elf_file->shstrtab_stream);
Index: gcc/lto/lto-coff.h
===================================================================
--- gcc/lto/lto-coff.h	(revision 0)
+++ gcc/lto/lto-coff.h	(revision 0)
@@ -0,0 +1,406 @@
+/* LTO routines for COFF object files.
+   Copyright 2009 Free Software Foundation, Inc.
+   Contributed by Dave Korn.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef LTO_COFF_H
+#define LTO_COFF_H
+
+/* Rather than implementing a libcoff to match libelf, or attempting to
+   integrate libbfd into GCC, this file is a self-contained (and very
+   minimal) COFF format object file reader/writer.  The generated files
+   will contain a COFF header, a number of COFF section headers, the 
+   section data itself, and a trailing string table for section names.  */
+
+/* Alignment of sections in a COFF object file.
+
+   The LTO writer uses zlib compression on the data that it streams into
+   LTO sections in the output object file.  Because these streams don't
+   have any embedded size information, the section in the object file must
+   be exactly sized to the data emitted; any trailing padding bytes will
+   be interpreted as partial and/or corrupt compressed data.
+
+   This is easy enough to do on COFF targets (with binutils 2.20.1 or
+   above) because we can specify 1-byte alignment for the LTO sections.
+   They are then emitted precisely-sized and byte-packed into the object
+   and the reader is happy when it parses them later.  This is currently
+   implemented in the x86/windows backed in i386_pe_asm_named_section()
+   in config/i386/winnt.c by detecting the LTO section name prefix, 
+
+   That would be sufficient, but for one thing.  At the start of the LTO
+   data is a header struct with (currently) a couple of version numbers and
+   some type info; see struct lto_header in lto-streamer.h.  If the sections
+   are byte-packed, this header will not necessarily be correctly-aligned
+   when it is read back into memory.
+
+   On x86 targets, which are currently the only LTO-COFF targets, misaligned
+   memory accesses aren't problematic (okay, inefficient, but not worth
+   worrying about two half-word memory reads per section in the context of
+   everything else the compiler has to do at the time!), but RISC targets may
+   fail on trying to access the header struct.  In this case, it will be
+   necessary to enable (preferably in a target-dependent fashion, but a few
+   bytes of padding are hardly an important issue if it comes down to it) the
+   COFF_ALIGNMENT macros below.
+
+   As currently implemented, this will emit padding to the necessary number
+   of bytes after each LTO section.  These bytes will constitute 'gaps' in
+   the object file structure, as they won't be covered by any section header.
+   This hasn't yet been tested, because no such RISC LTO-COFF target yet
+   exists.  If it causes problems further down the toolchain, it will be
+   necessary to adapt the code to emit additional section headers for these
+   padding bytes, but the odds are that it will "just work".
+
+  */
+
+#if 0
+#define COFF_ALIGNMENT	 (4)
+#define COFF_ALIGNMENTM1 (COFF_ALIGNMENT - 1)
+#define COFF_ALIGN(x)	 (((x) + COFF_ALIGNMENTM1) & ~COFF_ALIGNMENTM1)
+#else
+#define COFF_ALIGNMENT	 (1)
+#define COFF_ALIGN(x)	 (x)
+#endif
+
+/* COFF header machine codes.  */
+
+#define IMAGE_FILE_MACHINE_I386	(0x014c)
+
+/* Known header magics for validation, as an array initialiser.  */
+
+#define COFF_KNOWN_MACHINES \
+  { IMAGE_FILE_MACHINE_I386/*, ... add more here when working.  */ }
+
+/* COFF object file header, section and symbol flags and types.  These are
+   currently specific to PE-COFF, which is the only LTO-COFF format at the
+   time of writing.  Maintainers adding support for new COFF formats will
+   need to make these into target macros of some kind.  */
+
+/* COFF header characteristics.  */
+
+#define IMAGE_FILE_EXECUTABLE_IMAGE	(1 << 1)
+#define IMAGE_FILE_32BIT_MACHINE	(1 << 8)
+#define IMAGE_FILE_SYSTEM		(1 << 12)
+#define IMAGE_FILE_DLL			(1 << 13)
+
+/* Desired characteristics (for validation).  */
+
+#define COFF_CHARACTERISTICS \
+  (IMAGE_FILE_32BIT_MACHINE)
+
+/* Unwanted characteristics (for validation).  */
+
+#define COFF_NOT_CHARACTERISTICS \
+  (IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_SYSTEM | IMAGE_FILE_DLL)
+
+/* Section flags.  LTO emits byte-aligned read-only loadable data sections.  */
+
+#define IMAGE_SCN_CNT_INITIALIZED_DATA	 (1 << 6)
+#define IMAGE_SCN_CNT_UNINITIALIZED_DATA (1 << 7)
+#define IMAGE_SCN_ALIGN_1BYTES		 (0x1 << 20)
+#define IMAGE_SCN_MEM_DISCARDABLE	 (1 << 25)
+#define	IMAGE_SCN_MEM_SHARED		 (1 << 28)
+#define IMAGE_SCN_MEM_READ		 (1 << 30)
+
+#define COFF_SECTION_CHARACTERISTICS \
+  (IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_ALIGN_1BYTES | \
+  IMAGE_SCN_MEM_DISCARDABLE | IMAGE_SCN_MEM_SHARED | IMAGE_SCN_MEM_READ)
+
+/* Symbol-related constants.  */
+
+#define IMAGE_SYM_DEBUG		(-2)
+#define IMAGE_SYM_TYPE_NULL	(0)
+#define IMAGE_SYM_DTYPE_NULL	(0)
+#define IMAGE_SYM_CLASS_STATIC	(3)
+#define IMAGE_SYM_CLASS_FILE	(103)
+
+#define IMAGE_SYM_TYPE \
+  ((IMAGE_SYM_DTYPE_NULL << 4) | IMAGE_SYM_TYPE_NULL)
+
+/* Size of a COFF symbol in bytes.  */
+
+#define COFF_SYMBOL_SIZE	(18)
+
+/* On-disk file structures.  */
+
+struct Coff_header
+{
+  unsigned char Machine[2];
+  unsigned char NumberOfSections[2];
+  unsigned char TimeDateStamp[4];
+  unsigned char PointerToSymbolTable[4];
+  unsigned char NumberOfSymbols[4];
+  unsigned char SizeOfOptionalHeader[2];
+  unsigned char Characteristics[2];
+};
+typedef struct Coff_header Coff_header;
+
+struct Coff_section
+{
+  unsigned char Name[8];
+  unsigned char VirtualSize[4];
+  unsigned char VirtualAddress[4];
+  unsigned char SizeOfRawData[4];
+  unsigned char PointerToRawData[4];
+  unsigned char PointerToRelocations[4];
+  unsigned char PointerToLinenumbers[4];
+  unsigned char NumberOfRelocations[2];
+  unsigned char NumberOfLinenumbers[2];
+  unsigned char Characteristics[4];
+};
+typedef struct Coff_section Coff_section;
+
+struct Coff_symbol
+{
+  unsigned char Name[8];
+  unsigned char Value[4];
+  unsigned char SectionNumber[2];
+  unsigned char Type[2];
+  unsigned char StorageClass[1];
+  unsigned char NumberOfAuxSymbols[1];
+};
+typedef struct Coff_symbol Coff_symbol;
+
+struct Coff_aux_sym_file
+{
+  unsigned char FileName[18];
+};
+typedef struct Coff_aux_sym_file Coff_aux_sym_file;
+
+struct Coff_aux_sym_section
+{
+  unsigned char Length[4];
+  unsigned char NumberOfRelocations[2];
+  unsigned char NumberOfLineNumbers[2];
+  unsigned char Checksum[4];
+  unsigned char Number[2];
+  unsigned char Selection[1];
+  unsigned char Unused[3];
+};
+typedef struct Coff_aux_sym_section Coff_aux_sym_section;
+
+/* Accessor macros for the above structures.  */
+
+#define COFF_GET(struc,memb) \
+  ((COFFENDIAN ? get_be : get_le) (&(struc)->memb[0], sizeof ((struc)->memb)))
+
+#define COFF_PUT(struc,memb,val) \
+  ((COFFENDIAN ? put_be : put_le) (&(struc)->memb[0], sizeof ((struc)->memb), val))
+
+#define COFF_PUT_NDXSZ(struc,memb,val,ndx,sz) \
+  ((COFFENDIAN ? put_be : put_le) (&(struc)->memb[ndx], sz, val))
+
+/* In-memory file structures.  */
+
+/* Forward declared structs.  */
+
+struct lto_coff_data;
+struct lto_coff_section;
+struct lto_coff_file;
+
+/* Section data in output files is made of these.  */
+
+struct lto_coff_data
+{
+  /* Pointer to data block.  */
+  void *d_buf;
+
+  /* Size of data block.  */
+  ssize_t d_size;
+
+  /* Next data block for this section.  */
+  struct lto_coff_data *next;
+};
+typedef struct lto_coff_data lto_coff_data;
+
+/* This struct tracks the data for a section.  */
+
+struct lto_coff_section
+{
+  /* Singly-linked list of section's data blocks.  */
+  lto_coff_data *data_chain;
+
+  /* Offset in string table of name.  */
+  size_t strtab_offs;
+
+  /* Section type: 0 = real, 1 = dummy.  */
+  size_t type;
+
+  /* Section name.  */
+  const char *name;
+
+#if COFF_ALIGNMENT > 1
+  /* Number of trailing padding bytes needed.  */
+  ssize_t pad_needed;
+#endif
+
+  /* Raw section header data.  */
+  Coff_section coffsec;
+
+  /* Next section for this file.  */
+  struct lto_coff_section *next;
+};
+typedef struct lto_coff_section lto_coff_section;
+
+/* A COFF file.  */
+
+struct lto_coff_file 
+{
+  /* The base information.  */
+  lto_file base;
+
+  /* Common file members:  */
+
+  /* The system file descriptor for the file.  */
+  int fd;
+
+  /* The file's overall header.  */
+  Coff_header coffhdr;
+
+  /* All sections in a singly-linked list.  */
+  lto_coff_section *section_chain;
+
+  /* Readable file members:  */
+
+  /* File total size.  */
+  off_t file_size;
+
+  /* String table file offset, relative to base.offset.  */
+  off_t strtab_offs;
+
+  /* Writable file members:  */
+
+  /* The currently active section.  */
+  lto_coff_section *scn;
+
+  /* The output stream for section header names.  */
+  struct lto_output_stream *shstrtab_stream;
+
+  /* Linked list of data which must be freed *after* the file has been
+     closed.  This is an annoying limitation of libelf.  Which has been
+     faithfully reproduced here.  */
+  struct lto_char_ptr_base *data;
+};
+typedef struct lto_coff_file lto_coff_file;
+
+/* Data hunk iterator.  */
+
+#define COFF_FOR_ALL_DATA(sec,var) \
+  for (var = sec->data_chain; var; var = var->next)
+
+/* Section list iterator.  */
+
+#define COFF_FOR_ALL_SECTIONS(file,var) \
+  for (var = file->section_chain; var; var = var->next)
+
+/* Very simple endian-ness layer.  */
+
+#ifndef COFFENDIAN
+#define COFFENDIAN (BYTES_BIG_ENDIAN)
+#endif
+
+static inline unsigned int
+get_2_le (const unsigned char *ptr)
+{
+  return ptr[0] | (ptr[1] << 8);
+}
+
+static inline unsigned int
+get_4_le (const unsigned char *ptr)
+{
+  return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
+}
+
+static inline unsigned int
+get_2_be (const unsigned char *ptr)
+{
+  return ptr[0] | (ptr[1] << 8);
+}
+
+static inline unsigned int
+get_4_be (const unsigned char *ptr)
+{
+  return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
+}
+
+static inline unsigned int
+get_be (const unsigned char *ptr, size_t size)
+{
+  gcc_assert (size == 4 || size == 2);
+  return (size == 2) ? get_2_be (ptr) : get_4_be (ptr);
+}
+
+static inline unsigned int
+get_le (const unsigned char *ptr, size_t size)
+{
+  gcc_assert (size == 4 || size == 2);
+  return (size == 2) ? get_2_le (ptr) : get_4_le (ptr);
+}
+
+static inline void
+put_2_le (unsigned char *ptr, unsigned int data)
+{
+  ptr[0] = data & 0xff;
+  ptr[1] = (data >> 8) & 0xff;
+}
+
+static inline void
+put_4_le (unsigned char *ptr, unsigned int data)
+{
+  ptr[0] = data & 0xff;
+  ptr[1] = (data >> 8) & 0xff;
+  ptr[2] = (data >> 16) & 0xff;
+  ptr[3] = (data >> 24) & 0xff;
+}
+
+static inline void
+put_2_be (unsigned char *ptr, unsigned int data)
+{
+  ptr[1] = data & 0xff;
+  ptr[0] = (data >> 8) & 0xff;
+}
+
+static inline void
+put_4_be (unsigned char *ptr, unsigned int data)
+{
+  ptr[3] = data & 0xff;
+  ptr[2] = (data >> 8) & 0xff;
+  ptr[1] = (data >> 16) & 0xff;
+  ptr[0] = (data >> 24) & 0xff;
+}
+
+static inline void
+put_le (unsigned char *ptr, size_t size, unsigned int data)
+{
+  gcc_assert (size == 4 || size == 2);
+  (void) (size == 2 ? put_2_le : put_4_le) (ptr, data);
+}
+
+static inline void
+put_be (unsigned char *ptr, size_t size, unsigned int data)
+{
+  gcc_assert (size == 4 || size == 2);
+  (void) (size == 2 ? put_2_be : put_4_be) (ptr, data);
+}
+
+/* We use this for putting the string table size.  */
+
+#define COFF_PUT4(ptr, data) \
+  ((COFFENDIAN ? put_4_be : put_4_le) (ptr, data))
+
+
+#endif /* LTO_COFF_H */
Index: gcc/lto/lto-coff.c
===================================================================
--- gcc/lto/lto-coff.c	(revision 0)
+++ gcc/lto/lto-coff.c	(revision 0)
@@ -0,0 +1,845 @@
+/* LTO routines for COFF object files.
+   Copyright 2009 Free Software Foundation, Inc.
+   Contributed by Dave Korn.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "toplev.h"
+#include "lto.h"
+#include "tm.h"
+#include "libiberty.h"
+#include "ggc.h"
+#include "lto-streamer.h"
+#include "lto/lto-coff.h"
+
+
+/* Rather than implementing a libcoff to match libelf, or attempting to
+   integrate libbfd into GCC, this file is a self-contained (and very
+   minimal) COFF format object file reader/writer.  The generated files
+   will contain a COFF header, a number of COFF section headers, the 
+   section data itself, and a trailing string table for section names.  */
+
+/* Handle opening elf files on hosts, such as Windows, that may use 
+   text file handling that will break binary access.  */
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+/* Known header magics for validation, as an array.  */
+
+static const unsigned int coff_machine_array[] = COFF_KNOWN_MACHINES;
+
+/* Number of valid entries (no sentinel) in array.  */
+
+#define NUM_COFF_KNOWN_MACHINES	\
+	(sizeof (coff_machine_array) / sizeof (coff_machine_array[0]))
+
+/* Cached object file header.  */
+
+static Coff_header cached_coff_hdr;
+
+/* Flag to indicate if we have read and cached any header yet.  */
+
+static bool cached_coff_hdr_valid = false;
+
+/* The current output file.  */
+
+static lto_file *current_out_file;
+
+
+/* Sets the current output file to FILE.  Returns the old output file or
+   NULL.  */
+
+lto_file *
+lto_set_current_out_file (lto_file *file)
+{
+  lto_file *old_file = current_out_file;
+  current_out_file = file;
+  return old_file;
+}
+
+
+/* Returns the current output file.  */
+
+lto_file *
+lto_get_current_out_file (void)
+{
+  return current_out_file;
+}
+
+
+/* COFF section structure constructor.  */
+
+static lto_coff_section *
+coff_newsection (lto_coff_file *file, const char *name, size_t type)
+{
+  lto_coff_section *ptr, **chain_ptr_ptr;
+
+  ptr = XCNEW (lto_coff_section);
+  ptr->name = name;
+  ptr->type = type;
+
+  chain_ptr_ptr = &file->section_chain;
+  while (*chain_ptr_ptr)
+    chain_ptr_ptr = &(*chain_ptr_ptr)->next;
+  *chain_ptr_ptr = ptr;
+
+  return ptr;
+}
+
+
+/* COFF section data block structure constructor.  */
+
+static lto_coff_data *
+coff_newdata (lto_coff_section *sec)
+{
+  lto_coff_data *ptr, **chain_ptr_ptr;
+
+  ptr = XCNEW (lto_coff_data);
+
+  chain_ptr_ptr = &sec->data_chain;
+  while (*chain_ptr_ptr)
+    chain_ptr_ptr = &(*chain_ptr_ptr)->next;
+  *chain_ptr_ptr = ptr;
+
+  return ptr;
+}
+
+
+/* Initialize FILE, an LTO file object for FILENAME.  */
+
+static void
+lto_file_init (lto_file *file, const char *filename, off_t offset)
+{
+  file->filename = filename;
+  file->offset = offset;
+}
+
+/* Return an error string after an error, or a predetermined one
+   if ERRCODE is not -1.  */
+
+static const char *
+coff_errmsg (int errcode)
+{
+  return strerror (errcode == -1 ? errno : errcode);
+}
+
+/* Returns a hash code for P.  */
+
+static hashval_t
+hash_name (const void *p)
+{
+  const struct lto_section_slot *ds = (const struct lto_section_slot *) p;
+  return (hashval_t) htab_hash_string (ds->name);
+}
+
+/* Returns nonzero if P1 and P2 are equal.  */
+
+static int
+eq_name (const void *p1, const void *p2)
+{
+  const struct lto_section_slot *s1 =
+    (const struct lto_section_slot *) p1;
+  const struct lto_section_slot *s2 =
+    (const struct lto_section_slot *) p2;
+
+  return strcmp (s1->name, s2->name) == 0;
+}
+
+
+/* Build a hash table whose key is the section names and whose data is
+   the start and size of each section in the .o file.  */
+
+htab_t
+lto_obj_build_section_table (lto_file *lto_file) 
+{
+  lto_coff_file *coff_file = (lto_coff_file *)lto_file;
+  lto_coff_section *sec;
+  htab_t section_hash_table;
+  ssize_t strtab_size;
+  char *strtab;
+
+  section_hash_table = htab_create (37, hash_name, eq_name, free);
+
+  /* Seek to start of string table.  */
+  if (coff_file->strtab_offs != lseek (coff_file->fd,
+		coff_file->base.offset + coff_file->strtab_offs, SEEK_SET))
+    {
+      error ("altered or invalid COFF object file");
+      return section_hash_table;
+    }
+
+  strtab_size = coff_file->file_size - coff_file->strtab_offs;
+  strtab = XNEWVEC (char, strtab_size);
+  if (read (coff_file->fd, strtab, strtab_size) != strtab_size)
+    {
+      error ("invalid COFF object file string table");
+      return section_hash_table;
+    }
+
+  /* Scan sections looking at names.  */
+  COFF_FOR_ALL_SECTIONS(coff_file, sec)
+    {
+      struct lto_section_slot s_slot;
+      void **slot;
+      char *new_name;
+      int stringoffset;
+      char *name = (char *) &sec->coffsec.Name[0];
+
+      /* Skip dummy string section if by any chance we see it.  */
+      if (sec->type == 1)
+	continue;
+
+      if (name[0] == '/')
+	{
+	  if (1 != sscanf (&name[1], "%d", &stringoffset)
+		|| stringoffset < 0 || stringoffset >= strtab_size)
+	    {
+	      error ("invalid COFF section name string");
+	      continue;
+	    }
+	  name = strtab + stringoffset;
+	}
+      else
+	{
+	  /* If we cared about the VirtualSize field, we couldn't
+	     crudely trash it like this to guarantee nul-termination
+	     of the Name field.  But we don't, so we do.  */
+	  name[8] = 0;
+	}
+      if (strncmp (name, LTO_SECTION_NAME_PREFIX,
+			strlen (LTO_SECTION_NAME_PREFIX)) != 0)
+	  continue;
+
+      new_name = XNEWVEC (char, strlen (name) + 1);
+      strcpy (new_name, name);
+      s_slot.name = new_name;
+      slot = htab_find_slot (section_hash_table, &s_slot, INSERT);
+      if (*slot == NULL)
+	{
+	  struct lto_section_slot *new_slot = XNEW (struct lto_section_slot);
+
+	  new_slot->name = new_name;
+	  /* The offset into the file for this section.  */
+	  new_slot->start = coff_file->base.offset
+			+ COFF_GET(&sec->coffsec,PointerToRawData);
+	  new_slot->len = COFF_GET(&sec->coffsec,SizeOfRawData);
+	  *slot = new_slot;
+	}
+      else
+	{
+	  error ("two or more sections for %s:", new_name);
+	  return NULL;
+	}
+    }
+
+  free (strtab);
+  return section_hash_table;
+}
+
+
+/* Begin a new COFF section named NAME with type TYPE in the current output
+   file.  TYPE is an SHT_* macro from the libelf headers.  */
+
+static void
+lto_coff_begin_section_with_type (const char *name, size_t type)
+{
+  lto_coff_file *file;
+  size_t sh_name;
+
+  /* Grab the current output file and do some basic assertion checking.  */
+  file = (lto_coff_file *) lto_get_current_out_file (),
+  gcc_assert (file);
+  gcc_assert (!file->scn);
+
+  /* Create a new section.  */
+  file->scn = coff_newsection (file, name, type);
+  if (!file->scn)
+    fatal_error ("could not create a new COFF section: %s", coff_errmsg (-1));
+
+  /* Add a string table entry and record the offset.  */
+  gcc_assert (file->shstrtab_stream);
+  sh_name = file->shstrtab_stream->total_size;
+  lto_output_data_stream (file->shstrtab_stream, name, strlen (name) + 1);
+
+  /* Initialize the section header.  */
+  file->scn->strtab_offs = sh_name;
+}
+
+
+/* Begin a new COFF section named NAME in the current output file.  */
+
+void
+lto_obj_begin_section (const char *name)
+{
+  lto_coff_begin_section_with_type (name, 0);
+}
+
+
+/* Append DATA of length LEN to the current output section.  BASE is a pointer
+   to the output page containing DATA.  It is freed once the output file has
+   been written.  */
+
+void
+lto_obj_append_data (const void *data, size_t len, void *block)
+{
+  lto_coff_file *file;
+  lto_coff_data *coff_data;
+  struct lto_char_ptr_base *base = (struct lto_char_ptr_base *) block;
+
+  /* Grab the current output file and do some basic assertion checking.  */
+  file = (lto_coff_file *) lto_get_current_out_file ();
+  gcc_assert (file);
+  gcc_assert (file->scn);
+
+  coff_data = coff_newdata (file->scn);
+  if (!coff_data)
+    fatal_error ("could not append data to COFF section: %s", coff_errmsg (-1));
+
+  coff_data->d_buf = CONST_CAST (void *, data);
+  coff_data->d_size = len;
+
+  /* Chain all data blocks (from all sections) on one singly-linked
+     list for freeing en masse after the file is closed.  */
+  base->ptr = (char *)file->data;
+  file->data = base;
+}
+
+
+/* End the current output section.  This just does some assertion checking
+   and sets the current output file's scn member to NULL.  */
+
+void
+lto_obj_end_section (void)
+{
+  lto_coff_file *file;
+
+  /* Grab the current output file and validate some basic assertions.  */
+  file = (lto_coff_file *) lto_get_current_out_file ();
+  gcc_assert (file);
+  gcc_assert (file->scn);
+
+  file->scn = NULL;
+}
+
+
+/* Validate's COFF_FILE's executable header and, if cached_coff_hdr is
+   uninitialized, caches the results.  Also records the section header string
+   table's section index.  Returns true on success or false on failure.  */
+
+static bool
+validate_file (lto_coff_file *coff_file)
+{
+  size_t n, secnum;
+  unsigned int numsections, secheaderssize, numsyms;
+  off_t sectionsstart, symbolsstart, stringsstart;
+  unsigned int mach, charact;
+
+  /* Read and sanity check the raw header.  */
+  n = read (coff_file->fd, &coff_file->coffhdr, sizeof (coff_file->coffhdr));
+  if (n != sizeof (coff_file->coffhdr))
+    {
+      error ("not a COFF object file");
+      return false;
+    }
+
+  mach = COFF_GET(&coff_file->coffhdr, Machine);
+  for (n = 0; n < NUM_COFF_KNOWN_MACHINES; n++)
+    if (mach == coff_machine_array[n])
+      break;
+  if (n == NUM_COFF_KNOWN_MACHINES)
+    {
+      error ("not a recognized COFF object file");
+      return false;
+    }
+
+  charact = COFF_GET(&coff_file->coffhdr, Characteristics);
+  if (COFF_NOT_CHARACTERISTICS & charact)
+    {
+      /* DLL, EXE or SYS file.  */
+      error ("not a relocatable COFF object file");
+      return false;
+    }
+
+  if (COFF_CHARACTERISTICS != (COFF_CHARACTERISTICS & charact))
+    {
+      /* ECOFF/XCOFF/PE+ support not implemented.  */
+      error ("not a 32-bit COFF object file");
+      return false;
+    }
+
+  /* It validated OK, so cached it if we don't already have one.  */
+  if (!cached_coff_hdr_valid)
+    {
+      cached_coff_hdr_valid = true;
+      memcpy (&cached_coff_hdr, &coff_file->coffhdr, sizeof (cached_coff_hdr));
+    }
+
+  if (mach != COFF_GET(&cached_coff_hdr, Machine))
+    {
+      error ("inconsistent file architecture detected");
+      return false;
+    }
+
+  /* Read section headers and string table? */
+
+  numsections = COFF_GET(&coff_file->coffhdr, NumberOfSections);
+  secheaderssize = numsections * sizeof (Coff_section);
+  sectionsstart = sizeof (Coff_header) + secheaderssize;
+  symbolsstart = COFF_GET(&coff_file->coffhdr, PointerToSymbolTable);
+  numsyms = COFF_GET(&coff_file->coffhdr, NumberOfSymbols);
+  stringsstart = (symbolsstart + COFF_SYMBOL_SIZE * numsyms);
+
+#define CVOFFSETTTED(x) (coff_file->base.offset + (x))
+
+  if (numsections <= 0 || symbolsstart <= 0 || numsyms <= 0
+	|| (CVOFFSETTTED(sectionsstart) >= coff_file->file_size)
+	|| (CVOFFSETTTED(symbolsstart) >= coff_file->file_size)
+	|| (CVOFFSETTTED(stringsstart) >= coff_file->file_size))
+    {
+      error ("not a valid COFF object file");
+      return false;
+    }
+
+#undef CVOFFSETTTED
+
+  /* Record start of string table.  */
+  coff_file->strtab_offs = stringsstart;
+
+  /* Validate section table entries.  */
+  for (secnum = 0; secnum < numsections; secnum++)
+    {
+      Coff_section coffsec;
+      lto_coff_section *ltosec;
+      off_t size_raw, offs_raw, offs_relocs, offs_lines;
+      off_t num_relocs, num_lines;
+
+      n = read (coff_file->fd, &coffsec, sizeof (coffsec));
+      if (n != sizeof (coffsec))
+	{
+	  error ("short/missing COFF section table");
+	  return false;
+	}
+
+      size_raw = COFF_GET(&coffsec, SizeOfRawData);
+      offs_raw = COFF_GET(&coffsec, PointerToRawData);
+      offs_relocs = COFF_GET(&coffsec, PointerToRelocations);
+      offs_lines = COFF_GET(&coffsec, PointerToLinenumbers);
+      num_relocs = COFF_GET(&coffsec, NumberOfRelocations);
+      num_lines = COFF_GET(&coffsec, NumberOfLinenumbers);
+
+      if (size_raw < 0 || num_relocs < 0 || num_lines < 0
+	|| (size_raw
+	  && ((COFF_GET(&coffsec, Characteristics)
+	      & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
+	    ? (offs_raw != 0)
+	    : (offs_raw < sectionsstart || offs_raw >= coff_file->file_size)))
+	|| (num_relocs
+	  && (offs_relocs < sectionsstart
+	    || offs_relocs >= coff_file->file_size))
+	|| (num_lines
+	  && (offs_lines < sectionsstart
+	    || offs_lines >= coff_file->file_size)))
+	{
+	  error ("invalid COFF section table");
+	  return false;
+	}
+
+      /* Looks ok, so record its details.  We don't read the 
+         string table or set up names yet; we'll do that when
+	 we build the hash table.  */
+      ltosec = coff_newsection (coff_file, NULL, 0);
+      memcpy (&ltosec->coffsec, &coffsec, sizeof (ltosec->coffsec));
+    }
+
+  return true;
+}
+
+/* Initialize COFF_FILE's executable header using cached data from previously
+   read files.  */
+
+static void
+init_coffhdr (lto_coff_file *coff_file)
+{
+  gcc_assert (cached_coff_hdr_valid);
+  memset (&coff_file->coffhdr, 0, sizeof (coff_file->coffhdr));
+  COFF_PUT(&coff_file->coffhdr, Machine, COFF_GET(&cached_coff_hdr, Machine));
+  COFF_PUT(&coff_file->coffhdr, Characteristics, COFF_GET(&cached_coff_hdr, Characteristics));
+}
+
+/* Open COFF file FILENAME.  If WRITABLE is true, the file is opened for write
+   and, if necessary, created.  Otherwise, the file is opened for reading.
+   Returns the opened file.  */
+
+lto_file *
+lto_obj_file_open (const char *filename, bool writable)
+{
+  lto_coff_file *coff_file;
+  lto_file *result = NULL;
+  off_t offset;
+  const char *offset_p;
+  char *fname;
+  struct stat statbuf;
+
+  offset_p = strchr (filename, '@');
+  if (!offset_p)
+    {
+      fname = xstrdup (filename);
+      offset = 0;
+    }
+  else
+    {
+      /* The file started with '@' is a file containing command line
+	 options.  Stop if it doesn't exist.  */
+      if (offset_p == filename)
+	fatal_error ("command line option file '%s' does not exist",
+		     filename);
+
+      fname = (char *) xmalloc (offset_p - filename + 1);
+      memcpy (fname, filename, offset_p - filename);
+      fname[offset_p - filename] = '\0';
+      offset_p += 3; /* skip the @0x */
+      offset = lto_parse_hex (offset_p);
+    }
+
+  /* Set up.  */
+  coff_file = XCNEW (lto_coff_file);
+  result = (lto_file *) coff_file;
+  lto_file_init (result, fname, offset);
+  coff_file->fd = -1;
+
+  /* Open the file.  */
+  coff_file->fd = open (fname,
+    O_BINARY | (writable ? O_WRONLY | O_CREAT | O_TRUNC : O_RDONLY), 0666);
+
+  if (coff_file->fd == -1)
+    {
+      error ("could not open file %s", fname);
+      goto fail;
+    }
+
+  if (stat (fname, &statbuf) < 0)
+    {
+      error ("could not stat file %s", fname);
+      goto fail;
+    }
+
+  coff_file->file_size = statbuf.st_size;
+
+  if (offset != 0)
+    {
+      char ar_tail[12];
+      int size;
+
+      /* Surely not?  */
+      gcc_assert (!writable);
+
+      /* Seek to offset, or error.  */
+      if (lseek (coff_file->fd, offset, SEEK_SET) != (ssize_t) offset)
+	{
+	  error ("could not find archive member @0x%lx", (long) offset);
+	  goto fail;
+	}
+
+      /* Now seek back 12 chars and read the tail of the AR header to
+         find the length of the member file.  */
+      if (lseek (coff_file->fd, -12, SEEK_CUR) < 0
+	  || read (coff_file->fd, ar_tail, 12) != 12
+	  || lseek (coff_file->fd, 0, SEEK_CUR) != (ssize_t) offset
+	  || ar_tail[10] != '`' || ar_tail[11] != '\n')
+	{
+	  error ("could not find archive header @0x%lx", (long) offset);
+	  goto fail;
+	}
+
+      ar_tail[11] = 0;
+      if (sscanf (ar_tail, "%d", &size) != 1)
+	{
+	  error ("invalid archive header @0x%lx", (long) offset);
+	  goto fail;
+	}
+      coff_file->file_size = size;
+    }
+
+  if (writable)
+    {
+      init_coffhdr (coff_file);
+      coff_file->shstrtab_stream = XCNEW (struct lto_output_stream);
+    }
+  else
+    if (!validate_file (coff_file))
+      goto fail;
+
+  return result;
+
+ fail:
+  if (result)
+    lto_obj_file_close (result);
+  return NULL;
+}
+
+
+/* Close COFF file FILE and clean up any associated data structures.  If FILE
+   was opened for writing, the file's COFF data is written at this time, and
+   any cached data buffers are freed.  Return TRUE if there was an error.  */
+
+static bool
+coff_write_object_file (lto_coff_file *coff_file)
+{
+  lto_coff_section *cursec, *stringsec;
+  lto_coff_data *data;
+  size_t fileoffset, numsections, totalsecsize, numsyms, stringssize;
+  bool write_err = false;
+  int secnum;
+
+  /* Infer whether this file was opened for reading or writing from the
+     presence or absense of an initialised stream for the string table;
+     do nothing if it was opened for reading.  */
+  if (!coff_file->shstrtab_stream)
+    return false;
+  else
+    {
+      /* Write the COFF string table into a dummy new section that
+	 we will not write a header for.  */
+      lto_file *old_file = lto_set_current_out_file (&coff_file->base);
+      /* This recursively feeds in the data to a new section.  */
+      lto_coff_begin_section_with_type (".strtab", 1);
+      lto_write_stream (coff_file->shstrtab_stream);
+      lto_obj_end_section ();
+      lto_set_current_out_file (old_file);
+      free (coff_file->shstrtab_stream);
+    }
+
+  /* Layout the file.  Count sections (not dummy string section) and calculate
+     data size for all of them.  */
+  numsections = 0;
+  totalsecsize = 0;
+  stringssize = 0;
+  stringsec = NULL;
+  COFF_FOR_ALL_SECTIONS(coff_file, cursec)
+    {
+      lto_coff_data *data;
+      size_t cursecsize;
+      cursecsize = 0;
+      COFF_FOR_ALL_DATA(cursec,data)
+	cursecsize += data->d_size;
+      if (cursec->type == 0)
+	{
+	  ++numsections;
+	  totalsecsize += COFF_ALIGN(cursecsize);
+#if COFF_ALIGNMENT > 1
+	  cursec->pad_needed = COFF_ALIGN(cursecsize) - cursecsize;
+#endif
+	}
+      else
+        {
+	  stringssize = cursecsize;
+	  stringsec = cursec;
+	}
+      COFF_PUT(&cursec->coffsec, SizeOfRawData, cursecsize);
+    }
+
+  /* There is a file symbol and a section symbol per section,
+     and each of these has a single auxiliary symbol following.  */
+  numsyms = 2 * (1 + numsections);
+
+  /* Great!  Now we have enough info to fill out the file header.  */
+  COFF_PUT(&coff_file->coffhdr, NumberOfSections, numsections);
+  COFF_PUT(&coff_file->coffhdr, NumberOfSymbols, numsyms);
+  COFF_PUT(&coff_file->coffhdr, PointerToSymbolTable, sizeof (Coff_header)
+		+ numsections * sizeof (Coff_section) + totalsecsize);
+  /* The remaining members were initialised to zero or copied from
+     a cached header, so we leave them alone here.  */
+
+  /* Now position all the sections, and fill out their headers.  */
+  fileoffset = sizeof (Coff_header) + numsections * sizeof (Coff_section);
+  COFF_FOR_ALL_SECTIONS(coff_file, cursec)
+    {
+      /* Skip dummy string section.  */
+      if (cursec->type == 1)
+	continue;
+      COFF_PUT(&cursec->coffsec, PointerToRawData, fileoffset);
+      fileoffset += COFF_ALIGN (COFF_GET(&cursec->coffsec, SizeOfRawData));
+      COFF_PUT(&cursec->coffsec, Characteristics, COFF_SECTION_CHARACTERISTICS);
+      snprintf ((char *)&cursec->coffsec.Name[0], 8, "/%d", cursec->strtab_offs + 4);
+    }
+
+  /* We can write the data now.  As there's no way to indicate an error return
+     from this hook, error handling is limited to not wasting our time doing
+     any more writes in the event that any one fails.  */
+
+  /* Write the COFF header.  */
+  write_err = (write (coff_file->fd, &coff_file->coffhdr,
+		sizeof (coff_file->coffhdr)) != sizeof (coff_file->coffhdr));
+
+  /* Write the COFF section headers.  */
+  COFF_FOR_ALL_SECTIONS(coff_file, cursec)
+    if (cursec->type == 1)	/* Skip dummy string section.  */
+	continue;
+    else if (!write_err)
+      write_err = (write (coff_file->fd, &cursec->coffsec,
+		sizeof (cursec->coffsec)) != sizeof (cursec->coffsec));
+    else
+      break;
+
+  /* Write the COFF sections.  */
+  COFF_FOR_ALL_SECTIONS(coff_file, cursec)
+    {
+#if COFF_ALIGNMENT > 1
+      static const char padzeros[COFF_ALIGNMENT] = { 0 };
+#endif
+      /* Skip dummy string section.  */
+      if (cursec->type == 1)
+	continue;
+      COFF_FOR_ALL_DATA(cursec, data)
+	if (!write_err)
+	  write_err = (write (coff_file->fd, data->d_buf, data->d_size)
+		!= data->d_size);
+	else
+	  break;
+#if COFF_ALIGNMENT > 1
+      if (!write_err && cursec->pad_needed)
+	write_err = (write (coff_file->fd, padzeros, cursec->pad_needed)
+		!= cursec->pad_needed);
+#endif
+    }
+
+  /* Write the COFF symbol table.  */
+  if (!write_err)
+    {
+      union
+	{
+	  Coff_symbol sym;
+	  Coff_aux_sym_file file;
+	  Coff_aux_sym_section sec;
+	} symbols[2];
+      memset (&symbols[0], 0, sizeof (symbols));
+      strcpy ((char *) &symbols[0].sym.Name[0], ".file");
+      COFF_PUT(&symbols[0].sym, SectionNumber, IMAGE_SYM_DEBUG);
+      COFF_PUT(&symbols[0].sym, Type, IMAGE_SYM_TYPE);
+      symbols[0].sym.StorageClass[0] = IMAGE_SYM_CLASS_FILE;
+      symbols[0].sym.NumberOfAuxSymbols[0] = 1;
+      snprintf ((char *)symbols[1].file.FileName,
+		sizeof (symbols[1].file.FileName),
+		"%s", lbasename (coff_file->base.filename));
+      write_err = (write (coff_file->fd, &symbols[0], sizeof (symbols))
+		!= (2 * COFF_SYMBOL_SIZE));
+
+      /* Set up constant parts for section sym loop.  */
+      memset (&symbols[0], 0, sizeof (symbols));
+      COFF_PUT(&symbols[0].sym, Type, IMAGE_SYM_TYPE);
+      symbols[0].sym.StorageClass[0] = IMAGE_SYM_CLASS_STATIC;
+      symbols[0].sym.NumberOfAuxSymbols[0] = 1;
+
+      secnum = 1;
+      if (!write_err)
+	COFF_FOR_ALL_SECTIONS(coff_file, cursec)
+	  {
+	    /* Skip dummy string section.  */
+	    if (cursec->type == 1)
+	      continue;
+	    /* Reuse section name string for section symbol name.  */
+	    COFF_PUT_NDXSZ(&symbols[0].sym, Name, 0, 0, 4);
+	    COFF_PUT_NDXSZ(&symbols[0].sym, Name, cursec->strtab_offs + 4, 4, 4);
+	    COFF_PUT(&symbols[0].sym, SectionNumber, secnum++);
+	    COFF_PUT(&symbols[1].sec, Length,
+			COFF_GET(&cursec->coffsec, SizeOfRawData));
+	    if (!write_err)
+	      write_err = (write (coff_file->fd, &symbols[0], sizeof (symbols))
+			!= (2 * COFF_SYMBOL_SIZE));
+	    else
+	      break;
+	  }
+    }
+
+  /* Write the COFF string table.  */
+  if (!write_err)
+    {
+      unsigned char outlen[4];
+      COFF_PUT4(outlen, stringssize + 4);
+      if (!write_err)
+	write_err = (write (coff_file->fd, outlen, 4) != 4);
+      if (stringsec)
+	COFF_FOR_ALL_DATA(stringsec, data)
+	  if (!write_err)
+	write_err = (write (coff_file->fd, data->d_buf, data->d_size)
+			!= data->d_size);
+	else
+	  break;
+    }
+
+  return write_err;
+}
+
+/* Close COFF file FILE and clean up any associated data structures.  If FILE
+   was opened for writing, the file's COFF data is written at this time, and
+   any cached data buffers are freed.  */
+
+void
+lto_obj_file_close (lto_file *file)
+{
+  lto_coff_file *coff_file = (lto_coff_file *) file;
+  struct lto_char_ptr_base *cur, *tmp;
+  lto_coff_section *cursec, *nextsec;
+  bool write_err = false;
+
+  /* Write the COFF string table into a dummy new section that
+     we will not write a header for.  */
+  if (coff_file->shstrtab_stream)
+    coff_write_object_file (coff_file);
+
+  /* Close the file, we're done.  */
+  if (coff_file->fd != -1)
+    close (coff_file->fd);
+
+  /* Free any data buffers.  */
+  cur = coff_file->data;
+  while (cur)
+    {
+      tmp = cur;
+      cur = (struct lto_char_ptr_base *) cur->ptr;
+      free (tmp);
+    }
+
+  /* Free any sections and their data chains.  */
+  cursec = coff_file->section_chain;
+  while (cursec)
+    {
+      lto_coff_data *curdata, *nextdata;
+      nextsec = cursec->next;
+      curdata = cursec->data_chain;
+      while (curdata)
+	{
+	  nextdata = curdata->next;
+	  free (curdata);
+	  curdata = nextdata;
+	}
+      free (cursec);
+      cursec = nextsec;
+    }
+
+  free (file);
+
+  /* If there was an error, mention it.  */
+  if (write_err)
+    error ("I/O error writing COFF output file");
+}
+
Index: gcc/config/i386/t-cygming
===================================================================
--- gcc/config/i386/t-cygming	(revision 158195)
+++ gcc/config/i386/t-cygming	(working copy)
@@ -30,7 +30,7 @@ LIBGCC2_INCLUDES = -I$(srcdir)/../winsup/w32api/in
 
 winnt.o: $(srcdir)/config/i386/winnt.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
   $(TM_H) $(RTL_H) $(REGS_H) hard-reg-set.h output.h $(TREE_H) flags.h \
-  $(TM_P_H) toplev.h $(HASHTAB_H) $(GGC_H)
+  $(TM_P_H) toplev.h $(HASHTAB_H) $(GGC_H) $(LTO_STREAMER_H)
 	$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
 	$(srcdir)/config/i386/winnt.c
 
Index: gcc/config/i386/winnt.c
===================================================================
--- gcc/config/i386/winnt.c	(revision 158195)
+++ gcc/config/i386/winnt.c	(working copy)
@@ -35,6 +35,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "langhooks.h"
 #include "ggc.h"
 #include "target.h"
+#include "lto-streamer.h"
 
 /* i386/PE specific attribute support.
 
@@ -465,6 +466,12 @@ i386_pe_asm_named_section (const char *name, unsig
         *f++ = 's';
     }
 
+  /* LTO sections need 1-byte alignment to avoid confusing the
+     zlib decompression algorithm with trailing zero pad bytes.  */
+  if (strncmp (name, LTO_SECTION_NAME_PREFIX,
+			strlen (LTO_SECTION_NAME_PREFIX)) == 0)
+    *f++ = '0';
+
   *f = '\0';
 
   fprintf (asm_out_file, "\t.section\t%s,\"%s\"\n", name, flagchars);
@@ -485,6 +492,8 @@ i386_pe_asm_named_section (const char *name, unsig
     }
 }
 
+/* Beware, DECL may be NULL if compile_file() is emitting the LTO marker.  */
+
 void
 i386_pe_asm_output_aligned_decl_common (FILE *stream, tree decl,
 					const char *name, HOST_WIDE_INT size,
@@ -581,7 +590,8 @@ static GTY(()) struct export_list *export_head;
    these, so that we can output the export list at the end of the
    assembly.  We used to output these export symbols in each function,
    but that causes problems with GNU ld when the sections are
-   linkonce.  */
+   linkonce.  Beware, DECL may be NULL if compile_file() is emitting
+   the LTO marker.  */
 
 void
 i386_pe_maybe_record_exported_symbol (tree decl, const char *name, int is_data)
@@ -589,6 +599,9 @@ i386_pe_maybe_record_exported_symbol (tree decl, c
   rtx symbol;
   struct export_list *p;
 
+  if (!decl)
+    return;
+
   symbol = XEXP (DECL_RTL (decl), 0);
   gcc_assert (GET_CODE (symbol) == SYMBOL_REF);
   if (!SYMBOL_REF_DLLEXPORT_P (symbol))
Index: gcc/testsuite/lib/lto.exp
===================================================================
--- gcc/testsuite/lib/lto.exp	(revision 158195)
+++ gcc/testsuite/lib/lto.exp	(working copy)
@@ -16,7 +16,20 @@
 
 # Contributed by Diego Novillo <dnovillo@google.com>
 
+# Prune messages from gcc that aren't useful.
 
+proc lto_prune_vis_warns { text } {
+
+    # Many tests that use visibility will still pass on platforms that don't support it.
+    regsub -all "(^|\n)\[^\n\]*: warning: visibility attribute not supported in this configuration; ignored\[^\n\]*" $text "" text
+
+    # And any stray location lines.
+    regsub -all "(^|\n)\[^\n\]*: In function \[^\n\]*" $text "" text
+    regsub -all "(^|\n)In file included from :\[^\n\]*" $text "" text
+
+    return $text
+}
+
 # lto_init -- called at the start of each subdir of tests
 
 proc lto_init { args } {
@@ -147,6 +160,10 @@ proc lto-link-and-maybe-run { testname objlist des
     # Link the objects into an executable.
     set comp_output [${tool}_target_compile "$objlist" $dest executable \
 		     "$options"]
+
+    # Prune unimportant visibility warnings before checking output.
+    set comp_output [lto_prune_vis_warns $comp_output]
+
     if ![${tool}_check_compile "$testcase $testname link" "" \
 	 $dest $comp_output] then {
 	unresolved "$testcase $testname execute $optstr"

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]