This is the mail archive of the gdb-patches@sources.redhat.com mailing list for the GDB 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]

[RFA] dwarf2cfi.c improvements


Hi all,
this (quite long) patch addresses following issues in dwarf2cfi.c:
1) Read both .debug_frame and .eh_frame if they are present. Not all 
frames must be covered in both frames. Example: imagine an exec linked 
from file1.o that was compiled with -g and file2.o that was compiled 
without it. Than old gdb would parse only a .debug_frame that came from 
file1.o and wouldn't have any frame info for code from file2.o that 
provided only the .eh_frame. That's the reason why to parse both of them.
2) Of course we shouldn't have duplicate FDEs (one from .debug_frame and 
one from .eh_frame). If the file contains only an .eh_frame it's easy, 
there can't be duplicates, but if we parse both sections, we must check 
fde_chunks before adding new FDE from eh_frame. (see comments inside the 
patch).
3) The original dwarf2cfi.c didn't implement CIE Augmentation correctly. 
The main problem was a non-standard addressing (aug. 'R') that I had to 
rewrite completely. This fixes many problems with backtraces through 
relocatable (-fPIC compiled) objects without .debug_info.
4) parse_frame_info now also recognises aug. 'L' (= LSDA pointer present 
in FDE) but I was told I don't need LSDA in GDB, so I ignore its content.

2002-05-31  Michal Ludvig  <mludvig@suse.cz>

         * dwarf2cfi.c (dwarf_frame_buffer): Delete.
         (read_encoded_pointer): Add new argument,
         warn on indirect pointers.
         (execute_cfa_program): Warn on relative addressing,
         change messages.
         (frame_state_for, cfi_get_saved_register)
         (cfi_virtual_frame_pointer): Change message.
         (dwarf2_build_frame_info): Only call parse_frame_info
         for .debug_frame and .eh_frame.
         (parse_frame_info): New, derived from dwarf2_build_frame_info,
         fixed augmentation handling, added relative addressing,
         ignore duplicate FDEs. Added comments.

OK to commit?

Michal Ludvig
-- 
* SuSE CR, s.r.o     * mludvig@suse.cz
* +420 2 9654 5373   * http://www.suse.cz
Index: dwarf2cfi.c
===================================================================
RCS file: /cvs/src/src/gdb/dwarf2cfi.c,v
retrieving revision 1.7
diff -c -3 -p -r1.7 dwarf2cfi.c
*** dwarf2cfi.c	22 May 2002 13:21:19 -0000	1.7
--- dwarf2cfi.c	31 May 2002 15:15:18 -0000
*************** extern file_ptr dwarf_frame_offset;
*** 188,195 ****
  extern unsigned int dwarf_frame_size;
  extern file_ptr dwarf_eh_frame_offset;
  extern unsigned int dwarf_eh_frame_size;
