PR27723, Internal error in select_cie_for_fde

Message ID 20210413225235.GK5425@bubble.grove.modra.org
State New
Headers show
Series
  • PR27723, Internal error in select_cie_for_fde
Related show

Commit Message

H.J. Lu via Binutils April 13, 2021, 10:52 p.m.
This patch updates select_cie_for_fde to handle codes that may appear
at the start of an FDE.

	PR 27723
	* dw2gencfi.c (select_cie_for_fde): Handle DW_CFA_val_offset,
	DW_CFA_GNU_window_save and DW_CFA_restore_state.


-- 
Alan Modra
Australia Development Lab, IBM

Comments

H.J. Lu via Binutils April 14, 2021, 12:32 a.m. | #1
Let's make sure what we allow in the CIE initial instructions and what
select_cie_for_fde compares for a match is always in sync.  Also
correct the previous patch that allowed DW_CFA_GNU_window_save to be
part of the CIE initial instructions, which was likely a mistake.

	PR 27723
	* dw2gencfi.c (initial_cie_insn): New function, extracted from..
	(select_cie_for_fde): ..here.  Simplify.

diff --git a/gas/dw2gencfi.c b/gas/dw2gencfi.c
index ffaef52cd59..c0656146032 100644
--- a/gas/dw2gencfi.c
+++ b/gas/dw2gencfi.c
@@ -2043,6 +2043,63 @@ output_fde (struct fde_entry *fde, struct cie_entry *cie,
   symbol_set_value_now (end_address);
 }
 
+/* Allow these insns to be put in the initial sequence of a CIE.
+   If J is non-NULL, then compare I and J insns for a match.  */
+
+static inline bool
+initial_cie_insn (const struct cfi_insn_data *i, const struct cfi_insn_data *j)
+{
+  if (j && i->insn != j->insn)
+    return false;
+  switch (i->insn)
+    {
+    case DW_CFA_offset:
+    case DW_CFA_def_cfa:
+    case DW_CFA_val_offset:
+      if (j)
+	{
+	  if (i->u.ri.reg != j->u.ri.reg)
+	    return false;
+	  if (i->u.ri.offset != j->u.ri.offset)
+	    return false;
+	}
+      break;
+
+    case DW_CFA_register:
+      if (j)
+	{
+	  if (i->u.rr.reg1 != j->u.rr.reg1)
+	    return false;
+	  if (i->u.rr.reg2 != j->u.rr.reg2)
+	    return false;
+	}
+      break;
+
+    case DW_CFA_def_cfa_register:
+    case DW_CFA_restore:
+    case DW_CFA_undefined:
+    case DW_CFA_same_value:
+      if (j)
+	{
+	  if (i->u.r != j->u.r)
+	    return false;
+	}
+      break;
+
+    case DW_CFA_def_cfa_offset:
+      if (j)
+	{
+	  if (i->u.i != j->u.i)
+	    return false;
+	}
+      break;
+
+    default:
+      return false;
+    }
+  return true;
+}
+
 static struct cie_entry *
 select_cie_for_fde (struct fde_entry *fde, bool eh_frame,
 		    struct cfi_insn_data **pfirst, int align)
