[2/3] coff-go32-exe: support variable-length stubs

Message ID 20200319191923.34747-3-jwjagersma@gmail.com
State New
Headers show
Series
  • coff-go32-exe: support variable-length stubs
Related show

Commit Message

H.J. Lu via Binutils March 19, 2020, 7:19 p.m.
The following patch removes the hardcoded stub size limit for the
coff-go32-exe format, by reading the size from the stub's own header,
and setting bfd->origin to the beginning of the COFF image.  This is
safe since a GO32 executable should never appear in an archive.

The special pointer adjustment code using COFF_ADJUST_* is removed, so
now coff-go32-exe images are handled just like regular x86 COFF images.
If the stub is removed, the output will be identical to coff-go32.

The stub is read in go32_check_format, which sets bfd->origin to the
COFF header location.  A cleanup function is returned which resets the
origin before checking other formats.
On output, the stub is first written in go32exe_write_object_contents,
which is the new write_contents hook for this format.  With bfd->origin
set, writing then proceeds as usual, with the file offset automatically
taken into account.

All the COFF_ADJUST_{FILEHDR,SCHHDR,AUX}_{IN,OUT}_{PRE,POST} hooks in
coffswap.h can probably be removed now, since they don't appear to be
used by other formats.

bfd/
2020-03-19  Jan W. Jagersma  <jwjagersma@gmail.com>

	* coff-i386.c: Don't include go32exe.h. Allow overriding
	coff_write_object_contents via COFF_WRITE_CONTENTS.
	* coff-stgo32.c (go32exe_cleanup, go32exe_mkobject)
	(go32exe_write_object_contents): New functions.
	(go32exe_temp_stub, go32exe_temp_stub_size): New static globals.
	(COFF_WRITE_CONTENTS, GO32EXE_DEFAULT_STUB_SIZE): Define.
	(create_go32_stub): Remove check for 2k size limit.  Read stub
	from go32exe_temp_stub if present.
	(go32_stubbed_coff_bfd_copy_private_bfd_data): Allocate and
	copy variable-length stub.
	(go32_check_format): Read stub to go32exe_temp_stub, set
	origin, return go32exe_cleanup.
	(adjust_filehdr_in_post, adjust_filehdr_out_pre)
	(adjust_filehdr_out_post, adjust_scnhdr_in_post)
	(adjust_scnhdr_out_pre, adjust_scnhdr_out_post)
	(adjust_aux_in_post, adjust_aux_out_pre, adjust_aux_out_post):
	Remove functions and their associated #defines.
	* coffcode.h (coff_mkobject_hook): Remove stub copying code.
	* libcoff-in.h: (struct coff_tdata): New field stub_size.
	Rename field go32stub to stub.
	* libcoff.h: Regenerate.

include/
2020-03-19  Jan W. Jagersma  <jwjagersma@gmail.com>

	* coff/go32exe.h: Remove file.
	* coff/internal.h (struct internal_filehdr): Remove field
	go32stub.  Remove flag F_GO32STUB.
---
 bfd/coff-i386.c         |  22 +-
 bfd/coff-stgo32.c       | 436 ++++++++++++++++++----------------------
 bfd/coffcode.h          |   9 -
 bfd/libcoff-in.h        |   8 +-
 include/coff/go32exe.h  |  36 ----
 include/coff/internal.h |  13 +-
 6 files changed, 211 insertions(+), 313 deletions(-)
 delete mode 100644 include/coff/go32exe.h

-- 
2.25.2

Patch

diff --git a/bfd/coff-i386.c b/bfd/coff-i386.c
index 7fb59dd31c..d3075f5a63 100644
--- a/bfd/coff-i386.c
+++ b/bfd/coff-i386.c
@@ -31,10 +31,6 @@ 
 #include "coff/pe.h"
 #endif
 
-#ifdef COFF_GO32_EXE
-#include "coff/go32exe.h"
-#endif
-
 #ifndef bfd_pe_print_pdata
 #define bfd_pe_print_pdata	NULL
 #endif
@@ -663,23 +659,21 @@  const bfd_target
      bfd_getl32, bfd_getl_signed_32, bfd_putl32,
      bfd_getl16, bfd_getl_signed_16, bfd_putl16, /* hdrs */
 
