| 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 | |