xref: /netbsd-src/external/gpl3/gdb/dist/libiberty/simple-object-coff.c (revision 5173eb0a33e5d83890ba976253e703be4c92557c)
198b9484cSchristos /* simple-object-coff.c -- routines to manipulate COFF object files.
2*5173eb0aSchristos    Copyright (C) 2010-2024 Free Software Foundation, Inc.
398b9484cSchristos    Written by Ian Lance Taylor, Google.
498b9484cSchristos 
598b9484cSchristos This program is free software; you can redistribute it and/or modify it
698b9484cSchristos under the terms of the GNU General Public License as published by the
798b9484cSchristos Free Software Foundation; either version 2, or (at your option) any
898b9484cSchristos later version.
998b9484cSchristos 
1098b9484cSchristos This program is distributed in the hope that it will be useful,
1198b9484cSchristos but WITHOUT ANY WARRANTY; without even the implied warranty of
1298b9484cSchristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1398b9484cSchristos GNU General Public License for more details.
1498b9484cSchristos 
1598b9484cSchristos You should have received a copy of the GNU General Public License
1698b9484cSchristos along with this program; if not, write to the Free Software
1798b9484cSchristos Foundation, 51 Franklin Street - Fifth Floor,
1898b9484cSchristos Boston, MA 02110-1301, USA.  */
1998b9484cSchristos 
2098b9484cSchristos #include "config.h"
2198b9484cSchristos #include "libiberty.h"
2298b9484cSchristos #include "simple-object.h"
2398b9484cSchristos 
2498b9484cSchristos #include <errno.h>
2598b9484cSchristos #include <stddef.h>
2698b9484cSchristos 
2798b9484cSchristos #ifdef HAVE_STDLIB_H
2898b9484cSchristos #include <stdlib.h>
2998b9484cSchristos #endif
3098b9484cSchristos 
3198b9484cSchristos #ifdef HAVE_STDINT_H
3298b9484cSchristos #include <stdint.h>
3398b9484cSchristos #endif
3498b9484cSchristos 
3598b9484cSchristos #ifdef HAVE_STRING_H
3698b9484cSchristos #include <string.h>
3798b9484cSchristos #endif
3898b9484cSchristos 
3998b9484cSchristos #ifdef HAVE_INTTYPES_H
4098b9484cSchristos #include <inttypes.h>
4198b9484cSchristos #endif
4298b9484cSchristos 
4398b9484cSchristos #include "simple-object-common.h"
4498b9484cSchristos 
4598b9484cSchristos /* COFF structures and constants.  */
4698b9484cSchristos 
4798b9484cSchristos /* COFF file header.  */
4898b9484cSchristos 
4998b9484cSchristos struct external_filehdr
5098b9484cSchristos {
5198b9484cSchristos   unsigned char f_magic[2];	/* magic number			*/
5298b9484cSchristos   unsigned char f_nscns[2];	/* number of sections		*/
5398b9484cSchristos   unsigned char f_timdat[4];	/* time & date stamp		*/
5498b9484cSchristos   unsigned char f_symptr[4];	/* file pointer to symtab	*/
5598b9484cSchristos   unsigned char f_nsyms[4];	/* number of symtab entries	*/
5698b9484cSchristos   unsigned char f_opthdr[2];	/* sizeof(optional hdr)		*/
5798b9484cSchristos   unsigned char f_flags[2];	/* flags			*/
5898b9484cSchristos };
5998b9484cSchristos 
6098b9484cSchristos /* Bits for filehdr f_flags field.  */
6198b9484cSchristos 
6298b9484cSchristos #define F_EXEC			(0x0002)
6398b9484cSchristos #define IMAGE_FILE_SYSTEM	(0x1000)
6498b9484cSchristos #define IMAGE_FILE_DLL		(0x2000)
6598b9484cSchristos 
6698b9484cSchristos /* COFF section header.  */
6798b9484cSchristos 
6898b9484cSchristos struct external_scnhdr
6998b9484cSchristos {
7098b9484cSchristos   unsigned char s_name[8];	/* section name				*/
7198b9484cSchristos   unsigned char s_paddr[4];	/* physical address, aliased s_nlib 	*/
7298b9484cSchristos   unsigned char s_vaddr[4];	/* virtual address			*/
7398b9484cSchristos   unsigned char s_size[4];	/* section size				*/
7498b9484cSchristos   unsigned char s_scnptr[4];	/* file ptr to raw data for section 	*/
7598b9484cSchristos   unsigned char s_relptr[4];	/* file ptr to relocation		*/
7698b9484cSchristos   unsigned char s_lnnoptr[4];	/* file ptr to line numbers		*/
7798b9484cSchristos   unsigned char s_nreloc[2];	/* number of relocation entries		*/
7898b9484cSchristos   unsigned char s_nlnno[2];	/* number of line number entries	*/
7998b9484cSchristos   unsigned char s_flags[4];	/* flags				*/
8098b9484cSchristos };
8198b9484cSchristos 
8298b9484cSchristos /* The length of the s_name field in struct external_scnhdr.  */
8398b9484cSchristos 
8498b9484cSchristos #define SCNNMLEN (8)
8598b9484cSchristos 
8698b9484cSchristos /* Bits for scnhdr s_flags field.  This includes some bits defined
8798b9484cSchristos    only for PE.  This may need to be moved into coff_magic.  */
8898b9484cSchristos 
8998b9484cSchristos #define STYP_DATA			(1 << 6)
9098b9484cSchristos #define IMAGE_SCN_MEM_DISCARDABLE	(1 << 25)
9198b9484cSchristos #define IMAGE_SCN_MEM_SHARED		(1 << 28)
9298b9484cSchristos #define IMAGE_SCN_MEM_READ		(1 << 30)
9398b9484cSchristos 
9498b9484cSchristos #define IMAGE_SCN_ALIGN_POWER_BIT_POS	     20
9598b9484cSchristos #define IMAGE_SCN_ALIGN_POWER_CONST(val)     \
9698b9484cSchristos   (((val) + 1) << IMAGE_SCN_ALIGN_POWER_BIT_POS)
9798b9484cSchristos 
9898b9484cSchristos /* COFF symbol table entry.  */
9998b9484cSchristos 
10098b9484cSchristos #define E_SYMNMLEN	8	/* # characters in a symbol name	*/
10198b9484cSchristos 
10298b9484cSchristos struct external_syment
10398b9484cSchristos {
10498b9484cSchristos   union
10598b9484cSchristos   {
10698b9484cSchristos     unsigned char e_name[E_SYMNMLEN];
10798b9484cSchristos 
10898b9484cSchristos     struct
10998b9484cSchristos     {
11098b9484cSchristos       unsigned char e_zeroes[4];
11198b9484cSchristos       unsigned char e_offset[4];
11298b9484cSchristos     } e;
11398b9484cSchristos   } e;
11498b9484cSchristos 
11598b9484cSchristos   unsigned char e_value[4];
11698b9484cSchristos   unsigned char e_scnum[2];
11798b9484cSchristos   unsigned char e_type[2];
11898b9484cSchristos   unsigned char e_sclass[1];
11998b9484cSchristos   unsigned char e_numaux[1];
12098b9484cSchristos };
12198b9484cSchristos 
12298b9484cSchristos /* Length allowed for filename in aux sym format 4.  */
12398b9484cSchristos 
12498b9484cSchristos #define E_FILNMLEN	18
12598b9484cSchristos 
12698b9484cSchristos /* Omits x_sym and other unused variants.  */
12798b9484cSchristos 
12898b9484cSchristos union external_auxent
12998b9484cSchristos {
13098b9484cSchristos   /* Aux sym format 4: file.  */
13198b9484cSchristos   union
13298b9484cSchristos   {
13398b9484cSchristos     char x_fname[E_FILNMLEN];
13498b9484cSchristos     struct
13598b9484cSchristos     {
13698b9484cSchristos       unsigned char x_zeroes[4];
13798b9484cSchristos       unsigned char x_offset[4];
13898b9484cSchristos     } x_n;
13998b9484cSchristos   } x_file;
14098b9484cSchristos   /* Aux sym format 5: section.  */
14198b9484cSchristos   struct
14298b9484cSchristos   {
14398b9484cSchristos     unsigned char x_scnlen[4];		/* section length		*/
14498b9484cSchristos     unsigned char x_nreloc[2];		/* # relocation entries		*/
14598b9484cSchristos     unsigned char x_nlinno[2];		/* # line numbers		*/
14698b9484cSchristos     unsigned char x_checksum[4];	/* section COMDAT checksum	*/
14798b9484cSchristos     unsigned char x_associated[2];	/* COMDAT assoc section index	*/
14898b9484cSchristos     unsigned char x_comdat[1];		/* COMDAT selection number	*/
14998b9484cSchristos   } x_scn;
15098b9484cSchristos };
15198b9484cSchristos 
15298b9484cSchristos /* Symbol-related constants.  */
15398b9484cSchristos 
15498b9484cSchristos #define IMAGE_SYM_DEBUG		(-2)
15598b9484cSchristos #define IMAGE_SYM_TYPE_NULL	(0)
15698b9484cSchristos #define IMAGE_SYM_DTYPE_NULL	(0)
15798b9484cSchristos #define IMAGE_SYM_CLASS_STATIC	(3)
15898b9484cSchristos #define IMAGE_SYM_CLASS_FILE	(103)
15998b9484cSchristos 
16098b9484cSchristos #define IMAGE_SYM_TYPE \
16198b9484cSchristos   ((IMAGE_SYM_DTYPE_NULL << 4) | IMAGE_SYM_TYPE_NULL)
16298b9484cSchristos 
16398b9484cSchristos /* Private data for an simple_object_read.  */
16498b9484cSchristos 
16598b9484cSchristos struct simple_object_coff_read
16698b9484cSchristos {
16798b9484cSchristos   /* Magic number.  */
16898b9484cSchristos   unsigned short magic;
16998b9484cSchristos   /* Whether the file is big-endian.  */
17098b9484cSchristos   unsigned char is_big_endian;
17198b9484cSchristos   /* Number of sections.  */
17298b9484cSchristos   unsigned short nscns;
17398b9484cSchristos   /* File offset of symbol table.  */
17498b9484cSchristos   off_t symptr;
17598b9484cSchristos   /* Number of symbol table entries.  */
17698b9484cSchristos   unsigned int nsyms;
17798b9484cSchristos   /* Flags.  */
17898b9484cSchristos   unsigned short flags;
17998b9484cSchristos   /* Offset of section headers in file.  */
18098b9484cSchristos   off_t scnhdr_offset;
18198b9484cSchristos };
18298b9484cSchristos 
18398b9484cSchristos /* Private data for an simple_object_attributes.  */
18498b9484cSchristos 
18598b9484cSchristos struct simple_object_coff_attributes
18698b9484cSchristos {
18798b9484cSchristos   /* Magic number.  */
18898b9484cSchristos   unsigned short magic;
18998b9484cSchristos   /* Whether the file is big-endian.  */
19098b9484cSchristos   unsigned char is_big_endian;
19198b9484cSchristos   /* Flags.  */
19298b9484cSchristos   unsigned short flags;
19398b9484cSchristos };
19498b9484cSchristos 
19598b9484cSchristos /* There is no magic number which indicates a COFF file as opposed to
19698b9484cSchristos    any other sort of file.  Instead, each COFF file starts with a
19798b9484cSchristos    two-byte magic number which also indicates the type of the target.
19898b9484cSchristos    This struct holds a magic number as well as characteristics of that
19998b9484cSchristos    COFF format.  */
20098b9484cSchristos 
20198b9484cSchristos struct coff_magic_struct
20298b9484cSchristos {
20398b9484cSchristos   /* Magic number.  */
20498b9484cSchristos   unsigned short magic;
20598b9484cSchristos   /* Whether this magic number is for a big-endian file.  */
20698b9484cSchristos   unsigned char is_big_endian;
20798b9484cSchristos   /* Flag bits, in the f_flags fields, which indicates that this file
20898b9484cSchristos      is not a relocatable object file.  There is no flag which
20998b9484cSchristos      specifically indicates a relocatable object file, it is only
21098b9484cSchristos      implied by the absence of these flags.  */
21198b9484cSchristos   unsigned short non_object_flags;
21298b9484cSchristos };
21398b9484cSchristos 
21498b9484cSchristos /* This is a list of the COFF magic numbers which we recognize, namely
21598b9484cSchristos    the ones used on Windows.  More can be added as needed.  */
21698b9484cSchristos 
21798b9484cSchristos static const struct coff_magic_struct coff_magic[] =
21898b9484cSchristos {
21998b9484cSchristos   /* i386.  */
22098b9484cSchristos   { 0x14c, 0, F_EXEC | IMAGE_FILE_SYSTEM | IMAGE_FILE_DLL },
22198b9484cSchristos   /* x86_64.  */
22298b9484cSchristos   { 0x8664, 0, F_EXEC | IMAGE_FILE_SYSTEM | IMAGE_FILE_DLL }
22398b9484cSchristos };
22498b9484cSchristos 
22598b9484cSchristos /* See if we have a COFF file.  */
22698b9484cSchristos 
22798b9484cSchristos static void *
22898b9484cSchristos simple_object_coff_match (unsigned char header[SIMPLE_OBJECT_MATCH_HEADER_LEN],
22998b9484cSchristos 			  int descriptor, off_t offset,
23098b9484cSchristos 			  const char *segment_name ATTRIBUTE_UNUSED,
23198b9484cSchristos 			  const char **errmsg, int *err)
23298b9484cSchristos {
23398b9484cSchristos   size_t c;
23498b9484cSchristos   unsigned short magic_big;
23598b9484cSchristos   unsigned short magic_little;
23698b9484cSchristos   unsigned short magic;
23798b9484cSchristos   size_t i;
23898b9484cSchristos   int is_big_endian;
23998b9484cSchristos   unsigned short (*fetch_16) (const unsigned char *);
24098b9484cSchristos   unsigned int (*fetch_32) (const unsigned char *);
24198b9484cSchristos   unsigned char hdrbuf[sizeof (struct external_filehdr)];
24298b9484cSchristos   unsigned short flags;
24398b9484cSchristos   struct simple_object_coff_read *ocr;
24498b9484cSchristos 
24598b9484cSchristos   c = sizeof (coff_magic) / sizeof (coff_magic[0]);
24698b9484cSchristos   magic_big = simple_object_fetch_big_16 (header);
24798b9484cSchristos   magic_little = simple_object_fetch_little_16 (header);
24898b9484cSchristos   for (i = 0; i < c; ++i)
24998b9484cSchristos     {
25098b9484cSchristos       if (coff_magic[i].is_big_endian
25198b9484cSchristos 	  ? coff_magic[i].magic == magic_big
25298b9484cSchristos 	  : coff_magic[i].magic == magic_little)
25398b9484cSchristos 	break;
25498b9484cSchristos     }
25598b9484cSchristos   if (i >= c)
25698b9484cSchristos     {
25798b9484cSchristos       *errmsg = NULL;
25898b9484cSchristos       *err = 0;
25998b9484cSchristos       return NULL;
26098b9484cSchristos     }
26198b9484cSchristos   is_big_endian = coff_magic[i].is_big_endian;
26298b9484cSchristos 
26398b9484cSchristos   magic = is_big_endian ? magic_big : magic_little;
26498b9484cSchristos   fetch_16 = (is_big_endian
26598b9484cSchristos 	      ? simple_object_fetch_big_16
26698b9484cSchristos 	      : simple_object_fetch_little_16);
26798b9484cSchristos   fetch_32 = (is_big_endian
26898b9484cSchristos 	      ? simple_object_fetch_big_32
26998b9484cSchristos 	      : simple_object_fetch_little_32);
27098b9484cSchristos 
27198b9484cSchristos   if (!simple_object_internal_read (descriptor, offset, hdrbuf, sizeof hdrbuf,
27298b9484cSchristos 				    errmsg, err))
27398b9484cSchristos     return NULL;
27498b9484cSchristos 
27598b9484cSchristos   flags = fetch_16 (hdrbuf + offsetof (struct external_filehdr, f_flags));
27698b9484cSchristos   if ((flags & coff_magic[i].non_object_flags) != 0)
27798b9484cSchristos     {
27898b9484cSchristos       *errmsg = "not relocatable object file";
27998b9484cSchristos       *err = 0;
28098b9484cSchristos       return NULL;
28198b9484cSchristos     }
28298b9484cSchristos 
28398b9484cSchristos   ocr = XNEW (struct simple_object_coff_read);
28498b9484cSchristos   ocr->magic = magic;
28598b9484cSchristos   ocr->is_big_endian = is_big_endian;
28698b9484cSchristos   ocr->nscns = fetch_16 (hdrbuf + offsetof (struct external_filehdr, f_nscns));
28798b9484cSchristos   ocr->symptr = fetch_32 (hdrbuf
28898b9484cSchristos 			  + offsetof (struct external_filehdr, f_symptr));
28998b9484cSchristos   ocr->nsyms = fetch_32 (hdrbuf + offsetof (struct external_filehdr, f_nsyms));
29098b9484cSchristos   ocr->flags = flags;
29198b9484cSchristos   ocr->scnhdr_offset = (sizeof (struct external_filehdr)
29298b9484cSchristos 			+ fetch_16 (hdrbuf + offsetof (struct external_filehdr,
29398b9484cSchristos 						       f_opthdr)));
29498b9484cSchristos 
29598b9484cSchristos   return (void *) ocr;
29698b9484cSchristos }
29798b9484cSchristos 
29898b9484cSchristos /* Read the string table in a COFF file.  */
29998b9484cSchristos 
30098b9484cSchristos static char *
30198b9484cSchristos simple_object_coff_read_strtab (simple_object_read *sobj, size_t *strtab_size,
30298b9484cSchristos 				const char **errmsg, int *err)
30398b9484cSchristos {
30498b9484cSchristos   struct simple_object_coff_read *ocr =
30598b9484cSchristos     (struct simple_object_coff_read *) sobj->data;
30698b9484cSchristos   off_t strtab_offset;
30798b9484cSchristos   unsigned char strsizebuf[4];
30898b9484cSchristos   size_t strsize;
30998b9484cSchristos   char *strtab;
31098b9484cSchristos 
31198b9484cSchristos   strtab_offset = sobj->offset + ocr->symptr
31298b9484cSchristos 		  + ocr->nsyms * sizeof (struct external_syment);
31398b9484cSchristos   if (!simple_object_internal_read (sobj->descriptor, strtab_offset,
31498b9484cSchristos 				    strsizebuf, 4, errmsg, err))
31598b9484cSchristos     return NULL;
31698b9484cSchristos   strsize = (ocr->is_big_endian
31798b9484cSchristos 	     ? simple_object_fetch_big_32 (strsizebuf)
31898b9484cSchristos 	     : simple_object_fetch_little_32 (strsizebuf));
31998b9484cSchristos   strtab = XNEWVEC (char, strsize);
32098b9484cSchristos   if (!simple_object_internal_read (sobj->descriptor, strtab_offset,
32198b9484cSchristos 				    (unsigned char *) strtab, strsize, errmsg,
32298b9484cSchristos 				    err))
32398b9484cSchristos     {
32498b9484cSchristos       XDELETEVEC (strtab);
32598b9484cSchristos       return NULL;
32698b9484cSchristos     }
32798b9484cSchristos   *strtab_size = strsize;
32898b9484cSchristos   return strtab;
32998b9484cSchristos }
33098b9484cSchristos 
33198b9484cSchristos /* Find all sections in a COFF file.  */
33298b9484cSchristos 
33398b9484cSchristos static const char *
33498b9484cSchristos simple_object_coff_find_sections (simple_object_read *sobj,
33598b9484cSchristos 				  int (*pfn) (void *, const char *,
33698b9484cSchristos 					      off_t offset, off_t length),
33798b9484cSchristos 				  void *data,
33898b9484cSchristos 				  int *err)
33998b9484cSchristos {
34098b9484cSchristos   struct simple_object_coff_read *ocr =
34198b9484cSchristos     (struct simple_object_coff_read *) sobj->data;
34298b9484cSchristos   size_t scnhdr_size;
34398b9484cSchristos   unsigned char *scnbuf;
34498b9484cSchristos   const char *errmsg;
34598b9484cSchristos   unsigned int (*fetch_32) (const unsigned char *);
34698b9484cSchristos   unsigned int nscns;
34798b9484cSchristos   char *strtab;
34898b9484cSchristos   size_t strtab_size;
34998b9484cSchristos   unsigned int i;
35098b9484cSchristos 
35198b9484cSchristos   scnhdr_size = sizeof (struct external_scnhdr);
35298b9484cSchristos   scnbuf = XNEWVEC (unsigned char, scnhdr_size * ocr->nscns);
35398b9484cSchristos   if (!simple_object_internal_read (sobj->descriptor,
35498b9484cSchristos 				    sobj->offset + ocr->scnhdr_offset,
35598b9484cSchristos 				    scnbuf, scnhdr_size * ocr->nscns, &errmsg,
35698b9484cSchristos 				    err))
35798b9484cSchristos     {
35898b9484cSchristos       XDELETEVEC (scnbuf);
35998b9484cSchristos       return errmsg;
36098b9484cSchristos     }
36198b9484cSchristos 
36298b9484cSchristos   fetch_32 = (ocr->is_big_endian
36398b9484cSchristos 	      ? simple_object_fetch_big_32
36498b9484cSchristos 	      : simple_object_fetch_little_32);
36598b9484cSchristos 
36698b9484cSchristos   nscns = ocr->nscns;
36798b9484cSchristos   strtab = NULL;
36898b9484cSchristos   strtab_size = 0;
36998b9484cSchristos   for (i = 0; i < nscns; ++i)
37098b9484cSchristos     {
37198b9484cSchristos       unsigned char *scnhdr;
37298b9484cSchristos       unsigned char *scnname;
37398b9484cSchristos       char namebuf[SCNNMLEN + 1];
37498b9484cSchristos       char *name;
37598b9484cSchristos       off_t scnptr;
37698b9484cSchristos       unsigned int size;
37798b9484cSchristos 
37898b9484cSchristos       scnhdr = scnbuf + i * scnhdr_size;
37998b9484cSchristos       scnname = scnhdr + offsetof (struct external_scnhdr, s_name);
38098b9484cSchristos       memcpy (namebuf, scnname, SCNNMLEN);
38198b9484cSchristos       namebuf[SCNNMLEN] = '\0';
38298b9484cSchristos       name = &namebuf[0];
38398b9484cSchristos       if (namebuf[0] == '/')
38498b9484cSchristos 	{
38598b9484cSchristos 	  size_t strindex;
38698b9484cSchristos 	  char *end;
38798b9484cSchristos 
38898b9484cSchristos 	  strindex = strtol (namebuf + 1, &end, 10);
38998b9484cSchristos 	  if (*end == '\0')
39098b9484cSchristos 	    {
39198b9484cSchristos 	      /* The real section name is found in the string
39298b9484cSchristos 		 table.  */
39398b9484cSchristos 	      if (strtab == NULL)
39498b9484cSchristos 		{
39598b9484cSchristos 		  strtab = simple_object_coff_read_strtab (sobj,
39698b9484cSchristos 							   &strtab_size,
39798b9484cSchristos 							   &errmsg, err);
39898b9484cSchristos 		  if (strtab == NULL)
39998b9484cSchristos 		    {
40098b9484cSchristos 		      XDELETEVEC (scnbuf);
40198b9484cSchristos 		      return errmsg;
40298b9484cSchristos 		    }
40398b9484cSchristos 		}
40498b9484cSchristos 
40598b9484cSchristos 	      if (strindex < 4 || strindex >= strtab_size)
40698b9484cSchristos 		{
40798b9484cSchristos 		  XDELETEVEC (strtab);
40898b9484cSchristos 		  XDELETEVEC (scnbuf);
40998b9484cSchristos 		  *err = 0;
41098b9484cSchristos 		  return "section string index out of range";
41198b9484cSchristos 		}
41298b9484cSchristos 
41398b9484cSchristos 	      name = strtab + strindex;
41498b9484cSchristos 	    }
41598b9484cSchristos 	}
41698b9484cSchristos 
41798b9484cSchristos       scnptr = fetch_32 (scnhdr + offsetof (struct external_scnhdr, s_scnptr));
41898b9484cSchristos       size = fetch_32 (scnhdr + offsetof (struct external_scnhdr, s_size));
41998b9484cSchristos 
42098b9484cSchristos       if (!(*pfn) (data, name, scnptr, size))
42198b9484cSchristos 	break;
42298b9484cSchristos     }
42398b9484cSchristos 
42498b9484cSchristos   if (strtab != NULL)
42598b9484cSchristos     XDELETEVEC (strtab);
42698b9484cSchristos   XDELETEVEC (scnbuf);
42798b9484cSchristos 
42898b9484cSchristos   return NULL;
42998b9484cSchristos }
43098b9484cSchristos 
43198b9484cSchristos /* Fetch the attributes for an simple_object_read.  */
43298b9484cSchristos 
43398b9484cSchristos static void *
43498b9484cSchristos simple_object_coff_fetch_attributes (simple_object_read *sobj,
43598b9484cSchristos 				     const char **errmsg ATTRIBUTE_UNUSED,
43698b9484cSchristos 				     int *err ATTRIBUTE_UNUSED)
43798b9484cSchristos {
43898b9484cSchristos   struct simple_object_coff_read *ocr =
43998b9484cSchristos     (struct simple_object_coff_read *) sobj->data;
44098b9484cSchristos   struct simple_object_coff_attributes *ret;
44198b9484cSchristos 
44298b9484cSchristos   ret = XNEW (struct simple_object_coff_attributes);
44398b9484cSchristos   ret->magic = ocr->magic;
44498b9484cSchristos   ret->is_big_endian = ocr->is_big_endian;
44598b9484cSchristos   ret->flags = ocr->flags;
44698b9484cSchristos   return ret;
44798b9484cSchristos }
44898b9484cSchristos 
44998b9484cSchristos /* Release the private data for an simple_object_read.  */
45098b9484cSchristos 
45198b9484cSchristos static void
45298b9484cSchristos simple_object_coff_release_read (void *data)
45398b9484cSchristos {
45498b9484cSchristos   XDELETE (data);
45598b9484cSchristos }
45698b9484cSchristos 
45798b9484cSchristos /* Compare two attributes structures.  */
45898b9484cSchristos 
45998b9484cSchristos static const char *
46098b9484cSchristos simple_object_coff_attributes_merge (void *todata, void *fromdata, int *err)
46198b9484cSchristos {
46298b9484cSchristos   struct simple_object_coff_attributes *to =
46398b9484cSchristos     (struct simple_object_coff_attributes *) todata;
46498b9484cSchristos   struct simple_object_coff_attributes *from =
46598b9484cSchristos     (struct simple_object_coff_attributes *) fromdata;
46698b9484cSchristos 
46798b9484cSchristos   if (to->magic != from->magic || to->is_big_endian != from->is_big_endian)
46898b9484cSchristos     {
46998b9484cSchristos       *err = 0;
47098b9484cSchristos       return "COFF object format mismatch";
47198b9484cSchristos     }
47298b9484cSchristos   return NULL;
47398b9484cSchristos }
47498b9484cSchristos 
47598b9484cSchristos /* Release the private data for an attributes structure.  */
47698b9484cSchristos 
47798b9484cSchristos static void
47898b9484cSchristos simple_object_coff_release_attributes (void *data)
47998b9484cSchristos {
48098b9484cSchristos   XDELETE (data);
48198b9484cSchristos }
48298b9484cSchristos 
48398b9484cSchristos /* Prepare to write out a file.  */
48498b9484cSchristos 
48598b9484cSchristos static void *
48698b9484cSchristos simple_object_coff_start_write (void *attributes_data,
48798b9484cSchristos 				const char **errmsg ATTRIBUTE_UNUSED,
48898b9484cSchristos 				int *err ATTRIBUTE_UNUSED)
48998b9484cSchristos {
49098b9484cSchristos   struct simple_object_coff_attributes *attrs =
49198b9484cSchristos     (struct simple_object_coff_attributes *) attributes_data;
49298b9484cSchristos   struct simple_object_coff_attributes *ret;
49398b9484cSchristos 
49498b9484cSchristos   /* We're just going to record the attributes, but we need to make a
49598b9484cSchristos      copy because the user may delete them.  */
49698b9484cSchristos   ret = XNEW (struct simple_object_coff_attributes);
49798b9484cSchristos   *ret = *attrs;
49898b9484cSchristos   return ret;
49998b9484cSchristos }
50098b9484cSchristos 
50198b9484cSchristos /* Write out a COFF filehdr.  */
50298b9484cSchristos 
50398b9484cSchristos static int
50498b9484cSchristos simple_object_coff_write_filehdr (simple_object_write *sobj, int descriptor,
50598b9484cSchristos 				  unsigned int nscns, size_t symtab_offset,
50698b9484cSchristos 				  unsigned int nsyms, const char **errmsg,
50798b9484cSchristos 				  int *err)
50898b9484cSchristos {
50998b9484cSchristos   struct simple_object_coff_attributes *attrs =
51098b9484cSchristos     (struct simple_object_coff_attributes *) sobj->data;
51198b9484cSchristos   unsigned char hdrbuf[sizeof (struct external_filehdr)];
51298b9484cSchristos   unsigned char *hdr;
51398b9484cSchristos   void (*set_16) (unsigned char *, unsigned short);
51498b9484cSchristos   void (*set_32) (unsigned char *, unsigned int);
51598b9484cSchristos 
51698b9484cSchristos   hdr = &hdrbuf[0];
51798b9484cSchristos 
51898b9484cSchristos   set_16 = (attrs->is_big_endian
51998b9484cSchristos 	    ? simple_object_set_big_16
52098b9484cSchristos 	    : simple_object_set_little_16);
52198b9484cSchristos   set_32 = (attrs->is_big_endian
52298b9484cSchristos 	    ? simple_object_set_big_32
52398b9484cSchristos 	    : simple_object_set_little_32);
52498b9484cSchristos 
52598b9484cSchristos   memset (hdr, 0, sizeof (struct external_filehdr));
52698b9484cSchristos 
52798b9484cSchristos   set_16 (hdr + offsetof (struct external_filehdr, f_magic), attrs->magic);
52898b9484cSchristos   set_16 (hdr + offsetof (struct external_filehdr, f_nscns), nscns);
52998b9484cSchristos   /* f_timdat left as zero.  */
53098b9484cSchristos   set_32 (hdr + offsetof (struct external_filehdr, f_symptr), symtab_offset);
53198b9484cSchristos   set_32 (hdr + offsetof (struct external_filehdr, f_nsyms), nsyms);
53298b9484cSchristos   /* f_opthdr left as zero.  */
53398b9484cSchristos   set_16 (hdr + offsetof (struct external_filehdr, f_flags), attrs->flags);
53498b9484cSchristos 
53598b9484cSchristos   return simple_object_internal_write (descriptor, 0, hdrbuf,
53698b9484cSchristos 				       sizeof (struct external_filehdr),
53798b9484cSchristos 				       errmsg, err);
53898b9484cSchristos }
53998b9484cSchristos 
54098b9484cSchristos /* Write out a COFF section header.  */
54198b9484cSchristos 
54298b9484cSchristos static int
54398b9484cSchristos simple_object_coff_write_scnhdr (simple_object_write *sobj, int descriptor,
54498b9484cSchristos 				 const char *name, size_t *name_offset,
54598b9484cSchristos 				 off_t scnhdr_offset, size_t scnsize,
54698b9484cSchristos 				 off_t offset, unsigned int align,
54798b9484cSchristos 				 const char **errmsg, int *err)
54898b9484cSchristos {
54998b9484cSchristos   struct simple_object_coff_attributes *attrs =
55098b9484cSchristos     (struct simple_object_coff_attributes *) sobj->data;
55198b9484cSchristos   void (*set_32) (unsigned char *, unsigned int);
55298b9484cSchristos   unsigned char hdrbuf[sizeof (struct external_scnhdr)];
55398b9484cSchristos   unsigned char *hdr;
55498b9484cSchristos   size_t namelen;
55598b9484cSchristos   unsigned int flags;
55698b9484cSchristos 
55798b9484cSchristos   set_32 = (attrs->is_big_endian
55898b9484cSchristos 	    ? simple_object_set_big_32
55998b9484cSchristos 	    : simple_object_set_little_32);
56098b9484cSchristos 
56198b9484cSchristos   memset (hdrbuf, 0, sizeof hdrbuf);
56298b9484cSchristos   hdr = &hdrbuf[0];
56398b9484cSchristos 
56498b9484cSchristos   namelen = strlen (name);
56598b9484cSchristos   if (namelen <= SCNNMLEN)
56698b9484cSchristos     strncpy ((char *) hdr + offsetof (struct external_scnhdr, s_name), name,
56798b9484cSchristos 	     SCNNMLEN);
56898b9484cSchristos   else
56998b9484cSchristos     {
57098b9484cSchristos       snprintf ((char *) hdr + offsetof (struct external_scnhdr, s_name),
57198b9484cSchristos 		SCNNMLEN, "/%lu", (unsigned long) *name_offset);
57298b9484cSchristos       *name_offset += namelen + 1;
57398b9484cSchristos     }
57498b9484cSchristos 
57598b9484cSchristos   /* s_paddr left as zero.  */
57698b9484cSchristos   /* s_vaddr left as zero.  */
57798b9484cSchristos   set_32 (hdr + offsetof (struct external_scnhdr, s_size), scnsize);
57898b9484cSchristos   set_32 (hdr + offsetof (struct external_scnhdr, s_scnptr), offset);
57998b9484cSchristos   /* s_relptr left as zero.  */
58098b9484cSchristos   /* s_lnnoptr left as zero.  */
58198b9484cSchristos   /* s_nreloc left as zero.  */
58298b9484cSchristos   /* s_nlnno left as zero.  */
58398b9484cSchristos   flags = (STYP_DATA | IMAGE_SCN_MEM_DISCARDABLE | IMAGE_SCN_MEM_SHARED
58498b9484cSchristos 	   | IMAGE_SCN_MEM_READ);
58598b9484cSchristos   /* PE can represent alignment up to 13.  */
58698b9484cSchristos   if (align > 13)
58798b9484cSchristos     align = 13;
58898b9484cSchristos   flags |= IMAGE_SCN_ALIGN_POWER_CONST(align);
58998b9484cSchristos   set_32 (hdr + offsetof (struct external_scnhdr, s_flags), flags);
59098b9484cSchristos 
59198b9484cSchristos   return simple_object_internal_write (descriptor, scnhdr_offset, hdrbuf,
59298b9484cSchristos 				       sizeof (struct external_scnhdr),
59398b9484cSchristos 				       errmsg, err);
59498b9484cSchristos }
59598b9484cSchristos 
59698b9484cSchristos /* Write out a complete COFF file.  */
59798b9484cSchristos 
59898b9484cSchristos static const char *
59998b9484cSchristos simple_object_coff_write_to_file (simple_object_write *sobj, int descriptor,
60098b9484cSchristos 				  int *err)
60198b9484cSchristos {
60298b9484cSchristos   struct simple_object_coff_attributes *attrs =
60398b9484cSchristos     (struct simple_object_coff_attributes *) sobj->data;
60498b9484cSchristos   unsigned int nscns, secnum;
60598b9484cSchristos   simple_object_write_section *section;
60698b9484cSchristos   off_t scnhdr_offset;
60798b9484cSchristos   size_t symtab_offset;
60898b9484cSchristos   off_t secsym_offset;
60998b9484cSchristos   unsigned int nsyms;
61098b9484cSchristos   size_t offset;
61198b9484cSchristos   size_t name_offset;
61298b9484cSchristos   const char *errmsg;
61398b9484cSchristos   unsigned char strsizebuf[4];
61498b9484cSchristos   /* The interface doesn't give us access to the name of the input file
61598b9484cSchristos      yet.  We want to use its basename for the FILE symbol.  This is
61698b9484cSchristos      what 'gas' uses when told to assemble from stdin.  */
61798b9484cSchristos   const char *source_filename = "fake";
61898b9484cSchristos   size_t sflen;
61998b9484cSchristos   union
62098b9484cSchristos   {
62198b9484cSchristos     struct external_syment sym;
62298b9484cSchristos     union external_auxent aux;
62398b9484cSchristos   } syms[2];
62498b9484cSchristos   void (*set_16) (unsigned char *, unsigned short);
62598b9484cSchristos   void (*set_32) (unsigned char *, unsigned int);
62698b9484cSchristos 
62798b9484cSchristos   set_16 = (attrs->is_big_endian
62898b9484cSchristos 	    ? simple_object_set_big_16
62998b9484cSchristos 	    : simple_object_set_little_16);
63098b9484cSchristos   set_32 = (attrs->is_big_endian
63198b9484cSchristos 	    ? simple_object_set_big_32
63298b9484cSchristos 	    : simple_object_set_little_32);
63398b9484cSchristos 
63498b9484cSchristos   nscns = 0;
63598b9484cSchristos   for (section = sobj->sections; section != NULL; section = section->next)
63698b9484cSchristos     ++nscns;
63798b9484cSchristos 
63898b9484cSchristos   scnhdr_offset = sizeof (struct external_filehdr);
63998b9484cSchristos   offset = scnhdr_offset + nscns * sizeof (struct external_scnhdr);
64098b9484cSchristos   name_offset = 4;
64198b9484cSchristos   for (section = sobj->sections; section != NULL; section = section->next)
64298b9484cSchristos     {
64398b9484cSchristos       size_t mask;
64498b9484cSchristos       size_t new_offset;
64598b9484cSchristos       size_t scnsize;
64698b9484cSchristos       struct simple_object_write_section_buffer *buffer;
64798b9484cSchristos 
64898b9484cSchristos       mask = (1U << section->align) - 1;
64998b9484cSchristos       new_offset = offset & mask;
65098b9484cSchristos       new_offset &= ~ mask;
65198b9484cSchristos       while (new_offset > offset)
65298b9484cSchristos 	{
65398b9484cSchristos 	  unsigned char zeroes[16];
65498b9484cSchristos 	  size_t write;
65598b9484cSchristos 
65698b9484cSchristos 	  memset (zeroes, 0, sizeof zeroes);
65798b9484cSchristos 	  write = new_offset - offset;
65898b9484cSchristos 	  if (write > sizeof zeroes)
65998b9484cSchristos 	    write = sizeof zeroes;
66098b9484cSchristos 	  if (!simple_object_internal_write (descriptor, offset, zeroes, write,
66198b9484cSchristos 					     &errmsg, err))
66298b9484cSchristos 	    return errmsg;
66398b9484cSchristos 	}
66498b9484cSchristos 
66598b9484cSchristos       scnsize = 0;
66698b9484cSchristos       for (buffer = section->buffers; buffer != NULL; buffer = buffer->next)
66798b9484cSchristos 	{
66898b9484cSchristos 	  if (!simple_object_internal_write (descriptor, offset + scnsize,
66998b9484cSchristos 					     ((const unsigned char *)
67098b9484cSchristos 					      buffer->buffer),
67198b9484cSchristos 					     buffer->size, &errmsg, err))
67298b9484cSchristos 	    return errmsg;
67398b9484cSchristos 	  scnsize += buffer->size;
67498b9484cSchristos 	}
67598b9484cSchristos 
67698b9484cSchristos       if (!simple_object_coff_write_scnhdr (sobj, descriptor, section->name,
67798b9484cSchristos 					    &name_offset, scnhdr_offset,
67898b9484cSchristos 					    scnsize, offset, section->align,
67998b9484cSchristos 					    &errmsg, err))
68098b9484cSchristos 	return errmsg;
68198b9484cSchristos 
68298b9484cSchristos       scnhdr_offset += sizeof (struct external_scnhdr);
68398b9484cSchristos       offset += scnsize;
68498b9484cSchristos     }
68598b9484cSchristos 
68698b9484cSchristos   /* Symbol table is always half-word aligned.  */
68798b9484cSchristos   offset += (offset & 1);
68898b9484cSchristos   /* There is a file symbol and a section symbol per section,
68998b9484cSchristos      and each of these has a single auxiliary symbol following.  */
69098b9484cSchristos   nsyms = 2 * (nscns + 1);
69198b9484cSchristos   symtab_offset = offset;
69298b9484cSchristos   /* Advance across space reserved for symbol table to locate
69398b9484cSchristos      start of string table.  */
69498b9484cSchristos   offset += nsyms * sizeof (struct external_syment);
69598b9484cSchristos 
69698b9484cSchristos   /* Write out file symbol.  */
69798b9484cSchristos   memset (&syms[0], 0, sizeof (syms));
69898b9484cSchristos   strcpy ((char *)&syms[0].sym.e.e_name[0], ".file");
69998b9484cSchristos   set_16 (&syms[0].sym.e_scnum[0], IMAGE_SYM_DEBUG);
70098b9484cSchristos   set_16 (&syms[0].sym.e_type[0], IMAGE_SYM_TYPE);
70198b9484cSchristos   syms[0].sym.e_sclass[0] = IMAGE_SYM_CLASS_FILE;
70298b9484cSchristos   syms[0].sym.e_numaux[0] = 1;
70398b9484cSchristos   /* The name need not be nul-terminated if it fits into the x_fname field
70498b9484cSchristos      directly, but must be if it has to be placed into the string table.  */
70598b9484cSchristos   sflen = strlen (source_filename);
70698b9484cSchristos   if (sflen <= E_FILNMLEN)
70798b9484cSchristos     memcpy (&syms[1].aux.x_file.x_fname[0], source_filename, sflen);
70898b9484cSchristos   else
70998b9484cSchristos     {
71098b9484cSchristos       set_32 (&syms[1].aux.x_file.x_n.x_offset[0], name_offset);
71198b9484cSchristos       if (!simple_object_internal_write (descriptor, offset + name_offset,
71298b9484cSchristos 					 ((const unsigned char *)
71398b9484cSchristos 					  source_filename),
71498b9484cSchristos 					 sflen + 1, &errmsg, err))
71598b9484cSchristos 	return errmsg;
71698b9484cSchristos       name_offset += strlen (source_filename) + 1;
71798b9484cSchristos     }
71898b9484cSchristos   if (!simple_object_internal_write (descriptor, symtab_offset,
71998b9484cSchristos 				     (const unsigned char *) &syms[0],
72098b9484cSchristos 				     sizeof (syms), &errmsg, err))
72198b9484cSchristos     return errmsg;
72298b9484cSchristos 
72398b9484cSchristos   /* Write the string table length, followed by the strings and section
72498b9484cSchristos      symbols in step with each other.  */
72598b9484cSchristos   set_32 (strsizebuf, name_offset);
72698b9484cSchristos   if (!simple_object_internal_write (descriptor, offset, strsizebuf, 4,
72798b9484cSchristos 				     &errmsg, err))
72898b9484cSchristos     return errmsg;
72998b9484cSchristos 
73098b9484cSchristos   name_offset = 4;
73198b9484cSchristos   secsym_offset = symtab_offset + sizeof (syms);
73298b9484cSchristos   memset (&syms[0], 0, sizeof (syms));
73398b9484cSchristos   set_16 (&syms[0].sym.e_type[0], IMAGE_SYM_TYPE);
73498b9484cSchristos   syms[0].sym.e_sclass[0] = IMAGE_SYM_CLASS_STATIC;
73598b9484cSchristos   syms[0].sym.e_numaux[0] = 1;
73698b9484cSchristos   secnum = 1;
73798b9484cSchristos 
73898b9484cSchristos   for (section = sobj->sections; section != NULL; section = section->next)
73998b9484cSchristos     {
74098b9484cSchristos       size_t namelen;
74198b9484cSchristos       size_t scnsize;
74298b9484cSchristos       struct simple_object_write_section_buffer *buffer;
74398b9484cSchristos 
74498b9484cSchristos       namelen = strlen (section->name);
74598b9484cSchristos       set_16 (&syms[0].sym.e_scnum[0], secnum++);
74698b9484cSchristos       scnsize = 0;
74798b9484cSchristos       for (buffer = section->buffers; buffer != NULL; buffer = buffer->next)
74898b9484cSchristos 	scnsize += buffer->size;
74998b9484cSchristos       set_32 (&syms[1].aux.x_scn.x_scnlen[0], scnsize);
75098b9484cSchristos       if (namelen > SCNNMLEN)
75198b9484cSchristos 	{
75298b9484cSchristos 	  set_32 (&syms[0].sym.e.e.e_zeroes[0], 0);
75398b9484cSchristos 	  set_32 (&syms[0].sym.e.e.e_offset[0], name_offset);
75498b9484cSchristos 	  if (!simple_object_internal_write (descriptor, offset + name_offset,
75598b9484cSchristos 					     ((const unsigned char *)
75698b9484cSchristos 					      section->name),
75798b9484cSchristos 					     namelen + 1, &errmsg, err))
75898b9484cSchristos 	    return errmsg;
75998b9484cSchristos 	  name_offset += namelen + 1;
76098b9484cSchristos 	}
76198b9484cSchristos       else
76298b9484cSchristos 	{
76398b9484cSchristos 	  memcpy (&syms[0].sym.e.e_name[0], section->name,
76498b9484cSchristos 		  strlen (section->name));
76598b9484cSchristos 	  memset (&syms[0].sym.e.e_name[strlen (section->name)], 0,
76698b9484cSchristos 		  E_SYMNMLEN - strlen (section->name));
76798b9484cSchristos 	}
76898b9484cSchristos 
76998b9484cSchristos       if (!simple_object_internal_write (descriptor, secsym_offset,
77098b9484cSchristos 					 (const unsigned char *) &syms[0],
77198b9484cSchristos 					 sizeof (syms), &errmsg, err))
77298b9484cSchristos 	return errmsg;
77398b9484cSchristos       secsym_offset += sizeof (syms);
77498b9484cSchristos     }
77598b9484cSchristos 
77698b9484cSchristos   if (!simple_object_coff_write_filehdr (sobj, descriptor, nscns,
77798b9484cSchristos 					 symtab_offset, nsyms, &errmsg, err))
77898b9484cSchristos     return errmsg;
77998b9484cSchristos 
78098b9484cSchristos   return NULL;
78198b9484cSchristos }
78298b9484cSchristos 
78398b9484cSchristos /* Release the private data for an simple_object_write structure.  */
78498b9484cSchristos 
78598b9484cSchristos static void
78698b9484cSchristos simple_object_coff_release_write (void *data)
78798b9484cSchristos {
78898b9484cSchristos   XDELETE (data);
78998b9484cSchristos }
79098b9484cSchristos 
79198b9484cSchristos /* The COFF functions.  */
79298b9484cSchristos 
79398b9484cSchristos const struct simple_object_functions simple_object_coff_functions =
79498b9484cSchristos {
79598b9484cSchristos   simple_object_coff_match,
79698b9484cSchristos   simple_object_coff_find_sections,
79798b9484cSchristos   simple_object_coff_fetch_attributes,
79898b9484cSchristos   simple_object_coff_release_read,
79998b9484cSchristos   simple_object_coff_attributes_merge,
80098b9484cSchristos   simple_object_coff_release_attributes,
80198b9484cSchristos   simple_object_coff_start_write,
80298b9484cSchristos   simple_object_coff_write_to_file,
8034559860eSchristos   simple_object_coff_release_write,
8044559860eSchristos   NULL
80598b9484cSchristos };
806