+#ifndef COFF_CHECK_FORMAT
+#define COFF_CHECK_FORMAT coff_object_p
+#endif
+#ifndef COFF_WRITE_CONTENTS
+#define COFF_WRITE_CONTENTS coff_write_object_contents
+#endif
+
 /* Note that we allow an object file to be treated as a core file as well.  */
 
-#ifdef COFF_CHECK_FORMAT
   {				/* bfd_check_format */
     _bfd_dummy_target,
     COFF_CHECK_FORMAT,
     bfd_generic_archive_p,
     COFF_CHECK_FORMAT
   },
-#else
-  {
-    _bfd_dummy_target,
-    coff_object_p,
-    bfd_generic_archive_p,
-    coff_object_p
-  },
-#endif
   {				/* bfd_set_format */
     _bfd_bool_bfd_false_error,
     coff_mkobject,
@@ -688,7 +682,7 @@  const bfd_target
   },
   {				/* bfd_write_contents */
     _bfd_bool_bfd_false_error,
-    coff_write_object_contents,
+    COFF_WRITE_CONTENTS,
     _bfd_write_archive_contents,
     _bfd_bool_bfd_false_error
   },
diff --git a/bfd/coff-stgo32.c b/bfd/coff-stgo32.c
index 676022872a..c24785ea2c 100644
--- a/bfd/coff-stgo32.c
+++ b/bfd/coff-stgo32.c
@@ -23,17 +23,10 @@ 
    DOS executable program before the coff image to load it in memory
    and execute it. This is needed, because DOS cannot run coff files.
 
-   All the functions below are called by the corresponding functions
-   from coffswap.h.
-   The only thing what they do is to adjust the information stored in
-   the COFF file which are offset into the file.
-   This is needed, because DJGPP uses a very special way to load and run
-   the coff image. It loads the image in memory and assumes then, that the
-   image had no stub by using the filepointers as pointers in the coff
-   image and NOT in the file.
-
-   To be compatible with any existing executables I have fixed this
-   here and NOT in the DJGPP startup code.  */
+   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"
@@ -55,51 +48,16 @@ 
 
 #include "sysdep.h"
 #include "bfd.h"
-
-/* All that ..._PRE and ...POST functions are called from the corresponding
-   coff_swap... functions. The ...PRE functions are called at the beginning
-   of the function and the ...POST functions at the end of the swap routines.  */
-
-static void
-adjust_filehdr_in_post  (bfd *, void *, void *);
-static void
-adjust_filehdr_out_pre  (bfd *, void *, void *);
-static void
-adjust_filehdr_out_post  (bfd *, void *, void *);
-static void
-adjust_scnhdr_in_post  (bfd *, void *, void *);
-static void
-adjust_scnhdr_out_pre  (bfd *, void *, void *);
-static void
-adjust_scnhdr_out_post (bfd *, void *, void *);
-static void
-adjust_aux_in_post (bfd *, void *, int, int, int, int, void *);
-static void
-adjust_aux_out_pre (bfd *, void *, int, int, int, int, void *);
-static void
-adjust_aux_out_post (bfd *, void *, int, int, int, int, void *);
-static void
-create_go32_stub (bfd *);
-
-#define COFF_ADJUST_FILEHDR_IN_POST adjust_filehdr_in_post
-#define COFF_ADJUST_FILEHDR_OUT_PRE adjust_filehdr_out_pre
-#define COFF_ADJUST_FILEHDR_OUT_POST adjust_filehdr_out_post
-
-#define COFF_ADJUST_SCNHDR_IN_POST adjust_scnhdr_in_post
-#define COFF_ADJUST_SCNHDR_OUT_PRE adjust_scnhdr_out_pre
-#define COFF_ADJUST_SCNHDR_OUT_POST adjust_scnhdr_out_post
-
-#define COFF_ADJUST_AUX_IN_POST adjust_aux_in_post
-#define COFF_ADJUST_AUX_OUT_PRE adjust_aux_out_pre
-#define COFF_ADJUST_AUX_OUT_POST adjust_aux_out_post
+#include "coff/msdos.h"
 
 static bfd_cleanup go32_check_format (bfd *);
+static bfd_boolean go32exe_write_object_contents (bfd *);
+static bfd_boolean go32exe_mkobject (bfd *);
+static bfd_boolean go32_stubbed_coff_bfd_copy_private_bfd_data (bfd *, bfd *);
 
 #define COFF_CHECK_FORMAT go32_check_format
