Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 2 Dec 2018 20:07:50 -0800
From:      Mark Millard <marklmi@yahoo.com>
To:        FreeBSD PowerPC ML <freebsd-ppc@freebsd.org>, freebsd-hackers Hackers <freebsd-hackers@freebsd.org>
Subject:   How to get /lib/libgcc_s.so.1 's thrown exception handling to work for the likes of powerpc64 with buildworld via devel/powerpc64-xtoolchain-gcc
Message-ID:  <502FA1DF-65C2-41B6-BF10-7E712CD44BA9@yahoo.com>

next in thread | raw e-mail | index | archive | help
[This is without the longer term technique of getting
llvm's libunwind working.]

The code changes in my example are deliberately set up to
approximately minimize the number of lines with textual
changes. The patching fixes the handling of
DW_CFA_remember_state and DW_CFA_restore_state .

Without the changes _Unwind_RaiseException can end up
looping looking at the same frame over and over when
DW_CFA_remember_state and DW_CFA_restore_state are in
use. While gcc 4.2.1 generally avoids these, more
modern toolchains use them.

# svnlite diff --diff-cmd diff -x "-U12" =
/usr/src/contrib/gcc/unwind-dw2.*
Index: /usr/src/contrib/gcc/unwind-dw2.c
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- /usr/src/contrib/gcc/unwind-dw2.c	(revision 340287)
+++ /usr/src/contrib/gcc/unwind-dw2.c	(working copy)
@@ -949,32 +949,40 @@
	case DW_CFA_remember_state:
	  {
	    struct frame_state_reg_info *new_rs;
	    if (unused_rs)
	      {
		new_rs =3D unused_rs;
		unused_rs =3D unused_rs->prev;
	      }
	    else
	      new_rs =3D alloca (sizeof (struct frame_state_reg_info));

	    *new_rs =3D fs->regs;
+	    new_rs->cfa_offset =3D fs->cfa_offset;
+	    new_rs->cfa_reg =3D fs->cfa_reg;
+	    new_rs->cfa_exp =3D fs->cfa_exp;
+	    new_rs->cfa_how =3D fs->cfa_how;
	    fs->regs.prev =3D new_rs;
	  }
	  break;

	case DW_CFA_restore_state:
	  {
	    struct frame_state_reg_info *old_rs =3D fs->regs.prev;
	    fs->regs =3D *old_rs;
+	    fs->cfa_offset =3D fs->regs.cfa_offset;
+	    fs->cfa_reg =3D fs->regs.cfa_reg;
+	    fs->cfa_exp =3D fs->regs.cfa_exp;
+	    fs->cfa_how =3D fs->regs.cfa_how;
	    old_rs->prev =3D unused_rs;
	    unused_rs =3D old_rs;
	  }
	  break;

	case DW_CFA_def_cfa:
	  insn_ptr =3D read_uleb128 (insn_ptr, &fs->cfa_reg);
	  insn_ptr =3D read_uleb128 (insn_ptr, &utmp);
	  fs->cfa_offset =3D utmp;
	  fs->cfa_how =3D CFA_REG_OFFSET;
	  break;

Index: /usr/src/contrib/gcc/unwind-dw2.h
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- /usr/src/contrib/gcc/unwind-dw2.h	(revision 340287)
+++ /usr/src/contrib/gcc/unwind-dw2.h	(working copy)
@@ -28,60 +28,70 @@
   Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
   02110-1301, USA.  */

/* A target can override (perhaps for backward compatibility) how
   many dwarf2 columns are unwound.  */
#ifndef DWARF_FRAME_REGISTERS
#define DWARF_FRAME_REGISTERS FIRST_PSEUDO_REGISTER
#endif

/* The result of interpreting the frame unwind info for a frame.
   This is all symbolic at this point, as none of the values can
   be resolved until the target pc is located.  */
+enum frame_state_cfa_how_values {
+  CFA_UNSET,
+  CFA_REG_OFFSET,
+  CFA_EXP
+};
typedef struct
{
  /* Each register save state can be described in terms of a CFA slot,
     another register, or a location expression.  */
  struct frame_state_reg_info
  {
    struct {
      union {
	_Unwind_Word reg;
	_Unwind_Sword offset;
	const unsigned char *exp;
      } loc;
      enum {
	REG_UNSAVED,
	REG_SAVED_OFFSET,
	REG_SAVED_REG,
	REG_SAVED_EXP,
	REG_SAVED_VAL_OFFSET,
	REG_SAVED_VAL_EXP
      } how;
    } reg[DWARF_FRAME_REGISTERS+1];

    /* Used to implement DW_CFA_remember_state.  */
    struct frame_state_reg_info *prev;
+
+    /* The following are just for DW_CFA_{remember,restore}_state
+       handling. */
+    /* The CFA can be described in terms of a reg+offset or a
+       location expression.  */
+    _Unwind_Sword cfa_offset;
+    _Unwind_Word cfa_reg;
+    const unsigned char *cfa_exp;
+    enum frame_state_cfa_how_values cfa_how;
  } regs;

  /* The CFA can be described in terms of a reg+offset or a
     location expression.  */
  _Unwind_Sword cfa_offset;
  _Unwind_Word cfa_reg;
  const unsigned char *cfa_exp;
-  enum {
-    CFA_UNSET,
-    CFA_REG_OFFSET,
-    CFA_EXP
-  } cfa_how;
+  enum frame_state_cfa_how_values cfa_how;

  /* The PC described by the current frame state.  */
  void *pc;

  /* The information we care about from the CIE/FDE.  */
  _Unwind_Personality_Fn personality;
  _Unwind_Sword data_align;
  _Unwind_Word code_align;
  _Unwind_Word retaddr_column;
  unsigned char fde_encoding;
  unsigned char lsda_encoding;
  unsigned char saw_z;


Without such changes building devel/gdb and
devel/kyua via, say, g++8 but still binding
to /lib/libgcc_s.so.1 leads to failure of
both programs: both make use of thrown c++
exceptions in normal operation and
DW_CFA_remember_state and DW_CFA_restore_state
end up involved.

Even the following program fails, looping in
__Unwind_RaiseException:

#include <exception>

// -O2 context used (but not required for the problem
// to occur).

volatile unsigned int v =3D 1;

extern int f()
{
    volatile unsigned char c =3D 'a';
    v++; // despite volatile the access to v in g
         // was otherwise optimized out and the
         // std::exception was not followed by
         // code for f().
    return c;
}

extern void g()
{
    if (v) throw std::exception();
    f(); // ends up inlined but the problem is demonstrated.
}

int main(void)
{
    try {g();} // Used a separate function to avoid any potential
               // special handling of code in main. Call not
               // optimized out.
    catch (std::exception& e) {f();}
    return 0;
}


Previous to the changes the following were classified as
broken by timing out without completing:

# kyua report lib/atf/libatf-c++/detail/exceptions_test
=3D=3D=3D> Summary
Results read from =
/root/.kyua/store/results.usr_tests.20181203-001618-953311.db
Test cases: 3 total, 0 skipped, 0 expected failures, 0 broken, 0 failed
Total time: 0.064s

Using a gcc 4.2.1 based kernel and a devel/powerpc64-xtoolchain-gcc
based world, I've done a complete kyua test -k /usr/tests/Kyuafile
that completed. (The above report is based on that run.)

The reason for the gcc 4.2.1 kernel is the crash in
vnet_epair_init during the kyua test -k /usr/tests/Kyuafile
run when the kernel was built by devel/powerpc64-xtoolchain-gcc .
The linkage/relocation and vnet's code do not seem
to be matched when devel/powerpc64-xtoolchain-gcc
is used to build the kernel.


=3D=3D=3D
Mark Millard
marklmi at yahoo.com
( dsl-only.net went
away in early 2018-Mar)




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?502FA1DF-65C2-41B6-BF10-7E712CD44BA9>