1
0
Fork 0
mirror of https://github.com/tobast/libunwind-eh_elf.git synced 2024-11-17 13:47:37 +01:00

dwarf: Correct handling of DW_CFA_GNU_args_size

When resuming execution, DW_CFA_GNU_args_size from the current frame
must be added back to the stack pointer.  Clang now generates these frequently
at -O3.  A simple repro for x86_64, that will crash with clang ~3.9 or newer:

void f(int, int,int,int,int,int,int,int,int);

int main() {
  try {
    f(0,1,2,3,4,5,6,7,8);
  } catch (int) {
    return 0;
  }
  return 1;
}

Where f is something that throws an int, but in a different translation unit to
prevent optimization.

This results in cfi instructions before the call:
 .cfi_escape 0x2e, 0x20

Grabbing the args_size means fully parsing the cfi in the current frame, which
is unfortunate because it means nearly twice the work at each step.  The logic
to grab args_size can be in unw_step or get_proc_info (since this is always
called before resuming in stack unwinding).  Putting it in get_proc_info allows
the more common unw_step code to remain fast.

It would potentially fit in nicely with a proc info cache (as mentioned in the
if0 comment block)
This commit is contained in:
Dave Watson 2016-12-05 15:36:39 -08:00
parent f7fe1c9a7e
commit 4b63a536ee
3 changed files with 32 additions and 2 deletions

View file

@ -925,7 +925,19 @@ dwarf_make_proc_info (struct dwarf_cursor *c)
if (c->as->caching_policy == UNW_CACHE_NONE
|| get_cached_proc_info (c) < 0)
#endif
/* Lookup it up the slow way... */
return fetch_proc_info (c, c->ip, 0);
dwarf_state_record_t sr;
int ret;
/* Lookup it up the slow way... */
if ((ret = fetch_proc_info (c, c->ip, 1)) < 0)
return ret;
/* Also need to check if current frame contains
args_size, and set cursor appropriatly. Only
needed for unw_resume */
if ((ret = dwarf_create_state_record (c, &sr)) < 0)
return ret;
put_unwind_info (c, &c->pi);
c->args_size = sr.args_size;
return 0;
}

View file

@ -63,6 +63,15 @@ establish_machine_state (struct cursor *c)
(*access_reg) (as, reg, &val, 1, arg);
}
}
if (c->dwarf.args_size)
{
if (tdep_access_reg (c, ESP, &val, 0) >= 0)
{
val += c->dwarf.args_size;
(*access_reg) (as, ESP, &val, 1, arg);
}
}
return 0;
}

View file

@ -95,6 +95,15 @@ establish_machine_state (struct cursor *c)
(*access_reg) (as, reg, &val, 1, arg);
}
}
if (c->dwarf.args_size)
{
if (tdep_access_reg (c, RSP, &val, 0) >= 0)
{
val += c->dwarf.args_size;
(*access_reg) (as, RSP, &val, 1, arg);
}
}
return 0;
}