[英]C printf Segmentation fault (core dumped) while mixing with Assembly code
[英]Segmentation fault (core dumped) - C calling Assembly code for prime testing
我有一个从 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 代码中的注释部分是我想在汇编中编写的,但得到了我上面提到的错误。 根据我的研究,我正在尝试编写一个我无权访问的 memory 地址。 错误来自汇编代码,但我不知道确切位置,请问有什么可能的解决方案吗?
System V x86_64 调用约定要求在 function 调用期间保留寄存器rbx
、 rbp
rsp
r12
、 r13
、 r14
、 r15
。 您将ebx
寄存器用作rbx
的一部分,但不保留它。
直接的解决方案是将其保存在堆栈中:
bits 64
section .text
global isPrime
isPrime: push rbx
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:
pop rbx
mov eax, 1
ret
.checkAgain:
inc ecx
jmp .test
.prime: pop rbx
mov eax, 0
ret
注意:返回点有两个,所以每次返回前都要恢复rbx
。
也可以在每次迭代修改之前保存寄存器。 它以每次循环执行 push/pop 为代价节省了一条代码大小的指令。 div
很慢,但在某些现代 CPU 上这可能更慢。 (如何不做的例子):
...
.doif: push rbx ; don't do this, save/restore around the whole function
mov eax, [rdi]
mov edx, 0
mov ebx, [rsi]
div ebx
pop rbx
...
更好的解决方案是使用允许修改的临时寄存器而不是ebx
。
mov r8d, [rsi]
div r8d
或者甚至除以 memory 中的值:
div dword [rsi]
其他一些改进:
jle
指令是冗余的:CPU 继续执行正确的分支如果不执行jump if greater
。cdq
指令可用于将eax
中的值扩展到edx:eax
中。 注意:它的行为与mov edx,0
不同,因为它扩展了符号位。 但是我们收到指向有符号整数的指针,所以理论上我们应该使用cdq
和idiv
,而不是零扩展和div
。 (您不需要cdq
+ div
;如果您的输入为负数, div
会将其视为巨大的无符号,并在商不适合 32 位时出错。)test edx,edx
可以使用 edx 代替cmp edx,0
,这些指令短一个字节并且等于或更快。 那么jz/jnz
条件跳转更符合语义,但是和je
/ jne
一样的机器指令。xor eax, eax
将寄存器归零ecx
。 它节省了一条跳转指令。但是您的原始代码中存在逻辑错误:所有除法都是相同的值。 对于主要测试,您应该按顺序除以所有值。 (或者只是奇数,在检查一次偶数之后,这样你就可以将你的计数器增加 2。)
div ecx
所以最终代码是:
section .text
global isPrime
isPrime: mov edi, [rdi] ; load the pointed-to integers
mov esi, [rsi] ; better: pass by value in the first place
mov ecx, 2 ; starting divisor, and anything signed-less-than this is assumed prime.
; do {
.test: cmp ecx, esi
jg .prime ; if(ecx > endpoint) return yes
mov eax, edi
xor edx, edx ; zero-extend EAX into EDX:EAX, or use cdq/idiv for signed division
div ecx
inc ecx
test edx, edx
jnz .test ; }while( n % ecx == 0)
;; composite if we left the loop this way
mov eax, 1
ret
.prime: xor eax, eax ; eax=0
ret
您将 C 变量声明为 signed int
,但您在 asm 中使用 unsigned div
。 Unsigned 最有意义; 通常人们不会谈论负素数。
在 ESI/RSI 中传递第二个参数没有多大意义; 上限除数限制是质数测试算法的一部分,而不是调用者需要为其计算的东西。 mov esi, edi
; shr esi, 1
.
实际上,对于大数,您可以在sqrt(n)
而不是n/2
处更快地停止。 或者当n/divisor <= divisor
时,您实际上不必计算平方根。 请参阅执行此操作并递增 2 的循环的代码审查答案。
以防万一有人遇到类似问题并需要帮助:
section .text
global isPrime
isPrime: mov ecx, 2
.test: cmp ecx, [rsi]
jle .doif
jg .prime
.doif: mov eax, [rdi]
mov edx, 0
div ecx
cmp edx, 0
jg .checkAgain
mov eax, 1
ret
.checkAgain:
inc ecx
jmp .test
.prime: mov eax, 0
ret
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.