Don't rewind PC for GHC generated frames
Commit Message
GHC - the Haskell compiler generates code that violates one of
GDB's assumptions.
GDB assumes that the address in a frame was generated by the call
instruction and that it is the address of the call instruction
plus 1 (I'm rephrasing the comment in get_frame_address_in_block).
So to get the real address, one has to substract 1. This is doubly
beneficial because some functions are "noreturn" and don't have
further instructions after call, so GDB would be looking at gibberish.
All in all, this is good for C-like languages.
GHC generates completely different code. It uses jumps instead of call
and manages the stack itself. Furthermore every piece of code is preceeded
by some metadata called the Info Table. If we substract from the
program counter it ends up pointing to the metadata, which is undesirable.
GHC has a workaround for this [1] that works most of the time, it basically
lies in the DWARF data and extends the function one byte backwards.
That helps with making unwinding succeed most of the time, but then the
address is also used for looking up symbols and they can't be resolved.
This change disables program counter rewinding for GHC generated compilation
units.
Some additional context can be found here [2].
[1] https://phabricator.haskell.org/diffusion/GHC/browse/master/compiler/nativeGen/Dwarf/Types.hs;e9ae0cae9eb6a340473b339b5711ae76c6bdd045$399-417
[2] https://ghc.haskell.org/trac/ghc/wiki/DWARF
gdb/ChangeLog:
* dwarf2read.c (process_full_comp_unit): Populate producer_is_ghc.
* frame.c (get_frame_address_in_block): Don't rewind the program
counter for code generated by GHC.
* symtab.h (struct compunit_symtab): Add producer_is_ghc.
---
gdb/ChangeLog | 7 +++++++
gdb/dwarf2read.c | 4 ++++
gdb/frame.c | 7 +++++++
gdb/symtab.h | 3 +++
4 files changed, 21 insertions(+)
Comments
On 2018-02-01 01:54 PM, Bartosz Nitka wrote:
> GHC - the Haskell compiler generates code that violates one of
> GDB's assumptions.
>
> GDB assumes that the address in a frame was generated by the call
> instruction and that it is the address of the call instruction
> plus 1 (I'm rephrasing the comment in get_frame_address_in_block).
> So to get the real address, one has to substract 1. This is doubly
> beneficial because some functions are "noreturn" and don't have
> further instructions after call, so GDB would be looking at gibberish.
> All in all, this is good for C-like languages.
>
> GHC generates completely different code. It uses jumps instead of call
> and manages the stack itself. Furthermore every piece of code is preceeded
> by some metadata called the Info Table. If we substract from the
> program counter it ends up pointing to the metadata, which is undesirable.
> GHC has a workaround for this [1] that works most of the time, it basically
> lies in the DWARF data and extends the function one byte backwards.
> That helps with making unwinding succeed most of the time, but then the
> address is also used for looking up symbols and they can't be resolved.
>
> This change disables program counter rewinding for GHC generated compilation
> units.
>
> Some additional context can be found here [2].
>
> [1] https://phabricator.haskell.org/diffusion/GHC/browse/master/compiler/nativeGen/Dwarf/Types.hs;e9ae0cae9eb6a340473b339b5711ae76c6bdd045$399-417
> [2] https://ghc.haskell.org/trac/ghc/wiki/DWARF
Hi Bartosz,
I am unable to apply your patch, it seems that your mail client messed
up the formatting (among other things, replaced some tabs with spaces).
Would it be possible for you to send it using git-send-email? There are
instructions on setting up git-send-email with gmail here, if needed:
https://www.kernel.org/pub/software/scm/git/docs/git-send-email.html
Thanks,
Simon
@@ -1,3 +1,10 @@
+2018-02-01 Bartosz Nitka <niteria@gmail.com>
+
+ * dwarf2read.c (process_full_comp_unit): Populate producer_is_ghc.
+ * frame.c (get_frame_address_in_block): Don't rewind the program
+ counter for code generated by GHC.
+ * symtab.h (struct compunit_symtab): Add producer_is_ghc.
+
2018-02-01 Yao Qi <yao.qi@linaro.org>
* arm-tdep.c (arm_record_extension_space): Change ret to signed.
@@ -10501,6 +10501,10 @@ process_full_comp_unit (struct
dwarf2_per_cu_data *per_cu,
cust->epilogue_unwind_valid = 1;
cust->call_site_htab = cu->call_site_htab;
+
+ if (startswith (cu->producer,
+ "The Glorious Glasgow Haskell Compilation System"))
+ cust->producer_is_ghc = 1;
}
if (dwarf2_per_objfile->using_index)
@@ -2458,7 +2458,14 @@ get_frame_address_in_block (struct frame_info
*this_frame)
&& (get_frame_type (this_frame) == NORMAL_FRAME
|| get_frame_type (this_frame) == TAILCALL_FRAME
|| get_frame_type (this_frame) == INLINE_FRAME))
+ {
+ /* GHC intermixes metadata (info tables) with code, going back is
+ guaranteed to land us in the metadata. */
+ struct compunit_symtab *cust = find_pc_compunit_symtab (pc);
+ if (cust != NULL && cust->producer_is_ghc)
+ return pc;
return pc - 1;
+ }
return pc;
}
@@ -1432,6 +1432,9 @@ struct compunit_symtab
instruction). This is supported by GCC since 4.5.0. */
unsigned int epilogue_unwind_valid : 1;
+ /* This CU was produced by Glasgow Haskell Compiler */
+ unsigned int producer_is_ghc : 1;
+
/* struct call_site entries for this compilation unit or NULL. */
htab_t call_site_htab;