numatop: %{optflags} fail the 32bit build

Florian Weimer fweimer at redhat.com
Thu Sep 12 12:53:14 UTC 2013


On 09/12/2013 02:11 PM, Dridi Boukelmoune wrote:

>> This version should work in 32 bit mode, and only in 32 bit mode:
>>
>> void
>> cpuid(unsigned int *eax, unsigned int *ebx, unsigned int *ecx,
>>        unsigned int *edx)
>> {
>>    __asm volatile
>>      ("push %%ebx\n\t"
>>       "cpuid\n\t"
>>       "mov %%ebx, (%1)\n\t"
>>       "pop %%ebx"
>>       : "=a" (*eax),
>>         "=S" (ebx),
>>         "=c" (*ecx),
>>         "=d" (*edx)
>>       : "0" (*eax));
>> }
>
> I "kind of" understand what you're doing here, but it's not all clear.
>
> I get the push/pop instructions save and restore the reserved ebx
> register, which is needed because apparently the cpuid instruction
> would otherwise overwrite it.
>
> I don't understand the mov instruction, but I suppose you're storing
> ebx's value from cpuid "somewhere else" before restoring it with the
> pop instruction.

Correct, the intent is to write the %ebx register value to the address 
in the %esi register.  "(%1)" is a pointer dereference, as oppose to 
plain %1.

> I don't understand the last 5 lines of __asm in both functions, I've
> never seen this syntax before.

These are register constraints.  "a", "c", "d", "S" refer to the %eax, 
%ecx, %edx, %esi registers, respectively.  "=" marks output constraints. 
  The constraints before the final ":" are output registers, and after 
colon, there are the input registers.  There's just one, and "0" means 
to reuse the first output register.

Okay, silly me, I should have listed "S" among the output registers, 
instead the inputs:

void
cpuid(unsigned int *eax, unsigned int *ebx, unsigned int *ecx,
       unsigned int *edx)
{
   __asm volatile
     ("push %%ebx\n\t"
      "cpuid\n\t"
      "mov %%ebx, (%4)\n\t"
      "pop %%ebx"
      : "=a" (*eax),
        "=c" (*ecx),
        "=d" (*edx)
      : "0" (*eax),
        "S" (ebx));
}

I also forget that for full correctness, there should now be a "memory" 
clobber as well (in the clobber section after yet another colon):

void
cpuid(unsigned int *eax, unsigned int *ebx, unsigned int *ecx,
       unsigned int *edx)
{
   __asm volatile
     ("push %%ebx\n\t"
      "cpuid\n\t"
      "mov %%ebx, (%4)\n\t"
      "pop %%ebx"
      : "=a" (*eax),
        "=c" (*ecx),
        "=d" (*edx)
      : "0" (*eax),
        "S" (ebx)
      : "memory");
}

By the way, we could generate much better code if the registers were 
passed as an array or struct, so that they are in consecutive memory:

struct regs {
   unsigned eax, ebx, ecx, edx;
};

void
cpuid(struct regs *r)
{
   __asm volatile
     ("push %%ebx\n\t"
      "cpuid\n\t"
      "mov %%eax, (%0)\n\t"
      "mov %%ebx, 4(%0)\n\t"
      "mov %%ecx, 8(%0)\n\t"
      "mov %%edx, 12(%0)\n\t"
      "pop %%ebx"
      :
      : "S" (r)
      : "eax", "ecx", "edx", "memory");
}

Obviously, this needs adjustments to the callers.

-- 
Florian Weimer / Red Hat Product Security Team


More information about the devel mailing list