@@ -2088,75 +2145,15 @@ select_cie_for_fde (struct fde_entry *fde, bool eh_frame,
 	   i != cie->last && j != NULL;
 	   i = i->next, j = j->next)
 	{
-	  if (i->insn != j->insn)
-	    goto fail;
-	  switch (i->insn)
-	    {
-	    case DW_CFA_advance_loc:
-	    case DW_CFA_remember_state:
-	      /* We reached the first advance/remember in the FDE,
-		 but did not reach the end of the CIE list.  */
-	      goto fail;
-
-	    case DW_CFA_offset:
-	    case DW_CFA_def_cfa:
-	    case DW_CFA_val_offset:
-	      if (i->u.ri.reg != j->u.ri.reg)
-		goto fail;
-	      if (i->u.ri.offset != j->u.ri.offset)
-		goto fail;
-	      break;
-
-	    case DW_CFA_register:
-	      if (i->u.rr.reg1 != j->u.rr.reg1)
-		goto fail;
-	      if (i->u.rr.reg2 != j->u.rr.reg2)
-		goto fail;
-	      break;
-
-	    case DW_CFA_def_cfa_register:
-	    case DW_CFA_restore:
-	    case DW_CFA_undefined:
-	    case DW_CFA_same_value:
-	      if (i->u.r != j->u.r)
-		goto fail;
-	      break;
-
-	    case DW_CFA_def_cfa_offset:
-	      if (i->u.i != j->u.i)
-		goto fail;
-	      break;
-
-	    case CFI_escape:
-	    case CFI_val_encoded_addr:
-	    case CFI_label:
-	    case DW_CFA_restore_state:
-	    case DW_CFA_GNU_window_save:
-	      /* Don't bother matching these for now.  */
-	      goto fail;
-
-	    default:
-	      abort ();
-	    }
+	  if (!initial_cie_insn (i, j))
+	    break;
 	}
 
-      /* Success if we reached the end of the CIE list, and we've either
-	 run out of FDE entries or we've encountered an advance,
-	 remember, or escape.  */
-      if (i == cie->last
-	  && (!j
-	      || j->insn == DW_CFA_advance_loc
-	      || j->insn == DW_CFA_remember_state
-	      || j->insn == DW_CFA_GNU_window_save
-	      || j->insn == CFI_escape
-	      || j->insn == CFI_val_encoded_addr
-	      || j->insn == CFI_label))
+      if (i == cie->last)
 	{
 	  *pfirst = j;
 	  return cie;
 	}
-
-    fail:;
     }
 
   cie = XNEW (struct cie_entry);
@@ -2174,11 +2171,7 @@ select_cie_for_fde (struct fde_entry *fde, bool eh_frame,
 #endif
 
   for (i = cie->first; i ; i = i->next)
-    if (i->insn == DW_CFA_advance_loc
-	|| i->insn == DW_CFA_remember_state
-	|| i->insn == CFI_escape
-	|| i->insn == CFI_val_encoded_addr
-	|| i->insn == CFI_label)
+    if (!initial_cie_insn (i, NULL))
       break;
 
   cie->last = i;

-- 
Alan Modra
Australia Development Lab, IBM

Patch

diff --git a/gas/dw2gencfi.c b/gas/dw2gencfi.c
index 84029940b2e..ffaef52cd59 100644
--- a/gas/dw2gencfi.c
+++ b/gas/dw2gencfi.c
@@ -2100,6 +2100,7 @@  select_cie_for_fde (struct fde_entry *fde, bool eh_frame,
 
 	    case DW_CFA_offset:
 	    case DW_CFA_def_cfa:
+	    case DW_CFA_val_offset:
 	      if (i->u.ri.reg != j->u.ri.reg)
 		goto fail;
 	      if (i->u.ri.offset != j->u.ri.offset)
@@ -2129,6 +2130,8 @@  select_cie_for_fde (struct fde_entry *fde, bool eh_frame,
 	    case CFI_escape:
 	    case CFI_val_encoded_addr:
 	    case CFI_label:
+	    case DW_CFA_restore_state:
+	    case DW_CFA_GNU_window_save:
 	      /* Don't bother matching these for now.  */
 	      goto fail;
 
@@ -2144,6 +2147,7 @@  select_cie_for_fde (struct fde_entry *fde, bool eh_frame,
 	  && (!j
 	      || j->insn == DW_CFA_advance_loc
 	      || j->insn == DW_CFA_remember_state
+	      || j->insn == DW_CFA_GNU_window_save
 	      || j->insn == CFI_escape
 	      || j->insn == CFI_val_encoded_addr
 	      || j->insn == CFI_label))