Click here to Skip to main content
15,890,438 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I'm trying to make a function in C++/CLI though this block is unmanaged :

C#
int MXCSR_MASK()
{
    if (FXSAVE_SUPPORTED())
    {
        unsigned char *buffer = new unsigned char[512];
        __asm{
            fxsave [buffer]
        }
        return *(int*)(buffer + 28);
    }
    return 0;
}


this is simply supposed to get a flag from the CPU using the FXSAVE instruction. When it does the fxsave instruction, I get a System.AccessViolationException which would indicate that I have the wrong address, but I also tried first moving the pointer into eax and then using that. I also tried using an aligned buffer. In all tests the Exception strangely reports:

Access violation reading location 0xffffffff.

Is it possible that this instruction can't be used in user mode code or am I missing something?

Edit:

Thanks to the answerer below. Works like a charm. However, I changed it slightly to keep MSVC++ from complaining about ebp being modified:
int MXCSR_MASK()
{
    if (FXSAVE_SUPPORTED())
    {
        int result;
        __asm
        {
            mov     ebx, esp
            and     esp, 0FFFFFFF0h
            sub     esp, 512
            fxsave  [esp]
            mov     eax, DWORD PTR [esp + 28]
            mov     result, eax
            mov     esp, ebx
        }
        return result;
    }
    return 0;
}

I wonder why my manual changing of the pointer at runtime (just to see if it fixed it) to an aligned address did not work. Thanks again.
Posted
Updated 2-Apr-10 9:52am
v2

The fxsave instruction could be used both in user and kernel mode, but you are missing a constraint: the destination adress of that operation must be aligned on 16 bytes boundaries (see Intel® 64 and IA-32 Architectures Software Developer’s Manual, Volume 2A at page 3-455).
The problem is that memory allocated by the new operator is aligned on byte bundaries. You could use the code below:

C++
int MXCSR_MASK()
{
    if (FXSAVE_SUPPORTED())
    {
        int result;
        __asm
        {
            push    ebp
            mov     ebp, esp
            and     esp, 0FFFFFFF0h
            sub     esp, 512
            fxsave  [esp]
            mov     eax, DWORD PTR [esp + 28]
            mov     result, eax
            mov     esp, ebp
            pop     ebp
        }
        return result;
    }
    return 0;
}
 
Share this answer
 
I think you have manually modified the buffer variable to ensure that it points to an address aligned to 16 bytes boundaries; this is not enough because the fxsave instruction requires that all the memory accesses it does are aligned to 16 bytes boundaries, this means that:


  • the buffer pointed by buffer must be at an address aligned to 16 bytes bundaries
  • the address of the buffer variable must be aligned to 16 bytes boundaries


At runtime, you can add a watch to &buffer and see that, due to the function stack frame layout, the buffer variable is allocated on the stack at an address in the form XXXXXXX4h (e.g. on my PC I see &buffer = 0x0010FA84 and buffer = 0x01B48AF0).

You can fix the problem in two ways:


  1. add a dummy DWORD local variable to the function, declared on the line that preceed the buffer variable declaration (this will shift down the address of buffer on the stack by 4 bytes and make it aligned to 16 bytes boundsries)

    C++
    int MXCSR_MASK()
    {
       if (FXSAVE_SUPPORTED())
       {
          DWORD dummy = 0;
          unsigned char *buffer = new unsigned char[512];
          __asm fxsave [buffer]
          return *(int*)(buffer + 28);
       }
       return 0;
    }
  2. you can move the address pointed by buffer to a register and then call fxsave through it

    C++
    int MXCSR_MASK()
    {
       if (FXSAVE_SUPPORTED())
       {
          unsigned char *buffer = new unsigned char[512];
          __asm
          {
             mov eax, [buffer]
             fxsave [eax]
          }
          return *(int*)(buffer + 28);
       }
       return 0;
    }


However, the best solution is the one that I wrote on my previous answer, because it is not dependant on how the memory manager allocate your buffer, and its performance are better, because it make room for the 512 bytes stored by fxsave directly on the stack and immediately remove them; a call to the new operator involves some kind of work for the C++ runtime to allocate that memory on the heap and then to deallocate it when it's no longer needed.
 
Share this answer
 
v2

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900