1 /* LTO routines for COFF object files.
2 Copyright 2009, 2010 Free Software Foundation, Inc.
3 Contributed by Dave Korn.
5 This file is part of GCC.
7 GCC is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 3, or (at your option) any later
12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3. If not see
19 <http://www.gnu.org/licenses/>. */
23 #include "coretypes.h"
24 #include "diagnostic-core.h"
28 #include "libiberty.h"
30 #include "lto-streamer.h"
31 #include "lto/lto-coff.h"
34 /* Rather than implementing a libcoff to match libelf, or attempting to
35 integrate libbfd into GCC, this file is a self-contained (and very
36 minimal) COFF format object file reader/writer. The generated files
37 will contain a COFF header, a number of COFF section headers, the
38 section data itself, and a trailing string table for section names. */
40 /* Handle opening elf files on hosts, such as Windows, that may use
41 text file handling that will break binary access. */
47 /* Known header magics for validation, as an array. */
49 static const unsigned int coff_machine_array
[] = COFF_KNOWN_MACHINES
;
51 /* Number of valid entries (no sentinel) in array. */
53 #define NUM_COFF_KNOWN_MACHINES \
54 (sizeof (coff_machine_array) / sizeof (coff_machine_array[0]))
56 /* Cached object file header. */
58 static Coff_header cached_coff_hdr
;
60 /* Flag to indicate if we have read and cached any header yet. */
62 static bool cached_coff_hdr_valid
= false;
64 /* The current output file. */
66 static lto_file
*current_out_file
;
69 /* Sets the current output file to FILE. Returns the old output file or
73 lto_set_current_out_file (lto_file
*file
)
75 lto_file
*old_file
= current_out_file
;
76 current_out_file
= file
;
81 /* Returns the current output file. */
84 lto_get_current_out_file (void)
86 return current_out_file
;
90 /* COFF section structure constructor. */
92 static lto_coff_section
*
93 coff_newsection (lto_coff_file
*file
, const char *name
, size_t type
)
95 lto_coff_section
*ptr
, **chain_ptr_ptr
;
97 ptr
= XCNEW (lto_coff_section
);
101 chain_ptr_ptr
= &file
->section_chain
;
102 while (*chain_ptr_ptr
)
103 chain_ptr_ptr
= &(*chain_ptr_ptr
)->next
;
104 *chain_ptr_ptr
= ptr
;
110 /* COFF section data block structure constructor. */
112 static lto_coff_data
*
113 coff_newdata (lto_coff_section
*sec
)
115 lto_coff_data
*ptr
, **chain_ptr_ptr
;
117 ptr
= XCNEW (lto_coff_data
);
119 chain_ptr_ptr
= &sec
->data_chain
;
120 while (*chain_ptr_ptr
)
121 chain_ptr_ptr
= &(*chain_ptr_ptr
)->next
;
122 *chain_ptr_ptr
= ptr
;
128 /* Initialize FILE, an LTO file object for FILENAME. */
131 lto_file_init (lto_file
*file
, const char *filename
, off_t offset
)
133 file
->filename
= filename
;
134 file
->offset
= offset
;
137 /* Build a hash table whose key is the section names and whose data is
138 the start and size of each section in the .o file. */
141 lto_obj_build_section_table (lto_file
*lto_file
)
143 lto_coff_file
*coff_file
= (lto_coff_file
*)lto_file
;
144 lto_coff_section
*sec
;
145 htab_t section_hash_table
;
149 section_hash_table
= lto_obj_create_section_hash_table ();
151 /* Seek to start of string table. */
152 if (coff_file
->strtab_offs
!= lseek (coff_file
->fd
,
153 coff_file
->base
.offset
+ coff_file
->strtab_offs
, SEEK_SET
))
155 error ("altered or invalid COFF object file");
156 return section_hash_table
;
159 strtab_size
= coff_file
->file_size
- coff_file
->strtab_offs
;
160 strtab
= XNEWVEC (char, strtab_size
);
161 if (read (coff_file
->fd
, strtab
, strtab_size
) != strtab_size
)
163 error ("invalid COFF object file string table");
164 return section_hash_table
;
167 /* Scan sections looking at names. */
168 COFF_FOR_ALL_SECTIONS(coff_file
, sec
)
170 struct lto_section_slot s_slot
;
174 char *name
= (char *) &sec
->coffsec
.Name
[0];
176 /* Skip dummy string section if by any chance we see it. */
182 if (1 != sscanf (&name
[1], "%d", &stringoffset
)
183 || stringoffset
< 0 || stringoffset
>= strtab_size
)
185 error ("invalid COFF section name string");
188 name
= strtab
+ stringoffset
;
192 /* If we cared about the VirtualSize field, we couldn't
193 crudely trash it like this to guarantee nul-termination
194 of the Name field. But we don't, so we do. */
197 if (strncmp (name
, LTO_SECTION_NAME_PREFIX
,
198 strlen (LTO_SECTION_NAME_PREFIX
)) != 0)
201 new_name
= XNEWVEC (char, strlen (name
) + 1);
202 strcpy (new_name
, name
);
203 s_slot
.name
= new_name
;
204 slot
= htab_find_slot (section_hash_table
, &s_slot
, INSERT
);
207 struct lto_section_slot
*new_slot
= XNEW (struct lto_section_slot
);
209 new_slot
->name
= new_name
;
210 /* The offset into the file for this section. */
211 new_slot
->start
= coff_file
->base
.offset
212 + COFF_GET(&sec
->coffsec
,PointerToRawData
);
213 new_slot
->len
= COFF_GET(&sec
->coffsec
,SizeOfRawData
);
218 error ("two or more sections for %s:", new_name
);
224 return section_hash_table
;
228 /* Begin a new COFF section named NAME with type TYPE in the current output
229 file. TYPE is an SHT_* macro from the libelf headers. */
232 lto_coff_begin_section_with_type (const char *name
, size_t type
)
237 /* Grab the current output file and do some basic assertion checking. */
238 file
= (lto_coff_file
*) lto_get_current_out_file (),
240 gcc_assert (!file
->scn
);
242 /* Create a new section. */
243 file
->scn
= coff_newsection (file
, name
, type
);
245 fatal_error ("could not create a new COFF section: %m");
247 /* Add a string table entry and record the offset. */
248 gcc_assert (file
->shstrtab_stream
);
249 sh_name
= file
->shstrtab_stream
->total_size
;
250 lto_output_data_stream (file
->shstrtab_stream
, name
, strlen (name
) + 1);
252 /* Initialize the section header. */
253 file
->scn
->strtab_offs
= sh_name
;
257 /* Begin a new COFF section named NAME in the current output file. */
260 lto_obj_begin_section (const char *name
)
262 lto_coff_begin_section_with_type (name
, 0);
266 /* Append DATA of length LEN to the current output section. BASE is a pointer
267 to the output page containing DATA. It is freed once the output file has
271 lto_obj_append_data (const void *data
, size_t len
, void *block
)
274 lto_coff_data
*coff_data
;
275 struct lto_char_ptr_base
*base
= (struct lto_char_ptr_base
*) block
;
277 /* Grab the current output file and do some basic assertion checking. */
278 file
= (lto_coff_file
*) lto_get_current_out_file ();
280 gcc_assert (file
->scn
);
282 coff_data
= coff_newdata (file
->scn
);
284 fatal_error ("could not append data to COFF section: %m");
286 coff_data
->d_buf
= CONST_CAST (void *, data
);
287 coff_data
->d_size
= len
;
289 /* Chain all data blocks (from all sections) on one singly-linked
290 list for freeing en masse after the file is closed. */
291 base
->ptr
= (char *)file
->data
;
296 /* End the current output section. This just does some assertion checking
297 and sets the current output file's scn member to NULL. */
300 lto_obj_end_section (void)
304 /* Grab the current output file and validate some basic assertions. */
305 file
= (lto_coff_file
*) lto_get_current_out_file ();
307 gcc_assert (file
->scn
);
313 /* Validate's COFF_FILE's executable header and, if cached_coff_hdr is
314 uninitialized, caches the results. Also records the section header string
315 table's section index. Returns true on success or false on failure. */
318 validate_file (lto_coff_file
*coff_file
)
321 unsigned int numsections
, secheaderssize
, numsyms
;
322 off_t sectionsstart
, symbolsstart
, stringsstart
;
323 unsigned int mach
, charact
;
325 /* Read and sanity check the raw header. */
326 n
= read (coff_file
->fd
, &coff_file
->coffhdr
, sizeof (coff_file
->coffhdr
));
327 if (n
!= sizeof (coff_file
->coffhdr
))
329 error ("not a COFF object file");
333 mach
= COFF_GET(&coff_file
->coffhdr
, Machine
);
334 for (n
= 0; n
< NUM_COFF_KNOWN_MACHINES
; n
++)
335 if (mach
== coff_machine_array
[n
])
337 if (n
== NUM_COFF_KNOWN_MACHINES
)
339 error ("not a recognized COFF object file");
343 charact
= COFF_GET(&coff_file
->coffhdr
, Characteristics
);
344 if (COFF_NOT_CHARACTERISTICS
& charact
)
346 /* DLL, EXE or SYS file. */
347 error ("not a relocatable COFF object file");
351 if (mach
!= IMAGE_FILE_MACHINE_AMD64
352 && COFF_CHARACTERISTICS
!= (COFF_CHARACTERISTICS
& charact
))
354 /* ECOFF/XCOFF support not implemented. */
355 error ("not a 32-bit COFF object file");
359 /* It validated OK, so cached it if we don't already have one. */
360 if (!cached_coff_hdr_valid
)
362 cached_coff_hdr_valid
= true;
363 memcpy (&cached_coff_hdr
, &coff_file
->coffhdr
, sizeof (cached_coff_hdr
));
366 if (mach
!= COFF_GET(&cached_coff_hdr
, Machine
))
368 error ("inconsistent file architecture detected");
372 /* Read section headers and string table? */
374 numsections
= COFF_GET(&coff_file
->coffhdr
, NumberOfSections
);
375 secheaderssize
= numsections
* sizeof (Coff_section
);
376 sectionsstart
= sizeof (Coff_header
) + secheaderssize
;
377 symbolsstart
= COFF_GET(&coff_file
->coffhdr
, PointerToSymbolTable
);
378 numsyms
= COFF_GET(&coff_file
->coffhdr
, NumberOfSymbols
);
379 stringsstart
= (symbolsstart
+ COFF_SYMBOL_SIZE
* numsyms
);
381 #define CVOFFSETTTED(x) (coff_file->base.offset + (x))
383 if (numsections
<= 0 || symbolsstart
<= 0 || numsyms
<= 0
384 || (CVOFFSETTTED(sectionsstart
) >= coff_file
->file_size
)
385 || (CVOFFSETTTED(symbolsstart
) >= coff_file
->file_size
)
386 || (CVOFFSETTTED(stringsstart
) >= coff_file
->file_size
))
388 error ("not a valid COFF object file");
394 /* Record start of string table. */
395 coff_file
->strtab_offs
= stringsstart
;
397 /* Validate section table entries. */
398 for (secnum
= 0; secnum
< numsections
; secnum
++)
400 Coff_section coffsec
;
401 lto_coff_section
*ltosec
;
402 off_t size_raw
, offs_raw
, offs_relocs
, offs_lines
;
403 off_t num_relocs
, num_lines
;
405 n
= read (coff_file
->fd
, &coffsec
, sizeof (coffsec
));
406 if (n
!= sizeof (coffsec
))
408 error ("short/missing COFF section table");
412 size_raw
= COFF_GET(&coffsec
, SizeOfRawData
);
413 offs_raw
= COFF_GET(&coffsec
, PointerToRawData
);
414 offs_relocs
= COFF_GET(&coffsec
, PointerToRelocations
);
415 offs_lines
= COFF_GET(&coffsec
, PointerToLinenumbers
);
416 num_relocs
= COFF_GET(&coffsec
, NumberOfRelocations
);
417 num_lines
= COFF_GET(&coffsec
, NumberOfLinenumbers
);
419 if (size_raw
< 0 || num_relocs
< 0 || num_lines
< 0
421 && ((COFF_GET(&coffsec
, Characteristics
)
422 & IMAGE_SCN_CNT_UNINITIALIZED_DATA
)
424 : (offs_raw
< sectionsstart
|| offs_raw
>= coff_file
->file_size
)))
426 && (offs_relocs
< sectionsstart
427 || offs_relocs
>= coff_file
->file_size
))
429 && (offs_lines
< sectionsstart
430 || offs_lines
>= coff_file
->file_size
)))
432 error ("invalid COFF section table");
436 /* Looks ok, so record its details. We don't read the
437 string table or set up names yet; we'll do that when
438 we build the hash table. */
439 ltosec
= coff_newsection (coff_file
, NULL
, 0);
440 memcpy (<osec
->coffsec
, &coffsec
, sizeof (ltosec
->coffsec
));
446 /* Initialize COFF_FILE's executable header using cached data from previously
450 init_coffhdr (lto_coff_file
*coff_file
)
452 gcc_assert (cached_coff_hdr_valid
);
453 memset (&coff_file
->coffhdr
, 0, sizeof (coff_file
->coffhdr
));
454 COFF_PUT(&coff_file
->coffhdr
, Machine
, COFF_GET(&cached_coff_hdr
, Machine
));
455 COFF_PUT(&coff_file
->coffhdr
, Characteristics
, COFF_GET(&cached_coff_hdr
, Characteristics
));
458 /* Open COFF file FILENAME. If WRITABLE is true, the file is opened for write
459 and, if necessary, created. Otherwise, the file is opened for reading.
460 Returns the opened file. */
463 lto_obj_file_open (const char *filename
, bool writable
)
465 lto_coff_file
*coff_file
;
466 lto_file
*result
= NULL
;
468 const char *offset_p
;
472 offset_p
= strchr (filename
, '@');
475 fname
= xstrdup (filename
);
480 /* The file started with '@' is a file containing command line
481 options. Stop if it doesn't exist. */
482 if (offset_p
== filename
)
483 fatal_error ("command line option file '%s' does not exist",
486 fname
= (char *) xmalloc (offset_p
- filename
+ 1);
487 memcpy (fname
, filename
, offset_p
- filename
);
488 fname
[offset_p
- filename
] = '\0';
489 offset_p
+= 3; /* skip the @0x */
490 offset
= lto_parse_hex (offset_p
);
494 coff_file
= XCNEW (lto_coff_file
);
495 result
= (lto_file
*) coff_file
;
496 lto_file_init (result
, fname
, offset
);
500 coff_file
->fd
= open (fname
,
501 O_BINARY
| (writable
? O_WRONLY
| O_CREAT
| O_TRUNC
: O_RDONLY
), 0666);
503 if (coff_file
->fd
== -1)
505 error ("could not open file %s", fname
);
509 if (stat (fname
, &statbuf
) < 0)
511 error ("could not stat file %s", fname
);
515 coff_file
->file_size
= statbuf
.st_size
;
523 gcc_assert (!writable
);
525 /* Seek to offset, or error. */
526 if (lseek (coff_file
->fd
, offset
, SEEK_SET
) != (ssize_t
) offset
)
528 error ("could not find archive member @0x%lx", (long) offset
);
532 /* Now seek back 12 chars and read the tail of the AR header to
533 find the length of the member file. */
534 if (lseek (coff_file
->fd
, -12, SEEK_CUR
) < 0
535 || read (coff_file
->fd
, ar_tail
, 12) != 12
536 || lseek (coff_file
->fd
, 0, SEEK_CUR
) != (ssize_t
) offset
537 || ar_tail
[10] != '`' || ar_tail
[11] != '\n')
539 error ("could not find archive header @0x%lx", (long) offset
);
544 if (sscanf (ar_tail
, "%d", &size
) != 1)
546 error ("invalid archive header @0x%lx", (long) offset
);
549 coff_file
->file_size
= size
;
554 init_coffhdr (coff_file
);
555 coff_file
->shstrtab_stream
= XCNEW (struct lto_output_stream
);
558 if (!validate_file (coff_file
))
565 lto_obj_file_close (result
);
570 /* Close COFF file FILE and clean up any associated data structures. If FILE
571 was opened for writing, the file's COFF data is written at this time, and
572 any cached data buffers are freed. Return TRUE if there was an error. */
575 coff_write_object_file (lto_coff_file
*coff_file
)
577 lto_coff_section
*cursec
, *stringsec
;
579 size_t fileoffset
, numsections
, totalsecsize
, numsyms
, stringssize
;
580 bool write_err
= false;
583 /* Infer whether this file was opened for reading or writing from the
584 presence or absense of an initialised stream for the string table;
585 do nothing if it was opened for reading. */
586 if (!coff_file
->shstrtab_stream
)
590 /* Write the COFF string table into a dummy new section that
591 we will not write a header for. */
592 lto_file
*old_file
= lto_set_current_out_file (&coff_file
->base
);
593 /* This recursively feeds in the data to a new section. */
594 lto_coff_begin_section_with_type (".strtab", 1);
595 lto_write_stream (coff_file
->shstrtab_stream
);
596 lto_obj_end_section ();
597 lto_set_current_out_file (old_file
);
598 free (coff_file
->shstrtab_stream
);
601 /* Layout the file. Count sections (not dummy string section) and calculate
602 data size for all of them. */
607 COFF_FOR_ALL_SECTIONS(coff_file
, cursec
)
612 COFF_FOR_ALL_DATA(cursec
,data
)
613 cursecsize
+= data
->d_size
;
614 if (cursec
->type
== 0)
617 totalsecsize
+= COFF_ALIGN(cursecsize
);
618 #if COFF_ALIGNMENT > 1
619 cursec
->pad_needed
= COFF_ALIGN(cursecsize
) - cursecsize
;
624 stringssize
= cursecsize
;
627 COFF_PUT(&cursec
->coffsec
, SizeOfRawData
, cursecsize
);
630 /* There is a file symbol and a section symbol per section,
631 and each of these has a single auxiliary symbol following. */
632 numsyms
= 2 * (1 + numsections
);
634 /* Great! Now we have enough info to fill out the file header. */
635 COFF_PUT(&coff_file
->coffhdr
, NumberOfSections
, numsections
);
636 COFF_PUT(&coff_file
->coffhdr
, NumberOfSymbols
, numsyms
);
637 COFF_PUT(&coff_file
->coffhdr
, PointerToSymbolTable
, sizeof (Coff_header
)
638 + numsections
* sizeof (Coff_section
) + totalsecsize
);
639 /* The remaining members were initialised to zero or copied from
640 a cached header, so we leave them alone here. */
642 /* Now position all the sections, and fill out their headers. */
643 fileoffset
= sizeof (Coff_header
) + numsections
* sizeof (Coff_section
);
644 COFF_FOR_ALL_SECTIONS(coff_file
, cursec
)
646 /* Skip dummy string section. */
647 if (cursec
->type
== 1)
649 COFF_PUT(&cursec
->coffsec
, PointerToRawData
, fileoffset
);
650 fileoffset
+= COFF_ALIGN (COFF_GET(&cursec
->coffsec
, SizeOfRawData
));
651 COFF_PUT(&cursec
->coffsec
, Characteristics
, COFF_SECTION_CHARACTERISTICS
);
652 snprintf ((char *)&cursec
->coffsec
.Name
[0], 8, "/%d", cursec
->strtab_offs
+ 4);
655 /* We can write the data now. As there's no way to indicate an error return
656 from this hook, error handling is limited to not wasting our time doing
657 any more writes in the event that any one fails. */
659 /* Write the COFF header. */
660 write_err
= (write (coff_file
->fd
, &coff_file
->coffhdr
,
661 sizeof (coff_file
->coffhdr
)) != sizeof (coff_file
->coffhdr
));
663 /* Write the COFF section headers. */
664 COFF_FOR_ALL_SECTIONS(coff_file
, cursec
)
665 if (cursec
->type
== 1) /* Skip dummy string section. */
668 write_err
= (write (coff_file
->fd
, &cursec
->coffsec
,
669 sizeof (cursec
->coffsec
)) != sizeof (cursec
->coffsec
));
673 /* Write the COFF sections. */
674 COFF_FOR_ALL_SECTIONS(coff_file
, cursec
)
676 #if COFF_ALIGNMENT > 1
677 static const char padzeros
[COFF_ALIGNMENT
] = { 0 };
679 /* Skip dummy string section. */
680 if (cursec
->type
== 1)
682 COFF_FOR_ALL_DATA(cursec
, data
)
684 write_err
= (write (coff_file
->fd
, data
->d_buf
, data
->d_size
)
688 #if COFF_ALIGNMENT > 1
689 if (!write_err
&& cursec
->pad_needed
)
690 write_err
= (write (coff_file
->fd
, padzeros
, cursec
->pad_needed
)
691 != cursec
->pad_needed
);
695 /* Write the COFF symbol table. */
701 Coff_aux_sym_file file
;
702 Coff_aux_sym_section sec
;
704 memset (&symbols
[0], 0, sizeof (symbols
));
705 strcpy ((char *) &symbols
[0].sym
.Name
[0], ".file");
706 COFF_PUT(&symbols
[0].sym
, SectionNumber
, IMAGE_SYM_DEBUG
);
707 COFF_PUT(&symbols
[0].sym
, Type
, IMAGE_SYM_TYPE
);
708 symbols
[0].sym
.StorageClass
[0] = IMAGE_SYM_CLASS_FILE
;
709 symbols
[0].sym
.NumberOfAuxSymbols
[0] = 1;
710 snprintf ((char *)symbols
[1].file
.FileName
,
711 sizeof (symbols
[1].file
.FileName
),
712 "%s", lbasename (coff_file
->base
.filename
));
713 write_err
= (write (coff_file
->fd
, &symbols
[0], sizeof (symbols
))
714 != (2 * COFF_SYMBOL_SIZE
));
716 /* Set up constant parts for section sym loop. */
717 memset (&symbols
[0], 0, sizeof (symbols
));
718 COFF_PUT(&symbols
[0].sym
, Type
, IMAGE_SYM_TYPE
);
719 symbols
[0].sym
.StorageClass
[0] = IMAGE_SYM_CLASS_STATIC
;
720 symbols
[0].sym
.NumberOfAuxSymbols
[0] = 1;
724 COFF_FOR_ALL_SECTIONS(coff_file
, cursec
)
726 /* Skip dummy string section. */
727 if (cursec
->type
== 1)
729 /* Reuse section name string for section symbol name. */
730 COFF_PUT_NDXSZ(&symbols
[0].sym
, Name
, 0, 0, 4);
731 COFF_PUT_NDXSZ(&symbols
[0].sym
, Name
, cursec
->strtab_offs
+ 4, 4, 4);
732 COFF_PUT(&symbols
[0].sym
, SectionNumber
, secnum
++);
733 COFF_PUT(&symbols
[1].sec
, Length
,
734 COFF_GET(&cursec
->coffsec
, SizeOfRawData
));
736 write_err
= (write (coff_file
->fd
, &symbols
[0], sizeof (symbols
))
737 != (2 * COFF_SYMBOL_SIZE
));
743 /* Write the COFF string table. */
746 unsigned char outlen
[4];
747 COFF_PUT4(outlen
, stringssize
+ 4);
749 write_err
= (write (coff_file
->fd
, outlen
, 4) != 4);
752 COFF_FOR_ALL_DATA(stringsec
, data
)
754 write_err
= (write (coff_file
->fd
, data
->d_buf
, data
->d_size
)
764 /* Close COFF file FILE and clean up any associated data structures. If FILE
765 was opened for writing, the file's COFF data is written at this time, and
766 any cached data buffers are freed. */
769 lto_obj_file_close (lto_file
*file
)
771 lto_coff_file
*coff_file
= (lto_coff_file
*) file
;
772 struct lto_char_ptr_base
*cur
, *tmp
;
773 lto_coff_section
*cursec
, *nextsec
;
774 bool write_err
= false;
776 /* Write the COFF string table into a dummy new section that
777 we will not write a header for. */
778 if (coff_file
->shstrtab_stream
)
779 coff_write_object_file (coff_file
);
781 /* Close the file, we're done. */
782 if (coff_file
->fd
!= -1)
783 close (coff_file
->fd
);
785 /* Free any data buffers. */
786 cur
= coff_file
->data
;
790 cur
= (struct lto_char_ptr_base
*) cur
->ptr
;
794 /* Free any sections and their data chains. */
795 cursec
= coff_file
->section_chain
;
798 lto_coff_data
*curdata
, *nextdata
;
799 nextsec
= cursec
->next
;
800 curdata
= cursec
->data_chain
;
803 nextdata
= curdata
->next
;
813 /* If there was an error, mention it. */
815 error ("I/O error writing COFF output file");