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