This is the mail archive of the binutils@sourceware.org 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]
Other format: [Raw text]

RFC: Dumping of .pdata/.xdata for x64 PE+


Hello all,

I want to implement for binutils and gcc's 4.5 version the handling of
SEH for x64 windows PE format. So I implemented a dumping mechanism of
the .xdata structures for this OS. This patch isn't intended to be
approved as it is, it is more a request to review and discussion about
this subject. I would be happy if some people could comment on it.

As you know is SEH for x64 (like for ce) a structure based variant of
SEH and there isn't anymore the stack based variant present. There are
two different sections, which hold the required information. The
.pdata section and the .xdata section.

The major changes center around the approaches MS has taken to solve
the three major deficiencies found on x86 SEH.  First, it solved the
execution time overhead issue of adding and removing exception
handlers by moving all of the static exception handling information
into a static location in the binary.  This location, known as the
.pdata section, is accessable by the PE32+'s Exception Directory
Entry.  The structure of this section will be described in the
exception directory subsection of the pe-coff specification pretty
well.  By eliminating the need to add and remove exception handlers on
the fly, it has also eliminated the security issue found on x86 with
regard to overwriting the function pointer on stack of an exception
handler.  Perhaps most importantly, the process involved in unwinding
call frames has been drastically improved through the formalization of
the frame unwinding process itself.

The Exception Directory of a PE+ binary is used to convey the complete
list of functions that could be found during an unwind operation.
These functions are known as non-leaf functions, and they are
qualified as such if they either allocate space on the stack or call
other functions.  The IMAGE_RUNTIME_FUNCTION_ENTRY data structure is
used to describe the non-leaf functions, as shown below:

typedef struct _IMAGE_RUNTIME_FUNCTION_ENTRY {
    ULONG BeginAddress;
    ULONG EndAddress;
    ULONG UnwindInfoAddress;
} _IMAGE_RUNTIME_FUNCTION_ENTRY, *_PIMAGE_RUNTIME_FUNCTION_ENTRY;

The BeginAddress, EndAddress, and UnwindInfoAddress members are RVAs
pointing into the image.
The function described by an entry is in the range of BeginAddress <
EndAddress. To check if a PC is within a function can be check by
BeginAddress <= PC < EndAddress. The UnwindInfoAddress can have two
different meanings. First, it can be a reference to another runtime
function entry in .pdata. This is indicated, that the LSB is set to
one. Otherwise it points to an UNWIND_INFO structure (.xdata section,
which is normally put at link time into the .rdata section).

The UNWIND_INFO structure, describes a non-leaf function in terms of
its prologue size and frame register usage. And it describes the way
in which the stack is set up when the prologue for this non-leaf
function is executed.  This is done via an array of codes as accessed
through the UnwindCode array. This code information is store in
reverse order.

I put some further links to this e-mail, so that it is easier to find
the relevant information about this subject.

The biggest question for me (at the moment) are the language specific
termination handlers (flag 3). The data related to this entry kind
seems to be the establisher frame pointer. Also there are some unclear
points about the UnwindCode about ...LARGE elements. At top of MS
documentation the tell, that any element is scaled 8 (beside the XMM
ones, they are scaled 16), but in further documentation the tell that
the ...LARGE variant are describing it unscaled.

Cheers,
Kai

Bibliography

[1] Microsoft Corporation.  Exception Handling (x64).
    http://msdn2.microsoft.com/en-us/library/1eyas8tf(VS.80).aspx;

[2] Microsoft Corporation.  The Language Specific Handler.
    http://msdn2.microsoft.com/en-us/library/b6sf5kbd(VS.80).aspx;

[3] Microsoft Corporation.  Parameter Passing.
    http://msdn2.microsoft.com/en-us/library/zthk2dkh.aspx;

[4] Microsoft Corporation.  Register Usage.
    http://msdn2.microsoft.com/en-us/library/9z1stfyw(VS.80).aspx;

[5] Microsoft Corporation.  SEH in x86 Environments.
    http://msdn2.microsoft.com/en-US/library/ms253960.aspx;

[6] Microsoft Corporation.  Stack Usage.
    http://msdn2.microsoft.com/en-us/library/ew5tede7.aspx;

