/// -*- mode: asm; asm-comment-char: ?/ -*- /// /// Register dump and debugging for x86 /// /// (c) 2019 Straylight/Edgeware /// ///----- Licensing notice --------------------------------------------------- /// /// This file is part of Catacomb. /// /// Catacomb is free software: you can redistribute it and/or modify it /// under the terms of the GNU Library General Public License as published /// by the Free Software Foundation; either version 2 of the License, or /// (at your option) any later version. /// /// Catacomb is distributed in the hope that it will be useful, but /// WITHOUT ANY WARRANTY; without even the implied warranty of /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU /// Library General Public License for more details. /// /// You should have received a copy of the GNU Library General Public /// License along with Catacomb. If not, write to the Free Software /// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, /// USA. ///-------------------------------------------------------------------------- /// Preliminaries. #include "config.h" #include "asm-common.h" #include "regdump.h" .text .arch pentium4 .arch .xsave ///-------------------------------------------------------------------------- /// Main code. FUNC(regdump_gpsave) endprologue // On entry, r/esp should point to a return address and // `REGDUMP_GPSIZE' bytes of word-aligned storage to be the // general-purpose save area, with flags saved in the bottom word, // r/eax saved in the fourth, and (on 32-bit x86) ebx in the fifth. // On exit, the initial registers are saved in this space, and // modified: r/ebp points to the general-purpose save area, ecx // contains the number of bytes required in the extended save area, // ebx is preserved on 32-bit x86, and other general-purpose // registers are clobbered or used to communicate with // `regdump_xtsave' below. Doing anything other than lowering the // stack pointer and calling `regdump_xtsave' is not recommended. // Other code will insist that df is clear. cld // Save r/ebp and establish it pointing to the save area. mov [SP + WORDSZ + REGIX_BP*WORDSZ], BP lea BP, [SP + WORDSZ] // Save the other easy general-purpose registers. #if !CPUFAM_X86 mov [BP + REGIX_BX*WORDSZ], BX #endif mov [BP + REGIX_CX*WORDSZ], CX mov [BP + REGIX_DX*WORDSZ], DX mov [BP + REGIX_SI*WORDSZ], SI mov [BP + REGIX_DI*WORDSZ], DI #if CPUFAM_AMD64 mov [BP + REGIX_R8*WORDSZ], r8 mov [BP + REGIX_R9*WORDSZ], r9 mov [BP + REGIX_R10*WORDSZ], r10 mov [BP + REGIX_R11*WORDSZ], r11 mov [BP + REGIX_R12*WORDSZ], r12 mov [BP + REGIX_R13*WORDSZ], r13 mov [BP + REGIX_R14*WORDSZ], r14 mov [BP + REGIX_R15*WORDSZ], r15 #endif // Determine the previous stack pointer and save it. #if CPUFAM_AMD64 && ABI_SYSV lea AX, [BP + 128 + REGDUMP_GPSIZE] #else lea AX, [BP + REGDUMP_GPSIZE] #endif mov [BP + REGIX_SP*WORDSZ], AX // Collect the return address and save it as r/eip. mov AX, [SP] mov [BP + REGIX_IP*WORDSZ], AX // Save the segment registers. lea AX, [BP + REGIX_GPLIM*WORDSZ] mov [AX + 2*REGIX_CS], cs mov [AX + 2*REGIX_DS], ds mov [AX + 2*REGIX_SS], ss mov [AX + 2*REGIX_ES], es mov [AX + 2*REGIX_FS], fs mov [AX + 2*REGIX_GS], gs // Determine the extended save area size. Preserve ebx on 32-bit x86 // here, because the caller needs it for PLT-indirect calls. #if CPUFAM_X86 push ebx #endif mov eax, 0x01 cpuid test ecx, 1 << 26 je 1f mov eax, 0x0d mov ecx, 0x00 cpuid add ecx, regmap_size + 64 // map + align jmp 8f 1: mov ecx, 512 + regmap_size + 16 // fxsave + map + align // Done. 8: #if CPUFAM_X86 pop ebx #endif ret ENDFUNC FUNC(regdump_gprstr) endprologue // On entry, r/ebp points to a general-purpose save area, established // by `regdump_gpsave'. On exit, the general-purpose registers // (other than the stack pointer) are restored to their original // values. // We assume nobody actually fiddled with the segment registers. So // just the actual integer registers to do. mov AX, [BP + REGIX_AX*WORDSZ] mov BX, [BP + REGIX_BX*WORDSZ] mov CX, [BP + REGIX_CX*WORDSZ] mov DX, [BP + REGIX_DX*WORDSZ] mov SI, [BP + REGIX_SI*WORDSZ] mov DI, [BP + REGIX_DI*WORDSZ] #if CPUFAM_AMD64 mov r8, [BP + REGIX_R8*WORDSZ] mov r9, [BP + REGIX_R9*WORDSZ] mov r10, [BP + REGIX_R10*WORDSZ] mov r11, [BP + REGIX_R11*WORDSZ] mov r12, [BP + REGIX_R12*WORDSZ] mov r13, [BP + REGIX_R13*WORDSZ] mov r14, [BP + REGIX_R14*WORDSZ] mov r15, [BP + REGIX_R15*WORDSZ] #endif mov BP, [BP + REGIX_BP*WORDSZ] // Done. ret ENDFUNC #ifdef CPUFAM_AMD64 # define fxsave fxsave64 # define fxrstor fxrstor64 # define xsave xsave64 # define xrstor xrstor64 #endif FUNC(regdump_xtsave) endprologue // On entry, r/esp points to a return address and extended save area, // of size determined by `regdump_gpsave' above. On exit, the save // area is filled in and a handy map placed at its base, the x87 // floating-point state is reset, r/ebp is left pointing to the // register map, ebx is preserved on 32-bit x86, and the other // general registers are clobbered. // Start by filling in the easy parts of the map. mov [SP + WORDSZ + regmap_gp], BP lea BP, [SP + WORDSZ] xor eax, eax // clears rax too on amd64 mov [BP + regmap_avx], AX // Find out whether we use `xsave'. (Preserve ebx.) #if CPUFAM_X86 push ebx #endif mov eax, 0x01 cpuid test ecx, 1 << 26 je 5f // We have the `xsave' machinery. Select the base address. lea SI, [SP + WORDSZ + regmap_size + 63] and SI, ~63 mov [BP + regmap_fx], SI // Clear out the header area. xor eax, eax lea DI, [SI + 512] mov ecx, 16 rep stosd // Save the registers. mov eax, 0x00000007 xor edx, edx xsave [SI] // Establish the AVX pointer, if available. test dword ptr [SI + 512], 4 // = xstate_bv je 8f mov eax, 13 mov ecx, 2 cpuid add BX, SI mov [BP + regmap_avx], BX jmp 8f // We have only `fxsave'. Set the base address. 5: lea SI, [SP + WORDSZ + regmap_size + 15] and SI, ~15 mov [BP + regmap_fx], SI // Save the registers. fxsave [SI] // Clear the x87 state; otherwise it can cause trouble later. 8: fninit // Done. #if CPUFAM_X86 pop ebx #endif ret ENDFUNC FUNC(regdump_xtrstr) endprologue // On entry, r/ebp points to a register-save map. On exit, the // extended registers are restored from the save area; r/ebp is left // pointing to the general-purpose save area, ebx is preserved on // 32-bit x86, and the other general registers are clobbered. // Find the extended register dump. mov SI, [BP + regmap_fx] // Probe to find out whether we have `xsave'. #if CPUFAM_X86 push ebx #endif mov eax, 0x01 cpuid test ecx, 1 << 26 je 1f // We have the `xsave' machinery. mov eax, 0x00000007 xor edx, edx xrstor [SI] jmp 8f // We must fake it up. 1: fxrstor [SI] // Done. 8: mov BP, [BP + regmap_gp] #if CPUFAM_X86 pop ebx #endif ret ENDFUNC ///----- That's all, folks --------------------------------------------------