【发布时间】:2017-05-06 08:50:14
【问题描述】:
我正在尝试在 C 和 x86-64 汇编语言之间编写一个混合程序。该程序应使用 Collatz 函数计算介于 1 和给定参数 n 之间的数字的最大停止时间。 main 函数是用 C 语言编写的,在其 for 循环中调用了一个用汇编语言编写的外部函数。
但是,当我运行编译的混合程序时,我遇到了一个分段错误,它的值大于 2。使用 gdb 我发现错误是在我进行递归调用时出现的。这是我得到的错误:
Program received signal SIGSEGV, Segmentation fault.
0x00000000004006c3 in is_odd ()
C 代码:
#include <stdio.h>
#include <stdlib.h>
int noOfOp = 0;
extern int collatz(long long n);
// The main function. Main expects one parameter n.
// Then, it computes collatz(1), colllatz(2), ..., collataz(n) and finds the
// a number m, 1 <= m <= n with the maximum stopping time.
int main(int argc, char *argv[]){
if (argc != 2) {
printf("Parameter \"n\" is missing. \n");
return -1;
} else {
int max=0;
long long maxn=0;
int tmp=0;
long long n = atoll(argv[1]);
for (long long i=1 ; i<=n ; i++) {
tmp = collatz(i);
if (tmp > max) {
max = tmp;
maxn=i;
}
}
printf("The largest stopping time between 1 and %lld was %lld ", n,maxn);
printf("with the stopping time of %d. \n", max);
}
}
这是我编写的 x86-64 汇编代码。我希望这段代码能反映出我对汇编缺乏正确的理解。这是一项课堂作业,我们有四天的时间来完成这个新主题。通常我会阅读更多文档,但我只是没有时间。而且汇编语言很难。
.section .text
.global collatz
collatz:
pushq %rbp # save old base pointer
movq %rsp, %rbp # create new base pointer
subq $16, %rsp # local variable space
cmpq $1, %rdi # compare n to 1
je is_one # if n = 1, return noOfOp
incq noOfOp # else n > 1, then increment noOfOp
movq %rdi, %rdx # move n to register rdx
cqto # sign extend rdx:rax
movq $2, %rbx # move 2 to register rbx
idivq %rbx # n / 2 -- quotient is in rax, remainder in rdx
cmpq $1, %rdx # compare remainder to 1
je is_odd # if n is odd, jump to is_odd
jl is_even # else n is even, jump to is_even
leave # remake stack
ret # return
is_odd:
movq %rdi, %rdx # move n to register rdx
cqto # sign extend rdx:rax
movq $3, %rbx # move 3 to register rbx
imulq %rbx # n * 3 -- result is in rax:rdx
movq %rax, %rdi # move n to register rdi
incq %rdi # n = n + 1
call collatz # recursive call: collatz(3n+1) <---- this is where the segmentation fault seems to happen
leave # remake stack
ret # return
is_even:
movq %rax, %rdi # n = n / 2 (quotient from n/2 is still in rax)
call collatz # recursive call: collatz(n/2) <---- I seem to have gotten the same error here by commenting out most of the stuff in is_odd
leave # remake stack
ret # return
is_one:
movq noOfOp, %rax # set return value to the value of noOfOp variable
leave # remake stack
ret # return
感谢所有我能得到的帮助和建议。
【问题讨论】:
-
“通常我会阅读更多文档,但我只是没有时间” - 所以你将调试外包给我们?这不是堆栈溢出的工作原理!我们不是调试服务。见How to Ask。
-
使用调试器单步执行代码并验证退出条件。不知道为什么你希望它停止,
3n+1会增长到无穷大,即使你有时除以2。 PS:rbx是一个被调用者保存的寄存器。 PS #2:不赞成使用idiv除以2 :) PS #3:imul和3类似。 -
调用函数时你的C编译器的调用接口是什么?在调用
collatz()之前是否需要将参数值压入堆栈,或者它是否在寄存器rdi中传递?我要做的是用 C 语言编写collatz()函数,然后通过编译器在整个程序中运行它以生成汇编器输出,并查看编译器在递归调用中做了什么。 -
您使用
noOfOp就好像它是一个qword,但它在C 中是int。当我访问ASM 时,我从不相信ints...uint64_t是首选由我(“stdint.h”包括)。 -
你的代码有很多和slow-but-working hand-written asm vs. C++ question一样的性能问题。请参阅我的答案(以及其他几个)以了解如何使其快速。