Discussion:
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
Mark Millard via freebsd-hackers
2018-12-03 04:07:50 UTC
Permalink
[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
===================================================================
--- /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 = unused_rs;
unused_rs = unused_rs->prev;
}
else
new_rs = alloca (sizeof (struct frame_state_reg_info));

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

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

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

Index: /usr/src/contrib/gcc/unwind-dw2.h
===================================================================
--- /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 = 1;

extern int f()
{
volatile unsigned char c = '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
===> 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.


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

Loading...