1 /* Copyright (c) 2015-2023. The SimGrid Team. All rights reserved. */
3 /* This program is free software; you can redistribute it and/or modify it
4 * under the terms of the license (GNU LGPL) which comes with this package. */
6 /** \file Libunwind support for mc_address_space objects. */
8 // We need this for the register indices:
11 #include "src/mc/inspect/mc_unw.hpp"
12 #include "src/mc/inspect/Frame.hpp"
13 #include "src/mc/sosp/RemoteProcessMemory.hpp"
17 // On x86_64, libunwind unw_context_t has the same layout as ucontext_t:
18 #include <sys/types.h>
19 #include <sys/ucontext.h>
21 typedef register_t greg_t;
24 #include <libunwind.h>
26 namespace simgrid::mc {
28 // ***** Implementation
30 /** Get frame unwind information (libunwind method)
32 * Delegates to the local/ptrace implementation.
34 int UnwindContext::find_proc_info(unw_addr_space_t /*as*/, unw_word_t ip, unw_proc_info_t* pip, int need_unwind_info,
37 const simgrid::mc::UnwindContext* context = (simgrid::mc::UnwindContext*)arg;
38 return unw_get_accessors(context->process_->unw_underlying_addr_space)
39 ->find_proc_info(context->process_->unw_underlying_addr_space, ip, pip, need_unwind_info,
40 context->process_->unw_underlying_context);
43 /** Release frame unwind information (libunwind method)
45 * Delegates to the local/ptrace implementation.
47 void UnwindContext::put_unwind_info(unw_addr_space_t /*as*/, unw_proc_info_t* pip, void* arg) noexcept
49 const simgrid::mc::UnwindContext* context = (simgrid::mc::UnwindContext*)arg;
50 return unw_get_accessors(context->process_->unw_underlying_addr_space)
51 ->put_unwind_info(context->process_->unw_underlying_addr_space, pip, context->process_->unw_underlying_context);
54 /** (libunwind method)
58 int UnwindContext::get_dyn_info_list_addr(unw_addr_space_t /*as*/, unw_word_t* dilap, void* arg) noexcept
60 const simgrid::mc::UnwindContext* context = (simgrid::mc::UnwindContext*)arg;
61 return unw_get_accessors(context->process_->unw_underlying_addr_space)
62 ->get_dyn_info_list_addr(context->process_->unw_underlying_addr_space, dilap,
63 context->process_->unw_underlying_context);
66 /** Read from the target address space memory (libunwind method)
68 * Delegates to the `simgrid::mc::Process*`.
70 int UnwindContext::access_mem(unw_addr_space_t /*as*/, unw_word_t addr, unw_word_t* valp, int write, void* arg) noexcept
72 const simgrid::mc::UnwindContext* context = (simgrid::mc::UnwindContext*)arg;
74 return -UNW_EREADONLYREG;
75 context->address_space_->read_bytes(valp, sizeof(unw_word_t), remote(addr));
79 void* UnwindContext::get_reg(unw_context_t* context, unw_regnum_t regnum) noexcept
82 mcontext_t* mcontext = &context->uc_mcontext;
86 return &mcontext->gregs[REG_RAX];
88 return &mcontext->gregs[REG_RDX];
90 return &mcontext->gregs[REG_RCX];
92 return &mcontext->gregs[REG_RBX];
94 return &mcontext->gregs[REG_RSI];
96 return &mcontext->gregs[REG_RDI];
98 return &mcontext->gregs[REG_RBP];
100 return &mcontext->gregs[REG_RSP];
102 return &mcontext->gregs[REG_R8];
104 return &mcontext->gregs[REG_R9];
106 return &mcontext->gregs[REG_R10];
108 return &mcontext->gregs[REG_R11];
110 return &mcontext->gregs[REG_R12];
112 return &mcontext->gregs[REG_R13];
114 return &mcontext->gregs[REG_R14];
116 return &mcontext->gregs[REG_R15];
118 return &mcontext->gregs[REG_RIP];
119 #elif defined __FreeBSD__
121 return &mcontext->mc_rax;
123 return &mcontext->mc_rdx;
125 return &mcontext->mc_rcx;
127 return &mcontext->mc_rbx;
129 return &mcontext->mc_rsi;
131 return &mcontext->mc_rdi;
133 return &mcontext->mc_rbp;
135 return &mcontext->mc_rsp;
137 return &mcontext->mc_r8;
139 return &mcontext->mc_r9;
141 return &mcontext->mc_r10;
143 return &mcontext->mc_r11;
145 return &mcontext->mc_r12;
147 return &mcontext->mc_r13;
149 return &mcontext->mc_r14;
151 return &mcontext->mc_r15;
153 return &mcontext->mc_rip;
155 #error "Unable to get register from ucontext, please add your case"
165 /** Read a standard register (libunwind method)
167 int UnwindContext::access_reg(unw_addr_space_t /*as*/, unw_regnum_t regnum, unw_word_t* valp, int write,
170 auto* as_context = static_cast<simgrid::mc::UnwindContext*>(arg);
171 unw_context_t* context = &as_context->unwind_context_;
173 return -UNW_EREADONLYREG;
174 const greg_t* preg = (greg_t*)get_reg(context, regnum);
181 /** Find information about a function (libunwind method)
183 int UnwindContext::get_proc_name(unw_addr_space_t /*as*/, unw_word_t addr, char* bufp, size_t buf_len, unw_word_t* offp,
186 const simgrid::mc::UnwindContext* context = (simgrid::mc::UnwindContext*)arg;
187 const simgrid::mc::Frame* frame = context->process_->find_function(remote(addr));
190 *offp = (unw_word_t)frame->range.begin() - addr;
192 strncpy(bufp, frame->name.c_str(), buf_len);
193 if (bufp[buf_len - 1]) {
194 bufp[buf_len - 1] = 0;
203 unw_addr_space_t UnwindContext::createUnwindAddressSpace()
205 /** Virtual table for our `libunwind` implementation
207 * Stack unwinding on a `simgrid::mc::Process*` (for memory, unwinding information)
208 * and `ucontext_t` (for processor registers).
210 * It works with the `simgrid::mc::UnwindContext` context.
212 * Use nullptr as access_fpreg and resume, as we don't need them.
214 unw_accessors_t accessors = {&find_proc_info, &put_unwind_info, &get_dyn_info_list_addr, &access_mem, &access_reg,
215 nullptr, nullptr, &get_proc_name};
216 return unw_create_addr_space(&accessors, BYTE_ORDER);
219 void UnwindContext::initialize(simgrid::mc::RemoteProcessMemory& process_memory, const unw_context_t* c)
221 this->address_space_ = &process_memory;
222 this->process_ = &process_memory;
224 // Take a copy of the context for our own purpose:
225 this->unwind_context_ = *c;
226 #if SIMGRID_PROCESSOR_x86_64 || SIMGRID_PROCESSOR_i686
228 // On x86_64, ucontext_t contains a pointer to itself for FP registers.
229 // We don't really need support for FR registers as they are caller saved
230 // and probably never use those fields as libunwind-x86_64 does not read
231 // FP registers from the unw_context_t
232 // Let's ignore this and see what happens:
233 this->unwind_context_.uc_mcontext.fpregs = nullptr;
235 #elif SIMGRID_PROCESSOR_arm64
237 // On ARM64, ucontext_t doesn't contain `fpregs` and the FP registers
238 // are instead held in the `__reserved` field of the struct. It doesn't
239 // appear anything needs to be done here, although this should be verified
242 // Do we need to do any fixup like this?
243 #error Target CPU type is not handled.
247 unw_cursor_t UnwindContext::cursor()
250 xbt_assert(process_ != nullptr && address_space_ != nullptr &&
251 unw_init_remote(&cursor, process_->unw_addr_space, this) == 0,
252 "UnwindContext not initialized");
256 } // namespace simgrid::mc