Mercurial > hg > CbC > CbC_gcc
diff gcc/lto/lto-coff.c @ 63:b7f97abdc517 gcc-4.6-20100522
update gcc from gcc-4.5.0 to gcc-4.6
author | ryoma <e075725@ie.u-ryukyu.ac.jp> |
---|---|
date | Mon, 24 May 2010 12:47:05 +0900 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gcc/lto/lto-coff.c Mon May 24 12:47:05 2010 +0900 @@ -0,0 +1,846 @@ +/* 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 (mach != IMAGE_FILE_MACHINE_AMD64 + && COFF_CHARACTERISTICS != (COFF_CHARACTERISTICS & charact)) + { + /* ECOFF/XCOFF 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"); +} +