- 
- static char *dwarf_frame_buffer;
  
  
  extern char *dwarf2_read_section (struct objfile *objfile, file_ptr offset,
--- 188,193 ----
*************** static ULONGEST read_uleb128 (bfd *abfd,
*** 217,223 ****
  static LONGEST read_sleb128 (bfd *abfd, char **p);
  static CORE_ADDR read_pointer (bfd *abfd, char **p);
  static CORE_ADDR read_encoded_pointer (bfd *abfd, char **p,
! 				       unsigned char encoding);
  
  static LONGEST read_initial_length (bfd *abfd, char *buf, int *bytes_read);
  static ULONGEST read_length (bfd *abfd, char *buf, int *bytes_read,
--- 215,221 ----
  static LONGEST read_sleb128 (bfd *abfd, char **p);
  static CORE_ADDR read_pointer (bfd *abfd, char **p);
  static CORE_ADDR read_encoded_pointer (bfd *abfd, char **p,
! 			unsigned char encoding, int *flag_pcrel);
  
  static LONGEST read_initial_length (bfd *abfd, char *buf, int *bytes_read);
  static ULONGEST read_length (bfd *abfd, char *buf, int *bytes_read,
*************** read_pointer (bfd *abfd, char **p)
*** 487,496 ****
  }
  
  static CORE_ADDR
! read_encoded_pointer (bfd *abfd, char **p, unsigned char encoding)
  {
    CORE_ADDR ret;
! 
    switch (encoding & 0x0f)
      {
      case DW_EH_PE_absptr:
--- 485,497 ----
  }
  
  static CORE_ADDR
! read_encoded_pointer (bfd *abfd, char **p, unsigned char encoding, 
! 	int *flag_pcrel)
  {
    CORE_ADDR ret;
!   
!   if (flag_pcrel) *flag_pcrel = 0;
!   
    switch (encoding & 0x0f)
      {
      case DW_EH_PE_absptr:
*************** read_encoded_pointer (bfd *abfd, char **
*** 526,549 ****
  
      default:
        internal_error (__FILE__, __LINE__,
! 		      "read_encoded_pointer: unknown pointer encoding");
      }
  
    if (ret != 0)
!     switch (encoding & 0xf0)
        {
        case DW_EH_PE_absptr:
  	break;
        case DW_EH_PE_pcrel:
! 	ret += (CORE_ADDR) *p;
  	break;
        case DW_EH_PE_textrel:
        case DW_EH_PE_datarel:
        case DW_EH_PE_funcrel:
        default:
  	internal_error (__FILE__, __LINE__,
! 			"read_encoded_pointer: unknown pointer encoding");
        }
  
    return ret;
  }
--- 527,560 ----
  
      default:
        internal_error (__FILE__, __LINE__,
! 		      "%s: unknown pointer encoding: 0x%x",
! 		      __func__, encoding);
      }
  
    if (ret != 0)
!   {
!     switch (encoding & 0x70)
        {
        case DW_EH_PE_absptr:
  	break;
        case DW_EH_PE_pcrel:
! 	if (flag_pcrel) *flag_pcrel = 1;
  	break;
        case DW_EH_PE_textrel:
        case DW_EH_PE_datarel:
        case DW_EH_PE_funcrel:
        default:
  	internal_error (__FILE__, __LINE__,
! 			"%s: unknown pointer encoding: 0x%x",
! 			__func__, encoding);
        }
+     
+     if (encoding & DW_EH_PE_indirect)
+     {
+       warning ("%s: Unsupported pointer encoding: DW_EH_PE_indirect", 
+       	__func__);
+     }
+   }
  
    return ret;
  }
*************** execute_cfa_program ( struct objfile *ob
*** 597,602 ****
--- 608,614 ----
        unsigned char insn = *insn_ptr++;
        ULONGEST reg, uoffset;
        LONGEST offset;
+       int flag_pcrel;
  
        if (insn & DW_CFA_advance_loc)
  	fs->pc += (insn & 0x3f) * fs->code_align;
*************** execute_cfa_program ( struct objfile *ob
*** 618,624 ****
  	  {
  	  case DW_CFA_set_loc:
  	    fs->pc = read_encoded_pointer (objfile->obfd, &insn_ptr,
! 					   fs->addr_encoding);
  	    break;
  
  	  case DW_CFA_advance_loc1:
--- 630,642 ----
  	  {
  	  case DW_CFA_set_loc:
  	    fs->pc = read_encoded_pointer (objfile->obfd, &insn_ptr,
! 					   fs->addr_encoding, &flag_pcrel);
! 	    if (flag_pcrel)
! 	    {
! 	      warning ("%s: DW_CFA_set_loc uses relative addressing at pc=%p", 
! 	      	__func__, (void*)fs->pc);
! 	      fs->pc += (CORE_ADDR)insn_ptr;
! 	    }
  	    break;
  
  	  case DW_CFA_advance_loc1:
*************** execute_cfa_program ( struct objfile *ob
*** 766,772 ****
  	    break;
  
  	  default:
! 	    error ("dwarf cfi error: unknown cfa instruction %d.", insn);
  	  }
      }
  }
--- 784,795 ----
  	    break;
  
  	  default:
! 	    if(insn >= DW_CFA_lo_user && insn <= DW_CFA_hi_user)
! 		    warning("CFI: Unknown user CFA instruction 0x%x "
! 		    	"for frame %p", insn, (void*)fs->pc);
! 	    else
! 		    warning("CFI: Unknown CFA instruction 0x%x "
! 		    	"for frame %p", insn, (void*)fs->pc);
  	  }
      }
  }
*************** frame_state_for (struct context *context
*** 826,832 ****
    }
    else
  	internal_error (__FILE__, __LINE__,
! 		"%s(): Internal error: fde->cie_ptr==NULL !", 
  		__func__);
  }
  
--- 849,855 ----
    }
    else
  	internal_error (__FILE__, __LINE__,
! 		"%s: Internal error: fde->cie_ptr==NULL !", 
  		__func__);
  }
  
