WeHack BBS
libgcrypt get_cpuid() 内联汇编有 bug - 可打印的版本

+- WeHack BBS (https://bbs.wehack.space)
+-- 版块: 计算机技术 (https://bbs.wehack.space/forum-5.html)
+--- 版块: 程序设计讨论区 (https://bbs.wehack.space/forum-14.html)
+--- 主题: libgcrypt get_cpuid() 内联汇编有 bug (/thread-248.html)



libgcrypt get_cpuid() 内联汇编有 bug - vimacs - 04-23-2021

用gcc -m32 -march=i686 -Os编译之后,mpicalc --print-config报告的CPU特性不对,经过调试,最后发现是get_cpuid()出了问题,在这里记录一下。

代码:
#define NULL ((void*)0)

static void
get_cpuid(unsigned int in, unsigned int *eax, unsigned int *ebx,
          unsigned int *ecx, unsigned int *edx)
{
  unsigned int regs[4];

  asm volatile
    ("movl %%ebx, %%edi\n\t"     /* Save GOT register.  */
     "xorl %%ebx, %%ebx\n\t"
     "cpuid\n\t"
     "movl %%ebx, %1\n\t"
     "movl %%edi, %%ebx\n\t"     /* Restore GOT register. */
     : "=a" (regs[0]), "=g" (regs[1]), "=c" (regs[2]), "=d" (regs[3])
     : "0" (in), "2" (0), "3" (0)
     : "cc", "edi"
     );

  if (eax)
    *eax = regs[0];
  if (ebx)
    *ebx = regs[1];
  if (ecx)
    *ecx = regs[2];
  if (edx)
    *edx = regs[3];
}

unsigned int feature_detect()
{
  unsigned int fms, family, model, features, features2;
  unsigned int result = 0;

  get_cpuid(1, &fms, NULL, &features, &features2);
  family = ((fms & 0xf00) >> 8) + ((fms & 0xff00000) >> 20);
  model = ((fms & 0xf0) >> 4) + ((fms & 0xf0000) >> 12);

  if (family == 6) {
    if (model == 0x3a) {
      result |= 1;
    }
    get_cpuid(7, NULL, &features, NULL, NULL);
    // in my machine features=0x281
    if (features & 0x00000001) {
      result |= 2;
    }
  }
  return result;
}

int main()
{
  return feature_detect();
}

以上get_cpuid()来自libgcrypt 1.9.2. 在特定的编译参数下,会生成下面的代码:

代码:
mov    $0x7,%eax
mov    %ecx,%edx
mov    %ebx,%edi
xor    %ebx,%ebx
cpuid 
mov    %ebx,%ebx
mov    %edi,%ebx

结果就是执行了cpuid之后,本来代码是想把ebx的值存到另一个地方的,但是mov %ebx,%ebx使得ebx的值没被保存下来,于是后续代码想用ebx的值的时候就错了。产生这样的代码应该和寄存器分配有关。

由于libgcrypt的get_cpuid()在32位和64位的x86体系结构的实现代码不同,所以x86_64的系统中没发现有这样的问题。

compiler explorer 输出 https://gcc.godbolt.org/z/7e3scE5a7


RE: libgcrypt get_cpuid() 内联汇编有 bug - vimacs - 04-23-2021

查了一下GCC的内联汇编文档,应该是libgcrypt出错了。"=g"的意思是任意寄存器,寄存器分配可以把它分配到ebx上。

另外libgcrypt可以在configure时加上--disable-asm禁用CPU指令扩展加速,这样可以进一步缩小生成的库的体积。