1/* DWARF2 EH unwinding support for DragonFly BSD: AMD x86-64 and x86.
2   Copyright (C) 2014-2022 Free Software Foundation, Inc.
3   Contributed by John Marino <gnugcc@marino.st>
4
5This file is part of GCC.
6
7GCC is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 3, or (at your option)
10any later version.
11
12GCC is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15GNU General Public License for more details.
16
17Under Section 7 of GPL version 3, you are granted additional
18permissions described in the GCC Runtime Library Exception, version
193.1, as published by the Free Software Foundation.
20
21You should have received a copy of the GNU General Public License and
22a copy of the GCC Runtime Library Exception along with this program;
23see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
24<http://www.gnu.org/licenses/>.  */
25
26/* Do code reading to identify a signal frame, and set the frame
27   state data appropriately.  See unwind-dw2.c for the structs. */
28
29#include <sys/types.h>
30#include <sys/sysctl.h>
31#include <sys/param.h>
32#include <signal.h>
33#include <sys/ucontext.h>
34#include <machine/sigframe.h>
35#if __DragonFly_version > 400800
36#include <sys/kinfo.h>
37#endif
38
39
40#define REG_NAME(reg)	sf_uc.uc_mcontext.mc_## reg
41
42#ifdef __x86_64__
43#define MD_FALLBACK_FRAME_STATE_FOR x86_64_dragonfly_fallback_frame_state
44
45
46static int
47x86_64_outside_sigtramp_range (unsigned char *pc)
48{
49  static int sigtramp_range_determined = 0;
50  static unsigned char *sigtramp_start, *sigtramp_end;
51
52  if (sigtramp_range_determined == 0)
53    {
54#if __DragonFly_version > 400800
55      struct kinfo_sigtramp kst = {0};
56      size_t len = sizeof (kst);
57      int mib[3] = { CTL_KERN, KERN_PROC, KERN_PROC_SIGTRAMP };
58
59      sigtramp_range_determined = 1;
60      if (sysctl (mib, 3, &kst, &len, NULL, 0) == 0)
61      {
62        sigtramp_range_determined = 2;
63        sigtramp_start = kst.ksigtramp_start;
64        sigtramp_end   = kst.ksigtramp_end;
65      }
66#else
67      unsigned long ps_strings;
68      size_t len = sizeof (ps_strings);
69      int mib[2] = { CTL_KERN, KERN_PS_STRINGS };
70
71      sigtramp_range_determined = 1;
72      if (sysctl (mib, 2, &ps_strings, &len, NULL, 0) == 0)
73      {
74        sigtramp_range_determined = 2;
75        sigtramp_start = (unsigned char *)ps_strings - 32;
76        sigtramp_end   = (unsigned char *)ps_strings;
77      }
78#endif
79    }
80  if (sigtramp_range_determined < 2)  /* sysctl failed if < 2 */
81    return 1;
82
83  return (pc < sigtramp_start || pc >= sigtramp_end );
84}
85
86
87static _Unwind_Reason_Code
88x86_64_dragonfly_fallback_frame_state
89(struct _Unwind_Context *context, _Unwind_FrameState *fs)
90{
91  struct sigframe *sf;
92  long new_cfa;
93
94  if (x86_64_outside_sigtramp_range(context->ra))
95    return _URC_END_OF_STACK;
96
97  sf = (struct sigframe *) context->cfa;
98  new_cfa = sf->REG_NAME(rsp);
99  fs->regs.cfa_how = CFA_REG_OFFSET;
100  /* Register 7 is rsp  */
101  fs->regs.cfa_reg = 7;
102  fs->regs.cfa_offset = new_cfa - (long) context->cfa;
103
104  /* The SVR4 register numbering macros aren't usable in libgcc.  */
105  fs->regs.reg[0].how = REG_SAVED_OFFSET;
106  fs->regs.reg[0].loc.offset = (long)&sf->REG_NAME(rax) - new_cfa;
107  fs->regs.reg[1].how = REG_SAVED_OFFSET;
108  fs->regs.reg[1].loc.offset = (long)&sf->REG_NAME(rdx) - new_cfa;
109  fs->regs.reg[2].how = REG_SAVED_OFFSET;
110  fs->regs.reg[2].loc.offset = (long)&sf->REG_NAME(rcx) - new_cfa;
111  fs->regs.reg[3].how = REG_SAVED_OFFSET;
112  fs->regs.reg[3].loc.offset = (long)&sf->REG_NAME(rbx) - new_cfa;
113  fs->regs.reg[4].how = REG_SAVED_OFFSET;
114  fs->regs.reg[4].loc.offset = (long)&sf->REG_NAME(rsi) - new_cfa;
115  fs->regs.reg[5].how = REG_SAVED_OFFSET;
116  fs->regs.reg[5].loc.offset = (long)&sf->REG_NAME(rdi) - new_cfa;
117  fs->regs.reg[6].how = REG_SAVED_OFFSET;
118  fs->regs.reg[6].loc.offset = (long)&sf->REG_NAME(rbp) - new_cfa;
119  fs->regs.reg[8].how = REG_SAVED_OFFSET;
120  fs->regs.reg[8].loc.offset = (long)&sf->REG_NAME(r8) - new_cfa;
121  fs->regs.reg[9].how = REG_SAVED_OFFSET;
122  fs->regs.reg[9].loc.offset = (long)&sf->REG_NAME(r9) - new_cfa;
123  fs->regs.reg[10].how = REG_SAVED_OFFSET;
124  fs->regs.reg[10].loc.offset = (long)&sf->REG_NAME(r10) - new_cfa;
125  fs->regs.reg[11].how = REG_SAVED_OFFSET;
126  fs->regs.reg[11].loc.offset = (long)&sf->REG_NAME(r11) - new_cfa;
127  fs->regs.reg[12].how = REG_SAVED_OFFSET;
128  fs->regs.reg[12].loc.offset = (long)&sf->REG_NAME(r12) - new_cfa;
129  fs->regs.reg[13].how = REG_SAVED_OFFSET;
130  fs->regs.reg[13].loc.offset = (long)&sf->REG_NAME(r13) - new_cfa;
131  fs->regs.reg[14].how = REG_SAVED_OFFSET;
132  fs->regs.reg[14].loc.offset = (long)&sf->REG_NAME(r14) - new_cfa;
133  fs->regs.reg[15].how = REG_SAVED_OFFSET;
134  fs->regs.reg[15].loc.offset = (long)&sf->REG_NAME(r15) - new_cfa;
135  fs->regs.reg[16].how = REG_SAVED_OFFSET;
136  fs->regs.reg[16].loc.offset = (long)&sf->REG_NAME(rip) - new_cfa;
137  fs->retaddr_column = 16;
138  fs->signal_frame = 1;
139  return _URC_NO_REASON;
140}
141
142#else /* Next section is for i386  */
143
144#define MD_FALLBACK_FRAME_STATE_FOR x86_dragonfly_fallback_frame_state
145
146
147static void
148x86_sigtramp_range (unsigned char **start, unsigned char **end)
149{
150  unsigned long ps_strings;
151  int mib[2];
152  size_t len;
153
154  mib[0] = CTL_KERN;
155  mib[1] = KERN_PS_STRINGS;
156  len = sizeof (ps_strings);
157  sysctl (mib, 2, &ps_strings, &len, NULL, 0);
158
159  *start = (unsigned char *)ps_strings - 128;
160  *end   = (unsigned char *)ps_strings;
161}
162
163
164static _Unwind_Reason_Code
165x86_dragonfly_fallback_frame_state
166(struct _Unwind_Context *context, _Unwind_FrameState *fs)
167{
168  unsigned char *pc = context->ra;
169  unsigned char *sigtramp_start, *sigtramp_end;
170  struct sigframe *sf;
171  long new_cfa;
172
173  x86_sigtramp_range(&sigtramp_start, &sigtramp_end);
174
175  if (pc >= sigtramp_end || pc < sigtramp_start)
176    return _URC_END_OF_STACK;
177
178  sf = (struct sigframe *) context->cfa;
179  new_cfa = sf->REG_NAME(esp);
180  fs->regs.cfa_how = CFA_REG_OFFSET;
181  fs->regs.cfa_reg = 4;
182  fs->regs.cfa_offset = new_cfa - (long) context->cfa;
183
184  /* The SVR4 register numbering macros aren't usable in libgcc.  */
185  fs->regs.reg[0].how = REG_SAVED_OFFSET;
186  fs->regs.reg[0].loc.offset = (long)&sf->REG_NAME(eax) - new_cfa;
187  fs->regs.reg[3].how = REG_SAVED_OFFSET;
188  fs->regs.reg[3].loc.offset = (long)&sf->REG_NAME(ebx) - new_cfa;
189  fs->regs.reg[1].how = REG_SAVED_OFFSET;
190  fs->regs.reg[1].loc.offset = (long)&sf->REG_NAME(ecx) - new_cfa;
191  fs->regs.reg[2].how = REG_SAVED_OFFSET;
192  fs->regs.reg[2].loc.offset = (long)&sf->REG_NAME(edx) - new_cfa;
193  fs->regs.reg[6].how = REG_SAVED_OFFSET;
194  fs->regs.reg[6].loc.offset = (long)&sf->REG_NAME(esi) - new_cfa;
195  fs->regs.reg[7].how = REG_SAVED_OFFSET;
196  fs->regs.reg[7].loc.offset = (long)&sf->REG_NAME(edi) - new_cfa;
197  fs->regs.reg[5].how = REG_SAVED_OFFSET;
198  fs->regs.reg[5].loc.offset = (long)&sf->REG_NAME(ebp) - new_cfa;
199  fs->regs.reg[8].how = REG_SAVED_OFFSET;
200  fs->regs.reg[8].loc.offset = (long)&sf->REG_NAME(eip) - new_cfa;
201  fs->retaddr_column = 8;
202  fs->signal_frame = 1;
203  return _URC_NO_REASON;
204}
205#endif /* ifdef __x86_64__  */
206