-
-static bfd_boolean
-  go32_stubbed_coff_bfd_copy_private_bfd_data (bfd *, bfd *);
-
+#define COFF_WRITE_CONTENTS go32exe_write_object_contents
+#define coff_mkobject go32exe_mkobject
 #define coff_bfd_copy_private_bfd_data go32_stubbed_coff_bfd_copy_private_bfd_data
 
 #include "coff-i386.c"
@@ -110,160 +68,15 @@  static bfd_boolean
 
 /* These bytes are a 2048-byte DOS executable, which loads the COFF
    image into memory and then runs it. It is called 'stub'.  */
-
-static const unsigned char stub_bytes[GO32_STUBSIZE] =
+#define GO32EXE_DEFAULT_STUB_SIZE 2048
+static const unsigned char stub_bytes[GO32EXE_DEFAULT_STUB_SIZE] =
 {
 #include "go32stub.h"
 };
 
-/*
-   I have not commented each swap function below, because the
-   technique is in any function the same. For the ...in function,
-   all the pointers are adjusted by adding GO32_STUBSIZE and for the
-   ...out function, it is subtracted first and after calling the
-   standard swap function it is reset to the old value.  */
-
-/* This macro is used for adjusting the filepointers, which
-   is done only, if the pointer is nonzero.  */
-
-#define ADJUST_VAL(val,diff) \
-  if (val != 0) val += diff
-
-static void
-adjust_filehdr_in_post  (bfd *  abfd ATTRIBUTE_UNUSED,
-			 void * src,
-			 void * dst)
-{
-  FILHDR *filehdr_src = (FILHDR *) src;
-  struct internal_filehdr *filehdr_dst = (struct internal_filehdr *) dst;
-
-  ADJUST_VAL (filehdr_dst->f_symptr, GO32_STUBSIZE);
-
-  /* Save now the stub to be used later.  Put the stub data to FILEHDR_DST
-     first as coff_data (abfd) still does not exist.  It may not even be ever
-     created as we are just checking the file format of ABFD.  */
-  memcpy (filehdr_dst->go32stub, filehdr_src->stub, GO32_STUBSIZE);
-  filehdr_dst->f_flags |= F_GO32STUB;
-}
-
-static void
-adjust_filehdr_out_pre  (bfd * abfd, void * in, void * out)
-{
-  struct internal_filehdr *filehdr_in = (struct internal_filehdr *) in;
-  FILHDR *filehdr_out = (FILHDR *) out;
-
-  /* Generate the stub.  */
-  create_go32_stub (abfd);
-
-  /* Copy the stub to the file header.  */
-  if (coff_data (abfd)->go32stub != NULL)
-    memcpy (filehdr_out->stub, coff_data (abfd)->go32stub, GO32_STUBSIZE);
-  else
-    /* Use the default.  */
-    memcpy (filehdr_out->stub, stub_bytes, GO32_STUBSIZE);
-
-  ADJUST_VAL (filehdr_in->f_symptr, -GO32_STUBSIZE);
-}
-
-static void
-adjust_filehdr_out_post  (bfd *  abfd ATTRIBUTE_UNUSED,
-			  void * in,
-			  void * out ATTRIBUTE_UNUSED)
-{
-  struct internal_filehdr *filehdr_in = (struct internal_filehdr *) in;
-  /* Undo the above change.  */
-  ADJUST_VAL (filehdr_in->f_symptr, GO32_STUBSIZE);
-}
-
-static void
-adjust_scnhdr_in_post  (bfd *  abfd ATTRIBUTE_UNUSED,
-			void * ext ATTRIBUTE_UNUSED,
-			void * in)
-{
-  struct internal_scnhdr *scnhdr_int = (struct internal_scnhdr *) in;
-
-  ADJUST_VAL (scnhdr_int->s_scnptr, GO32_STUBSIZE);
-  ADJUST_VAL (scnhdr_int->s_relptr, GO32_STUBSIZE);
-  ADJUST_VAL (scnhdr_int->s_lnnoptr, GO32_STUBSIZE);
-}
-
-static void
-adjust_scnhdr_out_pre  (bfd *  abfd ATTRIBUTE_UNUSED,
-			void * in,
-			void * out ATTRIBUTE_UNUSED)
-{
-  struct internal_scnhdr *scnhdr_int = (struct internal_scnhdr *) in;
-
-  ADJUST_VAL (scnhdr_int->s_scnptr, -GO32_STUBSIZE);
-  ADJUST_VAL (scnhdr_int->s_relptr, -GO32_STUBSIZE);
-  ADJUST_VAL (scnhdr_int->s_lnnoptr, -GO32_STUBSIZE);
-}
-
-static void
-adjust_scnhdr_out_post (bfd *  abfd ATTRIBUTE_UNUSED,
-			void * in,
-			void * out ATTRIBUTE_UNUSED)
-{
-  struct internal_scnhdr *scnhdr_int = (struct internal_scnhdr *) in;
-
-  ADJUST_VAL (scnhdr_int->s_scnptr, GO32_STUBSIZE);
-  ADJUST_VAL (scnhdr_int->s_relptr, GO32_STUBSIZE);
-  ADJUST_VAL (scnhdr_int->s_lnnoptr, GO32_STUBSIZE);
-}
-
-static void
-adjust_aux_in_post (bfd * abfd ATTRIBUTE_UNUSED,
-		    void * ext1 ATTRIBUTE_UNUSED,
-		    int type,
-		    int in_class,
-		    int indx ATTRIBUTE_UNUSED,
-		    int numaux ATTRIBUTE_UNUSED,
-		    void * in1)
-{
-  union internal_auxent *in = (union internal_auxent *) in1;
-
-  if (in_class == C_BLOCK || in_class == C_FCN || ISFCN (type)
-      || ISTAG (in_class))
-    {
-      ADJUST_VAL (in->x_sym.x_fcnary.x_fcn.x_lnnoptr, GO32_STUBSIZE);
-    }
-}
-
-static void
-adjust_aux_out_pre (bfd *abfd ATTRIBUTE_UNUSED,
-		    void * inp,
-		    int type,
-		    int in_class,
-		    int indx ATTRIBUTE_UNUSED,
-		    int numaux ATTRIBUTE_UNUSED,
-		    void * extp ATTRIBUTE_UNUSED)
-{
-  union internal_auxent *in = (union internal_auxent *) inp;
-
-  if (in_class == C_BLOCK || in_class == C_FCN || ISFCN (type)
-      || ISTAG (in_class))
-    {
-      ADJUST_VAL (in->x_sym.x_fcnary.x_fcn.x_lnnoptr, -GO32_STUBSIZE);
-    }
-}
-
-static void
-adjust_aux_out_post (bfd *abfd ATTRIBUTE_UNUSED,
-		     void * inp,
-		     int type,
-		     int in_class,
-		     int indx ATTRIBUTE_UNUSED,
-		     int numaux ATTRIBUTE_UNUSED,
-		     void * extp ATTRIBUTE_UNUSED)
-{
-  union internal_auxent *in = (union internal_auxent *) inp;
-
-  if (in_class == C_BLOCK || in_class == C_FCN || ISFCN (type)
-      || ISTAG (in_class))
-    {
-      ADJUST_VAL (in->x_sym.x_fcnary.x_fcn.x_lnnoptr, GO32_STUBSIZE);
-    }
-}
+/* 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.
@@ -275,13 +88,16 @@  adjust_aux_out_post (bfd *abfd ATTRIBUTE_UNUSED,
    file.
 
    If there was any error, the standard stub (compiled in this file)
-   is taken.  */
+   is taken.
+
+   Ideally this function should exec '$(TARGET)-stubify' to generate
+   a stub, like gcc does.  */
 
 static void
 create_go32_stub (bfd *abfd)
 {
   /* Do it only once.  */
-  if (coff_data (abfd)->go32stub == NULL)
+  if (coff_data (abfd)->stub == NULL)
     {
       char *stub;
       struct stat st;
@@ -291,6 +107,22 @@  create_go32_stub (bfd *abfd)
       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).  */
@@ -323,13 +155,6 @@  create_go32_stub (bfd *abfd)
       if (_H (1))
 	coff_start += (long) _H (1) - 512L;
 
-      /* Currently there is only a fixed stub size of 2048 bytes
-	 supported.  */
-      if (coff_start != 2048)
-	{
-	  close (f);
-	  goto stub_end;
-	}
       exe_start = _H (4) * 16;
       if ((long) lseek (f, exe_start, SEEK_SET) != exe_start)
 	{
@@ -347,31 +172,35 @@  create_go32_stub (bfd *abfd)
 	  goto stub_end;
 	}
       /* Now we found a correct stub (hopefully).  */
-      coff_data (abfd)->go32stub = bfd_alloc (abfd, (bfd_size_type) coff_start);
-      if (coff_data (abfd)->go32stub == NULL)
+      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)->go32stub, coff_start)
+      if ((unsigned long) read (f, coff_data (abfd)->stub, coff_start)
 	  != coff_start)
 	{
-	  bfd_release (abfd, coff_data (abfd)->go32stub);
-	  coff_data (abfd)->go32stub = NULL;
+	  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)->go32stub == NULL)
+  if (coff_data (abfd)->stub == NULL)
     {
-      coff_data (abfd)->go32stub
-	= bfd_alloc (abfd, (bfd_size_type) GO32_STUBSIZE);
-      if (coff_data (abfd)->go32stub == 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)->go32stub, stub_bytes, GO32_STUBSIZE);
+      memcpy (coff_data (abfd)->stub, stub_bytes,
+	      GO32EXE_DEFAULT_STUB_SIZE);
+      coff_data (abfd)->stub_size = GO32EXE_DEFAULT_STUB_SIZE;
     }
 }
 