*************** compare_fde_unit (const void *a, const v
*** 1372,1409 ****
  
  /*  Build the cie_chunks and fde_chunks tables from informations
      in .debug_frame section.  */
! void
! dwarf2_build_frame_info (struct objfile *objfile)
  {
    bfd *abfd = objfile->obfd;
!   char *start = NULL;
!   char *end = NULL;
!   int from_eh = 0;
  
    obstack_init (&unwind_tmp_obstack);
  
!   dwarf_frame_buffer = 0;
  
!   if (dwarf_frame_offset)
!     {
!       dwarf_frame_buffer = dwarf2_read_section (objfile,
! 						dwarf_frame_offset,
! 						dwarf_frame_size);
! 
!       start = dwarf_frame_buffer;
!       end = dwarf_frame_buffer + dwarf_frame_size;
!     }
!   else if (dwarf_eh_frame_offset)
!     {
!       dwarf_frame_buffer = dwarf2_read_section (objfile,
! 						dwarf_eh_frame_offset,
! 						dwarf_eh_frame_size);
! 
!       start = dwarf_frame_buffer;
!       end = dwarf_frame_buffer + dwarf_eh_frame_size;
! 
!       from_eh = 1;
!     }
  
    if (start)
      {
--- 1395,1423 ----
  
  /*  Build the cie_chunks and fde_chunks tables from informations
      in .debug_frame section.  */
! static void
! parse_frame_info (struct objfile *objfile, file_ptr frame_offset, 
! 	unsigned int frame_size, int eh_frame)
  {
    bfd *abfd = objfile->obfd;
!   asection *curr_section_ptr;
!   char *start = NULL, *end = NULL, *frame_buffer = NULL;
!   char *curr_section_name, *aug_data;
!   struct cie_unit *last_cie=NULL;
!   int last_dup_fde = 0, aug_len, i;
!   CORE_ADDR curr_section_vma = 0;
  
    obstack_init (&unwind_tmp_obstack);
  
!   frame_buffer = dwarf2_read_section (objfile, frame_offset, frame_size);
  
!   start = frame_buffer;
!   end = frame_buffer + frame_size;
!   
!   curr_section_name = eh_frame?".eh_frame":".debug_frame";
!   curr_section_ptr = bfd_get_section_by_name (abfd, curr_section_name);
!   if (curr_section_ptr)
!     curr_section_vma = curr_section_ptr->vma;
  
    if (start)
      {
*************** dwarf2_build_frame_info (struct objfile 
*** 1411,1419 ****
  	{
  	  unsigned long length;
  	  ULONGEST cie_id;
! 	  ULONGEST unit_offset = start - dwarf_frame_buffer;
! 	  int bytes_read;
! 	  int dwarf64;
  	  char *block_end;
  
  	  length = read_initial_length (abfd, start, &bytes_read);
--- 1425,1432 ----
  	{
  	  unsigned long length;
  	  ULONGEST cie_id;
! 	  ULONGEST unit_offset = start - frame_buffer;
! 	  int bytes_read, dwarf64, flag_pcrel;
  	  char *block_end;
  
  	  length = read_initial_length (abfd, start, &bytes_read);
*************** dwarf2_build_frame_info (struct objfile 
*** 1424,1430 ****
  	  cie_id = read_length (abfd, start, &bytes_read, dwarf64);
  	  start += bytes_read;
  
! 	  if ((from_eh && cie_id == 0) || is_cie (cie_id, dwarf64))
  	    {
  	      struct cie_unit *cie = cie_unit_alloc ();
  	      char *aug;
--- 1437,1443 ----
  	  cie_id = read_length (abfd, start, &bytes_read, dwarf64);
  	  start += bytes_read;
  
! 	  if ((eh_frame && cie_id == 0) || is_cie (cie_id, dwarf64))
  	    {
  	      struct cie_unit *cie = cie_unit_alloc ();
  	      char *aug;
*************** dwarf2_build_frame_info (struct objfile 
*** 1440,1523 ****
  	      start++;		/* version */
  
  	      cie->augmentation = aug = start;
! 	      while (*start)
! 		start++;
! 	      start++;		/* skip past NUL */
  
  	      cie->code_align = read_uleb128 (abfd, &start);
  	      cie->data_align = read_sleb128 (abfd, &start);
  	      cie->ra = read_1u (abfd, &start);
  
  	      if (*aug == 'z')
  		{
! 		  int xtra = read_uleb128 (abfd, &start);
! 		  start += xtra;
  		  ++aug;
  		}
  
  	      while (*aug != '\0')
  		{
  		  if (aug[0] == 'e' && aug[1] == 'h')
  		    {
! 		      start += sizeof (void *);
! 		      aug += 2;
  		    }
  		  else if (aug[0] == 'R')
  		    {
! 		      cie->addr_encoding = *start++;
! 		      aug += 1;
  		    }
! 		  else if (aug[0] == 'P')
  		    {
! 		      CORE_ADDR ptr;
! 		      ptr = read_encoded_pointer (abfd, &start,
! 						  cie->addr_encoding);
! 		      aug += 1;
  		    }
  		  else
! 		    warning ("%s(): unknown augmentation", __func__);
  		}
  
! 	      cie->data = start;
! 	      cie->data_length = block_end - start;
  	    }
  	  else
  	    {
  	      struct fde_unit *fde;
  	      struct cie_unit *cie;
  
! 	      fde_chunks_need_space ();
! 	      fde = fde_unit_alloc ();
! 
! 	      fde_chunks.array[fde_chunks.elems++] = fde;
! 	      
! 	      fde->initial_location = read_pointer (abfd, &start)
! 	        + ANOFFSET (objfile->section_offsets, SECT_OFF_TEXT (objfile));
! 	      fde->address_range = read_pointer (abfd, &start);
! 
! 	      cie = cie_chunks;
! 	      while(cie)
  	      {
! 	        if (cie->objfile == objfile)
  		{
! 		  if (from_eh && (cie->offset == (unit_offset + bytes_read - cie_id)))
! 		      break;
! 		  if (!from_eh && (cie->offset == cie_id))
! 		    break;
  		}
  
! 		cie = cie->next;
  	      }
- 	    
- 	      if (!cie)
- 		error ("%s(): can't find CIE pointer", __func__);
- 	      fde->cie_ptr = cie;
  
! 	      if (cie->augmentation[0] == 'z')
! 		  read_uleb128 (abfd, &start);
  
! 	      fde->data = start;
! 	      fde->data_length = block_end - start;
  	    }
  	  start = block_end;
  	}
--- 1453,1630 ----
  	      start++;		/* version */
  
  	      cie->augmentation = aug = start;
! 	      while (*start++);	/* Skips last NULL as well */
  
  	      cie->code_align = read_uleb128 (abfd, &start);
  	      cie->data_align = read_sleb128 (abfd, &start);
  	      cie->ra = read_1u (abfd, &start);
  
+ 	      /* Augmentation:
+ 		 z	Indicates that a uleb128 is present to size the
+ 		 	augmentation section.
+ 		 L	Indicates the encoding (and thus presence) of
+ 			an LSDA pointer in the FDE augmentation.
+ 		 R	Indicates a non-default pointer encoding for
+ 			FDE code pointers.
+ 		 P	Indicates the presence of an encoding + language
+ 			personality routine in the CIE augmentation.
+ 	      
+ 	         [This info comes from GCC's dwarf2out.c]
+ 		*/
  	      if (*aug == 'z')
  		{
! 		  aug_len = read_uleb128 (abfd, &start);
! 		  aug_data = start;
! 		  start += aug_len;
  		  ++aug;
  		}
  
+ 	      cie->data = start;
+ 	      cie->data_length = block_end - cie->data;
+ 	      
  	      while (*aug != '\0')
  		{
  		  if (aug[0] == 'e' && aug[1] == 'h')
  		    {
! 		      aug_data += sizeof (void *);
! 		      aug++;
  		    }
  		  else if (aug[0] == 'R')
+ 		      cie->addr_encoding = *aug_data++;
+ 		  else if (aug[0] == 'P')
  		    {
! 		      CORE_ADDR pers_addr;
! 		      int pers_addr_enc;
! 		      
! 		      pers_addr_enc = *aug_data++;
! 		      /* We use (pers_addr_enc & 0x7f) to avoid 
! 		         warning about indirect relative addressing.
! 			 We don't need pers_addr value anyway and so we 
! 			 don't care if it is correct :-)  */
! 		      pers_addr = read_encoded_pointer (abfd, &aug_data,
! 					pers_addr_enc & 0x7f, NULL);
  		    }
! 		  else if (aug[0] == 'L' && eh_frame)
  		    {
! 		      int lsda_addr_enc;
! 
! 		      /* Perhaps we should save this to CIE for later use?
! 		         Do we need it for something in GDB?  */
! 		      lsda_addr_enc = *aug_data++;
  		    }
  		  else
! 		    warning ("CFI warning: unknown augmentation \"%c\""
! 		    	" in \"%s\" of\n"
! 		    	"\t%s", aug[0], curr_section_name, objfile->name);
! 		  aug++;
  		}
  
! 	      last_cie = cie;
  	    }
  	  else
  	    {
  	      struct fde_unit *fde;
  	      struct cie_unit *cie;
+ 	      int dup = 0;
+ 	      CORE_ADDR	init_loc, base_offset;
  
! 	      /* We assume that debug_frame is in order 
! 	         CIE,FDE,CIE,FDE,FDE,...  and thus the CIE for this FDE
! 	         should be stored in last_cie pointer. If not, we'll 
! 	         try to find it by the older way.  */
! 	      if(last_cie)
! 		cie = last_cie;
! 	      else
  	      {
! 		warning ("%s: last_cie == NULL. "
! 			"Perhaps a malformed %s section in '%s'...?\n", 
! 			__func__, curr_section_name, objfile->name);
! 
! 		cie = cie_chunks;
! 		while(cie)
  		{
! 		  if (cie->objfile == objfile)
! 		  {
! 		    if (eh_frame && 
! 		        (cie->offset == (unit_offset + bytes_read - cie_id)))
! 			   break;
! 		    if (!eh_frame && (cie->offset == cie_id))
! 		       break;
! 		  }
! 
! 		  cie = cie->next;
  		}
+ 		if (!cie)
+ 		   error ("%s: can't find CIE pointer", __func__); 
+ 	      }
  
! 	      /* start-frame_buffer gives offset from 
! 	         the beginning of actual section.  */
! 	      base_offset = curr_section_vma + start - frame_buffer;
! 		      
! 	      init_loc = read_encoded_pointer (abfd, &start, 
! 	      			cie->addr_encoding, &flag_pcrel);
! 	      
! 	      if (flag_pcrel)
! 		init_loc += base_offset;
! 	      
! 	      /* For relocatable objects we must add an offset telling
! 	         where the section is actually mapped in the memory.  */
! 	      init_loc += ANOFFSET (objfile->section_offsets, 
! 				SECT_OFF_TEXT (objfile));
! 	
! 	      /* If we have both .debug_frame and .eh_frame present in 
! 	         a file, we must eliminate duplicate FDEs. For now we'll 
! 		 run through all entries in fde_chunks and check it one 
! 		 by one. Perhaps in the future we can implement a faster 
! 		 searching algorithm.  */
! 	      /* eh_frame==2 indicates, that this file has an already 
! 		 parsed .debug_frame too. When eh_frame==1 it means, that no
! 		 .debug_frame is present and thus we don't need to check for
! 		 duplicities. eh_frame==0 means, that we parse .debug_frame
! 		 and don't need to care about duplicate FDEs, because
! 		 .debug_frame is parsed first.  */
! 	      for(i = 0; eh_frame == 2 && i < fde_chunks.elems; i++)
! 	      {
! 		      /* We assume that FDEs in .debug_frame and .eh_frame 
! 		         have the same order (if they are present, of course).
! 			 If we find a duplicate entry for one FDE and save
! 			 it's index to last_dup_fde it's very likely, that 
! 			 we'll find an entry for the following FDE right after 
! 			 the previous one. Thus in many cases we'll run this 
! 			 loop only once.  */
! 		      last_dup_fde = (last_dup_fde + i) % fde_chunks.elems;
! 		      if(fde_chunks.array[last_dup_fde]->initial_location 
! 			      == init_loc)
! 		      {
! 			      dup=1;
! 			      break;
! 		      }
  	      }
  
! 	      /* Allocate a new entry only if this FDE isn't a duplicate of
! 	         something we have already seen.   */
! 	      if(!dup)
! 	      {
! 		      fde_chunks_need_space ();
! 		      fde = fde_unit_alloc ();
! 
! 		      fde_chunks.array[fde_chunks.elems++] = fde;
! 		      
! 		      fde->initial_location = init_loc;
! 		      fde->address_range = read_encoded_pointer (abfd, &start,
! 		      	cie->addr_encoding, NULL);
! 
! 		      fde->cie_ptr = cie;
! 
! 		      /* Here we intentionally ignore augmentation data
! 		         from FDE, because we don't need them.  */
! 		      if (cie->augmentation[0] == 'z')
! 			  start += read_uleb128 (abfd, &start);
  
! 		      fde->data = start;
! 		      fde->data_length = block_end - start;
! 	      }
  	    }
  	  start = block_end;
  	}
*************** dwarf2_build_frame_info (struct objfile 
*** 1525,1531 ****
  	     sizeof (struct fde_unit *), compare_fde_unit);
      }
  }
! 
  
  /* Return the frame address.  */
  CORE_ADDR
--- 1632,1659 ----
  	     sizeof (struct fde_unit *), compare_fde_unit);
      }
  }
! 
! /* We must parse both .debug_frame section and .eh_frame because 
!    not all frames must be present in both of these sections.  */
! void
! dwarf2_build_frame_info (struct objfile *objfile)
! {
! 	int eh_frame;
! 
! 	/* If we have .debug_frame then the parser is called with 
! 	   eh_frame==0 for .debug_frame and eh_frame==2 for .eh_frame,
! 	   otherwise it's only called once for .eh_frame with argument
! 	   eh_frame==1  */
! 	eh_frame = 0;
! 	
! 	if (dwarf_frame_offset)
! 		parse_frame_info (objfile, dwarf_frame_offset, 
! 			dwarf_frame_size, eh_frame++);
! 	
! 	if (dwarf_eh_frame_offset)
! 		parse_frame_info (objfile, dwarf_eh_frame_offset, 
! 			dwarf_eh_frame_size, eh_frame+1);
! }
  
  /* Return the frame address.  */
  CORE_ADDR
*************** cfi_get_saved_register (char *raw_buffer
*** 1750,1756 ****
  	  break;
  	default:
  	  internal_error (__FILE__, __LINE__,
! 			  "cfi_get_saved_register: unknown register rule");
  	}
      }
  }
--- 1878,1884 ----
  	  break;
  	default:
  	  internal_error (__FILE__, __LINE__,
! 			  "%s: unknown register rule", __func__);
  	}
      }
  }
*************** cfi_virtual_frame_pointer (CORE_ADDR pc,
*** 1778,1784 ****
        *frame_offset = fs->cfa_offset;
      }
    else
!     error ("dwarf cfi error: CFA is not defined as CFA_REG_OFFSET");
  
    unwind_tmp_obstack_free ();
  }
--- 1906,1912 ----
        *frame_offset = fs->cfa_offset;
      }
    else
!     error ("%s: CFA is not defined as CFA_REG_OFFSET", __func__);
  
    unwind_tmp_obstack_free ();
  }

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