mirror of
https://github.com/Zenithsiz/ftmemsim-valgrind.git
synced 2026-02-07 12:44:45 +00:00
because it was always being set to VG_MIN_ALLOC_SZB, and so was just one more thing to get wrong, as well as exposing VG_MIN_ALLOC_SZB to more places than necessary. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@3340
390 lines
12 KiB
C
390 lines
12 KiB
C
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- Arch-specific registers, etc. x86/state.c ---*/
|
|
/*--------------------------------------------------------------------*/
|
|
|
|
/*
|
|
This file is part of Valgrind, a dynamic binary instrumentation
|
|
framework.
|
|
|
|
Copyright (C) 2000-2005 Nicholas Nethercote
|
|
njn25@cam.ac.uk
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the
|
|
License, or (at your option) any later version.
|
|
|
|
This program 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
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
02111-1307, USA.
|
|
|
|
The GNU General Public License is contained in the file COPYING.
|
|
*/
|
|
|
|
#include "core.h"
|
|
#include "x86_private.h"
|
|
#include <sys/ptrace.h>
|
|
|
|
#include "libvex_guest_x86.h"
|
|
|
|
/* Global and Local descriptor tables for threads.
|
|
|
|
See comments in libvex_guest_x86.h for LibVEX's model of x86
|
|
segment descriptors.
|
|
|
|
Mostly, threads never generate LDT (or GDT?) entries. Therefore,
|
|
we will initially start off with LDTs and GDTs being (HWord)NULL
|
|
and allocate them on demand.
|
|
*/
|
|
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- Determining arch/subarch. ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
/* Standard macro to see if a specific flag is changeable */
|
|
|
|
static inline Bool flag_is_changeable(UInt flag)
|
|
{
|
|
UInt f1, f2;
|
|
|
|
asm("pushfl\n\t"
|
|
"pushfl\n\t"
|
|
"popl %0\n\t"
|
|
"movl %0,%1\n\t"
|
|
"xorl %2,%0\n\t"
|
|
"pushl %0\n\t"
|
|
"popfl\n\t"
|
|
"pushfl\n\t"
|
|
"popl %0\n\t"
|
|
"popfl\n\t"
|
|
: "=&r" (f1), "=&r" (f2)
|
|
: "ir" (flag));
|
|
|
|
return ((f1^f2) & flag) != 0;
|
|
}
|
|
|
|
/* Does this CPU support the CPUID instruction? */
|
|
|
|
static Bool has_cpuid ( void )
|
|
{
|
|
/* 21 is the ID flag */
|
|
return flag_is_changeable(1<<21);
|
|
}
|
|
|
|
/* Actually do a CPUID on the host. */
|
|
|
|
static void do_cpuid ( UInt n,
|
|
UInt* a, UInt* b,
|
|
UInt* c, UInt* d )
|
|
{
|
|
__asm__ __volatile__ (
|
|
"cpuid"
|
|
: "=a" (*a), "=b" (*b), "=c" (*c), "=d" (*d) /* output */
|
|
: "0" (n) /* input */
|
|
);
|
|
}
|
|
|
|
|
|
// Returns the architecture and subarchitecture, or indicates
|
|
// that this subarchitecture is unable to run Valgrind
|
|
// Returns False to indicate we cannot proceed further.
|
|
Bool VGA_(getArchAndSubArch)( /*OUT*/VexArch* vex_arch,
|
|
/*OUT*/VexSubArch* vex_subarch )
|
|
{
|
|
Bool have_sse0, have_sse1, have_sse2;
|
|
UInt eax, ebx, ecx, edx;
|
|
|
|
if (!has_cpuid())
|
|
/* we can't do cpuid at all. Give up. */
|
|
return False;
|
|
|
|
do_cpuid(0, &eax, &ebx, &ecx, &edx);
|
|
if (eax < 1)
|
|
/* we can't ask for cpuid(x) for x > 0. Give up. */
|
|
return False;
|
|
|
|
/* get capabilities bits into edx */
|
|
do_cpuid(1, &eax, &ebx, &ecx, &edx);
|
|
|
|
have_sse0 = (edx & (1<<24)) != 0; /* True => have fxsave/fxrstor */
|
|
have_sse1 = (edx & (1<<25)) != 0; /* True => have sse insns */
|
|
have_sse2 = (edx & (1<<26)) != 0; /* True => have sse2 insns */
|
|
|
|
if (have_sse2 && have_sse1 && have_sse0) {
|
|
*vex_arch = VexArchX86;
|
|
*vex_subarch = VexSubArchX86_sse2;
|
|
return True;
|
|
}
|
|
|
|
if (have_sse1 && have_sse0) {
|
|
*vex_arch = VexArchX86;
|
|
*vex_subarch = VexSubArchX86_sse1;
|
|
return True;
|
|
}
|
|
|
|
if (have_sse0) {
|
|
*vex_arch = VexArchX86;
|
|
*vex_subarch = VexSubArchX86_sse0;
|
|
return True;
|
|
}
|
|
|
|
/* we need at least SSE state to operate. */
|
|
return False;
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- Initialising the first thread ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
/* Given a pointer to the ThreadArchState for thread 1 (the root
|
|
thread), initialise the VEX guest state, and copy in essential
|
|
starting values.
|
|
*/
|
|
void VGA_(init_thread1state) ( Addr client_eip,
|
|
Addr esp_at_startup,
|
|
/*MOD*/ ThreadArchState* arch )
|
|
{
|
|
vg_assert(0 == sizeof(VexGuestX86State) % 8);
|
|
|
|
/* Zero out the initial state, and set up the simulated FPU in a
|
|
sane way. */
|
|
LibVEX_GuestX86_initialise(&arch->vex);
|
|
|
|
/* Zero out the shadow area. */
|
|
VG_(memset)(&arch->vex_shadow, 0, sizeof(VexGuestX86State));
|
|
|
|
/* Put essential stuff into the new state. */
|
|
|
|
arch->vex.guest_ESP = esp_at_startup;
|
|
arch->vex.guest_EIP = client_eip;
|
|
|
|
/* initialise %cs, %ds and %ss to point at the operating systems
|
|
default code, data and stack segments */
|
|
asm volatile("movw %%cs, %0"
|
|
:
|
|
: "m" (arch->vex.guest_CS));
|
|
asm volatile("movw %%ds, %0"
|
|
:
|
|
: "m" (arch->vex.guest_DS));
|
|
asm volatile("movw %%ss, %0"
|
|
:
|
|
: "m" (arch->vex.guest_SS));
|
|
|
|
VG_TRACK( post_reg_write, Vg_CoreStartup, /*tid*/1, /*offset*/0,
|
|
sizeof(VexGuestArchState));
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- LDT/GDT stuff ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
/* Create a zeroed-out GDT. */
|
|
|
|
VexGuestX86SegDescr* VG_(alloc_zeroed_x86_GDT) ( void )
|
|
{
|
|
Int nbytes = VEX_GUEST_X86_GDT_NENT * sizeof(VexGuestX86SegDescr);
|
|
return VG_(arena_calloc)(VG_AR_CORE, nbytes, 1);
|
|
}
|
|
|
|
/* Create a zeroed-out LDT. */
|
|
|
|
VexGuestX86SegDescr* VG_(alloc_zeroed_x86_LDT) ( void )
|
|
{
|
|
Int nbytes = VEX_GUEST_X86_LDT_NENT * sizeof(VexGuestX86SegDescr);
|
|
return VG_(arena_calloc)(VG_AR_CORE, nbytes, 1);
|
|
}
|
|
|
|
/* Free up an LDT or GDT allocated by the above fns. */
|
|
|
|
static void free_LDT_or_GDT ( VexGuestX86SegDescr* dt )
|
|
{
|
|
vg_assert(dt);
|
|
VG_(arena_free)(VG_AR_CORE, (void*)dt);
|
|
}
|
|
|
|
/* Copy contents between two existing LDTs. */
|
|
|
|
static void copy_LDT_from_to ( VexGuestX86SegDescr* src,
|
|
VexGuestX86SegDescr* dst )
|
|
{
|
|
Int i;
|
|
vg_assert(src);
|
|
vg_assert(dst);
|
|
for (i = 0; i < VEX_GUEST_X86_LDT_NENT; i++)
|
|
dst[i] = src[i];
|
|
}
|
|
|
|
/* Free this thread's DTs, if it has any. */
|
|
|
|
static void deallocate_LGDTs_for_thread ( VexGuestX86State* vex )
|
|
{
|
|
vg_assert(sizeof(HWord) == sizeof(void*));
|
|
|
|
if (0)
|
|
VG_(printf)("deallocate_LGDTs_for_thread: "
|
|
"ldt = 0x%x, gdt = 0x%x\n",
|
|
vex->guest_LDT, vex->guest_GDT );
|
|
|
|
if (vex->guest_LDT != (HWord)NULL) {
|
|
free_LDT_or_GDT( (VexGuestX86SegDescr*)vex->guest_LDT );
|
|
vex->guest_LDT = (HWord)NULL;
|
|
}
|
|
|
|
if (vex->guest_GDT != (HWord)NULL) {
|
|
free_LDT_or_GDT( (VexGuestX86SegDescr*)vex->guest_GDT );
|
|
vex->guest_GDT = (HWord)NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- Thread stuff ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
void VGA_(cleanup_thread) ( ThreadArchState* arch )
|
|
{
|
|
/* Release arch-specific resources held by this thread. */
|
|
/* On x86, we have to dump the LDT and GDT. */
|
|
deallocate_LGDTs_for_thread( &arch->vex );
|
|
}
|
|
|
|
|
|
void VGA_(setup_child) ( /*OUT*/ ThreadArchState *child,
|
|
/*IN*/ ThreadArchState *parent )
|
|
{
|
|
/* We inherit our parent's guest state. */
|
|
child->vex = parent->vex;
|
|
child->vex_shadow = parent->vex_shadow;
|
|
/* We inherit our parent's LDT. */
|
|
if (parent->vex.guest_LDT == (HWord)NULL) {
|
|
/* We hope this is the common case. */
|
|
child->vex.guest_LDT = (HWord)NULL;
|
|
} else {
|
|
/* No luck .. we have to take a copy of the parent's. */
|
|
child->vex.guest_LDT = (HWord)VG_(alloc_zeroed_x86_LDT)();
|
|
copy_LDT_from_to( (VexGuestX86SegDescr*)parent->vex.guest_LDT,
|
|
(VexGuestX86SegDescr*)child->vex.guest_LDT );
|
|
}
|
|
|
|
/* We need an empty GDT. */
|
|
child->vex.guest_GDT = (HWord)NULL;
|
|
}
|
|
|
|
|
|
void VGA_(mark_from_registers)(ThreadId tid, void (*marker)(Addr))
|
|
{
|
|
ThreadState *tst = VG_(get_ThreadState)(tid);
|
|
ThreadArchState *arch = &tst->arch;
|
|
|
|
/* XXX ask tool about validity? */
|
|
(*marker)(arch->vex.guest_EAX);
|
|
(*marker)(arch->vex.guest_ECX);
|
|
(*marker)(arch->vex.guest_EDX);
|
|
(*marker)(arch->vex.guest_EBX);
|
|
(*marker)(arch->vex.guest_ESI);
|
|
(*marker)(arch->vex.guest_EDI);
|
|
(*marker)(arch->vex.guest_ESP);
|
|
(*marker)(arch->vex.guest_EBP);
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- Symtab stuff ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
/* This is the Intel register encoding -- integer regs. */
|
|
#define R_EAX 0
|
|
#define R_ECX 1
|
|
#define R_EDX 2
|
|
#define R_EBX 3
|
|
#define R_ESP 4
|
|
#define R_EBP 5
|
|
#define R_ESI 6
|
|
#define R_EDI 7
|
|
|
|
UInt *VGA_(reg_addr_from_tst)(Int regno, ThreadArchState *arch)
|
|
{
|
|
switch (regno) {
|
|
case R_EAX: return &arch->vex.guest_EAX;
|
|
case R_ECX: return &arch->vex.guest_ECX;
|
|
case R_EDX: return &arch->vex.guest_EDX;
|
|
case R_EBX: return &arch->vex.guest_EBX;
|
|
case R_ESP: return &arch->vex.guest_ESP;
|
|
case R_EBP: return &arch->vex.guest_EBP;
|
|
case R_ESI: return &arch->vex.guest_ESI;
|
|
case R_EDI: return &arch->vex.guest_EDI;
|
|
default: return NULL;
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- pointercheck ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
Bool VGA_(setup_pointercheck)(void)
|
|
{
|
|
vki_modify_ldt_t ldt = {
|
|
VG_POINTERCHECK_SEGIDX, // entry_number
|
|
VG_(client_base), // base_addr
|
|
(VG_(client_end)-VG_(client_base)) / VKI_PAGE_SIZE, // limit
|
|
1, // seg_32bit
|
|
0, // contents: data, RW, non-expanding
|
|
0, // ! read_exec_only
|
|
1, // limit_in_pages
|
|
0, // ! seg not present
|
|
1, // useable
|
|
};
|
|
int ret = VG_(do_syscall3)(__NR_modify_ldt, 1, (UWord)&ldt, sizeof(ldt));
|
|
if (ret < 0) {
|
|
VG_(message)(Vg_UserMsg,
|
|
"Warning: ignoring --pointercheck=yes, "
|
|
"because modify_ldt failed (errno=%d)", -ret);
|
|
return False;
|
|
} else {
|
|
return True;
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- Debugger-related operations ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
Int VGA_(ptrace_setregs_from_tst)(Int pid, ThreadArchState* arch)
|
|
{
|
|
struct vki_user_regs_struct regs;
|
|
|
|
regs.cs = arch->vex.guest_CS;
|
|
regs.ss = arch->vex.guest_SS;
|
|
regs.ds = arch->vex.guest_DS;
|
|
regs.es = arch->vex.guest_ES;
|
|
regs.fs = arch->vex.guest_FS;
|
|
regs.gs = arch->vex.guest_GS;
|
|
regs.eax = arch->vex.guest_EAX;
|
|
regs.ebx = arch->vex.guest_EBX;
|
|
regs.ecx = arch->vex.guest_ECX;
|
|
regs.edx = arch->vex.guest_EDX;
|
|
regs.esi = arch->vex.guest_ESI;
|
|
regs.edi = arch->vex.guest_EDI;
|
|
regs.ebp = arch->vex.guest_EBP;
|
|
regs.esp = arch->vex.guest_ESP;
|
|
regs.eflags = LibVEX_GuestX86_get_eflags(&arch->vex);
|
|
regs.eip = arch->vex.guest_EIP;
|
|
|
|
return ptrace(PTRACE_SETREGS, pid, NULL, ®s);
|
|
}
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- end ---*/
|
|
/*--------------------------------------------------------------------*/
|
|
|