This is the mail archive of the binutils@sourceware.cygnus.com mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Fix for winnt PE bfd problem


A couple of days ago I posted about a problem in BFD with reading
some winnt PE format executables. A fix follows.

The problem was related to the fact that PEI format executable
headers are permitted to have a variable length which is indicated by
a field in the fixed-length portion. Nearly all winnt exe files to
date use an identical length for this supposedly variable-length
header, but some recent ones I've come across have differed, breaking
the existing code. There was a self-described hack in the code which
attempted to deal with this case but which didn't work.

I've replaced the hack with a more elegant approach and have tried to
clean up some of the related code in the process.

Zik


bfd/ChangeLog:
----------------------------------------------------------------------------------
Wed Apr 26 23:37:15 2000  Zik Saleeba  (zik@zikzak.net)

        * coffgen.c, peicode.h, peigen.c: now allows for the fact that 
	winnt PE exe files have variable-length headers.

----------------------------------------------------------------------------------

include/coff/ChangeLog:
----------------------------------------------------------------------------------
Wed Apr 26 23:37:17 2000  Zik Saleeba  (zik@zikzak.net)

        * pe.h: now allows for the fact that winnt PE exe files have 
	variable-length headers.

----------------------------------------------------------------------------------



diff -rup binutils/src/bfd/coffgen.c binutils-fixed/src/bfd/coffgen.c
--- binutils/src/bfd/coffgen.c	Fri Apr  7 10:58:06 2000
+++ binutils-fixed/src/bfd/coffgen.c	Wed Apr 26 23:24:07 2000
@@ -255,9 +255,38 @@ coff_object_p (abfd)
   PTR filehdr;
   struct internal_filehdr internal_f;
   struct internal_aouthdr internal_a;
+  int lfa_offset = (char *)&internal_f.pe.e_lfanew - (char *)&internal_f;  /* will be 0x3c */;
 
   /* figure out how much to read */
-  filhsz = bfd_coff_filhsz (abfd);
+  if (strncmp(abfd->xvec->name, "pei-", 4) == 0)  /* XXX - is there a better way of identifying pei? */
+    {
+      /* NOTE - we use target-specific code here since there 
+       * seems no better place to handle PEI variable-length headers */
+      bfd_byte buffer[4];
+      
+      /* for PEI files the lfa longword at 0x3c tells us where the coff header starts */
+      if (bfd_seek (abfd, lfa_offset, SEEK_SET) != 0
+          || bfd_read (buffer, 1, 4, abfd) != 4)
+        {
+          if (bfd_get_error () != bfd_error_system_call)
+	    bfd_set_error (bfd_error_wrong_format);
+          return NULL;
+        }
+
+      filhsz = bfd_h_get_32 (abfd, buffer) + ((char *)&internal_f.f_target_id - (char *)&internal_f.pe.nt_signature);  /* 24 bytes from the nt signature to the end of the coff header */;
+      
+      /* go back to the start */
+      if (bfd_seek (abfd, 0, SEEK_SET) != 0)
+        {
+          if (bfd_get_error () != bfd_error_system_call)
+	    bfd_set_error (bfd_error_wrong_format);
+          return NULL;
+        }
+
+    }
+  else
+    filhsz = bfd_coff_filhsz (abfd);
+    
   aoutsz = bfd_coff_aoutsz (abfd);
 
   filehdr = bfd_alloc (abfd, filhsz);
