C程序中用Inline asm编写的算术函数导致Segmentation Fault
Arithmetic functions written in Inline asm in C program cause Segmentation Fault
得到以下乘法、除法和求余函数:
#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 吗?
您的内联汇编破坏了注册,但没有告诉 GCC 它们已被修改。这可能会导致问题,尤其是当您在不告知 GCC 的情况下破坏 EBX 时,因为它是生成的 32 位代码中的位置独立代码寄存器。
如果修改寄存器使用输出约束,input/output约束;或 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;
}
得到以下乘法、除法和求余函数:
#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 吗?
您的内联汇编破坏了注册,但没有告诉 GCC 它们已被修改。这可能会导致问题,尤其是当您在不告知 GCC 的情况下破坏 EBX 时,因为它是生成的 32 位代码中的位置独立代码寄存器。
如果修改寄存器使用输出约束,input/output约束;或 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;
}