我有一个汇编语言代码,我从C调用,但我一直得到错误:**分段错误(核心转储)**我不知道原因。
; this is the assembly code
section .text
global isPrime
isPrime: mov ecx, 2
.test: cmp ecx, [rsi]
jle .doif
jg .prime
.doif: mov eax, [rdi]
mov edx, 0
mov ebx, [rsi]
div ebx
cmp edx, 0
jle .notPrime
jg .checkAgain
.notPrime:
mov eax, 1
ret
.checkAgain:
inc ecx
jmp .test
.prime: mov eax, 0
ret
C代码:
// C code
#include <stdio.h>
extern int isPrime(int *number, int *mValue);
int main() {
int limit, m, input = 0;
printf("Enter the limit of the prime numbers:");
input = scanf("%d", &limit);
while (input != 1) {
printf("Not a number!\n");
scanf("%*[^\n]");
printf("Enter the limit of the prime numbers:");
input = scanf("%d", &limit);
}
for (int i = 2; i <= limit; ++i) {
m = i / 2;
int flag = isPrime(&i, &m); //this is what I'm trying to implement
// for (int j = 2; j <= m; j++) {
// if (i % j == 0) {
// printf("Number %d is not prime\n", i);
// flag = 1;
// break;
// }
// }
printf("%d\n", flag);
if (flag == 0)
printf("Number %d is prime\n", i);
}
return 0;
}
错误:
Enter the limit of the prime numbers:10
0
0
Segmentation fault (core dumped)
C代码中的注解部分是我想用汇编语言编写的,但出现了上面提到的错误。根据我的研究,我试图编写一个我没有访问权限的内存地址。错误来自汇编代码,但我不知道具体在哪里,请问有什么可能的解决方法吗?
2条答案
按热度按时间ni65a41a1#
System V x86_64调用约定要求在函数调用期间保留寄存器
rbx
、rsp
、rbp
、r12
、r13
、r14
、r15
。您将ebx
寄存器用作rbx
的一部分,但不保留它。简单的解决方案是将其保留在堆栈上:
注意:有两个返回点,所以每次返回前都要恢复
rbx
。也可以在每次迭代修改之前保存寄存器。它可以节省一个指令的代码大小,代价是每次通过循环执行push/pop。
div
很慢,但在一些现代CPU上可能会更慢。(示例如何不这样做):更好的解决方案是使用允许修改的暂存寄存器,而不是
ebx
。或者甚至除以内存中的值:
另一些改进:
jle
指令是冗余的:如果不执行jump if greater
,CPU继续执行正确分支。cdq
指令可用于将eax
中的值扩展到edx:eax
。注:它的行为与mov edx,0
不同,因为它扩展符号位。但是我们接收指向有符号整数的指针,所以理论上我们应该使用cdq
和idiv
,而不是零扩展和div
。(您不需要cdq
+div
;如果您输入是负数,div
会将其视为无符号的巨型值,而fault when the quotient doesn't fit in 32 bits会将其视为无符号的巨型值。)test edx,edx
来代替cmp edx,0
,这些指令比and equal or faster短一个字节,那么jz/jnz
条件跳转在语义上更合适,但是机器指令与je
/jne
相同。xor eax, eax
ecx
。它节省了一条跳转指令。**但您的原始代码中存在逻辑错误:所有的除法都是以相同的值进行。**对于质数测试,您应该依序除以所有的值。(或者只除以奇数值,在检查一次偶数之后,这样您就可以将计数器递增2。)
最后的代码是:
您将C变量声明为有符号的
int
,但在asm中使用的是无符号的div
。通常人们不会谈论负素数。在ESI/RSI中传递第二个参数没有多大意义;上限除数限制是素数测试算法的一部分,而不是调用者应该需要为其计算的东西。
mov esi, edi
;shr esi, 1
.对于大的数字,你可以更快地停止,在
sqrt(n)
而不是n/2
。或者在n/divisor <= divisor
时,所以你实际上不必计算平方根。请参见代码评审答案,了解一个循环,它可以做到这一点,并以2为增量。km0tfn4u2#
以防有人遇到类似问题并需要帮助: