【问题标题】:Arithmetic functions written in Inline asm in C program cause Segmentation Fault在 C 程序中用 Inline asm 编写的算术函数导致分段错误
【发布时间】:2019-12-26 12:32:57
【问题描述】:

得到以下函数来进行乘法、除法和求余数:

#define IS_EQUAL(var_, const_)          ((INT32)(var_) == (INT32)(const_))

// all these functions must use hexadecimals as constants according to syllabus
static inline INT32
math_qmultiply (INT32 num1, INT32 num2)
{
  INT32 retval = 0x0,
        count =  0x0;
  while (num2)
    {
      if (IS_EQUAL ((num2 & 0x1), 0x1))
        {
          retval += num1 << count;
        }
      count++;
      num2 = num2 >> 1;
    }
  return retval;
}

static inline INT32
math_qdivide (INT32 num1, INT32 num2)
{
  if (!num1)
    {
      return 0x0;
    }
  if (!num2)
    {
      return INT_MAX;
    }
  INT32 neg_result = FALSE;
  if (num1 < 0x0)
    {
      num1 = -num1;
      if (num2 < 0x0)
        {
          num2 = -num2;
        }
      else
        {
          neg_result = TRUE;
        }
    }
  else if (num2 < 0x0)
    {
      num2 = -num2;
      neg_result = TRUE;
    }
  INT32 quotient = 0x0;
  while (num1 >= num2)
    {
      num1 -= num2;
      quotient++;
    }
  if (neg_result)
    {
      quotient = -quotient;
    }
  return quotient;
}

static inline INT32
math_qmod (INT32 n1, INT32 n2)
{
  INT32 q = math_qdivide (n1, n2);
  INT32 p = math_qmultiply (q, n2);
  return n1 - p;
}

这三个函数用在下面的函数中:

static inline INT32
find_actual_x_for_char_idx (const INT32 char_idx)
{
  INT32 t1 = math_qdivide (char_idx, FRAME_COLS_COUNT);
  register INT32 divisor = math_qmultiply (FRAME_COLS_COUNT, t1);
  INT32 t2 = math_qmod(char_idx, FRAME_COLS_COUNT);
  register INT32 x = !t2 ? 0x0 : char_idx - divisor;
  return FRAME_TOP_LEFT_X + x;
}

这个函数产生正确的输出并且工作正常。

当我将先前函数的代码更改为这样时,问题就开始了:

INLINE_ATTRIB INT32
math_qmultiply (INT32 n1, INT32 n2)
{
  INT32 res = 0x0;
  asm volatile(
      "mov eax, %1;"
      "mov ebx, %2;"
      "mul ebx;"
      "mov %0, eax;"
      : "=r" (res)
      : "g" (n1),
      "g" (n2)
      );
  return res;
}

INLINE_ATTRIB INT32
math_qdivide (INT32 n1, INT32 n2)
{
  INT32 res = 0;
  asm volatile(
      "mov eax, %1;"
      "mov ebx, %2;"
      "xor edx, edx;"
      "div ebx;"
      "mov %0, eax;"
      : "=r" (res)
      : "g" (n1),
      "g" (n2)
      );
  return res;
}

INLINE_ATTRIB INT32
math_qmod (INT32 n1, INT32 n2)
{
  INT32 res = 0;
  asm volatile
    ("mov eax, %1;"
     "mov ebx, %2;"
     "xor edx, edx;"
     "div ebx;"
     "mov %0, edx;"
     : "=r" (res)
     : "g" (n1), "g" (n2)
     );
  return res;
}

现在我总是 SEGMENTATION FAULT,但当我分别检查这些函数时,它们会正确地进行除法、乘法和求余数。您能告诉我导致 SEGMENTATION FAULT 的原因吗?

【问题讨论】:

  • 您不应该在编译器背后更改寄存器。将它们列为已破坏或使用适当的约束(推荐)。也不需要为此使用内联汇编。
  • 可能是 ebx 的垃圾处理导致了这种情况。
  • @Jester 任务是使用 asm 来完成
  • @ronni_lao 当然可以,但正如 Jester 所说,您需要告诉 gcc 您修改了哪些寄存器。

标签: c gcc assembly x86 inline-assembly


【解决方案1】:

您的内联汇编破坏者在不告诉 GCC 已被修改的情况下注册。这可能会导致问题,尤其是当您在不告诉 GCC 的情况下破坏 EBX 时,因为它是生成的 32 位代码中的位置无关代码寄存器。

如果你修改一个寄存器,使用一个输出约束,输入/输出约束;或 clobber 告诉编译器寄存器已更改。您还可以通过告诉编译器将数据放在特定寄存器中来简化内联汇编。代码可能看起来像这样:

INLINE_ATTRIB INT32
math_qmultiply (INT32 n1, INT32 n2)
{
  INT32 res = 0x0;
  asm (
      "mul %2;"
      : "=a" (res)
      : "0" (n1),
        "r" (n2)
      : "edx"
      );
  return res;
}

INLINE_ATTRIB INT32
math_qdivide (INT32 n1, INT32 n2)
{
  INT32 res = 0;
  INT32 temp_edx = 0;

  asm (
      "div %3;"
      : "=a" (res),
        "+d" (temp_edx)
      : "0" (n1),
        "r" (n2)
      );
  return res;
}

INLINE_ATTRIB INT32
math_qmod (INT32 n1, INT32 n2)
{
  INT32 res = 0;

  asm
    (
     "div %2;"
     : "+d" (res),
       "+a" (n1)
     : "r" (n2)

     );
  return res;
}

【讨论】:

  • @ronni_lao:您可以使用asm("imul %1, %0" : "+r"(n1) : "g"(n2)) 来避免破坏 EDX,并允许内存或直接源操作数。只有div 才会被隐式的额外寄存器卡住。 (另外,如果 INT32 被签名,你应该使用cdq; idiv
猜你喜欢
  • 1970-01-01
  • 2011-09-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多