diff options
author | Dave Korn <dave.korn.cygwin@gmail.com> | 2010-04-27 02:22:40 +0000 |
---|---|---|
committer | Dave Korn <davek@gcc.gnu.org> | 2010-04-27 02:22:40 +0000 |
commit | 3bec79c52e613995dd3b07b119a67cd7ad3d72a8 (patch) | |
tree | f1cd7b961ac18dc1a7983cdd108f5b91bbc0cb46 /gcc/lto | |
parent | 45c384e375c0a05accdd60deaf54d0727dab9feb (diff) | |
download | gcc-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/ChangeLog | 10 | ||||
-rw-r--r-- | gcc/lto/Make-lang.in | 7 | ||||
-rw-r--r-- | gcc/lto/lto-coff.c | 845 | ||||
-rw-r--r-- | gcc/lto/lto-coff.h | 406 |
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 (<osec->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 */ |