Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
6df54b80e3182882287a2e6f4cf01a4c01aacbb4
[simgrid.git] / src / mc / mc_unw.cpp
1 /* Copyright (c) 2015. The SimGrid Team.
2  * All rights reserved.                                                     */
3
4 /* This program is free software; you can redistribute it and/or modify it
5  * under the terms of the license (GNU LGPL) which comes with this package. */
6
7 /** \file
8  *  Libunwind support for mc_address_space objects.
9  */
10
11 // We need this for the register indices:
12 // #define _GNU_SOURCE
13
14 #include <string.h>
15
16 // On x86_64, libunwind unw_context_t has the same layout as ucontext_t:
17 #include <sys/ucontext.h>
18
19 #include <libunwind.h>
20
21 #include "mc_object_info.h"
22 #include "mc_process.h"
23 #include "mc_unw.h"
24
25 extern "C" {
26
27 // ***** Implementation
28
29 /** Get frame unwind information (libunwind method)
30  *
31  *  Delegates to the local/ptrace implementation.
32  */
33 static int find_proc_info(unw_addr_space_t as,
34               unw_word_t ip, unw_proc_info_t *pip,
35               int need_unwind_info, void* arg)
36 {
37   mc_unw_context_t context = (mc_unw_context_t) arg;
38   return unw_get_accessors(context->process->unw_underlying_addr_space)->find_proc_info(
39     context->process->unw_underlying_addr_space, ip, pip,
40     need_unwind_info, context->process->unw_underlying_context
41   );
42 }
43
44 /** Release frame unwind information (libunwind method)
45  *
46  *  Delegates to the local/ptrace implementation.
47  */
48 static void put_unwind_info(unw_addr_space_t as,
49               unw_proc_info_t *pip, void* arg)
50 {
51   mc_unw_context_t context = (mc_unw_context_t) arg;
52   return unw_get_accessors(context->process->unw_underlying_addr_space)->put_unwind_info(
53     context->process->unw_underlying_addr_space, pip,
54     context->process->unw_underlying_context
55   );
56 }
57
58 /** (libunwind method)
59  *
60  *  Not implemented.
61  */
62 static int get_dyn_info_list_addr(unw_addr_space_t as,
63               unw_word_t *dilap, void* arg)
64 {
65   mc_unw_context_t context = (mc_unw_context_t) arg;
66   return unw_get_accessors(context->process->unw_underlying_addr_space)->get_dyn_info_list_addr(
67     context->process->unw_underlying_addr_space,
68     dilap,
69     context->process->unw_underlying_context
70   );
71 }
72
73 /** Read from the target address space memory (libunwind method)
74  *
75  *  Delegates to the `mc_process_t`.
76  */
77 static int access_mem(unw_addr_space_t as,
78               unw_word_t addr, unw_word_t *valp,
79               int write, void* arg)
80 {
81   mc_unw_context_t context = (mc_unw_context_t) arg;
82   if (write)
83     return - UNW_EREADONLYREG;
84   context->address_space->read_bytes(valp, sizeof(unw_word_t), addr);
85   return 0;
86 }
87
88 static void* get_reg(unw_context_t* context, unw_regnum_t regnum)
89 {
90 #ifdef __x86_64
91   mcontext_t* mcontext = &context->uc_mcontext;
92   switch (regnum) {
93   case UNW_X86_64_RAX: return &mcontext->gregs[REG_RAX];
94   case UNW_X86_64_RDX: return &mcontext->gregs[REG_RDX];
95   case UNW_X86_64_RCX: return &mcontext->gregs[REG_RCX];
96   case UNW_X86_64_RBX: return &mcontext->gregs[REG_RBX];
97   case UNW_X86_64_RSI: return &mcontext->gregs[REG_RSI];
98   case UNW_X86_64_RDI: return &mcontext->gregs[REG_RDI];
99   case UNW_X86_64_RBP: return &mcontext->gregs[REG_RBP];
100   case UNW_X86_64_RSP: return &mcontext->gregs[REG_RSP];
101   case UNW_X86_64_R8:  return &mcontext->gregs[REG_R8];
102   case UNW_X86_64_R9:  return &mcontext->gregs[REG_R9];
103   case UNW_X86_64_R10: return &mcontext->gregs[REG_R10];
104   case UNW_X86_64_R11: return &mcontext->gregs[REG_R11];
105   case UNW_X86_64_R12: return &mcontext->gregs[REG_R12];
106   case UNW_X86_64_R13: return &mcontext->gregs[REG_R13];
107   case UNW_X86_64_R14: return &mcontext->gregs[REG_R14];
108   case UNW_X86_64_R15: return &mcontext->gregs[REG_R15];
109   case UNW_X86_64_RIP: return &mcontext->gregs[REG_RIP];
110   default: return NULL;
111   }
112 #else
113   return NULL;
114 #endif
115 }
116
117 /** Read a standard register (libunwind method)
118  */
119 static int access_reg(unw_addr_space_t as,
120               unw_regnum_t regnum, unw_word_t *valp,
121               int write, void* arg)
122 {
123   mc_unw_context_t as_context = (mc_unw_context_t) arg;
124   unw_context_t* context = &as_context->context;
125   if (write)
126     return -UNW_EREADONLYREG;
127   greg_t* preg = (greg_t*) get_reg(context, regnum);
128   if (!preg)
129     return -UNW_EBADREG;
130   *valp = *preg;
131   return 0;
132 }
133
134 /** Read a floating-point register (libunwind method)
135  *
136  *  FP registers are caller-saved. The values saved by functions such as
137  *  `getcontext()` is not relevant for the caller. It is not really necessary
138  *  to save and handle them.
139  */
140 static int access_fpreg(unw_addr_space_t as,
141               unw_regnum_t regnum, unw_fpreg_t *fpvalp,
142               int write, void* arg)
143 {
144   return -UNW_EBADREG;
145 }
146
147 /** Resume the execution of the context (libunwind method)
148  *
149  * We don't use this.
150  */
151 static int resume(unw_addr_space_t as,
152               unw_cursor_t *cp, void* arg)
153 {
154   return -UNW_EUNSPEC;
155 }
156
157 /** Find informations about a function (libunwind method)
158  */
159 static int get_proc_name(unw_addr_space_t as,
160               unw_word_t addr, char *bufp,
161               size_t buf_len, unw_word_t *offp,
162               void* arg)
163 {
164   mc_unw_context_t context = (mc_unw_context_t) arg;
165   dw_frame_t frame = MC_process_find_function(context->process, (void*) addr);
166   if (!frame)
167     return - UNW_ENOINFO;
168   *offp = (unw_word_t) frame->low_pc - addr;
169
170   strncpy(bufp, frame->name, buf_len);
171   if (bufp[buf_len - 1]) {
172     bufp[buf_len - 1] = 0;
173     return -UNW_ENOMEM;
174   }
175
176   return 0;
177 }
178
179 // ***** Init
180
181 unw_accessors_t mc_unw_accessors =
182   {
183     &find_proc_info,
184     &put_unwind_info,
185     &get_dyn_info_list_addr,
186     &access_mem,
187     &access_reg,
188     &access_fpreg,
189     &resume,
190     &get_proc_name
191   };
192
193 // ***** Context management
194
195 int mc_unw_init_context(
196   mc_unw_context_t context, mc_process_t process, unw_context_t* c)
197 {
198   context->address_space = process;
199   context->process = process;
200
201   // Take a copy of the context for our own purpose:
202   context->context = *c;
203 #if defined(PROCESSOR_x86_64) || defined(PROCESSOR_i686)
204   // On x86_64, ucontext_t contains a pointer to itself for FP registers.
205   // We don't really need support for FR registers as they are caller saved
206   // and probably never use those fields as libunwind-x86_64 does not read
207   // FP registers from the unw_context_t
208   // but we fix the pointer in order to avoid dangling pointers:
209   context->context.uc_mcontext.fpregs = &(context->context.__fpregs_mem);
210 #else
211   // Do we need to do any fixup like this?
212   #error Target CPU type is not handled.
213 #endif
214
215   return 0;
216 }
217
218 int mc_unw_destroy_context(mc_unw_context_t context)
219 {
220   context->address_space = NULL;
221   context->process = NULL;
222   return 0;
223 }
224
225 // ***** Cursor management
226
227 int mc_unw_init_cursor(unw_cursor_t *cursor, mc_unw_context_t context)
228 {
229   if (!context->process || !context->address_space)
230     return -UNW_EUNSPEC;
231   mc_address_space_t as = context->address_space;
232
233   mc_process_t process = dynamic_cast<mc_process_t>(as);
234   if (process && MC_process_is_self(process))
235     return unw_init_local(cursor, &context->context);
236
237   return unw_init_remote(cursor, context->process->unw_addr_space, context);
238 }
239
240 }