public inbox for elfutils@sourceware.org
 help / color / mirror / Atom feed
* unwind non-PC registers using elfutils
@ 2021-10-03 16:38 Jacob Burkholder
  2021-11-10 14:09 ` Mark Wielaard
  0 siblings, 1 reply; 3+ messages in thread
From: Jacob Burkholder @ 2021-10-03 16:38 UTC (permalink / raw)
  To: elfutils-devel

Hi, I'm trying to figure out how to unwind registers other than the PC
using elfutils.  I can use dwfl_module_register_names to get the register
names, and then I'm trying to use dwarf_frame_register to get the register
values for a given frame.  I looked at the code in __libdwfl_frame_unwind
and there's another example in addrcfi.c.   dwarf_frame_register seems
quite a low level dwarf API, I'm not sure how to use the ops that are
returned, also the code used by __libdwfl_frame_unwind to evaluate the ops
is all static or internal so not callable.  Any guidance?  Do I need to
basically interpret the dwarf atoms in the ops returned by
dwarf_frame_register?  Any plans to make code like
frame_unwind.c:expr_eval() used by frame_unwind.c:handle_cfi externally
accessible?

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: unwind non-PC registers using elfutils
  2021-10-03 16:38 unwind non-PC registers using elfutils Jacob Burkholder
@ 2021-11-10 14:09 ` Mark Wielaard
  2021-11-11  6:23   ` Jacob Burkholder
  0 siblings, 1 reply; 3+ messages in thread
From: Mark Wielaard @ 2021-11-10 14:09 UTC (permalink / raw)
  To: Jacob Burkholder, elfutils-devel

Hi Jacob,

On Sun, 2021-10-03 at 09:38 -0700, Jacob Burkholder via Elfutils-devel wrote:
> Hi, I'm trying to figure out how to unwind registers other than the PC
> using elfutils.  I can use dwfl_module_register_names to get the register
> names, and then I'm trying to use dwarf_frame_register to get the register
> values for a given frame.  I looked at the code in __libdwfl_frame_unwind
> and there's another example in addrcfi.c.   dwarf_frame_register seems
> quite a low level dwarf API, I'm not sure how to use the ops that are
> returned, also the code used by __libdwfl_frame_unwind to evaluate the ops
> is all static or internal so not callable.  Any guidance?  Do I need to
> basically interpret the dwarf atoms in the ops returned by
> dwarf_frame_register?  Any plans to make code like
> frame_unwind.c:expr_eval() used by frame_unwind.c:handle_cfi externally
> accessible?

Sorry for the late reply. I missed this message earlier. As you
correctly observe the public API only supports unwinding of threads for
backtraces with just the PC. Internally most of the logic of unwinding
other registers is there, but we don't have a public API for it. We
really should. I filed:
https://sourceware.org/bugzilla/show_bug.cgi?id=28579

Could you add some requirements there?
The hard part is making sure the interface is actually useful.
What information do you have and what information do you want to get
out?

Thanks,

Mark

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: unwind non-PC registers using elfutils
  2021-11-10 14:09 ` Mark Wielaard
