Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

asm in C "too many memory references for `mov'"

Tags:

c

assembly

I've seen the post about the same error but i'm still get error :

 too many memory references for `mov'
 junk `hCPUIDmov buffer' after expression

... here's the code (mingw compiler / C::B) :


    #include iostream

    using namespace std;

    union aregister
    {
        int theint;
        unsigned bits[32];
    };

    union tonibbles
    {
        int integer;
        short parts[2];
    };

    void GetSerial()
    {
        int part1,part2,part3;
        aregister issupported;
        int buffer;

        __asm(
            "mov %eax, 01h"
            "CPUID"
            "mov buffer, edx"
        );//do the cpuid, move the edx (feature set register) to "buffer"


        issupported.theint = buffer;
        if(issupported.bits[18])//it is supported
        {
            __asm(
                "mov part1, eax"
                "mov %eax, 03h"
                "CPUID"
            );//move the first part into "part1" and call cpuid with the next subfunction to get
            //the next 64 bits

            __asm(
                "mov part2, edx"
                "mov part3, ecx"
            );//now we have all the 96 bits of the serial number


            tonibbles serial[3];//to split it up into two nibbles

            serial[0].integer = part1;//first part
            serial[1].integer = part2;//second
            serial[2].integer = part3;//third
        }
    }

like image 395
user2101247 Avatar asked Feb 23 '13 00:02

user2101247


2 Answers

Your assembly code is not correctly formatted for gcc.

Firstly, gcc uses AT&T syntax (EDIT: by default, thanks nrz), so it needs a % added for each register reference and a $ for immediate operands. The destination operand is always on the right side.

Secondly, you'll need to pass a line separator (for example \n\t) for a new line. Since gcc passes your string straight to the assembler, it requires a particular syntax.

You should usually try hard to minimize your assembler since it may cause problems for the optimizer. Simplest way to minimize the assembler required would probably be to break the cpuid instruction out into a function, and reuse that.

void cpuid(int32_t *peax, int32_t *pebx, int32_t *pecx, int32_t *pedx)
{
    __asm(
         "CPUID"
          /* All outputs (eax, ebx, ecx, edx) */
        : "=a"(*peax), "=b"(*pebx), "=c"(*pecx), "=d"(*pedx)   
          /* All inputs (eax) */
        : "a"(*peax)                                           
    );
}

Then just simply call using;

int a=1, b, c, d;

cpuid(&a, &b, &c, &d);

Another possibly more elegant way is to do it using macros.

like image 181
Joachim Isaksson Avatar answered Oct 20 '22 14:10

Joachim Isaksson


  1. Because of how C works,

    __asm(
        "mov %eax, 01h"
        "CPUID"
        "mov buffer, edx"
    );
    

    is equivalent to

    __asm("mov %eax, 01h" "CPUID" "mov buffer, edx");
    

    which is equivalent to

    __asm("mov %eax, 01hCPUIDmov buffer, edx");
    

    which isn't what you want.

  2. AT&T syntax (GAS's default) puts the destination register at the end.

  3. AT&T syntax requires immediates to be prefixed with $.

  4. You can't reference local variables like that; you need to pass them in as operands.

Wikipedia's article gives a working example that returns eax.

The following snippet might cover your use-cases (I'm not intricately familiar with GCC inline assembly or CPUID):

int eax, ebx, ecx, edx;
eax = 1;
__asm( "cpuid"
     : "+a" (eax), "+b" (ebx), "+c" (ecx), "+d" (edx));
buffer = edx
like image 37
tc. Avatar answered Oct 20 '22 15:10

tc.