|  | /* BFD back-end for Intel 386 COFF files (DJGPP variant with a stub). | 
|  | Copyright (C) 1997-2021 Free Software Foundation, Inc. | 
|  | Written by Robert Hoehne. | 
|  |  | 
|  | This file is part of BFD, the Binary File Descriptor library. | 
|  |  | 
|  | This program is free software; you can redistribute it and/or modify | 
|  | it under the terms of the GNU General Public License as published by | 
|  | the Free Software Foundation; either version 3 of the License, or | 
|  | (at your option) any later version. | 
|  |  | 
|  | This program is distributed in the hope that it will be useful, | 
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | GNU General Public License for more details. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License | 
|  | along with this program; if not, write to the Free Software | 
|  | Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, | 
|  | MA 02110-1301, USA.  */ | 
|  |  | 
|  | /* This file handles now also stubbed coff images. The stub is a small | 
|  | DOS executable program before the coff image to load it in memory | 
|  | and execute it. This is needed, because DOS cannot run coff files. | 
|  |  | 
|  | The COFF image is loaded in memory without the stub attached, so | 
|  | all offsets are relative to the beginning of the image, not the | 
|  | actual file.  We handle this in bfd by setting bfd->origin to where | 
|  | the COFF image starts.  */ | 
|  |  | 
|  | #define TARGET_SYM		i386_coff_go32stubbed_vec | 
|  | #define TARGET_NAME		"coff-go32-exe" | 
|  | #define TARGET_UNDERSCORE	'_' | 
|  | #define COFF_GO32_EXE | 
|  | #define COFF_LONG_SECTION_NAMES | 
|  | #define COFF_SUPPORT_GNU_LINKONCE | 
|  | #define COFF_LONG_FILENAMES | 
|  |  | 
|  | #define COFF_SECTION_ALIGNMENT_ENTRIES \ | 
|  | { COFF_SECTION_NAME_EXACT_MATCH (".data"), \ | 
|  | COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 4 }, \ | 
|  | { COFF_SECTION_NAME_EXACT_MATCH (".text"), \ | 
|  | COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 4 }, \ | 
|  | { COFF_SECTION_NAME_PARTIAL_MATCH (".debug"), \ | 
|  | COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 0 }, \ | 
|  | { COFF_SECTION_NAME_PARTIAL_MATCH (".gnu.linkonce.wi"), \ | 
|  | COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 0 } | 
|  |  | 
|  | /* Section contains extended relocations. */ | 
|  | #define IMAGE_SCN_LNK_NRELOC_OVFL (0x01000000) | 
|  |  | 
|  | #include "sysdep.h" | 
|  | #include "bfd.h" | 
|  | #include "coff/msdos.h" | 
|  |  | 
|  | static bfd_cleanup go32exe_check_format (bfd *); | 
|  | static bool go32exe_write_object_contents (bfd *); | 
|  | static bool go32exe_mkobject (bfd *); | 
|  | static bool go32exe_copy_private_bfd_data (bfd *, bfd *); | 
|  |  | 
|  | /* Defined in coff-go32.c.  */ | 
|  | bool _bfd_go32_mkobject (bfd *); | 
|  | void _bfd_go32_swap_scnhdr_in (bfd *, void *, void *); | 
|  | unsigned int _bfd_go32_swap_scnhdr_out (bfd *, void *, void *); | 
|  |  | 
|  | #define COFF_CHECK_FORMAT go32exe_check_format | 
|  | #define COFF_WRITE_CONTENTS go32exe_write_object_contents | 
|  | #define coff_mkobject go32exe_mkobject | 
|  | #define coff_bfd_copy_private_bfd_data go32exe_copy_private_bfd_data | 
|  | #define coff_SWAP_scnhdr_in _bfd_go32_swap_scnhdr_in | 
|  | #define coff_SWAP_scnhdr_out _bfd_go32_swap_scnhdr_out | 
|  |  | 
|  | #include "coff-i386.c" | 
|  |  | 
|  | /* This macro is used, because I cannot assume the endianness of the | 
|  | host system.  */ | 
|  | #define _H(index) (H_GET_16 (abfd, (header + index * 2))) | 
|  |  | 
|  | /* These bytes are a 2048-byte DOS executable, which loads the COFF | 
|  | image into memory and then runs it. It is called 'stub'.  */ | 
|  | #define GO32EXE_DEFAULT_STUB_SIZE 2048 | 
|  | static const unsigned char go32exe_default_stub[GO32EXE_DEFAULT_STUB_SIZE] = | 
|  | { | 
|  | #include "go32stub.h" | 
|  | }; | 
|  |  | 
|  | /* Temporary location for stub read from input file.  */ | 
|  | static char * go32exe_temp_stub = NULL; | 
|  | static bfd_size_type go32exe_temp_stub_size = 0; | 
|  |  | 
|  | /* That's the function, which creates the stub. There are | 
|  | different cases from where the stub is taken. | 
|  | At first the environment variable $(GO32STUB) is checked and then | 
|  | $(STUB) if it was not set. | 
|  | If it exists and points to a valid stub the stub is taken from | 
|  | that file. This file can be also a whole executable file, because | 
|  | the stub is computed from the exe information at the start of that | 
|  | file. | 
|  |  | 
|  | If there was any error, the standard stub (compiled in this file) | 
|  | is taken. | 
|  |  | 
|  | Ideally this function should exec '$(TARGET)-stubify' to generate | 
|  | a stub, like gcc does.  */ | 
|  |  | 
|  | static void | 
|  | go32exe_create_stub (bfd *abfd) | 
|  | { | 
|  | /* Do it only once.  */ | 
|  | if (coff_data (abfd)->stub == NULL) | 
|  | { | 
|  | char *stub; | 
|  | struct stat st; | 
|  | int f; | 
|  | unsigned char header[10]; | 
|  | char magic[8]; | 
|  | unsigned long coff_start; | 
|  | long exe_start; | 
|  |  | 
|  | /* If we read a stub from an input file, use that one.  */ | 
|  | if (go32exe_temp_stub != NULL) | 
|  | { | 
|  | coff_data (abfd)->stub = bfd_alloc (abfd, | 
|  | go32exe_temp_stub_size); | 
|  | if (coff_data (abfd)->stub == NULL) | 
|  | return; | 
|  | memcpy (coff_data (abfd)->stub, go32exe_temp_stub, | 
|  | go32exe_temp_stub_size); | 
|  | coff_data (abfd)->stub_size = go32exe_temp_stub_size; | 
|  | free (go32exe_temp_stub); | 
|  | go32exe_temp_stub = NULL; | 
|  | go32exe_temp_stub_size = 0; | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* Check at first the environment variable $(GO32STUB).  */ | 
|  | stub = getenv ("GO32STUB"); | 
|  | /* Now check the environment variable $(STUB).  */ | 
|  | if (stub == NULL) | 
|  | stub = getenv ("STUB"); | 
|  | if (stub == NULL) | 
|  | goto stub_end; | 
|  | if (stat (stub, &st) != 0) | 
|  | goto stub_end; | 
|  | #ifdef O_BINARY | 
|  | f = open (stub, O_RDONLY | O_BINARY); | 
|  | #else | 
|  | f = open (stub, O_RDONLY); | 
|  | #endif | 
|  | if (f < 0) | 
|  | goto stub_end; | 
|  | if (read (f, &header, sizeof (header)) < 0) | 
|  | { | 
|  | close (f); | 
|  | goto stub_end; | 
|  | } | 
|  | if (_H (0) != 0x5a4d)	/* It is not an exe file.  */ | 
|  | { | 
|  | close (f); | 
|  | goto stub_end; | 
|  | } | 
|  | /* Compute the size of the stub (it is every thing up | 
|  | to the beginning of the coff image).  */ | 
|  | coff_start = (long) _H (2) * 512L; | 
|  | if (_H (1)) | 
|  | coff_start += (long) _H (1) - 512L; | 
|  |  | 
|  | exe_start = _H (4) * 16; | 
|  | if ((long) lseek (f, exe_start, SEEK_SET) != exe_start) | 
|  | { | 
|  | close (f); | 
|  | goto stub_end; | 
|  | } | 
|  | if (read (f, &magic, 8) != 8) | 
|  | { | 
|  | close (f); | 
|  | goto stub_end; | 
|  | } | 
|  | if (! startswith (magic, "go32stub")) | 
|  | { | 
|  | close (f); | 
|  | goto stub_end; | 
|  | } | 
|  | /* Now we found a correct stub (hopefully).  */ | 
|  | coff_data (abfd)->stub = bfd_alloc (abfd, (bfd_size_type) coff_start); | 
|  | if (coff_data (abfd)->stub == NULL) | 
|  | { | 
|  | close (f); | 
|  | return; | 
|  | } | 
|  | lseek (f, 0L, SEEK_SET); | 
|  | if ((unsigned long) read (f, coff_data (abfd)->stub, coff_start) | 
|  | != coff_start) | 
|  | { | 
|  | bfd_release (abfd, coff_data (abfd)->stub); | 
|  | coff_data (abfd)->stub = NULL; | 
|  | } | 
|  | else | 
|  | coff_data (abfd)->stub_size = coff_start; | 
|  | close (f); | 
|  | } | 
|  | stub_end: | 
|  | /* There was something wrong above, so use now the standard builtin | 
|  | stub.  */ | 
|  | if (coff_data (abfd)->stub == NULL) | 
|  | { | 
|  | coff_data (abfd)->stub | 
|  | = bfd_alloc (abfd, (bfd_size_type) GO32EXE_DEFAULT_STUB_SIZE); | 
|  | if (coff_data (abfd)->stub == NULL) | 
|  | return; | 
|  | memcpy (coff_data (abfd)->stub, go32exe_default_stub, | 
|  | GO32EXE_DEFAULT_STUB_SIZE); | 
|  | coff_data (abfd)->stub_size = GO32EXE_DEFAULT_STUB_SIZE; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* If ibfd was a stubbed coff image, copy the stub from that bfd | 
|  | to the new obfd.  */ | 
|  |  | 
|  | static bool | 
|  | go32exe_copy_private_bfd_data (bfd *ibfd, bfd *obfd) | 
|  | { | 
|  | /* Check if both are the same targets.  */ | 
|  | if (ibfd->xvec != obfd->xvec) | 
|  | return true; | 
|  |  | 
|  | /* Make sure we have a source stub.  */ | 
|  | BFD_ASSERT (coff_data (ibfd)->stub != NULL); | 
|  |  | 
|  | /* Reallocate the output stub if necessary.  */ | 
|  | if (coff_data (ibfd)->stub_size > coff_data (obfd)->stub_size) | 
|  | coff_data (obfd)->stub = bfd_alloc (obfd, coff_data (ibfd)->stub_size); | 
|  | if (coff_data (obfd)->stub == NULL) | 
|  | return false; | 
|  |  | 
|  | /* Now copy the stub.  */ | 
|  | memcpy (coff_data (obfd)->stub, coff_data (ibfd)->stub, | 
|  | coff_data (ibfd)->stub_size); | 
|  | coff_data (obfd)->stub_size = coff_data (ibfd)->stub_size; | 
|  | obfd->origin = coff_data (obfd)->stub_size; | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /* Cleanup function, returned from check_format hook.  */ | 
|  |  | 
|  | static void | 
|  | go32exe_cleanup (bfd *abfd) | 
|  | { | 
|  | abfd->origin = 0; | 
|  |  | 
|  | free (go32exe_temp_stub); | 
|  | go32exe_temp_stub = NULL; | 
|  | go32exe_temp_stub_size = 0; | 
|  | } | 
|  |  | 
|  | /* Check that there is a GO32 stub and read it to go32exe_temp_stub. | 
|  | Then set abfd->origin so that the COFF image is read at the correct | 
|  | file offset.  */ | 
|  |  | 
|  | static bfd_cleanup | 
|  | go32exe_check_format (bfd *abfd) | 
|  | { | 
|  | struct external_DOS_hdr filehdr_dos; | 
|  | uint16_t num_pages; | 
|  | uint16_t last_page_size; | 
|  | uint32_t header_end; | 
|  | bfd_size_type stubsize; | 
|  |  | 
|  | /* This format can not appear in an archive.  */ | 
|  | if (abfd->origin != 0) | 
|  | { | 
|  | bfd_set_error (bfd_error_wrong_format); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | bfd_set_error (bfd_error_system_call); | 
|  |  | 
|  | /* Read in the stub file header, which is a DOS MZ executable.  */ | 
|  | if (bfd_bread (&filehdr_dos, DOS_HDR_SIZE, abfd) != DOS_HDR_SIZE) | 
|  | goto fail; | 
|  |  | 
|  | /* Make sure that this is an MZ executable.  */ | 
|  | if (H_GET_16 (abfd, filehdr_dos.e_magic) != IMAGE_DOS_SIGNATURE) | 
|  | goto fail_format; | 
|  |  | 
|  | /* Determine the size of the stub  */ | 
|  | num_pages = H_GET_16 (abfd, filehdr_dos.e_cp); | 
|  | last_page_size = H_GET_16 (abfd, filehdr_dos.e_cblp); | 
|  | stubsize = num_pages * 512; | 
|  | if (last_page_size != 0) | 
|  | stubsize += last_page_size - 512; | 
|  |  | 
|  | /* Save now the stub to be used later.  Put the stub data to a temporary | 
|  | location first as tdata still does not exist.  It may not even | 
|  | be ever created if we are just checking the file format of ABFD.  */ | 
|  | bfd_seek (abfd, 0, SEEK_SET); | 
|  | go32exe_temp_stub = bfd_malloc (stubsize); | 
|  | if (go32exe_temp_stub == NULL) | 
|  | goto fail; | 
|  | if (bfd_bread (go32exe_temp_stub, stubsize, abfd) != stubsize) | 
|  | goto fail; | 
|  | go32exe_temp_stub_size = stubsize; | 
|  |  | 
|  | /* Confirm that this is a go32stub.  */ | 
|  | header_end = H_GET_16 (abfd, filehdr_dos.e_cparhdr) * 16UL; | 
|  | if (go32exe_temp_stub_size < header_end | 
|  | || go32exe_temp_stub_size - header_end < sizeof "go32stub" - 1 | 
|  | || !startswith (go32exe_temp_stub + header_end, "go32stub")) | 
|  | goto fail_format; | 
|  |  | 
|  | /* Set origin to where the COFF header starts and seek there.  */ | 
|  | abfd->origin = stubsize; | 
|  | if (bfd_seek (abfd, 0, SEEK_SET) != 0) | 
|  | goto fail; | 
|  |  | 
|  | /* Call coff_object_p to read the COFF image.  If this fails then the file | 
|  | must be just a stub with no COFF data attached.  */ | 
|  | bfd_cleanup cleanup = coff_object_p (abfd); | 
|  | if (cleanup == NULL) | 
|  | goto fail; | 
|  | BFD_ASSERT (cleanup == _bfd_no_cleanup); | 
|  |  | 
|  | return go32exe_cleanup; | 
|  |  | 
|  | fail_format: | 
|  | bfd_set_error (bfd_error_wrong_format); | 
|  | fail: | 
|  | go32exe_cleanup (abfd); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /* Write the stub to the output file, then call coff_write_object_contents.  */ | 
|  |  | 
|  | static bool | 
|  | go32exe_write_object_contents (bfd *abfd) | 
|  | { | 
|  | const bfd_size_type pos = bfd_tell (abfd); | 
|  | const bfd_size_type stubsize = coff_data (abfd)->stub_size; | 
|  |  | 
|  | BFD_ASSERT (stubsize != 0); | 
|  |  | 
|  | bfd_set_error (bfd_error_system_call); | 
|  |  | 
|  | /* Write the stub.  */ | 
|  | abfd->origin = 0; | 
|  | if (bfd_seek (abfd, 0, SEEK_SET) != 0) | 
|  | return false; | 
|  | if (bfd_bwrite (coff_data (abfd)->stub, stubsize, abfd) != stubsize) | 
|  | return false; | 
|  |  | 
|  | /* Seek back to where we were.  */ | 
|  | abfd->origin = stubsize; | 
|  | if (bfd_seek (abfd, pos, SEEK_SET) != 0) | 
|  | return false; | 
|  |  | 
|  | return coff_write_object_contents (abfd); | 
|  | } | 
|  |  | 
|  | /* mkobject hook.  Called directly through bfd_set_format or via | 
|  | coff_mkobject_hook etc from bfd_check_format.  */ | 
|  |  | 
|  | static bool | 
|  | go32exe_mkobject (bfd *abfd) | 
|  | { | 
|  | /* Don't output to an archive.  */ | 
|  | if (abfd->my_archive != NULL) | 
|  | return false; | 
|  |  | 
|  | if (!_bfd_go32_mkobject (abfd)) | 
|  | return false; | 
|  |  | 
|  | go32exe_create_stub (abfd); | 
|  | if (coff_data (abfd)->stub == NULL) | 
|  | { | 
|  | bfd_release (abfd, coff_data (abfd)); | 
|  | return false; | 
|  | } | 
|  | abfd->origin = coff_data (abfd)->stub_size; | 
|  |  | 
|  | return true; | 
|  | } |