diff -rup binutils/src/bfd/peicode.h binutils-fixed/src/bfd/peicode.h
--- binutils/src/bfd/peicode.h	Thu Apr 13 11:08:04 2000
+++ binutils-fixed/src/bfd/peicode.h	Wed Apr 26 23:24:07 2000
@@ -202,13 +202,26 @@ coff_swap_filehdr_in (abfd, src, dst)
 {
   FILHDR *filehdr_src = (FILHDR *) src;
   struct internal_filehdr *filehdr_dst = (struct internal_filehdr *) dst;
-  filehdr_dst->f_magic = bfd_h_get_16(abfd, (bfd_byte *) filehdr_src->f_magic);
-  filehdr_dst->f_nscns = bfd_h_get_16(abfd, (bfd_byte *)filehdr_src-> f_nscns);
-  filehdr_dst->f_timdat = bfd_h_get_32(abfd, (bfd_byte *)filehdr_src-> f_timdat);
-
-  filehdr_dst->f_nsyms = bfd_h_get_32(abfd, (bfd_byte *)filehdr_src-> f_nsyms);
-  filehdr_dst->f_flags = bfd_h_get_16(abfd, (bfd_byte *)filehdr_src-> f_flags);
-  filehdr_dst->f_symptr = bfd_h_get_32 (abfd, (bfd_byte *) filehdr_src->f_symptr);
+  
+#if defined(FILHDRNT) && defined(COFF_IMAGE_WITH_PE)
+  unsigned short start_magic = bfd_h_get_16 (abfd, (bfd_byte *) filehdr_src->e_magic);
+  unsigned long lfa_offset = bfd_h_get_32(abfd, (bfd_byte *)filehdr_src-> e_lfanew);
+  FILHDRNT *ntfilehdr_src;
+  if (start_magic == DOSMAGIC && lfa_offset < 0x1000)   /* sanity check */
+    ntfilehdr_src = (FILHDRNT *)(src + lfa_offset);
+  else
+    ntfilehdr_src = (FILHDRNT *)filehdr_src->nt_hdr.nt_signature;
+#else
+  FILHDR *ntfilehdr_src = filehdr_src;
+#endif  
+  
+  filehdr_dst->f_magic = bfd_h_get_16(abfd, (bfd_byte *)ntfilehdr_src->f_magic);
+  filehdr_dst->f_nscns = bfd_h_get_16(abfd, (bfd_byte *)ntfilehdr_src->f_nscns);
+  filehdr_dst->f_timdat = bfd_h_get_32(abfd, (bfd_byte *)ntfilehdr_src->f_timdat);
+
+  filehdr_dst->f_nsyms = bfd_h_get_32(abfd, (bfd_byte *)ntfilehdr_src->f_nsyms);
+  filehdr_dst->f_flags = bfd_h_get_16(abfd, (bfd_byte *)ntfilehdr_src->f_flags);
+  filehdr_dst->f_symptr = bfd_h_get_32 (abfd, (bfd_byte *)ntfilehdr_src->f_symptr);
 
 #ifdef COFF_IMAGE_WITH_PE
   /* There are really two magic numbers involved; the magic number
@@ -221,7 +234,7 @@ coff_swap_filehdr_in (abfd, src, dst)
      correctly for a PEI file, check the e_magic number here, and, if
      it doesn't match, clobber the f_magic number so that we don't get
      a false match.  */
-  if (bfd_h_get_16 (abfd, (bfd_byte *) filehdr_src->e_magic) != DOSMAGIC)
+  if (start_magic != DOSMAGIC)
     filehdr_dst->f_magic = -1;
 #endif
 
@@ -234,7 +247,7 @@ coff_swap_filehdr_in (abfd, src, dst)
     }
 
   filehdr_dst->f_opthdr = bfd_h_get_16(abfd, 
-				       (bfd_byte *)filehdr_src-> f_opthdr);
+				       (bfd_byte *)ntfilehdr_src->f_opthdr);
 }
 
 #ifdef COFF_IMAGE_WITH_PE
@@ -1264,6 +1277,8 @@ pe_bfd_object_p (bfd * abfd)
   bfd_byte buffer[4];
   file_ptr offset;
   unsigned long signature;
