diff options
author | Ian Lance Taylor <iant@google.com> | 2010-11-02 14:40:44 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2010-11-02 14:40:44 +0000 |
commit | 1cfabf34d83637a5e9ffa11dc38379d017738cf7 (patch) | |
tree | b13f59ad5c9b124df2fcf21f33e4c80b19f23808 /libiberty/simple-object-coff.c | |
parent | fee3eacd6fea60bfc2bed8477315ea1f125a278c (diff) | |
download | gcc-1cfabf34d83637a5e9ffa11dc38379d017738cf7.zip gcc-1cfabf34d83637a5e9ffa11dc38379d017738cf7.tar.gz gcc-1cfabf34d83637a5e9ffa11dc38379d017738cf7.tar.bz2 |
simple-object.h: New file.
include/:
* simple-object.h: New file.
libiberty/:
* simple-object.c: New file.
* simple-object-common.h: New file.
* simple-object-elf.c: New file.
* simple-object-mach-o.c: New file.
* simple-object-coff.c: New file.
* simple-object.txh: New file.
* configure.ac: Add AC_TYPE_SSIZE_T.
* Makefile.in: Rebuild dependencies.
(CFILES): Add simple-object.c, simple-object-coff,
simple-object-elf.c, and simple-object-mach-o.c.
(REQUIRED_OFILES): Add corresponding object files.
* configure: Rebuild.
* config.in: Rebuild.
* functions.texi: Rebuild.
Co-Authored-By: Dave Korn <dave.korn.cygwin@gmail.com>
Co-Authored-By: Iain Sandoe <iains@gcc.gnu.org>
From-SVN: r166185
Diffstat (limited to 'libiberty/simple-object-coff.c')
-rw-r--r-- | libiberty/simple-object-coff.c | 804 |
1 files changed, 804 insertions, 0 deletions
diff --git a/libiberty/simple-object-coff.c b/libiberty/simple-object-coff.c new file mode 100644 index 0000000..9ba1dd4 --- /dev/null +++ b/libiberty/simple-object-coff.c @@ -0,0 +1,804 @@ +/* simple-object-coff.c -- routines to manipulate COFF object files. + Copyright 2010 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Google. + +This program 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 2, or (at your option) any +later version. + +This program 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 this program; if not, write to the Free Software +Foundation, 51 Franklin Street - Fifth Floor, +Boston, MA 02110-1301, USA. */ + +#include "config.h" +#include "libiberty.h" +#include "simple-object.h" + +#include <errno.h> +#include <stddef.h> + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif + +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif + +#include "simple-object-common.h" + +/* COFF structures and constants. */ + +/* COFF file header. */ + +struct external_filehdr +{ + unsigned char f_magic[2]; /* magic number */ + unsigned char f_nscns[2]; /* number of sections */ + unsigned char f_timdat[4]; /* time & date stamp */ + unsigned char f_symptr[4]; /* file pointer to symtab */ + unsigned char f_nsyms[4]; /* number of symtab entries */ + unsigned char f_opthdr[2]; /* sizeof(optional hdr) */ + unsigned char f_flags[2]; /* flags */ +}; + +/* Bits for filehdr f_flags field. */ + +#define F_EXEC (0x0002) +#define IMAGE_FILE_SYSTEM (0x1000) +#define IMAGE_FILE_DLL (0x2000) + +/* COFF section header. */ + +struct external_scnhdr +{ + unsigned char s_name[8]; /* section name */ + unsigned char s_paddr[4]; /* physical address, aliased s_nlib */ + unsigned char s_vaddr[4]; /* virtual address */ + unsigned char s_size[4]; /* section size */ + unsigned char s_scnptr[4]; /* file ptr to raw data for section */ + unsigned char s_relptr[4]; /* file ptr to relocation */ + unsigned char s_lnnoptr[4]; /* file ptr to line numbers */ + unsigned char s_nreloc[2]; /* number of relocation entries */ + unsigned char s_nlnno[2]; /* number of line number entries */ + unsigned char s_flags[4]; /* flags */ +}; + +/* The length of the s_name field in struct external_scnhdr. */ + +#define SCNNMLEN (8) + +/* Bits for scnhdr s_flags field. This includes some bits defined + only for PE. This may need to be moved into coff_magic. */ + +#define STYP_DATA (1 << 6) +#define IMAGE_SCN_MEM_DISCARDABLE (1 << 25) +#define IMAGE_SCN_MEM_SHARED (1 << 28) +#define IMAGE_SCN_MEM_READ (1 << 30) + +#define IMAGE_SCN_ALIGN_POWER_BIT_POS 20 +#define IMAGE_SCN_ALIGN_POWER_CONST(val) \ + (((val) + 1) << IMAGE_SCN_ALIGN_POWER_BIT_POS) + +/* COFF symbol table entry. */ + +#define E_SYMNMLEN 8 /* # characters in a symbol name */ + +struct external_syment +{ + union + { + unsigned char e_name[E_SYMNMLEN]; + + struct + { + unsigned char e_zeroes[4]; + unsigned char e_offset[4]; + } e; + } e; + + unsigned char e_value[4]; + unsigned char e_scnum[2]; + unsigned char e_type[2]; + unsigned char e_sclass[1]; + unsigned char e_numaux[1]; +}; + +/* Length allowed for filename in aux sym format 4. */ + +#define E_FILNMLEN 18 + +/* Omits x_sym and other unused variants. */ + +union external_auxent +{ + /* Aux sym format 4: file. */ + union + { + char x_fname[E_FILNMLEN]; + struct + { + unsigned char x_zeroes[4]; + unsigned char x_offset[4]; + } x_n; + } x_file; + /* Aux sym format 5: section. */ + struct + { + unsigned char x_scnlen[4]; /* section length */ + unsigned char x_nreloc[2]; /* # relocation entries */ + unsigned char x_nlinno[2]; /* # line numbers */ + unsigned char x_checksum[4]; /* section COMDAT checksum */ + unsigned char x_associated[2]; /* COMDAT assoc section index */ + unsigned char x_comdat[1]; /* COMDAT selection number */ + } x_scn; +}; + +/* 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) + +/* Private data for an simple_object_read. */ + +struct simple_object_coff_read +{ + /* Magic number. */ + unsigned short magic; + /* Whether the file is big-endian. */ + unsigned char is_big_endian; + /* Number of sections. */ + unsigned short nscns; + /* File offset of symbol table. */ + off_t symptr; + /* Number of symbol table entries. */ + unsigned int nsyms; + /* Flags. */ + unsigned short flags; + /* Offset of section headers in file. */ + off_t scnhdr_offset; +}; + +/* Private data for an simple_object_attributes. */ + +struct simple_object_coff_attributes +{ + /* Magic number. */ + unsigned short magic; + /* Whether the file is big-endian. */ + unsigned char is_big_endian; + /* Flags. */ + unsigned short flags; +}; + +/* There is no magic number which indicates a COFF file as opposed to + any other sort of file. Instead, each COFF file starts with a + two-byte magic number which also indicates the type of the target. + This struct holds a magic number as well as characteristics of that + COFF format. */ + +struct coff_magic_struct +{ + /* Magic number. */ + unsigned short magic; + /* Whether this magic number is for a big-endian file. */ + unsigned char is_big_endian; + /* Flag bits, in the f_flags fields, which indicates that this file + is not a relocatable object file. There is no flag which + specifically indicates a relocatable object file, it is only + implied by the absence of these flags. */ + unsigned short non_object_flags; +}; + +/* This is a list of the COFF magic numbers which we recognize, namely + the ones used on Windows. More can be added as needed. */ + +static const struct coff_magic_struct coff_magic[] = +{ + /* i386. */ + { 0x14c, 0, F_EXEC | IMAGE_FILE_SYSTEM | IMAGE_FILE_DLL }, + /* x86_64. */ + { 0x8664, 0, F_EXEC | IMAGE_FILE_SYSTEM | IMAGE_FILE_DLL } +}; + +/* See if we have a COFF file. */ + +static void * +simple_object_coff_match (unsigned char header[SIMPLE_OBJECT_MATCH_HEADER_LEN], + int descriptor, off_t offset, + const char *segment_name ATTRIBUTE_UNUSED, + const char **errmsg, int *err) +{ + size_t c; + unsigned short magic_big; + unsigned short magic_little; + unsigned short magic; + size_t i; + int is_big_endian; + unsigned short (*fetch_16) (const unsigned char *); + unsigned int (*fetch_32) (const unsigned char *); + unsigned char hdrbuf[sizeof (struct external_filehdr)]; + unsigned short flags; + struct simple_object_coff_read *ocr; + + c = sizeof (coff_magic) / sizeof (coff_magic[0]); + magic_big = simple_object_fetch_big_16 (header); + magic_little = simple_object_fetch_little_16 (header); + for (i = 0; i < c; ++i) + { + if (coff_magic[i].is_big_endian + ? coff_magic[i].magic == magic_big + : coff_magic[i].magic == magic_little) + break; + } + if (i >= c) + { + *errmsg = NULL; + *err = 0; + return NULL; + } + is_big_endian = coff_magic[i].is_big_endian; + + magic = is_big_endian ? magic_big : magic_little; + fetch_16 = (is_big_endian + ? simple_object_fetch_big_16 + : simple_object_fetch_little_16); + fetch_32 = (is_big_endian + ? simple_object_fetch_big_32 + : simple_object_fetch_little_32); + + if (!simple_object_internal_read (descriptor, offset, hdrbuf, sizeof hdrbuf, + errmsg, err)) + return NULL; + + flags = fetch_16 (hdrbuf + offsetof (struct external_filehdr, f_flags)); + if ((flags & coff_magic[i].non_object_flags) != 0) + { + *errmsg = "not relocatable object file"; + *err = 0; + return NULL; + } + + ocr = XNEW (struct simple_object_coff_read); + ocr->magic = magic; + ocr->is_big_endian = is_big_endian; + ocr->nscns = fetch_16 (hdrbuf + offsetof (struct external_filehdr, f_nscns)); + ocr->symptr = fetch_32 (hdrbuf + + offsetof (struct external_filehdr, f_symptr)); + ocr->nsyms = fetch_32 (hdrbuf + offsetof (struct external_filehdr, f_nsyms)); + ocr->flags = flags; + ocr->scnhdr_offset = (sizeof (struct external_filehdr) + + fetch_16 (hdrbuf + offsetof (struct external_filehdr, + f_opthdr))); + + return (void *) ocr; +} + +/* Read the string table in a COFF file. */ + +static char * +simple_object_coff_read_strtab (simple_object_read *sobj, size_t *strtab_size, + const char **errmsg, int *err) +{ + struct simple_object_coff_read *ocr = + (struct simple_object_coff_read *) sobj->data; + off_t strtab_offset; + unsigned char strsizebuf[4]; + size_t strsize; + char *strtab; + + strtab_offset = ocr->symptr + ocr->nsyms * sizeof (struct external_syment); + if (!simple_object_internal_read (sobj->descriptor, strtab_offset, + strsizebuf, 4, errmsg, err)) + return NULL; + strsize = (ocr->is_big_endian + ? simple_object_fetch_big_32 (strsizebuf) + : simple_object_fetch_little_32 (strsizebuf)); + strtab = XNEWVEC (char, strsize); + if (!simple_object_internal_read (sobj->descriptor, strtab_offset, + (unsigned char *) strtab, strsize, errmsg, + err)) + { + XDELETEVEC (strtab); + return NULL; + } + *strtab_size = strsize; + return strtab; +} + +/* Find all sections in a COFF file. */ + +static const char * +simple_object_coff_find_sections (simple_object_read *sobj, + int (*pfn) (void *, const char *, + off_t offset, off_t length), + void *data, + int *err) +{ + struct simple_object_coff_read *ocr = + (struct simple_object_coff_read *) sobj->data; + size_t scnhdr_size; + unsigned char *scnbuf; + const char *errmsg; + unsigned int (*fetch_32) (const unsigned char *); + unsigned int nscns; + char *strtab; + size_t strtab_size; + unsigned int i; + + scnhdr_size = sizeof (struct external_scnhdr); + scnbuf = XNEWVEC (unsigned char, scnhdr_size * ocr->nscns); + if (!simple_object_internal_read (sobj->descriptor, + sobj->offset + ocr->scnhdr_offset, + scnbuf, scnhdr_size * ocr->nscns, &errmsg, + err)) + { + XDELETEVEC (scnbuf); + return errmsg; + } + + fetch_32 = (ocr->is_big_endian + ? simple_object_fetch_big_32 + : simple_object_fetch_little_32); + + nscns = ocr->nscns; + strtab = NULL; + strtab_size = 0; + for (i = 0; i < nscns; ++i) + { + unsigned char *scnhdr; + unsigned char *scnname; + char namebuf[SCNNMLEN + 1]; + char *name; + off_t scnptr; + unsigned int size; + + scnhdr = scnbuf + i * scnhdr_size; + scnname = scnhdr + offsetof (struct external_scnhdr, s_name); + memcpy (namebuf, scnname, SCNNMLEN); + namebuf[SCNNMLEN] = '\0'; + name = &namebuf[0]; + if (namebuf[0] == '/') + { + size_t strindex; + char *end; + + strindex = strtol (namebuf + 1, &end, 10); + if (*end == '\0') + { + /* The real section name is found in the string + table. */ + if (strtab == NULL) + { + strtab = simple_object_coff_read_strtab (sobj, + &strtab_size, + &errmsg, err); + if (strtab == NULL) + { + XDELETEVEC (scnbuf); + return errmsg; + } + } + + if (strindex < 4 || strindex >= strtab_size) + { + XDELETEVEC (strtab); + XDELETEVEC (scnbuf); + *err = 0; + return "section string index out of range"; + } + + name = strtab + strindex; + } + } + + scnptr = fetch_32 (scnhdr + offsetof (struct external_scnhdr, s_scnptr)); + size = fetch_32 (scnhdr + offsetof (struct external_scnhdr, s_size)); + + if (!(*pfn) (data, name, scnptr, size)) + break; + } + + if (strtab != NULL) + XDELETEVEC (strtab); + XDELETEVEC (scnbuf); + + return NULL; +} + +/* Fetch the attributes for an simple_object_read. */ + +static void * +simple_object_coff_fetch_attributes (simple_object_read *sobj, + const char **errmsg ATTRIBUTE_UNUSED, + int *err ATTRIBUTE_UNUSED) +{ + struct simple_object_coff_read *ocr = + (struct simple_object_coff_read *) sobj->data; + struct simple_object_coff_attributes *ret; + + ret = XNEW (struct simple_object_coff_attributes); + ret->magic = ocr->magic; + ret->is_big_endian = ocr->is_big_endian; + ret->flags = ocr->flags; + return ret; +} + +/* Release the private data for an simple_object_read. */ + +static void +simple_object_coff_release_read (void *data) +{ + XDELETE (data); +} + +/* Compare two attributes structures. */ + +static const char * +simple_object_coff_attributes_compare (void *data1, void *data2, int *err) +{ + struct simple_object_coff_attributes *attrs1 = + (struct simple_object_coff_attributes *) data1; + struct simple_object_coff_attributes *attrs2 = + (struct simple_object_coff_attributes *) data2; + + if (attrs1->magic != attrs2->magic + || attrs1->is_big_endian != attrs2->is_big_endian) + { + *err = 0; + return "COFF object format mismatch"; + } + return NULL; +} + +/* Release the private data for an attributes structure. */ + +static void +simple_object_coff_release_attributes (void *data) +{ + XDELETE (data); +} + +/* Prepare to write out a file. */ + +static void * +simple_object_coff_start_write (void *attributes_data, + const char **errmsg ATTRIBUTE_UNUSED, + int *err ATTRIBUTE_UNUSED) +{ + struct simple_object_coff_attributes *attrs = + (struct simple_object_coff_attributes *) attributes_data; + struct simple_object_coff_attributes *ret; + + /* We're just going to record the attributes, but we need to make a + copy because the user may delete them. */ + ret = XNEW (struct simple_object_coff_attributes); + *ret = *attrs; + return ret; +} + +/* Write out a COFF filehdr. */ + +static int +simple_object_coff_write_filehdr (simple_object_write *sobj, int descriptor, + unsigned int nscns, size_t symtab_offset, + unsigned int nsyms, const char **errmsg, + int *err) +{ + struct simple_object_coff_attributes *attrs = + (struct simple_object_coff_attributes *) sobj->data; + unsigned char hdrbuf[sizeof (struct external_filehdr)]; + unsigned char *hdr; + void (*set_16) (unsigned char *, unsigned short); + void (*set_32) (unsigned char *, unsigned int); + + hdr = &hdrbuf[0]; + + set_16 = (attrs->is_big_endian + ? simple_object_set_big_16 + : simple_object_set_little_16); + set_32 = (attrs->is_big_endian + ? simple_object_set_big_32 + : simple_object_set_little_32); + + memset (hdr, 0, sizeof (struct external_filehdr)); + + set_16 (hdr + offsetof (struct external_filehdr, f_magic), attrs->magic); + set_16 (hdr + offsetof (struct external_filehdr, f_nscns), nscns); + /* f_timdat left as zero. */ + set_32 (hdr + offsetof (struct external_filehdr, f_symptr), symtab_offset); + set_32 (hdr + offsetof (struct external_filehdr, f_nsyms), nsyms); + /* f_opthdr left as zero. */ + set_16 (hdr + offsetof (struct external_filehdr, f_flags), attrs->flags); + + return simple_object_internal_write (descriptor, 0, hdrbuf, + sizeof (struct external_filehdr), + errmsg, err); +} + +/* Write out a COFF section header. */ + +static int +simple_object_coff_write_scnhdr (simple_object_write *sobj, int descriptor, + const char *name, size_t *name_offset, + off_t scnhdr_offset, size_t scnsize, + off_t offset, unsigned int align, + const char **errmsg, int *err) +{ + struct simple_object_coff_attributes *attrs = + (struct simple_object_coff_attributes *) sobj->data; + void (*set_32) (unsigned char *, unsigned int); + unsigned char hdrbuf[sizeof (struct external_scnhdr)]; + unsigned char *hdr; + size_t namelen; + unsigned int flags; + + set_32 = (attrs->is_big_endian + ? simple_object_set_big_32 + : simple_object_set_little_32); + + memset (hdrbuf, 0, sizeof hdrbuf); + hdr = &hdrbuf[0]; + + namelen = strlen (name); + if (namelen <= SCNNMLEN) + strncpy ((char *) hdr + offsetof (struct external_scnhdr, s_name), name, + SCNNMLEN); + else + { + snprintf ((char *) hdr + offsetof (struct external_scnhdr, s_name), + SCNNMLEN, "/%lu", (unsigned long) *name_offset); + *name_offset += namelen + 1; + } + + /* s_paddr left as zero. */ + /* s_vaddr left as zero. */ + set_32 (hdr + offsetof (struct external_scnhdr, s_size), scnsize); + set_32 (hdr + offsetof (struct external_scnhdr, s_scnptr), offset); + /* s_relptr left as zero. */ + /* s_lnnoptr left as zero. */ + /* s_nreloc left as zero. */ + /* s_nlnno left as zero. */ + flags = (STYP_DATA | IMAGE_SCN_MEM_DISCARDABLE | IMAGE_SCN_MEM_SHARED + | IMAGE_SCN_MEM_READ); + /* PE can represent alignment up to 13. */ + if (align > 13) + align = 13; + flags |= IMAGE_SCN_ALIGN_POWER_CONST(align); + set_32 (hdr + offsetof (struct external_scnhdr, s_flags), flags); + + return simple_object_internal_write (descriptor, scnhdr_offset, hdrbuf, + sizeof (struct external_scnhdr), + errmsg, err); +} + +/* Write out a complete COFF file. */ + +static const char * +simple_object_coff_write_to_file (simple_object_write *sobj, int descriptor, + int *err) +{ + struct simple_object_coff_attributes *attrs = + (struct simple_object_coff_attributes *) sobj->data; + unsigned int nscns, secnum; + simple_object_write_section *section; + off_t scnhdr_offset; + size_t symtab_offset; + off_t secsym_offset; + unsigned int nsyms; + size_t offset; + size_t name_offset; + const char *errmsg; + unsigned char strsizebuf[4]; + /* The interface doesn't give us access to the name of the input file + yet. We want to use its basename for the FILE symbol. This is + what 'gas' uses when told to assemble from stdin. */ + const char *source_filename = "fake"; + size_t sflen; + union + { + struct external_syment sym; + union external_auxent aux; + } syms[2]; + void (*set_16) (unsigned char *, unsigned short); + void (*set_32) (unsigned char *, unsigned int); + + set_16 = (attrs->is_big_endian + ? simple_object_set_big_16 + : simple_object_set_little_16); + set_32 = (attrs->is_big_endian + ? simple_object_set_big_32 + : simple_object_set_little_32); + + nscns = 0; + for (section = sobj->sections; section != NULL; section = section->next) + ++nscns; + + scnhdr_offset = sizeof (struct external_filehdr); + offset = scnhdr_offset + nscns * sizeof (struct external_scnhdr); + name_offset = 4; + for (section = sobj->sections; section != NULL; section = section->next) + { + size_t mask; + size_t new_offset; + size_t scnsize; + struct simple_object_write_section_buffer *buffer; + + mask = (1U << section->align) - 1; + new_offset = offset & mask; + new_offset &= ~ mask; + while (new_offset > offset) + { + unsigned char zeroes[16]; + size_t write; + + memset (zeroes, 0, sizeof zeroes); + write = new_offset - offset; + if (write > sizeof zeroes) + write = sizeof zeroes; + if (!simple_object_internal_write (descriptor, offset, zeroes, write, + &errmsg, err)) + return errmsg; + } + + scnsize = 0; + for (buffer = section->buffers; buffer != NULL; buffer = buffer->next) + { + if (!simple_object_internal_write (descriptor, offset + scnsize, + ((const unsigned char *) + buffer->buffer), + buffer->size, &errmsg, err)) + return errmsg; + scnsize += buffer->size; + } + + if (!simple_object_coff_write_scnhdr (sobj, descriptor, section->name, + &name_offset, scnhdr_offset, + scnsize, offset, section->align, + &errmsg, err)) + return errmsg; + + scnhdr_offset += sizeof (struct external_scnhdr); + offset += scnsize; + } + + /* Symbol table is always half-word aligned. */ + offset += (offset & 1); + /* There is a file symbol and a section symbol per section, + and each of these has a single auxiliary symbol following. */ + nsyms = 2 * (nscns + 1); + symtab_offset = offset; + /* Advance across space reserved for symbol table to locate + start of string table. */ + offset += nsyms * sizeof (struct external_syment); + + /* Write out file symbol. */ + memset (&syms[0], 0, sizeof (syms)); + strcpy ((char *)&syms[0].sym.e.e_name[0], ".file"); + set_16 (&syms[0].sym.e_scnum[0], IMAGE_SYM_DEBUG); + set_16 (&syms[0].sym.e_type[0], IMAGE_SYM_TYPE); + syms[0].sym.e_sclass[0] = IMAGE_SYM_CLASS_FILE; + syms[0].sym.e_numaux[0] = 1; + /* The name need not be nul-terminated if it fits into the x_fname field + directly, but must be if it has to be placed into the string table. */ + sflen = strlen (source_filename); + if (sflen <= E_FILNMLEN) + memcpy (&syms[1].aux.x_file.x_fname[0], source_filename, sflen); + else + { + set_32 (&syms[1].aux.x_file.x_n.x_offset[0], name_offset); + if (!simple_object_internal_write (descriptor, offset + name_offset, + ((const unsigned char *) + source_filename), + sflen + 1, &errmsg, err)) + return errmsg; + name_offset += strlen (source_filename) + 1; + } + if (!simple_object_internal_write (descriptor, symtab_offset, + (const unsigned char *) &syms[0], + sizeof (syms), &errmsg, err)) + return errmsg; + + /* Write the string table length, followed by the strings and section + symbols in step with each other. */ + set_32 (strsizebuf, name_offset); + if (!simple_object_internal_write (descriptor, offset, strsizebuf, 4, + &errmsg, err)) + return errmsg; + + name_offset = 4; + secsym_offset = symtab_offset + sizeof (syms); + memset (&syms[0], 0, sizeof (syms)); + set_16 (&syms[0].sym.e_type[0], IMAGE_SYM_TYPE); + syms[0].sym.e_sclass[0] = IMAGE_SYM_CLASS_STATIC; + syms[0].sym.e_numaux[0] = 1; + secnum = 1; + + for (section = sobj->sections; section != NULL; section = section->next) + { + size_t namelen; + size_t scnsize; + struct simple_object_write_section_buffer *buffer; + + namelen = strlen (section->name); + set_16 (&syms[0].sym.e_scnum[0], secnum++); + scnsize = 0; + for (buffer = section->buffers; buffer != NULL; buffer = buffer->next) + scnsize += buffer->size; + set_32 (&syms[1].aux.x_scn.x_scnlen[0], scnsize); + if (namelen > SCNNMLEN) + { + set_32 (&syms[0].sym.e.e.e_zeroes[0], 0); + set_32 (&syms[0].sym.e.e.e_offset[0], name_offset); + if (!simple_object_internal_write (descriptor, offset + name_offset, + ((const unsigned char *) + section->name), + namelen + 1, &errmsg, err)) + return errmsg; + name_offset += namelen + 1; + } + else + { + memcpy (&syms[0].sym.e.e_name[0], section->name, + strlen (section->name)); + memset (&syms[0].sym.e.e_name[strlen (section->name)], 0, + E_SYMNMLEN - strlen (section->name)); + } + + if (!simple_object_internal_write (descriptor, secsym_offset, + (const unsigned char *) &syms[0], + sizeof (syms), &errmsg, err)) + return errmsg; + secsym_offset += sizeof (syms); + } + + if (!simple_object_coff_write_filehdr (sobj, descriptor, nscns, + symtab_offset, nsyms, &errmsg, err)) + return errmsg; + + return NULL; +} + +/* Release the private data for an simple_object_write structure. */ + +static void +simple_object_coff_release_write (void *data) +{ + XDELETE (data); +} + +/* The COFF functions. */ + +const struct simple_object_functions simple_object_coff_functions = +{ + simple_object_coff_match, + simple_object_coff_find_sections, + simple_object_coff_fetch_attributes, + simple_object_coff_release_read, + simple_object_coff_attributes_compare, + simple_object_coff_release_attributes, + simple_object_coff_start_write, + simple_object_coff_write_to_file, + simple_object_coff_release_write +}; |