aboutsummaryrefslogtreecommitdiff
path: root/gcc/lto
diff options
context:
space:
mode:
authorDave Korn <dave.korn.cygwin@gmail.com>2010-04-27 02:22:40 +0000
committerDave Korn <davek@gcc.gnu.org>2010-04-27 02:22:40 +0000
commit3bec79c52e613995dd3b07b119a67cd7ad3d72a8 (patch)
treef1cd7b961ac18dc1a7983cdd108f5b91bbc0cb46 /gcc/lto
parent45c384e375c0a05accdd60deaf54d0727dab9feb (diff)
downloadgcc-3bec79c52e613995dd3b07b119a67cd7ad3d72a8.zip
gcc-3bec79c52e613995dd3b07b119a67cd7ad3d72a8.tar.gz
gcc-3bec79c52e613995dd3b07b119a67cd7ad3d72a8.tar.bz2
re PR lto/42776 (LTO doesn't work on non-ELF platforms.)
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. gcc/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-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. From-SVN: r158762
Diffstat (limited to 'gcc/lto')
-rw-r--r--gcc/lto/ChangeLog10
-rw-r--r--gcc/lto/Make-lang.in7
-rw-r--r--gcc/lto/lto-coff.c845
-rw-r--r--gcc/lto/lto-coff.h406
4 files changed, 1266 insertions, 2 deletions
diff --git a/gcc/lto/ChangeLog b/gcc/lto/ChangeLog
index 24212a1..b757655 100644
--- a/gcc/lto/ChangeLog
+++ b/gcc/lto/ChangeLog
@@ -1,3 +1,13 @@
+2010-04-27 Dave Korn <dave.korn.cygwin@gmail.com>
+
+ 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-coff.h: New file.
+ * lto-coff.c: Likewise.
+
2010-04-26 Richard Guenther <rguenther@suse.de>
* lto.c (lto_fixup_type): Deal with non-type TYPE_CONTEXT.
diff --git a/gcc/lto/Make-lang.in b/gcc/lto/Make-lang.in
index 33b0d9a..a06ab4a 100644
--- a/gcc/lto/Make-lang.in
+++ b/gcc/lto/Make-lang.in
@@ -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) coretypes.h opts.h \
$(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
diff --git a/gcc/lto/lto-coff.c b/gcc/lto/lto-coff.c
new file mode 100644
index 0000000..1814cfdb
--- /dev/null
+++ b/gcc/lto/lto-coff.c
@@ -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");
+}
+
diff --git a/gcc/lto/lto-coff.h b/gcc/lto/lto-coff.h
new file mode 100644
index 0000000..f069d0c
--- /dev/null
+++ b/gcc/lto/lto-coff.h
@@ -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[1] | (ptr[0] << 8);
+}
+
+static inline unsigned int
+get_4_be (const unsigned char *ptr)
+{
+ return ptr[3] | (ptr[2] << 8) | (ptr[1] << 16) | (ptr[0] << 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 */