1/* simple-object-coff.c -- routines to manipulate COFF object files.
2 Copyright (C) 2010-2026 Free Software Foundation, Inc.
3 Written by Ian Lance Taylor, Google.
4
5This program is free software; you can redistribute it and/or modify it
6under the terms of the GNU General Public License as published by the
7Free Software Foundation; either version 2, or (at your option) any
8later version.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, 51 Franklin Street - Fifth Floor,
18Boston, MA 02110-1301, USA. */
19
20#include "config.h"
21#include "libiberty.h"
22#include "simple-object.h"
23
24#include <errno.h>
25#include <stddef.h>
26
27#ifdef HAVE_STDLIB_H
28#include <stdlib.h>
29#endif
30
31#ifdef HAVE_STDINT_H
32#include <stdint.h>
33#endif
34
35#ifdef HAVE_STRING_H
36#include <string.h>
37#endif
38
39#ifdef HAVE_INTTYPES_H
40#include <inttypes.h>
41#endif
42
43#include "simple-object-common.h"
44
45/* COFF structures and constants. */
46
47/* COFF file header. */
48
49struct external_filehdr
50{
51 unsigned char f_magic[2]; /* magic number */
52 unsigned char f_nscns[2]; /* number of sections */
53 unsigned char f_timdat[4]; /* time & date stamp */
54 unsigned char f_symptr[4]; /* file pointer to symtab */
55 unsigned char f_nsyms[4]; /* number of symtab entries */
56 unsigned char f_opthdr[2]; /* sizeof(optional hdr) */
57 unsigned char f_flags[2]; /* flags */
58};
59
60/* BigObj COFF file header. */
61
62struct external_filehdr_bigobj
63{
64 unsigned char sig1[2]; /* Must be 0x0000 */
65 unsigned char sig2[2]; /* Must be 0xFFFF */
66 unsigned char version[2]; /* Version, currently 2 */
67 unsigned char machine[2]; /* Machine type */
68 unsigned char timdat[4]; /* time & date stamp */
69 unsigned char classid[16]; /* Magic GUID that identifies BigObj format */
70 unsigned char sizeofdata[4]; /* Size of data (unused, set to 0) */
71 unsigned char flags[4]; /* Flags (unused, set to 0) */
72 unsigned char metadatasize[4]; /* Metadata size (unused, set to 0) */
73 unsigned char metadataoffset[4]; /* Metadata offset (unused, set to 0) */
74 unsigned char nscns[4]; /* number of sections (32-bit!) */
75 unsigned char symptr[4]; /* file pointer to symtab */
76 unsigned char nsyms[4]; /* number of symtab entries */
77};
78
79/* The BigObj magic GUID (ClassID). */
80static const unsigned char bigobj_magic[16] =
81{
82 0xC7, 0xA1, 0xBA, 0xD1, 0xEE, 0xBA, 0xA9, 0x4B,
83 0xAF, 0x20, 0xFA, 0xF6, 0x6A, 0xA4, 0xDC, 0xB8
84};
85
86/* Bits for filehdr f_flags field. */
87
88#define F_EXEC (0x0002)
89#define IMAGE_FILE_SYSTEM (0x1000)
90#define IMAGE_FILE_DLL (0x2000)
91
92/* COFF section header. */
93
94struct external_scnhdr
95{
96 unsigned char s_name[8]; /* section name */
97 unsigned char s_paddr[4]; /* physical address, aliased s_nlib */
98 unsigned char s_vaddr[4]; /* virtual address */
99 unsigned char s_size[4]; /* section size */
100 unsigned char s_scnptr[4]; /* file ptr to raw data for section */
101 unsigned char s_relptr[4]; /* file ptr to relocation */
102 unsigned char s_lnnoptr[4]; /* file ptr to line numbers */
103 unsigned char s_nreloc[2]; /* number of relocation entries */
104 unsigned char s_nlnno[2]; /* number of line number entries */
105 unsigned char s_flags[4]; /* flags */
106};
107
108/* The length of the s_name field in struct external_scnhdr. */
109
110#define SCNNMLEN (8)
111
112/* Bits for scnhdr s_flags field. This includes some bits defined
113 only for PE. This may need to be moved into coff_magic. */
114
115#define STYP_DATA (1 << 6)
116#define IMAGE_SCN_MEM_DISCARDABLE (1 << 25)
117#define IMAGE_SCN_MEM_SHARED (1 << 28)
118#define IMAGE_SCN_MEM_READ (1 << 30)
119
120#define IMAGE_SCN_ALIGN_POWER_BIT_POS 20
121#define IMAGE_SCN_ALIGN_POWER_CONST(val) \
122 (((val) + 1) << IMAGE_SCN_ALIGN_POWER_BIT_POS)
123
124/* COFF symbol table entry. */
125
126#define E_SYMNMLEN 8 /* # characters in a symbol name */
127
128struct external_syment
129{
130 union
131 {
132 unsigned char e_name[E_SYMNMLEN];
133
134 struct
135 {
136 unsigned char e_zeroes[4];
137 unsigned char e_offset[4];
138 } e;
139 } e;
140
141 unsigned char e_value[4];
142 unsigned char e_scnum[2];
143 unsigned char e_type[2];
144 unsigned char e_sclass[1];
145 unsigned char e_numaux[1];
146};
147
148/* BigObj COFF symbol table entry (20 bytes instead of 18). */
149
150struct external_syment_bigobj
151{
152 union
153 {
154 unsigned char e_name[E_SYMNMLEN];
155
156 struct
157 {
158 unsigned char e_zeroes[4];
159 unsigned char e_offset[4];
160 } e;
161 } e;
162
163 unsigned char e_value[4];
164 unsigned char e_scnum[4]; /* 32-bit section number! */
165 unsigned char e_type[2];
166 unsigned char e_sclass[1];
167 unsigned char e_numaux[1];
168};
169
170/* Length allowed for filename in aux sym format 4. */
171
172#define E_FILNMLEN 18
173
174/* Omits x_sym and other unused variants. */
175
176union external_auxent
177{
178 /* Aux sym format 4: file. */
179 union
180 {
181 char x_fname[E_FILNMLEN];
182 struct
183 {
184 unsigned char x_zeroes[4];
185 unsigned char x_offset[4];
186 } x_n;
187 } x_file;
188 /* Aux sym format 5: section. */
189 struct
190 {
191 unsigned char x_scnlen[4]; /* section length */
192 unsigned char x_nreloc[2]; /* # relocation entries */
193 unsigned char x_nlinno[2]; /* # line numbers */
194 unsigned char x_checksum[4]; /* section COMDAT checksum */
195 unsigned char x_associated[2]; /* COMDAT assoc section index */
196 unsigned char x_comdat[1]; /* COMDAT selection number */
197 } x_scn;
198};
199
200/* BigObj auxiliary symbol (20 bytes to match symbol size). */
201
202union external_auxent_bigobj
203{
204 /* Aux sym format 4: file. */
205 union
206 {
207 char x_fname[E_FILNMLEN];
208 struct
209 {
210 unsigned char x_zeroes[4];
211 unsigned char x_offset[4];
212 } x_n;
213 } x_file;
214 /* Aux sym format 5: section. */
215 struct
216 {
217 unsigned char x_scnlen[4]; /* section length */
218 unsigned char x_nreloc[2]; /* # relocation entries */
219 unsigned char x_nlinno[2]; /* # line numbers */
220 unsigned char x_checksum[4]; /* section COMDAT checksum */
221 unsigned char x_associated[2]; /* COMDAT assoc section index */
222 unsigned char x_comdat[1]; /* COMDAT selection number */
223 unsigned char x_pad[3]; /* Padding to 20 bytes */
224 } x_scn;
225};
226
227/* Symbol-related constants. */
228
229#define IMAGE_SYM_DEBUG (-2)
230#define IMAGE_SYM_TYPE_NULL (0)
231#define IMAGE_SYM_DTYPE_NULL (0)
232#define IMAGE_SYM_CLASS_STATIC (3)
233#define IMAGE_SYM_CLASS_FILE (103)
234
235#define IMAGE_SYM_TYPE \
236 ((IMAGE_SYM_DTYPE_NULL << 4) | IMAGE_SYM_TYPE_NULL)
237
238/* Private data for an simple_object_read. */
239
240struct simple_object_coff_read
241{
242 /* Magic number. */
243 unsigned short magic;
244 /* Whether the file is big-endian. */
245 unsigned char is_big_endian;
246 /* Whether this is BigObj format. */
247 unsigned char is_bigobj;
248 /* Number of sections. */
249 unsigned int nscns;
250 /* File offset of symbol table. */
251 off_t symptr;
252 /* Number of symbol table entries. */
253 unsigned int nsyms;
254 /* Flags. */
255 unsigned short flags;
256 /* Offset of section headers in file. */
257 off_t scnhdr_offset;
258};
259
260/* Private data for an simple_object_attributes. */
261
262struct simple_object_coff_attributes
263{
264 /* Magic number. */
265 unsigned short magic;
266 /* Whether the file is big-endian. */
267 unsigned char is_big_endian;
268 /* Whether this is BigObj format. */
269 unsigned char is_bigobj;
270 /* Flags. */
271 unsigned short flags;
272};
273
274/* There is no magic number which indicates a COFF file as opposed to
275 any other sort of file. Instead, each COFF file starts with a
276 two-byte magic number which also indicates the type of the target.
277 This struct holds a magic number as well as characteristics of that
278 COFF format. */
279
280struct coff_magic_struct
281{
282 /* Magic number. */
283 unsigned short magic;
284 /* Whether this magic number is for a big-endian file. */
285 unsigned char is_big_endian;
286 /* Flag bits, in the f_flags fields, which indicates that this file
287 is not a relocatable object file. There is no flag which
288 specifically indicates a relocatable object file, it is only
289 implied by the absence of these flags. */
290 unsigned short non_object_flags;
291};
292
293/* This is a list of the COFF magic numbers which we recognize, namely
294 the ones used on Windows. More can be added as needed. */
295
296static const struct coff_magic_struct coff_magic[] =
297{
298 /* i386. */
299 { .magic: 0x14c, .is_big_endian: 0, F_EXEC | IMAGE_FILE_SYSTEM | IMAGE_FILE_DLL },
300 /* x86_64. */
301 { .magic: 0x8664, .is_big_endian: 0, F_EXEC | IMAGE_FILE_SYSTEM | IMAGE_FILE_DLL },
302 /* AArch64. */
303 { .magic: 0xaa64, .is_big_endian: 0, F_EXEC | IMAGE_FILE_SYSTEM | IMAGE_FILE_DLL }
304};
305
306/* See if we have a COFF file. */
307
308static void *
309simple_object_coff_match (unsigned char header[SIMPLE_OBJECT_MATCH_HEADER_LEN],
310 int descriptor, off_t offset,
311 const char *segment_name ATTRIBUTE_UNUSED,
312 const char **errmsg, int *err)
313{
314 size_t c;
315 unsigned short magic_big;
316 unsigned short magic_little;
317 unsigned short magic;
318 size_t i;
319 int is_big_endian;
320 unsigned short (*fetch_16) (const unsigned char *);
321 unsigned int (*fetch_32) (const unsigned char *);
322 unsigned char hdrbuf[sizeof (struct external_filehdr_bigobj)];
323 unsigned short flags;
324 struct simple_object_coff_read *ocr;
325 unsigned short sig1, sig2;
326
327 /* Try regular COFF first. */
328 c = sizeof (coff_magic) / sizeof (coff_magic[0]);
329 magic_big = simple_object_fetch_big_16 (buf: header);
330 magic_little = simple_object_fetch_little_16 (buf: header);
331 for (i = 0; i < c; ++i)
332 {
333 if (coff_magic[i].is_big_endian
334 ? coff_magic[i].magic == magic_big
335 : coff_magic[i].magic == magic_little)
336 break;
337 }
338
339 /* Check for BigObj if regular COFF didn't match. */
340 sig1 = simple_object_fetch_little_16 (buf: header);
341 sig2 = simple_object_fetch_little_16 (buf: header + 2);
342
343 if (i >= c && (sig1 != 0 || sig2 != 0xFFFF))
344 {
345 /* Not regular COFF and not BigObj. */
346 *errmsg = NULL;
347 *err = 0;
348 return NULL;
349 }
350
351 if (sig1 == 0 && sig2 == 0xFFFF)
352 {
353 /* This looks like BigObj. Verify the ClassID. */
354 unsigned char bigobj_hdrbuf[sizeof (struct external_filehdr_bigobj)];
355
356 if (!simple_object_internal_read (descriptor, offset, buffer: bigobj_hdrbuf,
357 size: sizeof bigobj_hdrbuf, errmsg, err))
358 return NULL;
359
360 if (memcmp (s1: bigobj_hdrbuf + offsetof (struct external_filehdr_bigobj,
361 classid),
362 s2: bigobj_magic, n: 16) != 0)
363 {
364 *errmsg = NULL;
365 *err = 0;
366 return NULL;
367 }
368
369 /* BigObj is always little-endian. */
370 is_big_endian = 0;
371
372 ocr = XNEW (struct simple_object_coff_read);
373 ocr->magic = simple_object_fetch_little_16
374 (buf: bigobj_hdrbuf
375 + offsetof (struct external_filehdr_bigobj, machine));
376 ocr->is_big_endian = 0;
377 ocr->is_bigobj = 1;
378 ocr->nscns = simple_object_fetch_little_32
379 (buf: bigobj_hdrbuf
380 + offsetof (struct external_filehdr_bigobj, nscns));
381 ocr->symptr = simple_object_fetch_little_32
382 (buf: bigobj_hdrbuf
383 + offsetof (struct external_filehdr_bigobj, symptr));
384 ocr->nsyms = simple_object_fetch_little_32
385 (buf: bigobj_hdrbuf
386 + offsetof (struct external_filehdr_bigobj, nsyms));
387 ocr->flags = simple_object_fetch_little_32
388 (buf: bigobj_hdrbuf
389 + offsetof (struct external_filehdr_bigobj, flags));
390 ocr->scnhdr_offset = sizeof (struct external_filehdr_bigobj);
391
392 return (void *) ocr;
393 }
394
395 /* Regular COFF. */
396 is_big_endian = coff_magic[i].is_big_endian;
397
398 magic = is_big_endian ? magic_big : magic_little;
399 fetch_16 = (is_big_endian
400 ? simple_object_fetch_big_16
401 : simple_object_fetch_little_16);
402 fetch_32 = (is_big_endian
403 ? simple_object_fetch_big_32
404 : simple_object_fetch_little_32);
405
406 if (!simple_object_internal_read (descriptor, offset, buffer: hdrbuf, size: sizeof (struct external_filehdr),
407 errmsg, err))
408 return NULL;
409
410 flags = fetch_16 (hdrbuf + offsetof (struct external_filehdr, f_flags));
411 if ((flags & coff_magic[i].non_object_flags) != 0)
412 {
413 *errmsg = "not relocatable object file";
414 *err = 0;
415 return NULL;
416 }
417
418 ocr = XNEW (struct simple_object_coff_read);
419 ocr->magic = magic;
420 ocr->is_big_endian = is_big_endian;
421 ocr->is_bigobj = 0;
422 ocr->nscns = fetch_16 (hdrbuf + offsetof (struct external_filehdr, f_nscns));
423 ocr->symptr = fetch_32 (hdrbuf
424 + offsetof (struct external_filehdr, f_symptr));
425 ocr->nsyms = fetch_32 (hdrbuf + offsetof (struct external_filehdr, f_nsyms));
426 ocr->flags = flags;
427 ocr->scnhdr_offset = (sizeof (struct external_filehdr)
428 + fetch_16 (hdrbuf + offsetof (struct external_filehdr,
429 f_opthdr)));
430
431 return (void *) ocr;
432}
433
434/* Read the string table in a COFF file. */
435
436static char *
437simple_object_coff_read_strtab (simple_object_read *sobj, size_t *strtab_size,
438 const char **errmsg, int *err)
439{
440 struct simple_object_coff_read *ocr =
441 (struct simple_object_coff_read *) sobj->data;
442 off_t strtab_offset;
443 unsigned char strsizebuf[4];
444 size_t strsize;
445 char *strtab;
446 size_t sym_size;
447
448 /* Symbol size depends on format. */
449 sym_size = ocr->is_bigobj ? sizeof (struct external_syment_bigobj)
450 : sizeof (struct external_syment);
451
452 strtab_offset = sobj->offset + ocr->symptr + ocr->nsyms * sym_size;
453 if (!simple_object_internal_read (descriptor: sobj->descriptor, offset: strtab_offset,
454 buffer: strsizebuf, size: 4, errmsg, err))
455 return NULL;
456 strsize = (ocr->is_big_endian
457 ? simple_object_fetch_big_32 (buf: strsizebuf)
458 : simple_object_fetch_little_32 (buf: strsizebuf));
459 strtab = XNEWVEC (char, strsize);
460 if (!simple_object_internal_read (descriptor: sobj->descriptor, offset: strtab_offset,
461 buffer: (unsigned char *) strtab, size: strsize, errmsg,
462 err))
463 {
464 XDELETEVEC (strtab);
465 return NULL;
466 }
467 *strtab_size = strsize;
468 return strtab;
469}
470
471/* Find all sections in a COFF file. */
472
473static const char *
474simple_object_coff_find_sections (simple_object_read *sobj,
475 int (*pfn) (void *, const char *,
476 off_t offset, off_t length),
477 void *data,
478 int *err)
479{
480 struct simple_object_coff_read *ocr =
481 (struct simple_object_coff_read *) sobj->data;
482 size_t scnhdr_size;
483 unsigned char *scnbuf;
484 const char *errmsg;
485 unsigned int (*fetch_32) (const unsigned char *);
486 unsigned int nscns;
487 char *strtab;
488 size_t strtab_size;
489 unsigned int i;
490
491 scnhdr_size = sizeof (struct external_scnhdr);
492 scnbuf = XNEWVEC (unsigned char, scnhdr_size * ocr->nscns);
493 if (!simple_object_internal_read (descriptor: sobj->descriptor,
494 offset: sobj->offset + ocr->scnhdr_offset,
495 buffer: scnbuf, size: scnhdr_size * ocr->nscns, errmsg: &errmsg,
496 err))
497 {
498 XDELETEVEC (scnbuf);
499 return errmsg;
500 }
501
502 fetch_32 = (ocr->is_big_endian
503 ? simple_object_fetch_big_32
504 : simple_object_fetch_little_32);
505
506 nscns = ocr->nscns;
507 strtab = NULL;
508 strtab_size = 0;
509 for (i = 0; i < nscns; ++i)
510 {
511 unsigned char *scnhdr;
512 unsigned char *scnname;
513 char namebuf[SCNNMLEN + 1];
514 char *name;
515 off_t scnptr;
516 unsigned int size;
517
518 scnhdr = scnbuf + i * scnhdr_size;
519 scnname = scnhdr + offsetof (struct external_scnhdr, s_name);
520 memcpy (dest: namebuf, src: scnname, SCNNMLEN);
521 namebuf[SCNNMLEN] = '\0';
522 name = &namebuf[0];
523 if (namebuf[0] == '/')
524 {
525 size_t strindex;
526 char *end;
527
528 strindex = strtol (nptr: namebuf + 1, endptr: &end, base: 10);
529 if (*end == '\0')
530 {
531 /* The real section name is found in the string
532 table. */
533 if (strtab == NULL)
534 {
535 strtab = simple_object_coff_read_strtab (sobj,
536 strtab_size: &strtab_size,
537 errmsg: &errmsg, err);
538 if (strtab == NULL)
539 {
540 XDELETEVEC (scnbuf);
541 return errmsg;
542 }
543 }
544
545 if (strindex < 4 || strindex >= strtab_size)
546 {
547 XDELETEVEC (strtab);
548 XDELETEVEC (scnbuf);
549 *err = 0;
550 return "section string index out of range";
551 }
552
553 name = strtab + strindex;
554 }
555 }
556
557 scnptr = fetch_32 (scnhdr + offsetof (struct external_scnhdr, s_scnptr));
558 size = fetch_32 (scnhdr + offsetof (struct external_scnhdr, s_size));
559
560 if (!(*pfn) (data, name, scnptr, size))
561 break;
562 }
563
564 if (strtab != NULL)
565 XDELETEVEC (strtab);
566 XDELETEVEC (scnbuf);
567
568 return NULL;
569}
570
571/* Fetch the attributes for an simple_object_read. */
572
573static void *
574simple_object_coff_fetch_attributes (simple_object_read *sobj,
575 const char **errmsg ATTRIBUTE_UNUSED,
576 int *err ATTRIBUTE_UNUSED)
577{
578 struct simple_object_coff_read *ocr =
579 (struct simple_object_coff_read *) sobj->data;
580 struct simple_object_coff_attributes *ret;
581
582 ret = XNEW (struct simple_object_coff_attributes);
583 ret->magic = ocr->magic;
584 ret->is_big_endian = ocr->is_big_endian;
585 ret->is_bigobj = ocr->is_bigobj;
586 ret->flags = ocr->flags;
587 return ret;
588}
589
590/* Release the private data for an simple_object_read. */
591
592static void
593simple_object_coff_release_read (void *data)
594{
595 XDELETE (data);
596}
597
598/* Compare two attributes structures. */
599
600static const char *
601simple_object_coff_attributes_merge (void *todata, void *fromdata, int *err)
602{
603 struct simple_object_coff_attributes *to =
604 (struct simple_object_coff_attributes *) todata;
605 struct simple_object_coff_attributes *from =
606 (struct simple_object_coff_attributes *) fromdata;
607
608 if (to->magic != from->magic
609 || to->is_big_endian != from->is_big_endian
610 || to->is_bigobj != from->is_bigobj)
611 {
612 *err = 0;
613 return "COFF object format mismatch";
614 }
615 return NULL;
616}
617
618/* Release the private data for an attributes structure. */
619
620static void
621simple_object_coff_release_attributes (void *data)
622{
623 XDELETE (data);
624}
625
626/* Prepare to write out a file. */
627
628static void *
629simple_object_coff_start_write (void *attributes_data,
630 const char **errmsg ATTRIBUTE_UNUSED,
631 int *err ATTRIBUTE_UNUSED)
632{
633 struct simple_object_coff_attributes *attrs =
634 (struct simple_object_coff_attributes *) attributes_data;
635 struct simple_object_coff_attributes *ret;
636
637 /* We're just going to record the attributes, but we need to make a
638 copy because the user may delete them. */
639 ret = XNEW (struct simple_object_coff_attributes);
640 *ret = *attrs;
641 return ret;
642}
643
644/* Write out a BigObj COFF filehdr. */
645
646static int
647simple_object_coff_write_filehdr_bigobj (simple_object_write *sobj,
648 int descriptor,
649 unsigned int nscns,
650 size_t symtab_offset,
651 unsigned int nsyms,
652 const char **errmsg, int *err)
653{
654 struct simple_object_coff_attributes *attrs =
655 (struct simple_object_coff_attributes *) sobj->data;
656 unsigned char hdrbuf[sizeof (struct external_filehdr_bigobj)];
657 unsigned char *hdr;
658 void (*set_16) (unsigned char *, unsigned short);
659 void (*set_32) (unsigned char *, unsigned int);
660
661 hdr = &hdrbuf[0];
662
663 /* BigObj is always little-endian. */
664 set_16 = simple_object_set_little_16;
665 set_32 = simple_object_set_little_32;
666
667 memset (s: hdr, c: 0, n: sizeof (struct external_filehdr_bigobj));
668
669 /* Set BigObj signatures. */
670 set_16 (hdr + offsetof (struct external_filehdr_bigobj, sig1), 0);
671 set_16 (hdr + offsetof (struct external_filehdr_bigobj, sig2), 0xFFFF);
672 set_16 (hdr + offsetof (struct external_filehdr_bigobj, version), 2);
673 set_16 (hdr + offsetof (struct external_filehdr_bigobj, machine),
674 attrs->magic);
675 /* timdat left as zero. */
676 /* Copy ClassID. */
677 memcpy (dest: hdr + offsetof (struct external_filehdr_bigobj, classid),
678 src: bigobj_magic, n: 16);
679 /* sizeofdata, flags, metadatasize, metadataoffset left as zero. */
680 set_32 (hdr + offsetof (struct external_filehdr_bigobj, nscns), nscns);
681 set_32 (hdr + offsetof (struct external_filehdr_bigobj, symptr),
682 symtab_offset);
683 set_32 (hdr + offsetof (struct external_filehdr_bigobj, nsyms), nsyms);
684
685 return simple_object_internal_write (descriptor, offset: 0, buffer: hdrbuf,
686 size: sizeof (struct external_filehdr_bigobj),
687 errmsg, err);
688}
689
690/* Write out a COFF filehdr. */
691
692static int
693simple_object_coff_write_filehdr (simple_object_write *sobj, int descriptor,
694 unsigned int nscns, size_t symtab_offset,
695 unsigned int nsyms, const char **errmsg,
696 int *err)
697{
698 struct simple_object_coff_attributes *attrs =
699 (struct simple_object_coff_attributes *) sobj->data;
700 unsigned char hdrbuf[sizeof (struct external_filehdr)];
701 unsigned char *hdr;
702 void (*set_16) (unsigned char *, unsigned short);
703 void (*set_32) (unsigned char *, unsigned int);
704
705 hdr = &hdrbuf[0];
706
707 set_16 = (attrs->is_big_endian
708 ? simple_object_set_big_16
709 : simple_object_set_little_16);
710 set_32 = (attrs->is_big_endian
711 ? simple_object_set_big_32
712 : simple_object_set_little_32);
713
714 memset (s: hdr, c: 0, n: sizeof (struct external_filehdr));
715
716 set_16 (hdr + offsetof (struct external_filehdr, f_magic), attrs->magic);
717 set_16 (hdr + offsetof (struct external_filehdr, f_nscns), nscns);
718 /* f_timdat left as zero. */
719 set_32 (hdr + offsetof (struct external_filehdr, f_symptr), symtab_offset);
720 set_32 (hdr + offsetof (struct external_filehdr, f_nsyms), nsyms);
721 /* f_opthdr left as zero. */
722 set_16 (hdr + offsetof (struct external_filehdr, f_flags), attrs->flags);
723
724 return simple_object_internal_write (descriptor, offset: 0, buffer: hdrbuf,
725 size: sizeof (struct external_filehdr),
726 errmsg, err);
727}
728
729/* Write out a COFF section header. */
730
731static int
732simple_object_coff_write_scnhdr (simple_object_write *sobj, int descriptor,
733 const char *name, size_t *name_offset,
734 off_t scnhdr_offset, size_t scnsize,
735 off_t offset, unsigned int align,
736 const char **errmsg, int *err)
737{
738 struct simple_object_coff_attributes *attrs =
739 (struct simple_object_coff_attributes *) sobj->data;
740 void (*set_32) (unsigned char *, unsigned int);
741 unsigned char hdrbuf[sizeof (struct external_scnhdr)];
742 unsigned char *hdr;
743 size_t namelen;
744 unsigned int flags;
745
746 set_32 = (attrs->is_big_endian
747 ? simple_object_set_big_32
748 : simple_object_set_little_32);
749
750 memset (s: hdrbuf, c: 0, n: sizeof hdrbuf);
751 hdr = &hdrbuf[0];
752
753 namelen = strlen (s: name);
754 if (namelen <= SCNNMLEN)
755 strncpy (dest: (char *) hdr + offsetof (struct external_scnhdr, s_name), src: name,
756 SCNNMLEN);
757 else
758 {
759 snprintf (s: (char *) hdr + offsetof (struct external_scnhdr, s_name),
760 SCNNMLEN, format: "/%lu", (unsigned long) *name_offset);
761 *name_offset += namelen + 1;
762 }
763
764 /* s_paddr left as zero. */
765 /* s_vaddr left as zero. */
766 set_32 (hdr + offsetof (struct external_scnhdr, s_size), scnsize);
767 set_32 (hdr + offsetof (struct external_scnhdr, s_scnptr), offset);
768 /* s_relptr left as zero. */
769 /* s_lnnoptr left as zero. */
770 /* s_nreloc left as zero. */
771 /* s_nlnno left as zero. */
772 flags = (STYP_DATA | IMAGE_SCN_MEM_DISCARDABLE | IMAGE_SCN_MEM_SHARED
773 | IMAGE_SCN_MEM_READ);
774 /* PE can represent alignment up to 13. */
775 if (align > 13)
776 align = 13;
777 flags |= IMAGE_SCN_ALIGN_POWER_CONST(align);
778 set_32 (hdr + offsetof (struct external_scnhdr, s_flags), flags);
779
780 return simple_object_internal_write (descriptor, offset: scnhdr_offset, buffer: hdrbuf,
781 size: sizeof (struct external_scnhdr),
782 errmsg, err);
783}
784
785/* Write out a complete COFF file. */
786
787static const char *
788simple_object_coff_write_to_file (simple_object_write *sobj, int descriptor,
789 int *err)
790{
791 struct simple_object_coff_attributes *attrs =
792 (struct simple_object_coff_attributes *) sobj->data;
793 unsigned int nscns, secnum;
794 simple_object_write_section *section;
795 off_t scnhdr_offset;
796 size_t symtab_offset;
797 off_t secsym_offset;
798 unsigned int nsyms;
799 size_t offset;
800 size_t name_offset;
801 const char *errmsg;
802 unsigned char strsizebuf[4];
803 /* The interface doesn't give us access to the name of the input file
804 yet. We want to use its basename for the FILE symbol. This is
805 what 'gas' uses when told to assemble from stdin. */
806 const char *source_filename = "fake";
807 size_t sflen;
808 size_t symsize;
809 void (*set_16) (unsigned char *, unsigned short);
810 void (*set_32) (unsigned char *, unsigned int);
811
812 /* Determine symbol size based on format. */
813 if (attrs->is_bigobj)
814 symsize = sizeof (struct external_syment_bigobj);
815 else
816 symsize = sizeof (struct external_syment);
817
818 set_16 = (attrs->is_big_endian
819 ? simple_object_set_big_16
820 : simple_object_set_little_16);
821 set_32 = (attrs->is_big_endian
822 ? simple_object_set_big_32
823 : simple_object_set_little_32);
824
825 nscns = 0;
826 for (section = sobj->sections; section != NULL; section = section->next)
827 ++nscns;
828
829 if (attrs->is_bigobj)
830 scnhdr_offset = sizeof (struct external_filehdr_bigobj);
831 else
832 scnhdr_offset = sizeof (struct external_filehdr);
833 offset = scnhdr_offset + nscns * sizeof (struct external_scnhdr);
834 name_offset = 4;
835 for (section = sobj->sections; section != NULL; section = section->next)
836 {
837 size_t mask;
838 size_t new_offset;
839 size_t scnsize;
840 struct simple_object_write_section_buffer *buffer;
841
842 mask = (1U << section->align) - 1;
843 new_offset = offset & mask;
844 new_offset &= ~ mask;
845 while (new_offset > offset)
846 {
847 unsigned char zeroes[16];
848 size_t write;
849
850 memset (s: zeroes, c: 0, n: sizeof zeroes);
851 write = new_offset - offset;
852 if (write > sizeof zeroes)
853 write = sizeof zeroes;
854 if (!simple_object_internal_write (descriptor, offset, buffer: zeroes, size: write,
855 errmsg: &errmsg, err))
856 return errmsg;
857 }
858
859 scnsize = 0;
860 for (buffer = section->buffers; buffer != NULL; buffer = buffer->next)
861 {
862 if (!simple_object_internal_write (descriptor, offset: offset + scnsize,
863 buffer: ((const unsigned char *)
864 buffer->buffer),
865 size: buffer->size, errmsg: &errmsg, err))
866 return errmsg;
867 scnsize += buffer->size;
868 }
869
870 if (!simple_object_coff_write_scnhdr (sobj, descriptor, name: section->name,
871 name_offset: &name_offset, scnhdr_offset,
872 scnsize, offset, align: section->align,
873 errmsg: &errmsg, err))
874 return errmsg;
875
876 scnhdr_offset += sizeof (struct external_scnhdr);
877 offset += scnsize;
878 }
879
880 /* Symbol table is always half-word aligned. */
881 offset += (offset & 1);
882 /* There is a file symbol and a section symbol per section,
883 and each of these has a single auxiliary symbol following. */
884 nsyms = 2 * (nscns + 1);
885 symtab_offset = offset;
886 /* Advance across space reserved for symbol table to locate
887 start of string table. */
888 offset += nsyms * symsize;
889
890 /* Write out file symbol. */
891 if (attrs->is_bigobj)
892 {
893 union
894 {
895 struct external_syment_bigobj sym;
896 union external_auxent_bigobj aux;
897 } syms[2];
898
899 memset (s: &syms[0], c: 0, n: sizeof (syms));
900 strcpy (dest: (char *)&syms[0].sym.e.e_name[0], src: ".file");
901 set_32 (&syms[0].sym.e_scnum[0], IMAGE_SYM_DEBUG);
902 set_16 (&syms[0].sym.e_type[0], IMAGE_SYM_TYPE);
903 syms[0].sym.e_sclass[0] = IMAGE_SYM_CLASS_FILE;
904 syms[0].sym.e_numaux[0] = 1;
905 /* The name need not be nul-terminated if it fits into the x_fname field
906 directly, but must be if it has to be placed into the string table. */
907 sflen = strlen (s: source_filename);
908 if (sflen <= E_FILNMLEN)
909 memcpy (dest: &syms[1].aux.x_file.x_fname[0], src: source_filename, n: sflen);
910 else
911 {
912 set_32 (&syms[1].aux.x_file.x_n.x_offset[0], name_offset);
913 if (!simple_object_internal_write (descriptor, offset: offset + name_offset,
914 buffer: ((const unsigned char *)
915 source_filename),
916 size: sflen + 1, errmsg: &errmsg, err))
917 return errmsg;
918 name_offset += strlen (s: source_filename) + 1;
919 }
920 if (!simple_object_internal_write (descriptor, offset: symtab_offset,
921 buffer: (const unsigned char *) &syms[0],
922 size: sizeof (syms), errmsg: &errmsg, err))
923 return errmsg;
924
925 /* Write the string table length, followed by the strings and section
926 symbols in step with each other. */
927 set_32 (strsizebuf, name_offset);
928 if (!simple_object_internal_write (descriptor, offset, buffer: strsizebuf, size: 4,
929 errmsg: &errmsg, err))
930 return errmsg;
931
932 name_offset = 4;
933 secsym_offset = symtab_offset + sizeof (syms);
934 memset (s: &syms[0], c: 0, n: sizeof (syms));
935 set_16 (&syms[0].sym.e_type[0], IMAGE_SYM_TYPE);
936 syms[0].sym.e_sclass[0] = IMAGE_SYM_CLASS_STATIC;
937 syms[0].sym.e_numaux[0] = 1;
938 secnum = 1;
939
940 for (section = sobj->sections; section != NULL; section = section->next)
941 {
942 size_t namelen;
943 size_t scnsize;
944 struct simple_object_write_section_buffer *buffer;
945
946 namelen = strlen (s: section->name);
947 set_32 (&syms[0].sym.e_scnum[0], secnum++);
948 scnsize = 0;
949 for (buffer = section->buffers; buffer != NULL; buffer = buffer->next)
950 scnsize += buffer->size;
951 set_32 (&syms[1].aux.x_scn.x_scnlen[0], scnsize);
952 if (namelen > SCNNMLEN)
953 {
954 set_32 (&syms[0].sym.e.e.e_zeroes[0], 0);
955 set_32 (&syms[0].sym.e.e.e_offset[0], name_offset);
956 if (!simple_object_internal_write (descriptor, offset: offset + name_offset,
957 buffer: ((const unsigned char *)
958 section->name),
959 size: namelen + 1, errmsg: &errmsg, err))
960 return errmsg;
961 name_offset += namelen + 1;
962 }
963 else
964 {
965 memcpy (dest: &syms[0].sym.e.e_name[0], src: section->name,
966 n: strlen (s: section->name));
967 memset (s: &syms[0].sym.e.e_name[strlen (s: section->name)], c: 0,
968 E_SYMNMLEN - strlen (s: section->name));
969 }
970
971 if (!simple_object_internal_write (descriptor, offset: secsym_offset,
972 buffer: (const unsigned char *) &syms[0],
973 size: sizeof (syms), errmsg: &errmsg, err))
974 return errmsg;
975 secsym_offset += sizeof (syms);
976 }
977 }
978 else
979 {
980 /* Regular COFF. */
981 union
982 {
983 struct external_syment sym;
984 union external_auxent aux;
985 } syms[2];
986
987 memset (s: &syms[0], c: 0, n: sizeof (syms));
988 strcpy (dest: (char *)&syms[0].sym.e.e_name[0], src: ".file");
989 set_16 (&syms[0].sym.e_scnum[0], IMAGE_SYM_DEBUG);
990 set_16 (&syms[0].sym.e_type[0], IMAGE_SYM_TYPE);
991 syms[0].sym.e_sclass[0] = IMAGE_SYM_CLASS_FILE;
992 syms[0].sym.e_numaux[0] = 1;
993 /* The name need not be nul-terminated if it fits into the x_fname field
994 directly, but must be if it has to be placed into the string table. */
995 sflen = strlen (s: source_filename);
996 if (sflen <= E_FILNMLEN)
997 memcpy (dest: &syms[1].aux.x_file.x_fname[0], src: source_filename, n: sflen);
998 else
999 {
1000 set_32 (&syms[1].aux.x_file.x_n.x_offset[0], name_offset);
1001 if (!simple_object_internal_write (descriptor, offset: offset + name_offset,
1002 buffer: ((const unsigned char *)
1003 source_filename),
1004 size: sflen + 1, errmsg: &errmsg, err))
1005 return errmsg;
1006 name_offset += strlen (s: source_filename) + 1;
1007 }
1008 if (!simple_object_internal_write (descriptor, offset: symtab_offset,
1009 buffer: (const unsigned char *) &syms[0],
1010 size: sizeof (syms), errmsg: &errmsg, err))
1011 return errmsg;
1012
1013 /* Write the string table length, followed by the strings and section
1014 symbols in step with each other. */
1015 set_32 (strsizebuf, name_offset);
1016 if (!simple_object_internal_write (descriptor, offset, buffer: strsizebuf, size: 4,
1017 errmsg: &errmsg, err))
1018 return errmsg;
1019
1020 name_offset = 4;
1021 secsym_offset = symtab_offset + sizeof (syms);
1022 memset (s: &syms[0], c: 0, n: sizeof (syms));
1023 set_16 (&syms[0].sym.e_type[0], IMAGE_SYM_TYPE);
1024 syms[0].sym.e_sclass[0] = IMAGE_SYM_CLASS_STATIC;
1025 syms[0].sym.e_numaux[0] = 1;
1026 secnum = 1;
1027
1028 for (section = sobj->sections; section != NULL; section = section->next)
1029 {
1030 size_t namelen;
1031 size_t scnsize;
1032 struct simple_object_write_section_buffer *buffer;
1033
1034 namelen = strlen (s: section->name);
1035 set_16 (&syms[0].sym.e_scnum[0], secnum++);
1036 scnsize = 0;
1037 for (buffer = section->buffers; buffer != NULL; buffer = buffer->next)
1038 scnsize += buffer->size;
1039 set_32 (&syms[1].aux.x_scn.x_scnlen[0], scnsize);
1040 if (namelen > SCNNMLEN)
1041 {
1042 set_32 (&syms[0].sym.e.e.e_zeroes[0], 0);
1043 set_32 (&syms[0].sym.e.e.e_offset[0], name_offset);
1044 if (!simple_object_internal_write (descriptor, offset: offset + name_offset,
1045 buffer: ((const unsigned char *)
1046 section->name),
1047 size: namelen + 1, errmsg: &errmsg, err))
1048 return errmsg;
1049 name_offset += namelen + 1;
1050 }
1051 else
1052 {
1053 memcpy (dest: &syms[0].sym.e.e_name[0], src: section->name,
1054 n: strlen (s: section->name));
1055 memset (s: &syms[0].sym.e.e_name[strlen (s: section->name)], c: 0,
1056 E_SYMNMLEN - strlen (s: section->name));
1057 }
1058
1059 if (!simple_object_internal_write (descriptor, offset: secsym_offset,
1060 buffer: (const unsigned char *) &syms[0],
1061 size: sizeof (syms), errmsg: &errmsg, err))
1062 return errmsg;
1063 secsym_offset += sizeof (syms);
1064 }
1065 }
1066
1067 if (attrs->is_bigobj)
1068 {
1069 if (!simple_object_coff_write_filehdr_bigobj (sobj, descriptor, nscns,
1070 symtab_offset, nsyms,
1071 errmsg: &errmsg, err))
1072 return errmsg;
1073 }
1074 else
1075 {
1076 if (!simple_object_coff_write_filehdr (sobj, descriptor, nscns,
1077 symtab_offset, nsyms, errmsg: &errmsg, err))
1078 return errmsg;
1079 }
1080
1081 return NULL;
1082}
1083
1084/* Release the private data for an simple_object_write structure. */
1085
1086static void
1087simple_object_coff_release_write (void *data)
1088{
1089 XDELETE (data);
1090}
1091
1092/* The COFF functions. */
1093
1094const struct simple_object_functions simple_object_coff_functions =
1095{
1096 .match: simple_object_coff_match,
1097 .find_sections: simple_object_coff_find_sections,
1098 .fetch_attributes: simple_object_coff_fetch_attributes,
1099 .release_read: simple_object_coff_release_read,
1100 .attributes_merge: simple_object_coff_attributes_merge,
1101 .release_attributes: simple_object_coff_release_attributes,
1102 .start_write: simple_object_coff_start_write,
1103 .write_to_file: simple_object_coff_write_to_file,
1104 .release_write: simple_object_coff_release_write,
1105 NULL
1106};
1107

source code of libiberty/simple-object-coff.c