1b3ac4aedSchristos /* simple-object-coff.c -- routines to manipulate COFF object files.
2*f22f0ef4Schristos Copyright (C) 2010-2022 Free Software Foundation, Inc.
3b3ac4aedSchristos Written by Ian Lance Taylor, Google.
4b3ac4aedSchristos
5b3ac4aedSchristos This program is free software; you can redistribute it and/or modify it
6b3ac4aedSchristos under the terms of the GNU General Public License as published by the
7b3ac4aedSchristos Free Software Foundation; either version 2, or (at your option) any
8b3ac4aedSchristos later version.
9b3ac4aedSchristos
10b3ac4aedSchristos This program is distributed in the hope that it will be useful,
11b3ac4aedSchristos but WITHOUT ANY WARRANTY; without even the implied warranty of
12b3ac4aedSchristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13b3ac4aedSchristos GNU General Public License for more details.
14b3ac4aedSchristos
15b3ac4aedSchristos You should have received a copy of the GNU General Public License
16b3ac4aedSchristos along with this program; if not, write to the Free Software
17b3ac4aedSchristos Foundation, 51 Franklin Street - Fifth Floor,
18b3ac4aedSchristos Boston, MA 02110-1301, USA. */
19b3ac4aedSchristos
20b3ac4aedSchristos #include "config.h"
21b3ac4aedSchristos #include "libiberty.h"
22b3ac4aedSchristos #include "simple-object.h"
23b3ac4aedSchristos
24b3ac4aedSchristos #include <errno.h>
25b3ac4aedSchristos #include <stddef.h>
26b3ac4aedSchristos
27b3ac4aedSchristos #ifdef HAVE_STDLIB_H
28b3ac4aedSchristos #include <stdlib.h>
29b3ac4aedSchristos #endif
30b3ac4aedSchristos
31b3ac4aedSchristos #ifdef HAVE_STDINT_H
32b3ac4aedSchristos #include <stdint.h>
33b3ac4aedSchristos #endif
34b3ac4aedSchristos
35b3ac4aedSchristos #ifdef HAVE_STRING_H
36b3ac4aedSchristos #include <string.h>
37b3ac4aedSchristos #endif
38b3ac4aedSchristos
39b3ac4aedSchristos #ifdef HAVE_INTTYPES_H
40b3ac4aedSchristos #include <inttypes.h>
41b3ac4aedSchristos #endif
42b3ac4aedSchristos
43b3ac4aedSchristos #include "simple-object-common.h"
44b3ac4aedSchristos
45b3ac4aedSchristos /* COFF structures and constants. */
46b3ac4aedSchristos
47b3ac4aedSchristos /* COFF file header. */
48b3ac4aedSchristos
49b3ac4aedSchristos struct external_filehdr
50b3ac4aedSchristos {
51b3ac4aedSchristos unsigned char f_magic[2]; /* magic number */
52b3ac4aedSchristos unsigned char f_nscns[2]; /* number of sections */
53b3ac4aedSchristos unsigned char f_timdat[4]; /* time & date stamp */
54b3ac4aedSchristos unsigned char f_symptr[4]; /* file pointer to symtab */
55b3ac4aedSchristos unsigned char f_nsyms[4]; /* number of symtab entries */
56b3ac4aedSchristos unsigned char f_opthdr[2]; /* sizeof(optional hdr) */
57b3ac4aedSchristos unsigned char f_flags[2]; /* flags */
58b3ac4aedSchristos };
59b3ac4aedSchristos
60b3ac4aedSchristos /* Bits for filehdr f_flags field. */
61b3ac4aedSchristos
62b3ac4aedSchristos #define F_EXEC (0x0002)
63b3ac4aedSchristos #define IMAGE_FILE_SYSTEM (0x1000)
64b3ac4aedSchristos #define IMAGE_FILE_DLL (0x2000)
65b3ac4aedSchristos
66b3ac4aedSchristos /* COFF section header. */
67b3ac4aedSchristos
68b3ac4aedSchristos struct external_scnhdr
69b3ac4aedSchristos {
70b3ac4aedSchristos unsigned char s_name[8]; /* section name */
71b3ac4aedSchristos unsigned char s_paddr[4]; /* physical address, aliased s_nlib */
72b3ac4aedSchristos unsigned char s_vaddr[4]; /* virtual address */
73b3ac4aedSchristos unsigned char s_size[4]; /* section size */
74b3ac4aedSchristos unsigned char s_scnptr[4]; /* file ptr to raw data for section */
75b3ac4aedSchristos unsigned char s_relptr[4]; /* file ptr to relocation */
76b3ac4aedSchristos unsigned char s_lnnoptr[4]; /* file ptr to line numbers */
77b3ac4aedSchristos unsigned char s_nreloc[2]; /* number of relocation entries */
78b3ac4aedSchristos unsigned char s_nlnno[2]; /* number of line number entries */
79b3ac4aedSchristos unsigned char s_flags[4]; /* flags */
80b3ac4aedSchristos };
81b3ac4aedSchristos
82b3ac4aedSchristos /* The length of the s_name field in struct external_scnhdr. */
83b3ac4aedSchristos
84b3ac4aedSchristos #define SCNNMLEN (8)
85b3ac4aedSchristos
86b3ac4aedSchristos /* Bits for scnhdr s_flags field. This includes some bits defined
87b3ac4aedSchristos only for PE. This may need to be moved into coff_magic. */
88b3ac4aedSchristos
89b3ac4aedSchristos #define STYP_DATA (1 << 6)
90b3ac4aedSchristos #define IMAGE_SCN_MEM_DISCARDABLE (1 << 25)
91b3ac4aedSchristos #define IMAGE_SCN_MEM_SHARED (1 << 28)
92b3ac4aedSchristos #define IMAGE_SCN_MEM_READ (1 << 30)
93b3ac4aedSchristos
94b3ac4aedSchristos #define IMAGE_SCN_ALIGN_POWER_BIT_POS 20
95b3ac4aedSchristos #define IMAGE_SCN_ALIGN_POWER_CONST(val) \
96b3ac4aedSchristos (((val) + 1) << IMAGE_SCN_ALIGN_POWER_BIT_POS)
97b3ac4aedSchristos
98b3ac4aedSchristos /* COFF symbol table entry. */
99b3ac4aedSchristos
100b3ac4aedSchristos #define E_SYMNMLEN 8 /* # characters in a symbol name */
101b3ac4aedSchristos
102b3ac4aedSchristos struct external_syment
103b3ac4aedSchristos {
104b3ac4aedSchristos union
105b3ac4aedSchristos {
106b3ac4aedSchristos unsigned char e_name[E_SYMNMLEN];
107b3ac4aedSchristos
108b3ac4aedSchristos struct
109b3ac4aedSchristos {
110b3ac4aedSchristos unsigned char e_zeroes[4];
111b3ac4aedSchristos unsigned char e_offset[4];
112b3ac4aedSchristos } e;
113b3ac4aedSchristos } e;
114b3ac4aedSchristos
115b3ac4aedSchristos unsigned char e_value[4];
116b3ac4aedSchristos unsigned char e_scnum[2];
117b3ac4aedSchristos unsigned char e_type[2];
118b3ac4aedSchristos unsigned char e_sclass[1];
119b3ac4aedSchristos unsigned char e_numaux[1];
120b3ac4aedSchristos };
121b3ac4aedSchristos
122b3ac4aedSchristos /* Length allowed for filename in aux sym format 4. */
123b3ac4aedSchristos
124b3ac4aedSchristos #define E_FILNMLEN 18
125b3ac4aedSchristos
126b3ac4aedSchristos /* Omits x_sym and other unused variants. */
127b3ac4aedSchristos
128b3ac4aedSchristos union external_auxent
129b3ac4aedSchristos {
130b3ac4aedSchristos /* Aux sym format 4: file. */
131b3ac4aedSchristos union
132b3ac4aedSchristos {
133b3ac4aedSchristos char x_fname[E_FILNMLEN];
134b3ac4aedSchristos struct
135b3ac4aedSchristos {
136b3ac4aedSchristos unsigned char x_zeroes[4];
137b3ac4aedSchristos unsigned char x_offset[4];
138b3ac4aedSchristos } x_n;
139b3ac4aedSchristos } x_file;
140b3ac4aedSchristos /* Aux sym format 5: section. */
141b3ac4aedSchristos struct
142b3ac4aedSchristos {
143b3ac4aedSchristos unsigned char x_scnlen[4]; /* section length */
144b3ac4aedSchristos unsigned char x_nreloc[2]; /* # relocation entries */
145b3ac4aedSchristos unsigned char x_nlinno[2]; /* # line numbers */
146b3ac4aedSchristos unsigned char x_checksum[4]; /* section COMDAT checksum */
147b3ac4aedSchristos unsigned char x_associated[2]; /* COMDAT assoc section index */
148b3ac4aedSchristos unsigned char x_comdat[1]; /* COMDAT selection number */
149b3ac4aedSchristos } x_scn;
150b3ac4aedSchristos };
151b3ac4aedSchristos
152b3ac4aedSchristos /* Symbol-related constants. */
153b3ac4aedSchristos
154b3ac4aedSchristos #define IMAGE_SYM_DEBUG (-2)
155b3ac4aedSchristos #define IMAGE_SYM_TYPE_NULL (0)
156b3ac4aedSchristos #define IMAGE_SYM_DTYPE_NULL (0)
157b3ac4aedSchristos #define IMAGE_SYM_CLASS_STATIC (3)
158b3ac4aedSchristos #define IMAGE_SYM_CLASS_FILE (103)
159b3ac4aedSchristos
160b3ac4aedSchristos #define IMAGE_SYM_TYPE \
161b3ac4aedSchristos ((IMAGE_SYM_DTYPE_NULL << 4) | IMAGE_SYM_TYPE_NULL)
162b3ac4aedSchristos
163b3ac4aedSchristos /* Private data for an simple_object_read. */
164b3ac4aedSchristos
165b3ac4aedSchristos struct simple_object_coff_read
166b3ac4aedSchristos {
167b3ac4aedSchristos /* Magic number. */
168b3ac4aedSchristos unsigned short magic;
169b3ac4aedSchristos /* Whether the file is big-endian. */
170b3ac4aedSchristos unsigned char is_big_endian;
171b3ac4aedSchristos /* Number of sections. */
172b3ac4aedSchristos unsigned short nscns;
173b3ac4aedSchristos /* File offset of symbol table. */
174b3ac4aedSchristos off_t symptr;
175b3ac4aedSchristos /* Number of symbol table entries. */
176b3ac4aedSchristos unsigned int nsyms;
177b3ac4aedSchristos /* Flags. */
178b3ac4aedSchristos unsigned short flags;
179b3ac4aedSchristos /* Offset of section headers in file. */
180b3ac4aedSchristos off_t scnhdr_offset;
181b3ac4aedSchristos };
182b3ac4aedSchristos
183b3ac4aedSchristos /* Private data for an simple_object_attributes. */
184b3ac4aedSchristos
185b3ac4aedSchristos struct simple_object_coff_attributes
186b3ac4aedSchristos {
187b3ac4aedSchristos /* Magic number. */
188b3ac4aedSchristos unsigned short magic;
189b3ac4aedSchristos /* Whether the file is big-endian. */
190b3ac4aedSchristos unsigned char is_big_endian;
191b3ac4aedSchristos /* Flags. */
192b3ac4aedSchristos unsigned short flags;
193b3ac4aedSchristos };
194b3ac4aedSchristos
195b3ac4aedSchristos /* There is no magic number which indicates a COFF file as opposed to
196b3ac4aedSchristos any other sort of file. Instead, each COFF file starts with a
197b3ac4aedSchristos two-byte magic number which also indicates the type of the target.
198b3ac4aedSchristos This struct holds a magic number as well as characteristics of that
199b3ac4aedSchristos COFF format. */
200b3ac4aedSchristos
201b3ac4aedSchristos struct coff_magic_struct
202b3ac4aedSchristos {
203b3ac4aedSchristos /* Magic number. */
204b3ac4aedSchristos unsigned short magic;
205b3ac4aedSchristos /* Whether this magic number is for a big-endian file. */
206b3ac4aedSchristos unsigned char is_big_endian;
207b3ac4aedSchristos /* Flag bits, in the f_flags fields, which indicates that this file
208b3ac4aedSchristos is not a relocatable object file. There is no flag which
209b3ac4aedSchristos specifically indicates a relocatable object file, it is only
210b3ac4aedSchristos implied by the absence of these flags. */
211b3ac4aedSchristos unsigned short non_object_flags;
212b3ac4aedSchristos };
213b3ac4aedSchristos
214b3ac4aedSchristos /* This is a list of the COFF magic numbers which we recognize, namely
215b3ac4aedSchristos the ones used on Windows. More can be added as needed. */
216b3ac4aedSchristos
217b3ac4aedSchristos static const struct coff_magic_struct coff_magic[] =
218b3ac4aedSchristos {
219b3ac4aedSchristos /* i386. */
220b3ac4aedSchristos { 0x14c, 0, F_EXEC | IMAGE_FILE_SYSTEM | IMAGE_FILE_DLL },
221b3ac4aedSchristos /* x86_64. */
222b3ac4aedSchristos { 0x8664, 0, F_EXEC | IMAGE_FILE_SYSTEM | IMAGE_FILE_DLL }
223b3ac4aedSchristos };
224b3ac4aedSchristos
225b3ac4aedSchristos /* See if we have a COFF file. */
226b3ac4aedSchristos
227b3ac4aedSchristos 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)228b3ac4aedSchristos simple_object_coff_match (unsigned char header[SIMPLE_OBJECT_MATCH_HEADER_LEN],
229b3ac4aedSchristos int descriptor, off_t offset,
230b3ac4aedSchristos const char *segment_name ATTRIBUTE_UNUSED,
231b3ac4aedSchristos const char **errmsg, int *err)
232b3ac4aedSchristos {
233b3ac4aedSchristos size_t c;
234b3ac4aedSchristos unsigned short magic_big;
235b3ac4aedSchristos unsigned short magic_little;
236b3ac4aedSchristos unsigned short magic;
237b3ac4aedSchristos size_t i;
238b3ac4aedSchristos int is_big_endian;
239b3ac4aedSchristos unsigned short (*fetch_16) (const unsigned char *);
240b3ac4aedSchristos unsigned int (*fetch_32) (const unsigned char *);
241b3ac4aedSchristos unsigned char hdrbuf[sizeof (struct external_filehdr)];
242b3ac4aedSchristos unsigned short flags;
243b3ac4aedSchristos struct simple_object_coff_read *ocr;
244b3ac4aedSchristos
245b3ac4aedSchristos c = sizeof (coff_magic) / sizeof (coff_magic[0]);
246b3ac4aedSchristos magic_big = simple_object_fetch_big_16 (header);
247b3ac4aedSchristos magic_little = simple_object_fetch_little_16 (header);
248b3ac4aedSchristos for (i = 0; i < c; ++i)
249b3ac4aedSchristos {
250b3ac4aedSchristos if (coff_magic[i].is_big_endian
251b3ac4aedSchristos ? coff_magic[i].magic == magic_big
252b3ac4aedSchristos : coff_magic[i].magic == magic_little)
253b3ac4aedSchristos break;
254b3ac4aedSchristos }
255b3ac4aedSchristos if (i >= c)
256b3ac4aedSchristos {
257b3ac4aedSchristos *errmsg = NULL;
258b3ac4aedSchristos *err = 0;
259b3ac4aedSchristos return NULL;
260b3ac4aedSchristos }
261b3ac4aedSchristos is_big_endian = coff_magic[i].is_big_endian;
262b3ac4aedSchristos
263b3ac4aedSchristos magic = is_big_endian ? magic_big : magic_little;
264b3ac4aedSchristos fetch_16 = (is_big_endian
265b3ac4aedSchristos ? simple_object_fetch_big_16
266b3ac4aedSchristos : simple_object_fetch_little_16);
267b3ac4aedSchristos fetch_32 = (is_big_endian
268b3ac4aedSchristos ? simple_object_fetch_big_32
269b3ac4aedSchristos : simple_object_fetch_little_32);
270b3ac4aedSchristos
271b3ac4aedSchristos if (!simple_object_internal_read (descriptor, offset, hdrbuf, sizeof hdrbuf,
272b3ac4aedSchristos errmsg, err))
273b3ac4aedSchristos return NULL;
274b3ac4aedSchristos
275b3ac4aedSchristos flags = fetch_16 (hdrbuf + offsetof (struct external_filehdr, f_flags));
276b3ac4aedSchristos if ((flags & coff_magic[i].non_object_flags) != 0)
277b3ac4aedSchristos {
278b3ac4aedSchristos *errmsg = "not relocatable object file";
279b3ac4aedSchristos *err = 0;
280b3ac4aedSchristos return NULL;
281b3ac4aedSchristos }
282b3ac4aedSchristos
283b3ac4aedSchristos ocr = XNEW (struct simple_object_coff_read);
284b3ac4aedSchristos ocr->magic = magic;
285b3ac4aedSchristos ocr->is_big_endian = is_big_endian;
286b3ac4aedSchristos ocr->nscns = fetch_16 (hdrbuf + offsetof (struct external_filehdr, f_nscns));
287b3ac4aedSchristos ocr->symptr = fetch_32 (hdrbuf
288b3ac4aedSchristos + offsetof (struct external_filehdr, f_symptr));
289b3ac4aedSchristos ocr->nsyms = fetch_32 (hdrbuf + offsetof (struct external_filehdr, f_nsyms));
290b3ac4aedSchristos ocr->flags = flags;
291b3ac4aedSchristos ocr->scnhdr_offset = (sizeof (struct external_filehdr)
292b3ac4aedSchristos + fetch_16 (hdrbuf + offsetof (struct external_filehdr,
293b3ac4aedSchristos f_opthdr)));
294b3ac4aedSchristos
295b3ac4aedSchristos return (void *) ocr;
296b3ac4aedSchristos }
297b3ac4aedSchristos
298b3ac4aedSchristos /* Read the string table in a COFF file. */
299b3ac4aedSchristos
300b3ac4aedSchristos static char *
simple_object_coff_read_strtab(simple_object_read * sobj,size_t * strtab_size,const char ** errmsg,int * err)301b3ac4aedSchristos simple_object_coff_read_strtab (simple_object_read *sobj, size_t *strtab_size,
302b3ac4aedSchristos const char **errmsg, int *err)
303b3ac4aedSchristos {
304b3ac4aedSchristos struct simple_object_coff_read *ocr =
305b3ac4aedSchristos (struct simple_object_coff_read *) sobj->data;
306b3ac4aedSchristos off_t strtab_offset;
307b3ac4aedSchristos unsigned char strsizebuf[4];
308b3ac4aedSchristos size_t strsize;
309b3ac4aedSchristos char *strtab;
310b3ac4aedSchristos
31105caefcfSchristos strtab_offset = sobj->offset + ocr->symptr
31205caefcfSchristos + ocr->nsyms * sizeof (struct external_syment);
313b3ac4aedSchristos if (!simple_object_internal_read (sobj->descriptor, strtab_offset,
314b3ac4aedSchristos strsizebuf, 4, errmsg, err))
315b3ac4aedSchristos return NULL;
316b3ac4aedSchristos strsize = (ocr->is_big_endian
317b3ac4aedSchristos ? simple_object_fetch_big_32 (strsizebuf)
318b3ac4aedSchristos : simple_object_fetch_little_32 (strsizebuf));
319b3ac4aedSchristos strtab = XNEWVEC (char, strsize);
320b3ac4aedSchristos if (!simple_object_internal_read (sobj->descriptor, strtab_offset,
321b3ac4aedSchristos (unsigned char *) strtab, strsize, errmsg,
322b3ac4aedSchristos err))
323b3ac4aedSchristos {
324b3ac4aedSchristos XDELETEVEC (strtab);
325b3ac4aedSchristos return NULL;
326b3ac4aedSchristos }
327b3ac4aedSchristos *strtab_size = strsize;
328b3ac4aedSchristos return strtab;
329b3ac4aedSchristos }
330b3ac4aedSchristos
331b3ac4aedSchristos /* Find all sections in a COFF file. */
332b3ac4aedSchristos
333b3ac4aedSchristos 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)334b3ac4aedSchristos simple_object_coff_find_sections (simple_object_read *sobj,
335b3ac4aedSchristos int (*pfn) (void *, const char *,
336b3ac4aedSchristos off_t offset, off_t length),
337b3ac4aedSchristos void *data,
338b3ac4aedSchristos int *err)
339b3ac4aedSchristos {
340b3ac4aedSchristos struct simple_object_coff_read *ocr =
341b3ac4aedSchristos (struct simple_object_coff_read *) sobj->data;
342b3ac4aedSchristos size_t scnhdr_size;
343b3ac4aedSchristos unsigned char *scnbuf;
344b3ac4aedSchristos const char *errmsg;
345b3ac4aedSchristos unsigned int (*fetch_32) (const unsigned char *);
346b3ac4aedSchristos unsigned int nscns;
347b3ac4aedSchristos char *strtab;
348b3ac4aedSchristos size_t strtab_size;
349b3ac4aedSchristos unsigned int i;
350b3ac4aedSchristos
351b3ac4aedSchristos scnhdr_size = sizeof (struct external_scnhdr);
352b3ac4aedSchristos scnbuf = XNEWVEC (unsigned char, scnhdr_size * ocr->nscns);
353b3ac4aedSchristos if (!simple_object_internal_read (sobj->descriptor,
354b3ac4aedSchristos sobj->offset + ocr->scnhdr_offset,
355b3ac4aedSchristos scnbuf, scnhdr_size * ocr->nscns, &errmsg,
356b3ac4aedSchristos err))
357b3ac4aedSchristos {
358b3ac4aedSchristos XDELETEVEC (scnbuf);
359b3ac4aedSchristos return errmsg;
360b3ac4aedSchristos }
361b3ac4aedSchristos
362b3ac4aedSchristos fetch_32 = (ocr->is_big_endian
363b3ac4aedSchristos ? simple_object_fetch_big_32
364b3ac4aedSchristos : simple_object_fetch_little_32);
365b3ac4aedSchristos
366b3ac4aedSchristos nscns = ocr->nscns;
367b3ac4aedSchristos strtab = NULL;
368b3ac4aedSchristos strtab_size = 0;
369b3ac4aedSchristos for (i = 0; i < nscns; ++i)
370b3ac4aedSchristos {
371b3ac4aedSchristos unsigned char *scnhdr;
372b3ac4aedSchristos unsigned char *scnname;
373b3ac4aedSchristos char namebuf[SCNNMLEN + 1];
374b3ac4aedSchristos char *name;
375b3ac4aedSchristos off_t scnptr;
376b3ac4aedSchristos unsigned int size;
377b3ac4aedSchristos
378b3ac4aedSchristos scnhdr = scnbuf + i * scnhdr_size;
379b3ac4aedSchristos scnname = scnhdr + offsetof (struct external_scnhdr, s_name);
380b3ac4aedSchristos memcpy (namebuf, scnname, SCNNMLEN);
381b3ac4aedSchristos namebuf[SCNNMLEN] = '\0';
382b3ac4aedSchristos name = &namebuf[0];
383b3ac4aedSchristos if (namebuf[0] == '/')
384b3ac4aedSchristos {
385b3ac4aedSchristos size_t strindex;
386b3ac4aedSchristos char *end;
387b3ac4aedSchristos
388b3ac4aedSchristos strindex = strtol (namebuf + 1, &end, 10);
389b3ac4aedSchristos if (*end == '\0')
390b3ac4aedSchristos {
391b3ac4aedSchristos /* The real section name is found in the string
392b3ac4aedSchristos table. */
393b3ac4aedSchristos if (strtab == NULL)
394b3ac4aedSchristos {
395b3ac4aedSchristos strtab = simple_object_coff_read_strtab (sobj,
396b3ac4aedSchristos &strtab_size,
397b3ac4aedSchristos &errmsg, err);
398b3ac4aedSchristos if (strtab == NULL)
399b3ac4aedSchristos {
400b3ac4aedSchristos XDELETEVEC (scnbuf);
401b3ac4aedSchristos return errmsg;
402b3ac4aedSchristos }
403b3ac4aedSchristos }
404b3ac4aedSchristos
405b3ac4aedSchristos if (strindex < 4 || strindex >= strtab_size)
406b3ac4aedSchristos {
407b3ac4aedSchristos XDELETEVEC (strtab);
408b3ac4aedSchristos XDELETEVEC (scnbuf);
409b3ac4aedSchristos *err = 0;
410b3ac4aedSchristos return "section string index out of range";
411b3ac4aedSchristos }
412b3ac4aedSchristos
413b3ac4aedSchristos name = strtab + strindex;
414b3ac4aedSchristos }
415b3ac4aedSchristos }
416b3ac4aedSchristos
417b3ac4aedSchristos scnptr = fetch_32 (scnhdr + offsetof (struct external_scnhdr, s_scnptr));
418b3ac4aedSchristos size = fetch_32 (scnhdr + offsetof (struct external_scnhdr, s_size));
419b3ac4aedSchristos
420b3ac4aedSchristos if (!(*pfn) (data, name, scnptr, size))
421b3ac4aedSchristos break;
422b3ac4aedSchristos }
423b3ac4aedSchristos
424b3ac4aedSchristos if (strtab != NULL)
425b3ac4aedSchristos XDELETEVEC (strtab);
426b3ac4aedSchristos XDELETEVEC (scnbuf);
427b3ac4aedSchristos
428b3ac4aedSchristos return NULL;
429b3ac4aedSchristos }
430b3ac4aedSchristos
431b3ac4aedSchristos /* Fetch the attributes for an simple_object_read. */
432b3ac4aedSchristos
433b3ac4aedSchristos static void *
simple_object_coff_fetch_attributes(simple_object_read * sobj,const char ** errmsg ATTRIBUTE_UNUSED,int * err ATTRIBUTE_UNUSED)434b3ac4aedSchristos simple_object_coff_fetch_attributes (simple_object_read *sobj,
435b3ac4aedSchristos const char **errmsg ATTRIBUTE_UNUSED,
436b3ac4aedSchristos int *err ATTRIBUTE_UNUSED)
437b3ac4aedSchristos {
438b3ac4aedSchristos struct simple_object_coff_read *ocr =
439b3ac4aedSchristos (struct simple_object_coff_read *) sobj->data;
440b3ac4aedSchristos struct simple_object_coff_attributes *ret;
441b3ac4aedSchristos
442b3ac4aedSchristos ret = XNEW (struct simple_object_coff_attributes);
443b3ac4aedSchristos ret->magic = ocr->magic;
444b3ac4aedSchristos ret->is_big_endian = ocr->is_big_endian;
445b3ac4aedSchristos ret->flags = ocr->flags;
446b3ac4aedSchristos return ret;
447b3ac4aedSchristos }
448b3ac4aedSchristos
449b3ac4aedSchristos /* Release the private data for an simple_object_read. */
450b3ac4aedSchristos
451b3ac4aedSchristos static void
simple_object_coff_release_read(void * data)452b3ac4aedSchristos simple_object_coff_release_read (void *data)
453b3ac4aedSchristos {
454b3ac4aedSchristos XDELETE (data);
455b3ac4aedSchristos }
456b3ac4aedSchristos
457b3ac4aedSchristos /* Compare two attributes structures. */
458b3ac4aedSchristos
459b3ac4aedSchristos static const char *
simple_object_coff_attributes_merge(void * todata,void * fromdata,int * err)46005caefcfSchristos simple_object_coff_attributes_merge (void *todata, void *fromdata, int *err)
461b3ac4aedSchristos {
46205caefcfSchristos struct simple_object_coff_attributes *to =
46305caefcfSchristos (struct simple_object_coff_attributes *) todata;
46405caefcfSchristos struct simple_object_coff_attributes *from =
46505caefcfSchristos (struct simple_object_coff_attributes *) fromdata;
466b3ac4aedSchristos
46705caefcfSchristos if (to->magic != from->magic || to->is_big_endian != from->is_big_endian)
468b3ac4aedSchristos {
469b3ac4aedSchristos *err = 0;
470b3ac4aedSchristos return "COFF object format mismatch";
471b3ac4aedSchristos }
472b3ac4aedSchristos return NULL;
473b3ac4aedSchristos }
474b3ac4aedSchristos
475b3ac4aedSchristos /* Release the private data for an attributes structure. */
476b3ac4aedSchristos
477b3ac4aedSchristos static void
simple_object_coff_release_attributes(void * data)478b3ac4aedSchristos simple_object_coff_release_attributes (void *data)
479b3ac4aedSchristos {
480b3ac4aedSchristos XDELETE (data);
481b3ac4aedSchristos }
482b3ac4aedSchristos
483b3ac4aedSchristos /* Prepare to write out a file. */
484b3ac4aedSchristos
485b3ac4aedSchristos static void *
simple_object_coff_start_write(void * attributes_data,const char ** errmsg ATTRIBUTE_UNUSED,int * err ATTRIBUTE_UNUSED)486b3ac4aedSchristos simple_object_coff_start_write (void *attributes_data,
487b3ac4aedSchristos const char **errmsg ATTRIBUTE_UNUSED,
488b3ac4aedSchristos int *err ATTRIBUTE_UNUSED)
489b3ac4aedSchristos {
490b3ac4aedSchristos struct simple_object_coff_attributes *attrs =
491b3ac4aedSchristos (struct simple_object_coff_attributes *) attributes_data;
492b3ac4aedSchristos struct simple_object_coff_attributes *ret;
493b3ac4aedSchristos
494b3ac4aedSchristos /* We're just going to record the attributes, but we need to make a
495b3ac4aedSchristos copy because the user may delete them. */
496b3ac4aedSchristos ret = XNEW (struct simple_object_coff_attributes);
497b3ac4aedSchristos *ret = *attrs;
498b3ac4aedSchristos return ret;
499b3ac4aedSchristos }
500b3ac4aedSchristos
501b3ac4aedSchristos /* Write out a COFF filehdr. */
502b3ac4aedSchristos
503b3ac4aedSchristos 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)504b3ac4aedSchristos simple_object_coff_write_filehdr (simple_object_write *sobj, int descriptor,
505b3ac4aedSchristos unsigned int nscns, size_t symtab_offset,
506b3ac4aedSchristos unsigned int nsyms, const char **errmsg,
507b3ac4aedSchristos int *err)
508b3ac4aedSchristos {
509b3ac4aedSchristos struct simple_object_coff_attributes *attrs =
510b3ac4aedSchristos (struct simple_object_coff_attributes *) sobj->data;
511b3ac4aedSchristos unsigned char hdrbuf[sizeof (struct external_filehdr)];
512b3ac4aedSchristos unsigned char *hdr;
513b3ac4aedSchristos void (*set_16) (unsigned char *, unsigned short);
514b3ac4aedSchristos void (*set_32) (unsigned char *, unsigned int);
515b3ac4aedSchristos
516b3ac4aedSchristos hdr = &hdrbuf[0];
517b3ac4aedSchristos
518b3ac4aedSchristos set_16 = (attrs->is_big_endian
519b3ac4aedSchristos ? simple_object_set_big_16
520b3ac4aedSchristos : simple_object_set_little_16);
521b3ac4aedSchristos set_32 = (attrs->is_big_endian
522b3ac4aedSchristos ? simple_object_set_big_32
523b3ac4aedSchristos : simple_object_set_little_32);
524b3ac4aedSchristos
525b3ac4aedSchristos memset (hdr, 0, sizeof (struct external_filehdr));
526b3ac4aedSchristos
527b3ac4aedSchristos set_16 (hdr + offsetof (struct external_filehdr, f_magic), attrs->magic);
528b3ac4aedSchristos set_16 (hdr + offsetof (struct external_filehdr, f_nscns), nscns);
529b3ac4aedSchristos /* f_timdat left as zero. */
530b3ac4aedSchristos set_32 (hdr + offsetof (struct external_filehdr, f_symptr), symtab_offset);
531b3ac4aedSchristos set_32 (hdr + offsetof (struct external_filehdr, f_nsyms), nsyms);
532b3ac4aedSchristos /* f_opthdr left as zero. */
533b3ac4aedSchristos set_16 (hdr + offsetof (struct external_filehdr, f_flags), attrs->flags);
534b3ac4aedSchristos
535b3ac4aedSchristos return simple_object_internal_write (descriptor, 0, hdrbuf,
536b3ac4aedSchristos sizeof (struct external_filehdr),
537b3ac4aedSchristos errmsg, err);
538b3ac4aedSchristos }
539b3ac4aedSchristos
540b3ac4aedSchristos /* Write out a COFF section header. */
541b3ac4aedSchristos
542b3ac4aedSchristos 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)543b3ac4aedSchristos simple_object_coff_write_scnhdr (simple_object_write *sobj, int descriptor,
544b3ac4aedSchristos const char *name, size_t *name_offset,
545b3ac4aedSchristos off_t scnhdr_offset, size_t scnsize,
546b3ac4aedSchristos off_t offset, unsigned int align,
547b3ac4aedSchristos const char **errmsg, int *err)
548b3ac4aedSchristos {
549b3ac4aedSchristos struct simple_object_coff_attributes *attrs =
550b3ac4aedSchristos (struct simple_object_coff_attributes *) sobj->data;
551b3ac4aedSchristos void (*set_32) (unsigned char *, unsigned int);
552b3ac4aedSchristos unsigned char hdrbuf[sizeof (struct external_scnhdr)];
553b3ac4aedSchristos unsigned char *hdr;
554b3ac4aedSchristos size_t namelen;
555b3ac4aedSchristos unsigned int flags;
556b3ac4aedSchristos
557b3ac4aedSchristos set_32 = (attrs->is_big_endian
558b3ac4aedSchristos ? simple_object_set_big_32
559b3ac4aedSchristos : simple_object_set_little_32);
560b3ac4aedSchristos
561b3ac4aedSchristos memset (hdrbuf, 0, sizeof hdrbuf);
562b3ac4aedSchristos hdr = &hdrbuf[0];
563b3ac4aedSchristos
564b3ac4aedSchristos namelen = strlen (name);
565b3ac4aedSchristos if (namelen <= SCNNMLEN)
566b3ac4aedSchristos strncpy ((char *) hdr + offsetof (struct external_scnhdr, s_name), name,
567b3ac4aedSchristos SCNNMLEN);
568b3ac4aedSchristos else
569b3ac4aedSchristos {
570b3ac4aedSchristos snprintf ((char *) hdr + offsetof (struct external_scnhdr, s_name),
571b3ac4aedSchristos SCNNMLEN, "/%lu", (unsigned long) *name_offset);
572b3ac4aedSchristos *name_offset += namelen + 1;
573b3ac4aedSchristos }
574b3ac4aedSchristos
575b3ac4aedSchristos /* s_paddr left as zero. */
576b3ac4aedSchristos /* s_vaddr left as zero. */
577b3ac4aedSchristos set_32 (hdr + offsetof (struct external_scnhdr, s_size), scnsize);
578b3ac4aedSchristos set_32 (hdr + offsetof (struct external_scnhdr, s_scnptr), offset);
579b3ac4aedSchristos /* s_relptr left as zero. */
580b3ac4aedSchristos /* s_lnnoptr left as zero. */
581b3ac4aedSchristos /* s_nreloc left as zero. */
582b3ac4aedSchristos /* s_nlnno left as zero. */
583b3ac4aedSchristos flags = (STYP_DATA | IMAGE_SCN_MEM_DISCARDABLE | IMAGE_SCN_MEM_SHARED
584b3ac4aedSchristos | IMAGE_SCN_MEM_READ);
585b3ac4aedSchristos /* PE can represent alignment up to 13. */
586b3ac4aedSchristos if (align > 13)
587b3ac4aedSchristos align = 13;
588b3ac4aedSchristos flags |= IMAGE_SCN_ALIGN_POWER_CONST(align);
589b3ac4aedSchristos set_32 (hdr + offsetof (struct external_scnhdr, s_flags), flags);
590b3ac4aedSchristos
591b3ac4aedSchristos return simple_object_internal_write (descriptor, scnhdr_offset, hdrbuf,
592b3ac4aedSchristos sizeof (struct external_scnhdr),
593b3ac4aedSchristos errmsg, err);
594b3ac4aedSchristos }
595b3ac4aedSchristos
596b3ac4aedSchristos /* Write out a complete COFF file. */
597b3ac4aedSchristos
598b3ac4aedSchristos static const char *
simple_object_coff_write_to_file(simple_object_write * sobj,int descriptor,int * err)599b3ac4aedSchristos simple_object_coff_write_to_file (simple_object_write *sobj, int descriptor,
600b3ac4aedSchristos int *err)
601b3ac4aedSchristos {
602b3ac4aedSchristos struct simple_object_coff_attributes *attrs =
603b3ac4aedSchristos (struct simple_object_coff_attributes *) sobj->data;
604b3ac4aedSchristos unsigned int nscns, secnum;
605b3ac4aedSchristos simple_object_write_section *section;
606b3ac4aedSchristos off_t scnhdr_offset;
607b3ac4aedSchristos size_t symtab_offset;
608b3ac4aedSchristos off_t secsym_offset;
609b3ac4aedSchristos unsigned int nsyms;
610b3ac4aedSchristos size_t offset;
611b3ac4aedSchristos size_t name_offset;
612b3ac4aedSchristos const char *errmsg;
613b3ac4aedSchristos unsigned char strsizebuf[4];
614b3ac4aedSchristos /* The interface doesn't give us access to the name of the input file
615b3ac4aedSchristos yet. We want to use its basename for the FILE symbol. This is
616b3ac4aedSchristos what 'gas' uses when told to assemble from stdin. */
617b3ac4aedSchristos const char *source_filename = "fake";
618b3ac4aedSchristos size_t sflen;
619b3ac4aedSchristos union
620b3ac4aedSchristos {
621b3ac4aedSchristos struct external_syment sym;
622b3ac4aedSchristos union external_auxent aux;
623b3ac4aedSchristos } syms[2];
624b3ac4aedSchristos void (*set_16) (unsigned char *, unsigned short);
625b3ac4aedSchristos void (*set_32) (unsigned char *, unsigned int);
626b3ac4aedSchristos
627b3ac4aedSchristos set_16 = (attrs->is_big_endian
628b3ac4aedSchristos ? simple_object_set_big_16
629b3ac4aedSchristos : simple_object_set_little_16);
630b3ac4aedSchristos set_32 = (attrs->is_big_endian
631b3ac4aedSchristos ? simple_object_set_big_32
632b3ac4aedSchristos : simple_object_set_little_32);
633b3ac4aedSchristos
634b3ac4aedSchristos nscns = 0;
635b3ac4aedSchristos for (section = sobj->sections; section != NULL; section = section->next)
636b3ac4aedSchristos ++nscns;
637b3ac4aedSchristos
638b3ac4aedSchristos scnhdr_offset = sizeof (struct external_filehdr);
639b3ac4aedSchristos offset = scnhdr_offset + nscns * sizeof (struct external_scnhdr);
640b3ac4aedSchristos name_offset = 4;
641b3ac4aedSchristos for (section = sobj->sections; section != NULL; section = section->next)
642b3ac4aedSchristos {
643b3ac4aedSchristos size_t mask;
644b3ac4aedSchristos size_t new_offset;
645b3ac4aedSchristos size_t scnsize;
646b3ac4aedSchristos struct simple_object_write_section_buffer *buffer;
647b3ac4aedSchristos
648b3ac4aedSchristos mask = (1U << section->align) - 1;
649b3ac4aedSchristos new_offset = offset & mask;
650b3ac4aedSchristos new_offset &= ~ mask;
651b3ac4aedSchristos while (new_offset > offset)
652b3ac4aedSchristos {
653b3ac4aedSchristos unsigned char zeroes[16];
654b3ac4aedSchristos size_t write;
655b3ac4aedSchristos
656b3ac4aedSchristos memset (zeroes, 0, sizeof zeroes);
657b3ac4aedSchristos write = new_offset - offset;
658b3ac4aedSchristos if (write > sizeof zeroes)
659b3ac4aedSchristos write = sizeof zeroes;
660b3ac4aedSchristos if (!simple_object_internal_write (descriptor, offset, zeroes, write,
661b3ac4aedSchristos &errmsg, err))
662b3ac4aedSchristos return errmsg;
663b3ac4aedSchristos }
664b3ac4aedSchristos
665b3ac4aedSchristos scnsize = 0;
666b3ac4aedSchristos for (buffer = section->buffers; buffer != NULL; buffer = buffer->next)
667b3ac4aedSchristos {
668b3ac4aedSchristos if (!simple_object_internal_write (descriptor, offset + scnsize,
669b3ac4aedSchristos ((const unsigned char *)
670b3ac4aedSchristos buffer->buffer),
671b3ac4aedSchristos buffer->size, &errmsg, err))
672b3ac4aedSchristos return errmsg;
673b3ac4aedSchristos scnsize += buffer->size;
674b3ac4aedSchristos }
675b3ac4aedSchristos
676b3ac4aedSchristos if (!simple_object_coff_write_scnhdr (sobj, descriptor, section->name,
677b3ac4aedSchristos &name_offset, scnhdr_offset,
678b3ac4aedSchristos scnsize, offset, section->align,
679b3ac4aedSchristos &errmsg, err))
680b3ac4aedSchristos return errmsg;
681b3ac4aedSchristos
682b3ac4aedSchristos scnhdr_offset += sizeof (struct external_scnhdr);
683b3ac4aedSchristos offset += scnsize;
684b3ac4aedSchristos }
685b3ac4aedSchristos
686b3ac4aedSchristos /* Symbol table is always half-word aligned. */
687b3ac4aedSchristos offset += (offset & 1);
688b3ac4aedSchristos /* There is a file symbol and a section symbol per section,
689b3ac4aedSchristos and each of these has a single auxiliary symbol following. */
690b3ac4aedSchristos nsyms = 2 * (nscns + 1);
691b3ac4aedSchristos symtab_offset = offset;
692b3ac4aedSchristos /* Advance across space reserved for symbol table to locate
693b3ac4aedSchristos start of string table. */
694b3ac4aedSchristos offset += nsyms * sizeof (struct external_syment);
695b3ac4aedSchristos
696b3ac4aedSchristos /* Write out file symbol. */
697b3ac4aedSchristos memset (&syms[0], 0, sizeof (syms));
698b3ac4aedSchristos strcpy ((char *)&syms[0].sym.e.e_name[0], ".file");
699b3ac4aedSchristos set_16 (&syms[0].sym.e_scnum[0], IMAGE_SYM_DEBUG);
700b3ac4aedSchristos set_16 (&syms[0].sym.e_type[0], IMAGE_SYM_TYPE);
701b3ac4aedSchristos syms[0].sym.e_sclass[0] = IMAGE_SYM_CLASS_FILE;
702b3ac4aedSchristos syms[0].sym.e_numaux[0] = 1;
703b3ac4aedSchristos /* The name need not be nul-terminated if it fits into the x_fname field
704b3ac4aedSchristos directly, but must be if it has to be placed into the string table. */
705b3ac4aedSchristos sflen = strlen (source_filename);
706b3ac4aedSchristos if (sflen <= E_FILNMLEN)
707b3ac4aedSchristos memcpy (&syms[1].aux.x_file.x_fname[0], source_filename, sflen);
708b3ac4aedSchristos else
709b3ac4aedSchristos {
710b3ac4aedSchristos set_32 (&syms[1].aux.x_file.x_n.x_offset[0], name_offset);
711b3ac4aedSchristos if (!simple_object_internal_write (descriptor, offset + name_offset,
712b3ac4aedSchristos ((const unsigned char *)
713b3ac4aedSchristos source_filename),
714b3ac4aedSchristos sflen + 1, &errmsg, err))
715b3ac4aedSchristos return errmsg;
716b3ac4aedSchristos name_offset += strlen (source_filename) + 1;
717b3ac4aedSchristos }
718b3ac4aedSchristos if (!simple_object_internal_write (descriptor, symtab_offset,
719b3ac4aedSchristos (const unsigned char *) &syms[0],
720b3ac4aedSchristos sizeof (syms), &errmsg, err))
721b3ac4aedSchristos return errmsg;
722b3ac4aedSchristos
723b3ac4aedSchristos /* Write the string table length, followed by the strings and section
724b3ac4aedSchristos symbols in step with each other. */
725b3ac4aedSchristos set_32 (strsizebuf, name_offset);
726b3ac4aedSchristos if (!simple_object_internal_write (descriptor, offset, strsizebuf, 4,
727b3ac4aedSchristos &errmsg, err))
728b3ac4aedSchristos return errmsg;
729b3ac4aedSchristos
730b3ac4aedSchristos name_offset = 4;
731b3ac4aedSchristos secsym_offset = symtab_offset + sizeof (syms);
732b3ac4aedSchristos memset (&syms[0], 0, sizeof (syms));
733b3ac4aedSchristos set_16 (&syms[0].sym.e_type[0], IMAGE_SYM_TYPE);
734b3ac4aedSchristos syms[0].sym.e_sclass[0] = IMAGE_SYM_CLASS_STATIC;
735b3ac4aedSchristos syms[0].sym.e_numaux[0] = 1;
736b3ac4aedSchristos secnum = 1;
737b3ac4aedSchristos
738b3ac4aedSchristos for (section = sobj->sections; section != NULL; section = section->next)
739b3ac4aedSchristos {
740b3ac4aedSchristos size_t namelen;
741b3ac4aedSchristos size_t scnsize;
742b3ac4aedSchristos struct simple_object_write_section_buffer *buffer;
743b3ac4aedSchristos
744b3ac4aedSchristos namelen = strlen (section->name);
745b3ac4aedSchristos set_16 (&syms[0].sym.e_scnum[0], secnum++);
746b3ac4aedSchristos scnsize = 0;
747b3ac4aedSchristos for (buffer = section->buffers; buffer != NULL; buffer = buffer->next)
748b3ac4aedSchristos scnsize += buffer->size;
749b3ac4aedSchristos set_32 (&syms[1].aux.x_scn.x_scnlen[0], scnsize);
750b3ac4aedSchristos if (namelen > SCNNMLEN)
751b3ac4aedSchristos {
752b3ac4aedSchristos set_32 (&syms[0].sym.e.e.e_zeroes[0], 0);
753b3ac4aedSchristos set_32 (&syms[0].sym.e.e.e_offset[0], name_offset);
754b3ac4aedSchristos if (!simple_object_internal_write (descriptor, offset + name_offset,
755b3ac4aedSchristos ((const unsigned char *)
756b3ac4aedSchristos section->name),
757b3ac4aedSchristos namelen + 1, &errmsg, err))
758b3ac4aedSchristos return errmsg;
759b3ac4aedSchristos name_offset += namelen + 1;
760b3ac4aedSchristos }
761b3ac4aedSchristos else
762b3ac4aedSchristos {
763b3ac4aedSchristos memcpy (&syms[0].sym.e.e_name[0], section->name,
764b3ac4aedSchristos strlen (section->name));
765b3ac4aedSchristos memset (&syms[0].sym.e.e_name[strlen (section->name)], 0,
766b3ac4aedSchristos E_SYMNMLEN - strlen (section->name));
767b3ac4aedSchristos }
768b3ac4aedSchristos
769b3ac4aedSchristos if (!simple_object_internal_write (descriptor, secsym_offset,
770b3ac4aedSchristos (const unsigned char *) &syms[0],
771b3ac4aedSchristos sizeof (syms), &errmsg, err))
772b3ac4aedSchristos return errmsg;
773b3ac4aedSchristos secsym_offset += sizeof (syms);
774b3ac4aedSchristos }
775b3ac4aedSchristos
776b3ac4aedSchristos if (!simple_object_coff_write_filehdr (sobj, descriptor, nscns,
777b3ac4aedSchristos symtab_offset, nsyms, &errmsg, err))
778b3ac4aedSchristos return errmsg;
779b3ac4aedSchristos
780b3ac4aedSchristos return NULL;
781b3ac4aedSchristos }
782b3ac4aedSchristos
783b3ac4aedSchristos /* Release the private data for an simple_object_write structure. */
784b3ac4aedSchristos
785b3ac4aedSchristos static void
simple_object_coff_release_write(void * data)786b3ac4aedSchristos simple_object_coff_release_write (void *data)
787b3ac4aedSchristos {
788b3ac4aedSchristos XDELETE (data);
789b3ac4aedSchristos }
790b3ac4aedSchristos
791b3ac4aedSchristos /* The COFF functions. */
792b3ac4aedSchristos
793b3ac4aedSchristos const struct simple_object_functions simple_object_coff_functions =
794b3ac4aedSchristos {
795b3ac4aedSchristos simple_object_coff_match,
796b3ac4aedSchristos simple_object_coff_find_sections,
797b3ac4aedSchristos simple_object_coff_fetch_attributes,
798b3ac4aedSchristos simple_object_coff_release_read,
79905caefcfSchristos simple_object_coff_attributes_merge,
800b3ac4aedSchristos simple_object_coff_release_attributes,
801b3ac4aedSchristos simple_object_coff_start_write,
802b3ac4aedSchristos simple_object_coff_write_to_file,
80398f124a6Schristos simple_object_coff_release_write,
80498f124a6Schristos NULL
805b3ac4aedSchristos };
806