| // Copyright 2014 The Crashpad Authors. All rights reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "snapshot/cpu_context.h" |
| |
| #include <stddef.h> |
| #include <string.h> |
| |
| #include "base/logging.h" |
| #include "base/stl_util.h" |
| #include "util/misc/arraysize.h" |
| #include "util/misc/implicit_cast.h" |
| |
| namespace crashpad { |
| |
| namespace { |
| |
| // Sanity-check complex structures to ensure interoperability. |
| static_assert(sizeof(CPUContextX86::Fsave) == 108, "CPUContextX86::Fsave size"); |
| static_assert(sizeof(CPUContextX86::Fxsave) == 512, |
| "CPUContextX86::Fxsave size"); |
| static_assert(sizeof(CPUContextX86_64::Fxsave) == 512, |
| "CPUContextX86_64::Fxsave size"); |
| |
| enum { |
| kX87TagValid = 0, |
| kX87TagZero, |
| kX87TagSpecial, |
| kX87TagEmpty, |
| }; |
| |
| } // namespace |
| |
| // static |
| void CPUContextX86::FxsaveToFsave(const Fxsave& fxsave, Fsave* fsave) { |
| fsave->fcw = fxsave.fcw; |
| fsave->reserved_1 = 0; |
| fsave->fsw = fxsave.fsw; |
| fsave->reserved_2 = 0; |
| fsave->ftw = FxsaveToFsaveTagWord(fxsave.fsw, fxsave.ftw, fxsave.st_mm); |
| fsave->reserved_3 = 0; |
| fsave->fpu_ip = fxsave.fpu_ip; |
| fsave->fpu_cs = fxsave.fpu_cs; |
| fsave->fop = fxsave.fop; |
| fsave->fpu_dp = fxsave.fpu_dp; |
| fsave->fpu_ds = fxsave.fpu_ds; |
| fsave->reserved_4 = 0; |
| static_assert(ArraySize(fsave->st) == ArraySize(fxsave.st_mm), |
| "FPU stack registers must be equivalent"); |
| for (size_t index = 0; index < base::size(fsave->st); ++index) { |
| memcpy(fsave->st[index], fxsave.st_mm[index].st, sizeof(fsave->st[index])); |
| } |
| } |
| |
| // static |
| void CPUContextX86::FsaveToFxsave(const Fsave& fsave, Fxsave* fxsave) { |
| fxsave->fcw = fsave.fcw; |
| fxsave->fsw = fsave.fsw; |
| fxsave->ftw = FsaveToFxsaveTagWord(fsave.ftw); |
| fxsave->reserved_1 = 0; |
| fxsave->fop = fsave.fop; |
| fxsave->fpu_ip = fsave.fpu_ip; |
| fxsave->fpu_cs = fsave.fpu_cs; |
| fxsave->reserved_2 = 0; |
| fxsave->fpu_dp = fsave.fpu_dp; |
| fxsave->fpu_ds = fsave.fpu_ds; |
| fxsave->reserved_3 = 0; |
| fxsave->mxcsr = 0; |
| fxsave->mxcsr_mask = 0; |
| static_assert(ArraySize(fxsave->st_mm) == ArraySize(fsave.st), |
| "FPU stack registers must be equivalent"); |
| for (size_t index = 0; index < base::size(fsave.st); ++index) { |
| memcpy(fxsave->st_mm[index].st, fsave.st[index], sizeof(fsave.st[index])); |
| memset(fxsave->st_mm[index].st_reserved, |
| 0, |
| sizeof(fxsave->st_mm[index].st_reserved)); |
| } |
| memset(fxsave->xmm, 0, sizeof(*fxsave) - offsetof(Fxsave, xmm)); |
| } |
| |
| // static |
| uint16_t CPUContextX86::FxsaveToFsaveTagWord( |
| uint16_t fsw, |
| uint8_t fxsave_tag, |
| const CPUContextX86::X87OrMMXRegister st_mm[8]) { |
| // The x87 tag word (in both abridged and full form) identifies physical |
| // registers, but |st_mm| is arranged in logical stack order. In order to map |
| // physical tag word bits to the logical stack registers they correspond to, |
| // the “stack top” value from the x87 status word is necessary. |
| int stack_top = (fsw >> 11) & 0x7; |
| |
| uint16_t fsave_tag = 0; |
| for (int physical_index = 0; physical_index < 8; ++physical_index) { |
| bool fxsave_bit = (fxsave_tag & (1 << physical_index)) != 0; |
| uint8_t fsave_bits; |
| |
| if (fxsave_bit) { |
| int st_index = (physical_index + 8 - stack_top) % 8; |
| const CPUContextX86::X87Register& st = st_mm[st_index].st; |
| |
| uint32_t exponent = ((st[9] & 0x7f) << 8) | st[8]; |
| if (exponent == 0x7fff) { |
| // Infinity, NaN, pseudo-infinity, or pseudo-NaN. If it was important to |
| // distinguish between these, the J bit and the M bit (the most |
| // significant bit of |fraction|) could be consulted. |
| fsave_bits = kX87TagSpecial; |
| } else { |
| // The integer bit the “J bit”. |
| bool integer_bit = (st[7] & 0x80) != 0; |
| if (exponent == 0) { |
| uint64_t fraction = ((implicit_cast<uint64_t>(st[7]) & 0x7f) << 56) | |
| (implicit_cast<uint64_t>(st[6]) << 48) | |
| (implicit_cast<uint64_t>(st[5]) << 40) | |
| (implicit_cast<uint64_t>(st[4]) << 32) | |
| (implicit_cast<uint32_t>(st[3]) << 24) | |
| (st[2] << 16) | (st[1] << 8) | st[0]; |
| if (!integer_bit && fraction == 0) { |
| fsave_bits = kX87TagZero; |
| } else { |
| // Denormal (if the J bit is clear) or pseudo-denormal. |
| fsave_bits = kX87TagSpecial; |
| } |
| } else if (integer_bit) { |
| fsave_bits = kX87TagValid; |
| } else { |
| // Unnormal. |
| fsave_bits = kX87TagSpecial; |
| } |
| } |
| } else { |
| fsave_bits = kX87TagEmpty; |
| } |
| |
| fsave_tag |= (fsave_bits << (physical_index * 2)); |
| } |
| |
| return fsave_tag; |
| } |
| |
| // static |
| uint8_t CPUContextX86::FsaveToFxsaveTagWord(uint16_t fsave_tag) { |
| uint8_t fxsave_tag = 0; |
| for (int physical_index = 0; physical_index < 8; ++physical_index) { |
| const uint8_t fsave_bits = (fsave_tag >> (physical_index * 2)) & 0x3; |
| const bool fxsave_bit = fsave_bits != kX87TagEmpty; |
| fxsave_tag |= fxsave_bit << physical_index; |
| } |
| return fxsave_tag; |
| } |
| |
| uint64_t CPUContext::InstructionPointer() const { |
| switch (architecture) { |
| case kCPUArchitectureX86: |
| return x86->eip; |
| case kCPUArchitectureX86_64: |
| return x86_64->rip; |
| case kCPUArchitectureARM: |
| return arm->pc; |
| case kCPUArchitectureARM64: |
| return arm64->pc; |
| default: |
| NOTREACHED(); |
| return ~0ull; |
| } |
| } |
| |
| uint64_t CPUContext::StackPointer() const { |
| switch (architecture) { |
| case kCPUArchitectureX86: |
| return x86->esp; |
| case kCPUArchitectureX86_64: |
| return x86_64->rsp; |
| case kCPUArchitectureARM: |
| return arm->sp; |
| case kCPUArchitectureARM64: |
| return arm64->sp; |
| default: |
| NOTREACHED(); |
| return ~0ull; |
| } |
| } |
| |
| bool CPUContext::Is64Bit() const { |
| switch (architecture) { |
| case kCPUArchitectureX86_64: |
| case kCPUArchitectureARM64: |
| case kCPUArchitectureMIPS64EL: |
| return true; |
| case kCPUArchitectureX86: |
| case kCPUArchitectureARM: |
| case kCPUArchitectureMIPSEL: |
| return false; |
| default: |
| NOTREACHED(); |
| return false; |
| } |
| } |
| |
| } // namespace crashpad |