@@ -385,40 +214,169 @@  go32_stubbed_coff_bfd_copy_private_bfd_data (bfd *ibfd, bfd *obfd)
   if (ibfd->xvec != obfd->xvec)
     return TRUE;
 
-  /* Check if we have a source stub.  */
-  if (coff_data (ibfd)->go32stub == NULL)
-    return TRUE;
+  /* Make sure we have a source stub.  */
+  BFD_ASSERT (coff_data (ibfd)->stub != NULL);
 
-  /* As adjust_filehdr_out_pre may get called only after this function,
-     optionally allocate the output stub.  */
-  if (coff_data (obfd)->go32stub == NULL)
-    coff_data (obfd)->go32stub = bfd_alloc (obfd,
-					  (bfd_size_type) GO32_STUBSIZE);
+  /* Allocate the output stub.  */
+  coff_data (obfd)->stub = bfd_alloc (obfd, coff_data (ibfd)->stub_size);
+  if (coff_data (obfd)->stub == NULL)
+    return FALSE;
 
   /* Now copy the stub.  */
-  if (coff_data (obfd)->go32stub != NULL)
-    memcpy (coff_data (obfd)->go32stub, coff_data (ibfd)->go32stub,
-	    GO32_STUBSIZE);
+  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;
 }
 
-/* coff_object_p only checks 2 bytes F_MAGIC at GO32_STUBSIZE inside the file
-   which is too fragile.  */
+/* Cleanup function, returned from check_format hook.  */
+
+static void
+go32exe_cleanup (bfd *abfd)
+{
+  abfd->origin = 0;
+
+  if (go32exe_temp_stub != NULL)
+    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
 go32_check_format (bfd *abfd)
 {
-  char mz[2];
-
-  if (bfd_bread (mz, 2, abfd) != 2 || mz[0] != 'M' || mz[1] != 'Z')
+  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 (! CONST_STRNEQ (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 bfd_boolean
+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 NULL;
+    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 bfd_boolean
+go32exe_mkobject (bfd *abfd)
+{
+  coff_data_type *coff = NULL;
+  const bfd_size_type amt = sizeof (coff_data_type);
+
+  /* Don't output to an archive.  */
+  if (abfd->my_archive != NULL)
+    return FALSE;
+
+  abfd->tdata.coff_obj_data = bfd_zalloc (abfd, amt);
+  if (abfd->tdata.coff_obj_data == NULL)
+    return FALSE;
+  coff = coff_data (abfd);
+  coff->symbols = NULL;
+  coff->conversion_table = NULL;
+  coff->raw_syments = NULL;
+  coff->relocbase = 0;
+  coff->local_toc_sym_map = 0;
 
-  return coff_object_p (abfd);
+  create_go32_stub (abfd);
+  if (coff->stub == NULL)
+    {
+      bfd_release (abfd, coff);
+      return FALSE;
+    }
+  abfd->origin = coff->stub_size;
+
+/*  make_abs_section(abfd);*/ /* ??? */
+
+  return TRUE;
 }
diff --git a/bfd/coffcode.h b/bfd/coffcode.h
index 27158a061c..3bee5d2f9d 100644
--- a/bfd/coffcode.h
+++ b/bfd/coffcode.h
@@ -2076,15 +2076,6 @@  coff_mkobject_hook (bfd * abfd,
     abfd->flags |= HAS_DEBUG;
 #endif
 
-  if ((internal_f->f_flags & F_GO32STUB) != 0)
-    {
-      coff->go32stub = (char *) bfd_alloc (abfd, (bfd_size_type) GO32_STUBSIZE);
-      if (coff->go32stub == NULL)
-	return NULL;
-    }
-  if (coff->go32stub != NULL)
-    memcpy (coff->go32stub, internal_f->go32stub, GO32_STUBSIZE);
-
   return coff;
 }
 #endif
diff --git a/bfd/libcoff-in.h b/bfd/libcoff-in.h
index 3030a65fa7..858328a6f1 100644
--- a/bfd/libcoff-in.h
+++ b/bfd/libcoff-in.h
@@ -114,9 +114,11 @@  typedef struct coff_tdata
      used by ARM code.  */
   flagword flags;
 
-  /* coff-stgo32 EXE stub header after BFD tdata has been allocated.  Its data
-     is kept in internal_filehdr.go32stub beforehand.  */
-  char *go32stub;
+  /* A stub (extra data prepended before the COFF image) and its size.
+     Used by coff-go32-exe, it contains executable data that loads the
+     COFF object into memory.  */
+  char * stub;
+  bfd_size_type stub_size;
 } coff_data_type;
 
 /* Tdata for pe image files.  */
diff --git a/include/coff/go32exe.h b/include/coff/go32exe.h
deleted file mode 100644
index af6de2c759..0000000000
--- a/include/coff/go32exe.h
+++ /dev/null
@@ -1,36 +0,0 @@ 
-/* COFF information for PC running go32.
-
-   Copyright (C) 2001-2020 Free Software Foundation, Inc.
-
-   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.  */
-
-struct external_filehdr_go32_exe
-  {
-    char stub[GO32_STUBSIZE]; /* the stub to load the image */
-			/* the standard COFF header     */
-    char f_magic[2];	/* magic number			*/
-    char f_nscns[2];	/* number of sections		*/
-    char f_timdat[4];	/* time & date stamp		*/
-    char f_symptr[4];	/* file pointer to symtab	*/
-    char f_nsyms[4];	/* number of symtab entries	*/
-    char f_opthdr[2];	/* sizeof(optional hdr)		*/
-    char f_flags[2];	/* flags			*/
-  };
-
-#undef FILHDR
-#define	FILHDR	struct external_filehdr_go32_exe
-#undef FILHSZ
-#define	FILHSZ	GO32_STUBSIZE+20
diff --git a/include/coff/internal.h b/include/coff/internal.h
index 86fe07066a..2b6c08cb50 100644
--- a/include/coff/internal.h
+++ b/include/coff/internal.h
@@ -58,19 +58,10 @@  struct internal_extra_pe_filehdr
   bfd_vma  nt_signature;   	/* required NT signature, 0x4550 */
 };
 
-#define GO32_STUBSIZE 2048
-
 struct internal_filehdr
 {
   struct internal_extra_pe_filehdr pe;
 
-  /* coff-stgo32 EXE stub header before BFD tdata has been allocated.
-     Its data is kept in INTERNAL_FILEHDR.GO32STUB afterwards.
-
-     F_GO32STUB is set iff go32stub contains a valid data.  Artifical headers
-     created in BFD have no pre-set go32stub.  */
-  char go32stub[GO32_STUBSIZE];
-
   /* Standard coff internal info.  */
   unsigned short f_magic;	/* magic number			*/
   unsigned int   f_nscns;	/* number of sections		*/
@@ -93,8 +84,7 @@  struct internal_filehdr
  	F_AR32W		file is 32-bit big-endian
  	F_DYNLOAD	rs/6000 aix: dynamically loadable w/imports & exports
  	F_SHROBJ	rs/6000 aix: file is a shared object
-	F_DLL           PE format DLL
-	F_GO32STUB      Field go32stub contains valid data.  */
+	F_DLL           PE format DLL  */
 
 #define	F_RELFLG	(0x0001)
 #define	F_EXEC		(0x0002)
@@ -106,7 +96,6 @@  struct internal_filehdr
 #define	F_DYNLOAD	(0x1000)
 #define	F_SHROBJ	(0x2000)
 #define F_DLL           (0x2000)
-#define F_GO32STUB      (0x4000)
 
 /* Extra structure which is used in the optional header.  */
 typedef struct _IMAGE_DATA_DIRECTORY