[7] Microsoft Corporation.  Unwind Data Definitions in C.
    http://msdn2.microsoft.com/en-us/library/ssa62fwe(VS.80).aspx;

-- 
|  (\_/) This is Bunny. Copy and paste
| (='.'=) Bunny into your signature to help
| (")_(") him gain world domination
Index: src/bfd/coff-x86_64.c
===================================================================
--- src.orig/bfd/coff-x86_64.c
+++ src/bfd/coff-x86_64.c
@@ -711,14 +711,9 @@ coff_amd64_is_local_label_name (bfd *abf
 
 #endif /* TARGET_UNDERSCORE */
 
-#ifdef PE
-#undef  bfd_pe_print_pdata
-#define bfd_pe_print_pdata   _bfd_pex64_print_pdata
-#else  /* PE */
 #ifndef bfd_pe_print_pdata
 #define bfd_pe_print_pdata   NULL
 #endif
-#endif /* PE */
 
 #include "coffcode.h"
 
Index: src/bfd/peXXigen.c
===================================================================
--- src.orig/bfd/peXXigen.c
+++ src/bfd/peXXigen.c
@@ -1897,6 +1897,327 @@ _bfd_XX_print_ce_compressed_pdata (bfd *
 
 #ifdef COFF_WITH_pex64
 /* The PE+ x64 variant.  */
+
+static const char *pex_regs[16] = {
+  "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
+  "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"
+};
+
+static void
+pep_xdata_print_uwd_codes(bfd_byte *dta, bfd_vma count, FILE *file, bfd_vma pc_addr)
+{
+  bfd_vma i;
+  bfd_vma tmp = 0;
+  bfd_byte *insns[256];
+  bfd_vma insns_count = 0;
+
+  if (!count || !dta)
+    return;
+  /* Sort array ascending. Note: it is stored in reveresed order.  */
+  for (i = 0; i < count; i++)
+    {
+      bfd_byte *t;
+      
+      t = insns[insns_count++] = &dta[i * 2];
+      switch (t[1] & 0xf)
+        {
+        case 0: case 2:
+	case 3: case 10:
+	  break;
+	case 1:
+	  if (((t[1]>>4)&0xf)==0)
+	    {
+	      i += 1;
+	      break;
+	    }
+	  else if (((t[1]>>4)&0xf)==1)
+	    {
+	      i += 2;
+	      break;
+	    }
+	  /* fall through.  */
+	default:
+	  fprintf (file, "\t contains unknown code (%u).\n", (unsigned int) (t[1]&0xf));
+	  return;
+	case 4: case 6: case 8:
+	  i++;
+	  break;
+	case 5: case 7: case 9:
+	  i += 2;
+	  break;
+        }
+    }
+  fprintf (file, "\t At pc 0x");
+  fprintf_vma (file, pc_addr);
+  fprintf (file, " there are the following saves (in logical order).\n");
+  for (i = insns_count; i > 0;)
+    {
+      --i;
+      dta = insns[i];
+      fprintf (file, "\t  insn ends at pc+0x%02x: ", (unsigned int) dta[0]);
+      switch (dta[1]&0xf) {
+	case 0: /* UWOP_PUSH_NONVOL.  */
+	  fprintf (file, "push %s.\n", pex_regs[((dta[1]>>4)&0xf)]);
+	  break;
+	case 1: /* UWOP_ALLOC_LARGE.  */
+	  if (((dta[1]>>4)&0xf) == 0)
+	    tmp = (bfd_vma) (*((unsigned short *)&dta[2]));
+	  else
+	    tmp = (bfd_vma) (*((unsigned int *)&dta[2]));
+	  tmp *= 8;
+	  fprintf (file, "save stack region of size 0x");
+	  fprintf_vma (file, tmp);
+	  fprintf (file,".\n");
+	  break;
+	case 2: /* UWOP_ALLOC_SMALL.  */
+	  tmp = (bfd_vma) ((dta[1]>>4)&0xf);
+	  tmp += 1;
+	  tmp *= 8;
+	  fprintf (file, "save stack region of size 0x");
+	  fprintf_vma (file, tmp);
+	  fprintf (file,".\n");
+	  break;
+	case 3:  /* UWOP_SET_FPREG.  */
+	  tmp = (bfd_vma) ((dta[1]>>4)&0xf);
+	  tmp *= 16;
+	  fprintf (file, "FPReg = (FrameReg) + 0x");
+	  fprintf_vma (file, tmp);
+	  fprintf (file, ".\n");
+	  break;
+	case 4: /* UWOP_SAVE_NONVOL.  */
+	  fprintf (file, "mov %s at 0x", pex_regs[((dta[1]>>4)&0xf)]);
+	  tmp = (bfd_vma) (*((unsigned short *) &dta[2]));
+	  tmp *= 8;
+	  fprintf_vma (file, tmp);
+	  fprintf (file, ".\n");
+	  break;
+	case 5: /* UWOP_SAVE_NONVOL_FAR.  */
+	  fprintf (file, "mov %s at 0x", pex_regs[((dta[1]>>4)&0xf)]);
+	  tmp = (bfd_vma) (*((unsigned int *) &dta[2]));
+	  fprintf_vma (file, tmp);
+	  fprintf (file, ".\n");
+	  break;
+	case 6: /* UWOP_SAVE_XMM.  */
+	  tmp = (bfd_vma) (*((unsigned short *) &dta[2]));
+	  tmp *= 8;
+	  fprintf (file, "mov mm%u at 0x", (unsigned int)((dta[1]>>4)&0xf));
+	  fprintf_vma (file, tmp);
+	  fprintf (file, ".\n");
+	  break;
+	case 7: /* UWOP_SAVE_XMM_FAR.  */
+	  tmp = (bfd_vma) (*((unsigned int *) &dta[2]));
+	  fprintf (file, "mov mm%u at 0x", (unsigned int)((dta[1]>>4)&0xf));
+	  fprintf_vma (file, tmp);
+	  fprintf (file, ".\n");
+	  break;
+	case 8: /* UWOP_SAVE_XMM128.  */
+	  tmp = (bfd_vma) (*((unsigned short *) &dta[2]));
+	  tmp *= 16;
+	  fprintf (file, "mov xmm%u at 0x", (unsigned int)((dta[1]>>4)&0xf));
+	  fprintf_vma (file, tmp);
+	  fprintf (file, ".\n");
+	  break;
+	case 9: /* UWOP_SAVE_XMM128_FAR.  */
+	  tmp = (bfd_vma) (*((unsigned int *) &dta[2]));
+	  fprintf (file, "mov xmm%u at 0x", (unsigned int)((dta[1]>>4)&0xf));
+	  fprintf_vma (file, tmp);
+	  fprintf (file, ".\n");
+	  break;
+	case 10: /* UWOP_PUSH_MACHFRAME.  */
+	  fprintf (file, "interrupt entry (SS, old RSP, EFLAGS, CS, RIP");
+	  if (((dta[1]>>4)&0xf) == 0)
+	    {
+	      fprintf (file, ")");
+	    }
+	  else if (((dta[1]>>4)&0xf) == 1)
+	    {
+	      fprintf (file, ",ErrorCode)");
+	    }
+	  else
+	    {
+	      fprintf (file, ", unknown(%u))", ((dta[1]>>4)&0xf));
+	    }
+	  fprintf (file,".\n");
+	  break;
+	default:
+	  fprintf (file, "unknown code %u.\n", (unsigned int) (dta[1]&0xf));
+	  break;
+      }
+    }
+}
+
+static asection *
+pep_get_section_by_rva (bfd *abfd, bfd_vma addr, const char *sec_name)
+{
+  asection *section = bfd_get_section_by_name (abfd, sec_name);
+  bfd_vma vsize;
+  bfd_size_type datasize = 0;
+
+  if (section == NULL
+      || coff_section_data (abfd, section) == NULL
+      || pei_section_data (abfd, section) == NULL)
+    return NULL;
+  vsize = section->vma - pe_data (abfd)->pe_opthdr.ImageBase;
+  datasize = section->size;
+  if (!datasize || vsize > addr || (vsize + datasize) < addr)
+    return NULL;
+  return section;
+}
+
+static void
+pep_dump_xdata (FILE *file, bfd *abfd, bfd_vma addr, bfd_vma pc_addr)
+{
+  asection *section = pep_get_section_by_rva (abfd, addr, ".rdata");
+  bfd_vma vsize;
+  bfd_byte *data = NULL;
+
+  if (!section)
+    section = pep_get_section_by_rva (abfd, addr, ".data");
+  if (!section)
+    section = pep_get_section_by_rva (abfd, addr, ".xdata");
+  if (!section)
+    {
+      section = pep_get_section_by_rva (abfd, addr, ".pdata");
+      if (section)
+        {
+	  addr &= ~1;
+	  fprintf (file, "\t Shares information with pdata element at 0x");
+	  fprintf_vma (file, addr + pe_data (abfd)->pe_opthdr.ImageBase);
+	  fprintf (file, ".\n");
+        }
+      return;
+    }
+  vsize = section->vma - pe_data (abfd)->pe_opthdr.ImageBase;
+  addr -= vsize;
+  if (bfd_malloc_and_get_section (abfd, section, &data))
+    {
+      bfd_vma version;
+      bfd_vma flags;
+      bfd_vma sizeof_prologue;
+      bfd_vma count_of_codes;
+      bfd_vma frame_register;
+      bfd_vma frame_offset;
+
+      /* Array of UNWIND_CODE with count_of_codes elements. This
+         this structure has byte size and the following definition:
+	  UBYTE CodeOffset;
+	  UBYTE UnwindOperationCode : 4;
+	  UBYTE OperationInfo : 4;
+       */
+      bfd_byte *unwind_code;
+      /* if flag has bit 0 set, there is an exception hander.  */
+      bfd_vma exception_handler = 0;
+      /* if flag has bit 2 set, there is a function entry present.  */
+      bfd_vma function_entry = 0;
+      /* if flag has bit 0 set, there is an additionally exception data present.  */
+
+      if (!data) return;
+      version = (bfd_vma) data[addr];
+      flags = (version >> 3) & 0x1f;
+      version &= 3;
+      sizeof_prologue = (bfd_vma) data[addr + 1];
+      count_of_codes = (bfd_vma) data[addr + 2];
+      frame_register = (bfd_vma) data[addr + 3];
+      frame_offset = (frame_register >> 4) & 0xf;
+      frame_register &= 0xf;
+      addr += 4;
+      unwind_code = &data[addr];
+      addr += ((count_of_codes + 1) & ~1) * 2;
+
+      if (version != 1)
+	fprintf (file, "\tVersion %u (unknown).\n", (unsigned int) version);
+      fprintf (file, "\tFlags: ");
+      switch ((flags & 0x1f))
+        {
+          case 0:
+	    fprintf (file, "none");
+	    break;
+	  case 1:
+	    fprintf (file, "UNW_FLAG_EHANDLER");
+	    break;
+	  case 2:
+	    fprintf (file, "UNW_FLAG_UHANDLER");
+	    break;
+	  case 3:
+	    fprintf (file, "UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER");
+	    break;
+	  case 4:
+	    fprintf (file, "UNW_FLAG_CHAININFO");
+	    break;
+	  default:
+	    fprintf (file, "unknown flags 0x%x", (unsigned int) flags);
+	    break;
+        }
+      fprintf (file, ".\n");
+      if (count_of_codes != 0)
+	fprintf (file, "\tEntry has %u codes.", (unsigned int) count_of_codes);
+      fprintf (file, "\tPrologue size: %u, Frame offset = 0x%x.\n",
+	       (unsigned int) sizeof_prologue, (unsigned int) frame_offset);
+      fprintf (file, "\tFrame register is %s.\n",
+	frame_register == 0 ? "CFA" : pex_regs[(unsigned int) frame_register]);
+
+      pep_xdata_print_uwd_codes (unwind_code, count_of_codes, file, pc_addr);
+
+      if ((flags & 1) != 0)
+        {
+	  exception_handler = bfd_get_32 (abfd, data + addr);
+          addr += 4;
+        }
+      else if ((flags & 2) != 0)
+	{
+	  exception_handler = bfd_get_32 (abfd, data + addr);
+          addr += 4;
+	}
+      else if ((flags & 4) != 0)
+        {
+	  function_entry = bfd_get_32 (abfd, data + addr);
+	  addr += 4; /* RUNTIME_FUNCTION * */
+        }
+      if ((flags & 3) == 3)
+	{
+	  fprintf (file, "\t Argument at Framebase + 0x%x",
+	    (unsigned int) bfd_get_32 (abfd, data + addr));
+	  if (bfd_get_32 (abfd, data + addr + 4) != 0
+	      || bfd_get_32 (abfd, data + addr + 8) != 0)
+	    fprintf (file, " and unknown data 0x%x, 0x%x",
+	    (unsigned int) bfd_get_32 (abfd, data + addr + 4),
+	    (unsigned int) bfd_get_32 (abfd, data + addr + 8));
+	  fprintf (file, ".\n");
+	  addr += 12; /* RUNTIME_FUNCTION */
+	}
+
+      if ((flags & 3) == 1)
+	fprintf (file, "\texception_handler at 0x%x.\n", (unsigned int) exception_handler);
+      else if ((flags & 4) != 0)
+	fprintf (file, "\tFunction Entry: 0x%x\n", (unsigned int) function_entry);
+      if ((flags & 2) != 0)
+	{
+	  fprintf (file, "\texception_handler/terminate at 0x%x.\n", (unsigned int) exception_handler);
+	}
+      if ((flags & 3) == 1 || (flags & 3) == 2)
+	{
+	  bfd_vma i;
+	  bfd_vma count = bfd_get_32 (abfd, data + addr);
+	  addr += 4;
+	  fprintf (file, "\t 0x%x # of scope(s)\n", (unsigned int) count);
+	  for (i = 0; i < count && i < 255; i++)
+	    {
+	      fprintf (file, "\t scope #%u: BeginAddress: 0x%x, EndAddress: 0x%x,\n"
+		"\t\tHandlerAddress:0x%x, JumpTarget:0x%x\n",(unsigned int) (i+1),
+		(unsigned int) bfd_get_32 (abfd, data + addr),
+		(unsigned int) bfd_get_32 (abfd, data + addr + 4),
+		(unsigned int) bfd_get_32 (abfd, data + addr + 8),
+		(unsigned int) bfd_get_32 (abfd, data + addr + 12));
+	      addr += 16;
+	    }
+	  if (i < count)
+	    fprintf (file, "\t ...\n");
+	}
+    }
+  if (data != NULL)
+    free (data);
+}
+
 bfd_boolean
 _bfd_pex64_print_pdata (bfd *abfd, void *vfile)
 {
@@ -1959,13 +2280,27 @@ _bfd_pex64_print_pdata (bfd *abfd, void 
       fputc (' ', file);
       fprintf_vma (file, i + section->vma);
       fprintf (file, ":\t");
+      begin_addr += pe_data (abfd)->pe_opthdr.ImageBase;
       fprintf_vma (file, begin_addr);
       fputc (' ', file);
+      end_addr += pe_data (abfd)->pe_opthdr.ImageBase;
       fprintf_vma (file, end_addr);
       fputc (' ', file);
       fprintf_vma (file, unwind_data_addr);
-
       fprintf (file, "\n");
+
+      if (unwind_data_addr != 0)
+	{
+	  if ((unwind_data_addr & 1) != 0)
+	    {
+	      unwind_data_addr &= ~1;
+	      fprintf (file, "\t shares information with pdata element at 0x");
+	      fprintf_vma (file, unwind_data_addr + pe_data (abfd)->pe_opthdr.ImageBase);
+	      fprintf (file, ".\n");
+	    }
+	  else
+	    pep_dump_xdata (file, abfd, unwind_data_addr, begin_addr);
+	}
     }
 
   free (data);
Index: src/bfd/pei-x86_64.c
===================================================================
--- src.orig/bfd/pei-x86_64.c
+++ src/bfd/pei-x86_64.c
@@ -25,9 +25,9 @@
 
 #define TARGET_SYM 		x86_64pei_vec
 #define TARGET_NAME 		"pei-x86-64"
-#define COFF_IMAGE_WITH_PE
 #define COFF_WITH_PE
 #define COFF_WITH_pex64
+#define COFF_IMAGE_WITH_PE
 #define PCRELOFFSET 		TRUE
 #define TARGET_UNDERSCORE 	'_'
 /* Long section names not allowed in executable images, only object files.  */
@@ -53,4 +53,6 @@
 { COFF_SECTION_NAME_PARTIAL_MATCH (".gnu.linkonce.wi."), \
   COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 0 }
 
+#define bfd_pe_print_pdata   _bfd_pex64_print_pdata
+
 #include "coff-x86_64.c"

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