@ 2021-11-11  6:23   ` Jacob Burkholder
  0 siblings, 0 replies; 3+ messages in thread
From: Jacob Burkholder @ 2021-11-11  6:23 UTC (permalink / raw)
  To: Mark Wielaard; +Cc: elfutils-devel

[-- Attachment #1: Type: text/plain, Size: 3103 bytes --]

Hi Mark, thanks for your mail.

On Wed, Nov 10, 2021 at 6:09 AM Mark Wielaard <mark@klomp.org> wrote:
>
> Hi Jacob,
>
> On Sun, 2021-10-03 at 09:38 -0700, Jacob Burkholder via Elfutils-devel wrote:
> > Hi, I'm trying to figure out how to unwind registers other than the PC
> > using elfutils.  I can use dwfl_module_register_names to get the register
> > names, and then I'm trying to use dwarf_frame_register to get the register
> > values for a given frame.  I looked at the code in __libdwfl_frame_unwind
> > and there's another example in addrcfi.c.   dwarf_frame_register seems
> > quite a low level dwarf API, I'm not sure how to use the ops that are
> > returned, also the code used by __libdwfl_frame_unwind to evaluate the ops
> > is all static or internal so not callable.  Any guidance?  Do I need to
> > basically interpret the dwarf atoms in the ops returned by
> > dwarf_frame_register?  Any plans to make code like
> > frame_unwind.c:expr_eval() used by frame_unwind.c:handle_cfi externally
> > accessible?
>
> Sorry for the late reply. I missed this message earlier. As you
> correctly observe the public API only supports unwinding of threads for
> backtraces with just the PC. Internally most of the logic of unwinding
> other registers is there, but we don't have a public API for it. We
> really should. I filed:
> https://sourceware.org/bugzilla/show_bug.cgi?id=28579
>
> Could you add some requirements there?
> The hard part is making sure the interface is actually useful.
> What information do you have and what information do you want to get
> out?

I developed a patch against elfutils-0.185 which adds some APIs I use that
do what I want, it also includes some bug fixes.  It is just a proof of concept
at this point.

I exported __libdwfl_frame_reg_get and __libdwfl_frame_reg_set and I also
added an exported wrapper function for expr_eval.

I added dwfl_module_getebl as a convenience, any code that uses the disasm
functions needs an ebl pointer, Dwfl_Module has one but there's no way to get
at it and creating a new ebl instance just to disassemble can be error prone.

Getting the unwound register values was easy, they're already unwound there's
just no way to read them from the Dwfl_Frame.

Unwinding function parameters and local variables was a bit harder and I needed
to be able to call expr_eval.  The basic usage model is pretty much the same as
expr_eval is used inside of elfutils:

1. Call dwarf_getscopes with the pc of the frame of interest.
2. Find the enclosing DW_TAG_subprogram or DW_TAG_inlined_subroutine.
3. For each DW_TAG_formal_parameter or DW_TAG_variable child, find the
DW_AT_location, then call dwarf_getlocations and find the location that
corresponds to the pc.
4. Call __libdwfl_expr_eval and pass in the ops and nops, the value is returned.

I currently always pass NULL for the Dwarf_Frame, it doesn't look that
complicated
to get the Dwarf_Frame to pass in but I'm not sure in which cases it is needed.

There's some header mess adding these functions to libdwfl.h because the Dwarf
types are defined in libdw.h.

>
> Thanks,
>
> Mark

[-- Attachment #2: 0-libdwfl.h --]
[-- Type: text/x-chdr, Size: 5718 bytes --]

diff -Nru elfutils-0.185.orig/backends/x86_64_init.c elfutils-0.185/backends/x86_64_init.c
--- elfutils-0.185.orig/backends/x86_64_init.c	2021-05-22 11:25:24.000000000 -0700
+++ elfutils-0.185/backends/x86_64_init.c	2021-10-03 22:11:47.050651661 -0700
@@ -60,7 +60,7 @@
   HOOK (eh, disasm);
   HOOK (eh, abi_cfi);
   /* gcc/config/ #define DWARF_FRAME_REGISTERS.  */
-  eh->frame_nregs = 17;
+  eh->frame_nregs = 67;
   HOOK (eh, set_initial_registers_tid);
   HOOK (eh, unwind);
   HOOK (eh, check_reloc_target_type);
diff -Nru elfutils-0.185.orig/backends/x86_64_initreg.c elfutils-0.185/backends/x86_64_initreg.c
--- elfutils-0.185.orig/backends/x86_64_initreg.c	2021-05-22 11:25:24.000000000 -0700
+++ elfutils-0.185/backends/x86_64_initreg.c	2021-10-03 21:52:42.251129764 -0700
@@ -68,6 +68,21 @@
   dwarf_regs[14] = user_regs.r14;
   dwarf_regs[15] = user_regs.r15;
   dwarf_regs[16] = user_regs.rip;
-  return setfunc (0, 17, dwarf_regs, arg);
+  if (!setfunc (0, 17, dwarf_regs, arg))
+    return false;
+  dwarf_regs[0] = user_regs.eflags;
+  dwarf_regs[1] = user_regs.es;
+  dwarf_regs[2] = user_regs.cs;
+  dwarf_regs[3] = user_regs.ss;
+  dwarf_regs[4] = user_regs.ds;
+  dwarf_regs[5] = user_regs.fs;
+  dwarf_regs[6] = user_regs.gs;
+  if (!setfunc (49, 7, dwarf_regs, arg))
+    return false;
+  dwarf_regs[0] = user_regs.fs_base;
+  dwarf_regs[1] = user_regs.gs_base;
+  if (!setfunc (58, 2, dwarf_regs, arg))
+    return false;
+  return true;
 #endif /* __x86_64__ */
 }
diff -Nru elfutils-0.185.orig/libasm/disasm_str.c elfutils-0.185/libasm/disasm_str.c
--- elfutils-0.185.orig/libasm/disasm_str.c	2021-05-22 11:25:24.000000000 -0700
+++ elfutils-0.185/libasm/disasm_str.c	2021-10-10 12:11:45.840383058 -0700
@@ -52,7 +52,7 @@
     return len - buffer->len;
 
   buffer->buf = mempcpy (buffer->buf, str, len);
-  buffer->len = len;
+  buffer->len -= len;
 
   return 0;
 }
diff -Nru elfutils-0.185.orig/libdw/libdw.map elfutils-0.185/libdw/libdw.map
--- elfutils-0.185.orig/libdw/libdw.map	2021-05-22 11:25:24.000000000 -0700
+++ elfutils-0.185/libdw/libdw.map	2021-10-18 22:46:59.033075060 -0700
@@ -360,3 +360,11 @@
     # presume that NULL is only returned on error (otherwise ELF_K_NONE).
     dwelf_elf_begin;
 } ELFUTILS_0.175;
+
+ELFUTILS_0.185 {
+  global:
+    __libdwfl_frame_reg_get;
+    __libdwfl_frame_reg_set;
+    dwfl_module_getebl;
+    __libdwfl_expr_eval;
+} ELFUTILS_0.177;
diff -Nru elfutils-0.185.orig/libdwfl/dwfl_module_getdwarf.c elfutils-0.185/libdwfl/dwfl_module_getdwarf.c
--- elfutils-0.185.orig/libdwfl/dwfl_module_getdwarf.c	2021-05-22 11:25:24.000000000 -0700
+++ elfutils-0.185/libdwfl/dwfl_module_getdwarf.c	2021-10-10 10:52:06.204694821 -0700
@@ -1296,6 +1296,12 @@
     }
 }
 
+void *
+dwfl_module_getebl (Dwfl_Module *mod)
+{
+  __libdwfl_module_getebl (mod);
+  return mod->ebl;
+}
 
 /* Try to open a libebl backend for MOD.  */
 Dwfl_Error
diff -Nru elfutils-0.185.orig/libdwfl/frame_unwind.c elfutils-0.185/libdwfl/frame_unwind.c
--- elfutils-0.185.orig/libdwfl/frame_unwind.c	2021-05-22 11:25:24.000000000 -0700
+++ elfutils-0.185/libdwfl/frame_unwind.c	2021-10-18 22:43:50.357209634 -0700
@@ -44,7 +44,6 @@
 #define DWARF_EXPR_STEPS_MAX 0x1000
 
 bool
-internal_function
 __libdwfl_frame_reg_get (Dwfl_Frame *state, unsigned regno, Dwarf_Addr *val)
 {
   Ebl *ebl = state->thread->process->ebl;
@@ -61,7 +60,6 @@
 }
 
 bool
-internal_function
 __libdwfl_frame_reg_set (Dwfl_Frame *state, unsigned regno, Dwarf_Addr val)
 {
   Ebl *ebl = state->thread->process->ebl;
@@ -507,6 +505,13 @@
 #undef pop
 }
 
+bool
+__libdwfl_expr_eval (Dwfl_Frame *state, Dwarf_Frame *frame, const Dwarf_Op *ops,
+  size_t nops, Dwarf_Addr *result, Dwarf_Addr bias)
+{
+  return expr_eval(state, frame, ops, nops, result, bias);
+}
+
 static Dwfl_Frame *
 new_unwound (Dwfl_Frame *state)
 {
diff -Nru elfutils-0.185.orig/libdwfl/libdwfl.h elfutils-0.185/libdwfl/libdwfl.h
--- elfutils-0.185.orig/libdwfl/libdwfl.h	2021-05-22 11:25:24.000000000 -0700
+++ elfutils-0.185/libdwfl/libdwfl.h	2021-10-18 22:45:23.065138899 -0700
@@ -795,6 +795,23 @@
 bool dwfl_frame_pc (Dwfl_Frame *state, Dwarf_Addr *pc, bool *isactivation)
   __nonnull_attribute__ (1, 2);
 
+/* Fetch value from Dwfl_Frame->regs indexed by DWARF REGNO.
+   No error code is set if the function returns FALSE.  */
+bool __libdwfl_frame_reg_get (Dwfl_Frame *state, unsigned regno,
+			      Dwarf_Addr *val);
+
+/* Store value to Dwfl_Frame->regs indexed by DWARF REGNO.
+   No error code is set if the function returns FALSE.  */
+bool __libdwfl_frame_reg_set (Dwfl_Frame *state, unsigned regno,
+			      Dwarf_Addr val);
+
+void *dwfl_module_getebl (Dwfl_Module *mod);
+
+#ifdef _LIBDW_H
+bool __libdwfl_expr_eval (Dwfl_Frame *state, Dwarf_Frame *frame,
+  const Dwarf_Op *ops, size_t nops, Dwarf_Addr *result, Dwarf_Addr bias);
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff -Nru elfutils-0.185.orig/libdwfl/libdwflP.h elfutils-0.185/libdwfl/libdwflP.h
--- elfutils-0.185.orig/libdwfl/libdwflP.h	2021-05-22 11:25:24.000000000 -0700
+++ elfutils-0.185/libdwfl/libdwflP.h	2021-10-03 09:53:38.887119596 -0700
@@ -276,18 +276,6 @@
   Dwarf_Addr regs[];
 };
 
-/* Fetch value from Dwfl_Frame->regs indexed by DWARF REGNO.
-   No error code is set if the function returns FALSE.  */
-bool __libdwfl_frame_reg_get (Dwfl_Frame *state, unsigned regno,
-			      Dwarf_Addr *val)
-  internal_function;
-
-/* Store value to Dwfl_Frame->regs indexed by DWARF REGNO.
-   No error code is set if the function returns FALSE.  */
-bool __libdwfl_frame_reg_set (Dwfl_Frame *state, unsigned regno,
-			      Dwarf_Addr val)
-  internal_function;
-
 /* Information cached about each CU in Dwfl_Module.dw.  */
 struct dwfl_cu
 {

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2021-11-11  6:23 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-10-03 16:38 unwind non-PC registers using elfutils Jacob Burkholder
2021-11-10 14:09 ` Mark Wielaard
2021-11-11  6:23   ` Jacob Burkholder

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).