1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | // See the LICENSE file in the project root for more information. |
4 | |
5 | #include "createdump.h" |
6 | #include <asm/ptrace.h> |
7 | |
8 | #ifndef THUMB_CODE |
9 | #define THUMB_CODE 1 |
10 | #endif |
11 | |
12 | #ifndef __GLIBC__ |
13 | typedef int __ptrace_request; |
14 | #endif |
15 | |
16 | #define FPREG_ErrorOffset(fpregs) *(DWORD*)&((fpregs).rip) |
17 | #define FPREG_ErrorSelector(fpregs) *(((WORD*)&((fpregs).rip)) + 2) |
18 | #define FPREG_DataOffset(fpregs) *(DWORD*)&((fpregs).rdp) |
19 | #define FPREG_DataSelector(fpregs) *(((WORD*)&((fpregs).rdp)) + 2) |
20 | |
21 | extern CrashInfo* g_crashInfo; |
22 | |
23 | ThreadInfo::ThreadInfo(pid_t tid) : |
24 | m_tid(tid) |
25 | { |
26 | } |
27 | |
28 | ThreadInfo::~ThreadInfo() |
29 | { |
30 | } |
31 | |
32 | bool |
33 | ThreadInfo::Initialize(ICLRDataTarget* pDataTarget) |
34 | { |
35 | if (!CrashInfo::GetStatus(m_tid, &m_ppid, &m_tgid, nullptr)) |
36 | { |
37 | return false; |
38 | } |
39 | if (pDataTarget != nullptr) |
40 | { |
41 | if (!GetRegistersWithDataTarget(pDataTarget)) |
42 | { |
43 | return false; |
44 | } |
45 | } |
46 | else { |
47 | if (!GetRegistersWithPTrace()) |
48 | { |
49 | return false; |
50 | } |
51 | } |
52 | |
53 | #if defined(__arm__) |
54 | TRACE("Thread %04x PC %08lx SP %08lx\n" , m_tid, (unsigned long)m_gpRegisters.ARM_pc, (unsigned long)m_gpRegisters.ARM_sp); |
55 | #else |
56 | TRACE("Thread %04x RIP %016llx RSP %016llx\n" , m_tid, (unsigned long long)m_gpRegisters.rip, (unsigned long long)m_gpRegisters.rsp); |
57 | #endif |
58 | return true; |
59 | } |
60 | |
61 | void |
62 | ThreadInfo::ResumeThread() |
63 | { |
64 | if (ptrace(PTRACE_DETACH, m_tid, nullptr, nullptr) != -1) |
65 | { |
66 | int waitStatus; |
67 | waitpid(m_tid, &waitStatus, __WALL); |
68 | } |
69 | } |
70 | |
71 | // Helper for UnwindNativeFrames |
72 | static void |
73 | GetFrameLocation(CONTEXT* pContext, uint64_t* ip, uint64_t* sp) |
74 | { |
75 | #if defined(__x86_64__) |
76 | *ip = pContext->Rip; |
77 | *sp = pContext->Rsp; |
78 | #elif defined(__i386__) |
79 | *ip = pContext->Eip; |
80 | *sp = pContext->Esp; |
81 | #elif defined(__arm__) |
82 | *ip = pContext->Pc & ~THUMB_CODE; |
83 | *sp = pContext->Sp; |
84 | #endif |
85 | } |
86 | |
87 | // Helper for UnwindNativeFrames |
88 | static BOOL |
89 | ReadMemoryAdapter(PVOID address, PVOID buffer, SIZE_T size) |
90 | { |
91 | return g_crashInfo->ReadMemory(address, buffer, size); |
92 | } |
93 | |
94 | void |
95 | ThreadInfo::UnwindNativeFrames(CrashInfo& crashInfo, CONTEXT* pContext) |
96 | { |
97 | // For each native frame |
98 | while (true) |
99 | { |
100 | uint64_t ip = 0, sp = 0; |
101 | GetFrameLocation(pContext, &ip, &sp); |
102 | |
103 | TRACE("Unwind: sp %" PRIA PRIx64 " ip %" PRIA PRIx64 "\n" , sp, ip); |
104 | if (ip == 0) { |
105 | break; |
106 | } |
107 | // Add two pages around the instruction pointer to the core dump |
108 | crashInfo.InsertMemoryRegion(ip - PAGE_SIZE, PAGE_SIZE * 2); |
109 | |
110 | // Look up the ip address to get the module base address |
111 | uint64_t baseAddress = crashInfo.GetBaseAddress(ip); |
112 | if (baseAddress == 0) { |
113 | TRACE("Unwind: module base not found ip %" PRIA PRIx64 "\n" , ip); |
114 | break; |
115 | } |
116 | |
117 | // Unwind the native frame adding all the memory accessed to the |
118 | // core dump via the read memory adapter. |
119 | if (!PAL_VirtualUnwindOutOfProc(pContext, nullptr, baseAddress, ReadMemoryAdapter)) { |
120 | TRACE("Unwind: PAL_VirtualUnwindOutOfProc returned false\n" ); |
121 | break; |
122 | } |
123 | } |
124 | } |
125 | |
126 | bool |
127 | ThreadInfo::UnwindThread(CrashInfo& crashInfo, IXCLRDataProcess* pClrDataProcess) |
128 | { |
129 | TRACE("Unwind: thread %04x\n" , Tid()); |
130 | |
131 | // Get starting native context for the thread |
132 | CONTEXT context; |
133 | GetThreadContext(CONTEXT_ALL, &context); |
134 | |
135 | // Unwind the native frames at the top of the stack |
136 | UnwindNativeFrames(crashInfo, &context); |
137 | |
138 | if (pClrDataProcess != nullptr) |
139 | { |
140 | ReleaseHolder<IXCLRDataTask> pTask; |
141 | ReleaseHolder<IXCLRDataStackWalk> pStackwalk; |
142 | |
143 | // Get the managed stack walker for this thread |
144 | if (SUCCEEDED(pClrDataProcess->GetTaskByOSThreadID(Tid(), &pTask))) |
145 | { |
146 | pTask->CreateStackWalk( |
147 | CLRDATA_SIMPFRAME_UNRECOGNIZED | |
148 | CLRDATA_SIMPFRAME_MANAGED_METHOD | |
149 | CLRDATA_SIMPFRAME_RUNTIME_MANAGED_CODE | |
150 | CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE, |
151 | &pStackwalk); |
152 | } |
153 | |
154 | // For each managed frame (if any) |
155 | if (pStackwalk != nullptr) |
156 | { |
157 | TRACE("Unwind: managed frames\n" ); |
158 | do |
159 | { |
160 | // Get the managed stack frame context |
161 | if (pStackwalk->GetContext(CONTEXT_ALL, sizeof(context), nullptr, (BYTE *)&context) != S_OK) { |
162 | TRACE("Unwind: stack walker GetContext FAILED\n" ); |
163 | break; |
164 | } |
165 | |
166 | // Unwind all the native frames after the managed frame |
167 | UnwindNativeFrames(crashInfo, &context); |
168 | |
169 | } while (pStackwalk->Next() == S_OK); |
170 | } |
171 | } |
172 | |
173 | return true; |
174 | } |
175 | |
176 | bool |
177 | ThreadInfo::GetRegistersWithPTrace() |
178 | { |
179 | if (ptrace((__ptrace_request)PTRACE_GETREGS, m_tid, nullptr, &m_gpRegisters) == -1) |
180 | { |
181 | fprintf(stderr, "ptrace(GETREGS, %d) FAILED %d (%s)\n" , m_tid, errno, strerror(errno)); |
182 | return false; |
183 | } |
184 | if (ptrace((__ptrace_request)PTRACE_GETFPREGS, m_tid, nullptr, &m_fpRegisters) == -1) |
185 | { |
186 | fprintf(stderr, "ptrace(GETFPREGS, %d) FAILED %d (%s)\n" , m_tid, errno, strerror(errno)); |
187 | return false; |
188 | } |
189 | #if defined(__i386__) |
190 | if (ptrace((__ptrace_request)PTRACE_GETFPXREGS, m_tid, nullptr, &m_fpxRegisters) == -1) |
191 | { |
192 | fprintf(stderr, "ptrace(GETFPXREGS, %d) FAILED %d (%s)\n" , m_tid, errno, strerror(errno)); |
193 | return false; |
194 | } |
195 | #elif defined(__arm__) && defined(__VFP_FP__) && !defined(__SOFTFP__) |
196 | |
197 | #if defined(ARM_VFPREGS_SIZE) |
198 | assert(sizeof(m_vfpRegisters) == ARM_VFPREGS_SIZE); |
199 | #endif |
200 | |
201 | if (ptrace((__ptrace_request)PTRACE_GETVFPREGS, m_tid, nullptr, &m_vfpRegisters) == -1) |
202 | { |
203 | fprintf(stderr, "ptrace(PTRACE_GETVFPREGS, %d) FAILED %d (%s)\n" , m_tid, errno, strerror(errno)); |
204 | return false; |
205 | } |
206 | #endif |
207 | return true; |
208 | } |
209 | |
210 | bool |
211 | ThreadInfo::GetRegistersWithDataTarget(ICLRDataTarget* pDataTarget) |
212 | { |
213 | CONTEXT context; |
214 | context.ContextFlags = CONTEXT_ALL; |
215 | if (pDataTarget->GetThreadContext(m_tid, context.ContextFlags, sizeof(context), reinterpret_cast<PBYTE>(&context)) != S_OK) |
216 | { |
217 | return false; |
218 | } |
219 | #if defined(__x86_64__) |
220 | m_gpRegisters.rbp = context.Rbp; |
221 | m_gpRegisters.rip = context.Rip; |
222 | m_gpRegisters.cs = context.SegCs; |
223 | m_gpRegisters.eflags = context.EFlags; |
224 | m_gpRegisters.ss = context.SegSs; |
225 | m_gpRegisters.rsp = context.Rsp; |
226 | m_gpRegisters.rdi = context.Rdi; |
227 | |
228 | m_gpRegisters.rsi = context.Rsi; |
229 | m_gpRegisters.rbx = context.Rbx; |
230 | m_gpRegisters.rdx = context.Rdx; |
231 | m_gpRegisters.rcx = context.Rcx; |
232 | m_gpRegisters.rax = context.Rax; |
233 | m_gpRegisters.orig_rax = context.Rax; |
234 | m_gpRegisters.r8 = context.R8; |
235 | m_gpRegisters.r9 = context.R9; |
236 | m_gpRegisters.r10 = context.R10; |
237 | m_gpRegisters.r11 = context.R11; |
238 | m_gpRegisters.r12 = context.R12; |
239 | m_gpRegisters.r13 = context.R13; |
240 | m_gpRegisters.r14 = context.R14; |
241 | m_gpRegisters.r15 = context.R15; |
242 | |
243 | m_gpRegisters.ds = context.SegDs; |
244 | m_gpRegisters.es = context.SegEs; |
245 | m_gpRegisters.fs = context.SegFs; |
246 | m_gpRegisters.gs = context.SegGs; |
247 | m_gpRegisters.fs_base = 0; |
248 | m_gpRegisters.gs_base = 0; |
249 | |
250 | m_fpRegisters.cwd = context.FltSave.ControlWord; |
251 | m_fpRegisters.swd = context.FltSave.StatusWord; |
252 | m_fpRegisters.ftw = context.FltSave.TagWord; |
253 | m_fpRegisters.fop = context.FltSave.ErrorOpcode; |
254 | |
255 | FPREG_ErrorOffset(m_fpRegisters) = context.FltSave.ErrorOffset; |
256 | FPREG_ErrorSelector(m_fpRegisters) = context.FltSave.ErrorSelector; |
257 | FPREG_DataOffset(m_fpRegisters) = context.FltSave.DataOffset; |
258 | FPREG_DataSelector(m_fpRegisters) = context.FltSave.DataSelector; |
259 | |
260 | m_fpRegisters.mxcsr = context.FltSave.MxCsr; |
261 | m_fpRegisters.mxcr_mask = context.FltSave.MxCsr_Mask; |
262 | |
263 | assert(sizeof(context.FltSave.FloatRegisters) == sizeof(m_fpRegisters.st_space)); |
264 | memcpy(m_fpRegisters.st_space, context.FltSave.FloatRegisters, sizeof(m_fpRegisters.st_space)); |
265 | |
266 | assert(sizeof(context.FltSave.XmmRegisters) == sizeof(m_fpRegisters.xmm_space)); |
267 | memcpy(m_fpRegisters.xmm_space, context.FltSave.XmmRegisters, sizeof(m_fpRegisters.xmm_space)); |
268 | #elif defined(__arm__) |
269 | m_gpRegisters.ARM_sp = context.Sp; |
270 | m_gpRegisters.ARM_lr = context.Lr; |
271 | m_gpRegisters.ARM_pc = context.Pc; |
272 | m_gpRegisters.ARM_cpsr = context.Cpsr; |
273 | |
274 | m_gpRegisters.ARM_r0 = context.R0; |
275 | m_gpRegisters.ARM_ORIG_r0 = context.R0; |
276 | m_gpRegisters.ARM_r1 = context.R1; |
277 | m_gpRegisters.ARM_r2 = context.R2; |
278 | m_gpRegisters.ARM_r3 = context.R3; |
279 | m_gpRegisters.ARM_r4 = context.R4; |
280 | m_gpRegisters.ARM_r5 = context.R5; |
281 | m_gpRegisters.ARM_r6 = context.R6; |
282 | m_gpRegisters.ARM_r7 = context.R7; |
283 | m_gpRegisters.ARM_r8 = context.R8; |
284 | m_gpRegisters.ARM_r9 = context.R9; |
285 | m_gpRegisters.ARM_r10 = context.R10; |
286 | m_gpRegisters.ARM_fp = context.R11; |
287 | m_gpRegisters.ARM_ip = context.R12; |
288 | |
289 | #if defined(__VFP_FP__) && !defined(__SOFTFP__) |
290 | m_vfpRegisters.fpscr = context.Fpscr; |
291 | |
292 | assert(sizeof(context.D) == sizeof(m_vfpRegisters.fpregs)); |
293 | memcpy(m_vfpRegisters.fpregs, context.D, sizeof(context.D)); |
294 | #endif |
295 | #else |
296 | #error Platform not supported |
297 | #endif |
298 | return true; |
299 | } |
300 | |
301 | void |
302 | ThreadInfo::GetThreadStack(CrashInfo& crashInfo) |
303 | { |
304 | uint64_t startAddress; |
305 | size_t size; |
306 | |
307 | #if defined(__arm__) |
308 | startAddress = m_gpRegisters.ARM_sp & PAGE_MASK; |
309 | #else |
310 | startAddress = m_gpRegisters.rsp & PAGE_MASK; |
311 | #endif |
312 | size = 4 * PAGE_SIZE; |
313 | |
314 | MemoryRegion search(0, startAddress, startAddress + PAGE_SIZE); |
315 | const MemoryRegion* region = CrashInfo::SearchMemoryRegions(crashInfo.OtherMappings(), search); |
316 | if (region != nullptr) { |
317 | |
318 | // Use the mapping found for the size of the thread's stack |
319 | size = region->EndAddress() - startAddress; |
320 | |
321 | if (g_diagnostics) |
322 | { |
323 | TRACE("Thread %04x stack found in other mapping (size %08zx): " , m_tid, size); |
324 | region->Trace(); |
325 | } |
326 | } |
327 | crashInfo.InsertMemoryRegion(startAddress, size); |
328 | } |
329 | |
330 | void |
331 | ThreadInfo::GetThreadContext(uint32_t flags, CONTEXT* context) const |
332 | { |
333 | context->ContextFlags = flags; |
334 | #if defined(__x86_64__) |
335 | if ((flags & CONTEXT_CONTROL) == CONTEXT_CONTROL) |
336 | { |
337 | context->Rbp = m_gpRegisters.rbp; |
338 | context->Rip = m_gpRegisters.rip; |
339 | context->SegCs = m_gpRegisters.cs; |
340 | context->EFlags = m_gpRegisters.eflags; |
341 | context->SegSs = m_gpRegisters.ss; |
342 | context->Rsp = m_gpRegisters.rsp; |
343 | } |
344 | if ((flags & CONTEXT_INTEGER) == CONTEXT_INTEGER) |
345 | { |
346 | context->Rdi = m_gpRegisters.rdi; |
347 | context->Rsi = m_gpRegisters.rsi; |
348 | context->Rbx = m_gpRegisters.rbx; |
349 | context->Rdx = m_gpRegisters.rdx; |
350 | context->Rcx = m_gpRegisters.rcx; |
351 | context->Rax = m_gpRegisters.rax; |
352 | context->R8 = m_gpRegisters.r8; |
353 | context->R9 = m_gpRegisters.r9; |
354 | context->R10 = m_gpRegisters.r10; |
355 | context->R11 = m_gpRegisters.r11; |
356 | context->R12 = m_gpRegisters.r12; |
357 | context->R13 = m_gpRegisters.r13; |
358 | context->R14 = m_gpRegisters.r14; |
359 | context->R15 = m_gpRegisters.r15; |
360 | } |
361 | if ((flags & CONTEXT_SEGMENTS) == CONTEXT_SEGMENTS) |
362 | { |
363 | context->SegDs = m_gpRegisters.ds; |
364 | context->SegEs = m_gpRegisters.es; |
365 | context->SegFs = m_gpRegisters.fs; |
366 | context->SegGs = m_gpRegisters.gs; |
367 | } |
368 | if ((flags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT) |
369 | { |
370 | context->FltSave.ControlWord = m_fpRegisters.cwd; |
371 | context->FltSave.StatusWord = m_fpRegisters.swd; |
372 | context->FltSave.TagWord = m_fpRegisters.ftw; |
373 | context->FltSave.ErrorOpcode = m_fpRegisters.fop; |
374 | |
375 | context->FltSave.ErrorOffset = FPREG_ErrorOffset(m_fpRegisters); |
376 | context->FltSave.ErrorSelector = FPREG_ErrorSelector(m_fpRegisters); |
377 | context->FltSave.DataOffset = FPREG_DataOffset(m_fpRegisters); |
378 | context->FltSave.DataSelector = FPREG_DataSelector(m_fpRegisters); |
379 | |
380 | context->FltSave.MxCsr = m_fpRegisters.mxcsr; |
381 | context->FltSave.MxCsr_Mask = m_fpRegisters.mxcr_mask; |
382 | |
383 | assert(sizeof(context->FltSave.FloatRegisters) == sizeof(m_fpRegisters.st_space)); |
384 | memcpy(context->FltSave.FloatRegisters, m_fpRegisters.st_space, sizeof(context->FltSave.FloatRegisters)); |
385 | |
386 | assert(sizeof(context->FltSave.XmmRegisters) == sizeof(m_fpRegisters.xmm_space)); |
387 | memcpy(context->FltSave.XmmRegisters, m_fpRegisters.xmm_space, sizeof(context->FltSave.XmmRegisters)); |
388 | } |
389 | // TODO: debug registers? |
390 | #elif defined(__arm__) |
391 | if ((flags & CONTEXT_CONTROL) == CONTEXT_CONTROL) |
392 | { |
393 | context->Sp = m_gpRegisters.ARM_sp; |
394 | context->Lr = m_gpRegisters.ARM_lr; |
395 | context->Pc = m_gpRegisters.ARM_pc; |
396 | context->Cpsr = m_gpRegisters.ARM_cpsr; |
397 | |
398 | } |
399 | if ((flags & CONTEXT_INTEGER) == CONTEXT_INTEGER) |
400 | { |
401 | context->R0 = m_gpRegisters.ARM_r0; |
402 | context->R1 = m_gpRegisters.ARM_r1; |
403 | context->R2 = m_gpRegisters.ARM_r2; |
404 | context->R3 = m_gpRegisters.ARM_r3; |
405 | context->R4 = m_gpRegisters.ARM_r4; |
406 | context->R5 = m_gpRegisters.ARM_r5; |
407 | context->R6 = m_gpRegisters.ARM_r6; |
408 | context->R7 = m_gpRegisters.ARM_r7; |
409 | context->R8 = m_gpRegisters.ARM_r8; |
410 | context->R9 = m_gpRegisters.ARM_r9; |
411 | context->R10 = m_gpRegisters.ARM_r10; |
412 | context->R11 = m_gpRegisters.ARM_fp; |
413 | context->R12 = m_gpRegisters.ARM_ip; |
414 | } |
415 | if ((flags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT) |
416 | { |
417 | #if defined(__VFP_FP__) && !defined(__SOFTFP__) |
418 | context->Fpscr = m_vfpRegisters.fpscr; |
419 | |
420 | assert(sizeof(context->D) == sizeof(m_vfpRegisters.fpregs)); |
421 | memcpy(context->D, m_vfpRegisters.fpregs, sizeof(context->D)); |
422 | #endif |
423 | } |
424 | #else |
425 | #error Platform not supported |
426 | #endif |
427 | } |
428 | |