Home | History | Annotate | Line # | Download | only in bfd
coff-stgo32.c revision 1.1.1.9
      1 /* BFD back-end for Intel 386 COFF files (DJGPP variant with a stub).
      2    Copyright (C) 1997-2024 Free Software Foundation, Inc.
      3    Written by Robert Hoehne.
      4 
      5    This file is part of BFD, the Binary File Descriptor library.
      6 
      7    This program is free software; you can redistribute it and/or modify
      8    it under the terms of the GNU General Public License as published by
      9    the Free Software Foundation; either version 3 of the License, or
     10    (at your option) any later version.
     11 
     12    This program is distributed in the hope that it will be useful,
     13    but WITHOUT ANY WARRANTY; without even the implied warranty of
     14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15    GNU General Public License for more details.
     16 
     17    You should have received a copy of the GNU General Public License
     18    along with this program; if not, write to the Free Software
     19    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
     20    MA 02110-1301, USA.  */
     21 
     22 /* This file handles now also stubbed coff images. The stub is a small
     23    DOS executable program before the coff image to load it in memory
     24    and execute it. This is needed, because DOS cannot run coff files.
     25 
     26    The COFF image is loaded in memory without the stub attached, so
     27    all offsets are relative to the beginning of the image, not the
     28    actual file.  We handle this in bfd by setting bfd->origin to where
     29    the COFF image starts.  */
     30 
     31 #define TARGET_SYM		i386_coff_go32stubbed_vec
     32 #define TARGET_NAME		"coff-go32-exe"
     33 #define TARGET_UNDERSCORE	'_'
     34 #define COFF_GO32_EXE
     35 #define COFF_LONG_SECTION_NAMES
     36 #define COFF_SUPPORT_GNU_LINKONCE
     37 #define COFF_LONG_FILENAMES
     38 
     39 #define COFF_SECTION_ALIGNMENT_ENTRIES \
     40 { COFF_SECTION_NAME_EXACT_MATCH (".data"), \
     41   COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 4 }, \
     42 { COFF_SECTION_NAME_EXACT_MATCH (".text"), \
     43   COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 4 }, \
     44 { COFF_SECTION_NAME_PARTIAL_MATCH (".debug"), \
     45   COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 0 }, \
     46 { COFF_SECTION_NAME_PARTIAL_MATCH (".gnu.linkonce.wi"), \
     47   COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 0 }
     48 
     49 /* Section contains extended relocations. */
     50 #define IMAGE_SCN_LNK_NRELOC_OVFL (0x01000000)
     51 
     52 #include "sysdep.h"
     53 #include "bfd.h"
     54 #include "coff/msdos.h"
     55 
     56 static bfd_cleanup go32exe_check_format (bfd *);
     57 static bool go32exe_write_object_contents (bfd *);
     58 static bool go32exe_mkobject (bfd *);
     59 static bool go32exe_copy_private_bfd_data (bfd *, bfd *);
     60 
     61 /* Defined in coff-go32.c.  */
     62 bool _bfd_go32_mkobject (bfd *);
     63 void _bfd_go32_swap_scnhdr_in (bfd *, void *, void *);
     64 unsigned int _bfd_go32_swap_scnhdr_out (bfd *, void *, void *);
     65 
     66 #define COFF_CHECK_FORMAT go32exe_check_format
     67 #define COFF_WRITE_CONTENTS go32exe_write_object_contents
     68 #define coff_mkobject go32exe_mkobject
     69 #define coff_bfd_copy_private_bfd_data go32exe_copy_private_bfd_data
     70 #define coff_SWAP_scnhdr_in _bfd_go32_swap_scnhdr_in
     71 #define coff_SWAP_scnhdr_out _bfd_go32_swap_scnhdr_out
     72 
     73 #include "coff-i386.c"
     74 
     75 /* This macro is used, because I cannot assume the endianness of the
     76    host system.  */
     77 #define _H(index) (H_GET_16 (abfd, (header + index * 2)))
     78 
     79 /* These bytes are a 2048-byte DOS executable, which loads the COFF
     80    image into memory and then runs it. It is called 'stub'.  */
     81 #define GO32EXE_DEFAULT_STUB_SIZE 2048
     82 static const unsigned char go32exe_default_stub[GO32EXE_DEFAULT_STUB_SIZE] =
     83 {
     84 #include "go32stub.h"
     85 };
     86 
     87 /* Temporary location for stub read from input file.  */
     88 static char * go32exe_temp_stub = NULL;
     89 static bfd_size_type go32exe_temp_stub_size = 0;
     90 
     91 /* That's the function, which creates the stub. There are
     92    different cases from where the stub is taken.
     93    At first the environment variable $(GO32STUB) is checked and then
     94    $(STUB) if it was not set.
     95    If it exists and points to a valid stub the stub is taken from
     96    that file. This file can be also a whole executable file, because
     97    the stub is computed from the exe information at the start of that
     98    file.
     99 
    100    If there was any error, the standard stub (compiled in this file)
    101    is taken.
    102 
    103    Ideally this function should exec '$(TARGET)-stubify' to generate
    104    a stub, like gcc does.  */
    105 
    106 static void
    107 go32exe_create_stub (bfd *abfd)
    108 {
    109   /* Do it only once.  */
    110   if (coff_data (abfd)->stub == NULL)
    111     {
    112       char *stub;
    113       struct stat st;
    114       int f;
    115       unsigned char header[10];
    116       char magic[8];
    117       unsigned long coff_start;
    118       long exe_start;
    119 
    120       /* If we read a stub from an input file, use that one.  */
    121       if (go32exe_temp_stub != NULL)
    122 	{
    123 	  coff_data (abfd)->stub = bfd_alloc (abfd,
    124 						  go32exe_temp_stub_size);
    125 	  if (coff_data (abfd)->stub == NULL)
    126 	    return;
    127 	  memcpy (coff_data (abfd)->stub, go32exe_temp_stub,
    128 		  go32exe_temp_stub_size);
    129 	  coff_data (abfd)->stub_size = go32exe_temp_stub_size;
    130 	  free (go32exe_temp_stub);
    131 	  go32exe_temp_stub = NULL;
    132 	  go32exe_temp_stub_size = 0;
    133 	  return;
    134 	}
    135 
    136       /* Check at first the environment variable $(GO32STUB).  */
    137       stub = getenv ("GO32STUB");
    138       /* Now check the environment variable $(STUB).  */
    139       if (stub == NULL)
    140 	stub = getenv ("STUB");
    141       if (stub == NULL)
    142 	goto stub_end;
    143       if (stat (stub, &st) != 0)
    144 	goto stub_end;
    145 #ifdef O_BINARY
    146       f = open (stub, O_RDONLY | O_BINARY);
    147 #else
    148       f = open (stub, O_RDONLY);
    149 #endif
    150       if (f < 0)
    151 	goto stub_end;
    152       if (read (f, &header, sizeof (header)) < 0)
    153 	{
    154 	  close (f);
    155 	  goto stub_end;
    156 	}
    157       if (_H (0) != 0x5a4d)	/* It is not an exe file.  */
    158 	{
    159 	  close (f);
    160 	  goto stub_end;
    161 	}
    162       /* Compute the size of the stub (it is every thing up
    163 	 to the beginning of the coff image).  */
    164       coff_start = (long) _H (2) * 512L;
    165       if (_H (1))
    166 	coff_start += (long) _H (1) - 512L;
    167 
    168       exe_start = _H (4) * 16;
    169       if ((long) lseek (f, exe_start, SEEK_SET) != exe_start)
    170 	{
    171 	  close (f);
    172 	  goto stub_end;
    173 	}
    174       if (read (f, &magic, 8) != 8)
    175 	{
    176 	  close (f);
    177 	  goto stub_end;
    178 	}
    179       if (! startswith (magic, "go32stub"))
    180 	{
    181 	  close (f);
    182 	  goto stub_end;
    183 	}
    184       /* Now we found a correct stub (hopefully).  */
    185       coff_data (abfd)->stub = bfd_alloc (abfd, (bfd_size_type) coff_start);
    186       if (coff_data (abfd)->stub == NULL)
    187 	{
    188 	  close (f);
    189 	  return;
    190 	}
    191       lseek (f, 0L, SEEK_SET);
    192       if ((unsigned long) read (f, coff_data (abfd)->stub, coff_start)
    193 	  != coff_start)
    194 	{
    195 	  bfd_release (abfd, coff_data (abfd)->stub);
    196 	  coff_data (abfd)->stub = NULL;
    197 	}
    198       else
    199 	coff_data (abfd)->stub_size = coff_start;
    200       close (f);
    201     }
    202  stub_end:
    203   /* There was something wrong above, so use now the standard builtin
    204      stub.  */
    205   if (coff_data (abfd)->stub == NULL)
    206     {
    207       coff_data (abfd)->stub
    208 	= bfd_alloc (abfd, (bfd_size_type) GO32EXE_DEFAULT_STUB_SIZE);
    209       if (coff_data (abfd)->stub == NULL)
    210 	return;
    211       memcpy (coff_data (abfd)->stub, go32exe_default_stub,
    212 	      GO32EXE_DEFAULT_STUB_SIZE);
    213       coff_data (abfd)->stub_size = GO32EXE_DEFAULT_STUB_SIZE;
    214     }
    215 }
    216 
    217 /* If ibfd was a stubbed coff image, copy the stub from that bfd
    218    to the new obfd.  */
    219 
    220 static bool
    221 go32exe_copy_private_bfd_data (bfd *ibfd, bfd *obfd)
    222 {
    223   /* Check if both are the same targets.  */
    224   if (ibfd->xvec != obfd->xvec)
    225     return true;
    226 
    227   /* Make sure we have a source stub.  */
    228   BFD_ASSERT (coff_data (ibfd)->stub != NULL);
    229 
    230   /* Reallocate the output stub if necessary.  */
    231   if (coff_data (ibfd)->stub_size > coff_data (obfd)->stub_size)
    232     coff_data (obfd)->stub = bfd_alloc (obfd, coff_data (ibfd)->stub_size);
    233   if (coff_data (obfd)->stub == NULL)
    234     return false;
    235 
    236   /* Now copy the stub.  */
    237   memcpy (coff_data (obfd)->stub, coff_data (ibfd)->stub,
    238 	  coff_data (ibfd)->stub_size);
    239   coff_data (obfd)->stub_size = coff_data (ibfd)->stub_size;
    240   obfd->origin = coff_data (obfd)->stub_size;
    241 
    242   return true;
    243 }
    244 
    245 /* Cleanup function, returned from check_format hook.  */
    246 
    247 static void
    248 go32exe_cleanup (bfd *abfd)
    249 {
    250   abfd->origin = 0;
    251   coff_object_cleanup (abfd);
    252 
    253   free (go32exe_temp_stub);
    254   go32exe_temp_stub = NULL;
    255   go32exe_temp_stub_size = 0;
    256 }
    257 
    258 /* Check that there is a GO32 stub and read it to go32exe_temp_stub.
    259    Then set abfd->origin so that the COFF image is read at the correct
    260    file offset.  */
    261 
    262 static bfd_cleanup
    263 go32exe_check_format (bfd *abfd)
    264 {
    265   struct external_DOS_hdr filehdr_dos;
    266   uint16_t num_pages;
    267   uint16_t last_page_size;
    268   uint32_t header_end;
    269   bfd_size_type stubsize;
    270 
    271   /* This format can not appear in an archive.  */
    272   if (abfd->origin != 0)
    273     {
    274       bfd_set_error (bfd_error_wrong_format);
    275       return NULL;
    276     }
    277 
    278   bfd_set_error (bfd_error_system_call);
    279 
    280   /* Read in the stub file header, which is a DOS MZ executable.  */
    281   if (bfd_read (&filehdr_dos, DOS_HDR_SIZE, abfd) != DOS_HDR_SIZE)
    282     goto fail;
    283 
    284   /* Make sure that this is an MZ executable.  */
    285   if (H_GET_16 (abfd, filehdr_dos.e_magic) != IMAGE_DOS_SIGNATURE)
    286     goto fail_format;
    287 
    288   /* Determine the size of the stub  */
    289   num_pages = H_GET_16 (abfd, filehdr_dos.e_cp);
    290   last_page_size = H_GET_16 (abfd, filehdr_dos.e_cblp);
    291   stubsize = num_pages * 512;
    292   if (last_page_size != 0)
    293     stubsize += last_page_size - 512;
    294 
    295   ufile_ptr filesize = bfd_get_file_size (abfd);
    296   if (filesize != 0 && stubsize > filesize)
    297     goto fail_format;
    298 
    299   /* Save now the stub to be used later.  Put the stub data to a temporary
    300      location first as tdata still does not exist.  It may not even
    301      be ever created if we are just checking the file format of ABFD.  */
    302   if (bfd_seek (abfd, 0, SEEK_SET) != 0)
    303     goto fail;
    304   go32exe_temp_stub = bfd_malloc (stubsize);
    305   if (go32exe_temp_stub == NULL)
    306     goto fail;
    307   if (bfd_read (go32exe_temp_stub, stubsize, abfd) != stubsize)
    308     goto fail;
    309   go32exe_temp_stub_size = stubsize;
    310 
    311   /* Confirm that this is a go32stub.  */
    312   header_end = H_GET_16 (abfd, filehdr_dos.e_cparhdr) * 16UL;
    313   if (go32exe_temp_stub_size < header_end
    314       || go32exe_temp_stub_size - header_end < sizeof "go32stub" - 1
    315       || !startswith (go32exe_temp_stub + header_end, "go32stub"))
    316     goto fail_format;
    317 
    318   /* Set origin to where the COFF header starts and seek there.  */
    319   abfd->origin = stubsize;
    320   if (bfd_seek (abfd, 0, SEEK_SET) != 0)
    321     goto fail;
    322 
    323   /* Call coff_object_p to read the COFF image.  If this fails then the file
    324      must be just a stub with no COFF data attached.  */
    325   bfd_cleanup cleanup = coff_object_p (abfd);
    326   if (cleanup == NULL)
    327     goto fail;
    328   BFD_ASSERT (cleanup == coff_object_cleanup);
    329 
    330   return go32exe_cleanup;
    331 
    332  fail_format:
    333   bfd_set_error (bfd_error_wrong_format);
    334  fail:
    335   go32exe_cleanup (abfd);
    336   return NULL;
    337 }
    338 
    339 /* Write the stub to the output file, then call coff_write_object_contents.  */
    340 
    341 static bool
    342 go32exe_write_object_contents (bfd *abfd)
    343 {
    344   const bfd_size_type pos = bfd_tell (abfd);
    345   const bfd_size_type stubsize = coff_data (abfd)->stub_size;
    346 
    347   BFD_ASSERT (stubsize != 0);
    348 
    349   bfd_set_error (bfd_error_system_call);
    350 
    351   /* Write the stub.  */
    352   abfd->origin = 0;
    353   if (bfd_seek (abfd, 0, SEEK_SET) != 0)
    354     return false;
    355   if (bfd_write (coff_data (abfd)->stub, stubsize, abfd) != stubsize)
    356     return false;
    357 
    358   /* Seek back to where we were.  */
    359   abfd->origin = stubsize;
    360   if (bfd_seek (abfd, pos, SEEK_SET) != 0)
    361     return false;
    362 
    363   return coff_write_object_contents (abfd);
    364 }
    365 
    366 /* mkobject hook.  Called directly through bfd_set_format or via
    367    coff_mkobject_hook etc from bfd_check_format.  */
    368 
    369 static bool
    370 go32exe_mkobject (bfd *abfd)
    371 {
    372   /* Don't output to an archive.  */
    373   if (abfd->my_archive != NULL)
    374     return false;
    375 
    376   if (!_bfd_go32_mkobject (abfd))
    377     return false;
    378 
    379   go32exe_create_stub (abfd);
    380   if (coff_data (abfd)->stub == NULL)
    381     {
    382       bfd_release (abfd, coff_data (abfd));
    383       return false;
    384     }
    385   abfd->origin = coff_data (abfd)->stub_size;
    386 
    387   return true;
    388 }
    389