+  struct external_PEI_filehdr pe_hdr;
+  int lfa_offset;
 
   /* Detect if this a Microsoft Import Library Format element.  */
   if (bfd_seek (abfd, 0x00, SEEK_SET) != 0
@@ -1279,7 +1294,8 @@ pe_bfd_object_p (bfd * abfd)
   if (signature == 0xffff0000)
     return pe_ILF_object_p (abfd);
   
-  if (bfd_seek (abfd, 0x3c, SEEK_SET) != 0
+  lfa_offset = pe_hdr.e_lfanew - (char *)&pe_hdr;  /* will be 0x3c */
+  if (bfd_seek (abfd, lfa_offset, SEEK_SET) != 0
       || bfd_read (buffer, 1, 4, abfd) != 4)
     {
       if (bfd_get_error () != bfd_error_system_call)
@@ -1299,21 +1315,14 @@ pe_bfd_object_p (bfd * abfd)
 
   signature = bfd_h_get_32 (abfd, buffer);
 
-  if (signature != 0x4550)
+  if (signature != NT_SIGNATURE)   /* will be 0x00004550 */
     {
       bfd_set_error (bfd_error_wrong_format);
       return NULL;
     }
   
-  /* Here is the hack.  coff_object_p wants to read filhsz bytes to
-     pick up the COFF header.  We adjust so that that will work.  20
-     is the size of the i386 COFF filehdr.  */
-  if (bfd_seek (abfd,
-		(bfd_tell (abfd)
-		 - bfd_coff_filhsz (abfd)
-		 + 20),
-		SEEK_SET)
-      != 0)
+  /* return to the start of the file */
+  if (bfd_seek (abfd, 0x0, SEEK_SET) != 0)
     {
       if (bfd_get_error () != bfd_error_system_call)
 	bfd_set_error (bfd_error_wrong_format);
diff -rup binutils/src/bfd/peigen.c binutils-fixed/src/bfd/peigen.c
--- binutils/src/bfd/peigen.c	Tue Apr 18 14:03:16 2000
+++ binutils-fixed/src/bfd/peigen.c	Wed Apr 26 23:24:07 2000
@@ -787,15 +787,15 @@ _bfd_pei_only_swap_filehdr_out (abfd, in
 
 
 
-  bfd_h_put_16(abfd, filehdr_in->f_magic, (bfd_byte *) filehdr_out->f_magic);
-  bfd_h_put_16(abfd, filehdr_in->f_nscns, (bfd_byte *) filehdr_out->f_nscns);
+  bfd_h_put_16(abfd, filehdr_in->f_magic, (bfd_byte *) filehdr_out->nt_hdr.f_magic);
+  bfd_h_put_16(abfd, filehdr_in->f_nscns, (bfd_byte *) filehdr_out->nt_hdr.f_nscns);
 
-  bfd_h_put_32(abfd, time (0), (bfd_byte *) filehdr_out->f_timdat);
+  bfd_h_put_32(abfd, time (0), (bfd_byte *) filehdr_out->nt_hdr.f_timdat);
   PUT_FILEHDR_SYMPTR (abfd, (bfd_vma) filehdr_in->f_symptr,
-		      (bfd_byte *) filehdr_out->f_symptr);
-  bfd_h_put_32(abfd, filehdr_in->f_nsyms, (bfd_byte *) filehdr_out->f_nsyms);
-  bfd_h_put_16(abfd, filehdr_in->f_opthdr, (bfd_byte *) filehdr_out->f_opthdr);
-  bfd_h_put_16(abfd, filehdr_in->f_flags, (bfd_byte *) filehdr_out->f_flags);
+		      (bfd_byte *) filehdr_out->nt_hdr.f_symptr);
+  bfd_h_put_32(abfd, filehdr_in->f_nsyms, (bfd_byte *) filehdr_out->nt_hdr.f_nsyms);
+  bfd_h_put_16(abfd, filehdr_in->f_opthdr, (bfd_byte *) filehdr_out->nt_hdr.f_opthdr);
+  bfd_h_put_16(abfd, filehdr_in->f_flags, (bfd_byte *) filehdr_out->nt_hdr.f_flags);
 
   /* put in extra dos header stuff.  This data remains essentially
      constant, it just has to be tacked on to the beginning of all exes
@@ -843,7 +843,7 @@ _bfd_pei_only_swap_filehdr_out (abfd, in
 
   /* also put in the NT signature */
   bfd_h_put_32(abfd, filehdr_in->pe.nt_signature,
-	       (bfd_byte *) filehdr_out->nt_signature);
+	       (bfd_byte *) filehdr_out->nt_hdr.nt_signature);
 
 
 
diff -rup binutils/src/include/coff/pe.h binutils-fixed/src/include/coff/pe.h
--- binutils/src/include/coff/pe.h	Tue Apr 18 09:45:22 2000
+++ binutils-fixed/src/include/coff/pe.h	Wed Apr 26 23:27:42 2000
@@ -108,6 +108,21 @@
 #undef  FILNMLEN
 #define FILNMLEN	18	/* # characters in a file name		*/
 
+struct external_PEI_ntfilehdr
+{
+  char nt_signature[4];		/* required NT signature, 0x4550 */ 
+
+  /* From standard 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			*/
+};
+
 struct external_PEI_filehdr
 {
   /* DOS header fields */
@@ -131,17 +146,12 @@ struct external_PEI_filehdr
   char e_res2[10][2];		/* Reserved words, all 0x0 */
   char e_lfanew[4];		/* File address of new exe header, 0x80 */
   char dos_message[16][4];	/* other stuff, always follow DOS header */
-  char nt_signature[4];		/* required NT signature, 0x4550 */ 
-
-  /* From standard 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			*/
+  
+                                /* the NT COFF header (used here when writing a 
+                                 * fixed-size header, otherwise ignored since 
+                                 * this header can move around depending 
+                                 * on e_lfanew) */
+  struct external_PEI_ntfilehdr nt_hdr; 
 };
 
 #ifdef COFF_IMAGE_WITH_PE
@@ -150,6 +160,7 @@ struct external_PEI_filehdr
 
 #undef  FILHDR
 #define FILHDR struct external_PEI_filehdr
+#define FILHDRNT struct external_PEI_ntfilehdr
 #undef  FILHSZ
 #define FILHSZ 152
 

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]